1 /*
2 Qalculate (library)
3
4 Copyright (C) 2003-2007, 2008, 2016 Hanna Knutsson (hanna.knutsson@protonmail.com)
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 */
11
12 #include "support.h"
13
14 #include "util.h"
15 #include <stdarg.h>
16 #include "Number.h"
17
18 #include <string.h>
19 #include <time.h>
20 #include <iconv.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <sstream>
24 #include <fstream>
25 #ifdef HAVE_LIBCURL
26 # include <curl/curl.h>
27 #endif
28 #ifdef HAVE_ICU
29 # include <unicode/ucasemap.h>
30 #endif
31 #include <fstream>
32 #ifdef _WIN32
33 # include <winsock2.h>
34 # include <windows.h>
35 # include <shlobj.h>
36 # include <direct.h>
37 # include <knownfolders.h>
38 # include <initguid.h>
39 # include <shlobj.h>
40 #else
41 # ifdef HAVE_PIPE2
42 # include <fcntl.h>
43 # endif
44 # include <utime.h>
45 # include <unistd.h>
46 # include <pwd.h>
47 #endif
48
49 using std::string;
50 using std::vector;
51 using std::ifstream;
52 using std::ofstream;
53
operator ()(const char * s1,const char * s2) const54 bool eqstr::operator()(const char *s1, const char *s2) const {
55 return strcmp(s1, s2) == 0;
56 }
57
58 #ifdef HAVE_ICU
59 UCaseMap *ucm = NULL;
60 #endif
61
sleep_ms(int milliseconds)62 void sleep_ms(int milliseconds) {
63 #ifdef _WIN32
64 Sleep(milliseconds);
65 #elif _POSIX_C_SOURCE >= 199309L
66 struct timespec ts;
67 ts.tv_sec = milliseconds / 1000;
68 ts.tv_nsec = (milliseconds % 1000) * 1000000;
69 nanosleep(&ts, NULL);
70 #else
71 usleep(milliseconds * 1000);
72 #endif
73 }
74
now(int & hour,int & min,int & sec)75 void now(int &hour, int &min, int &sec) {
76 time_t t = time(NULL);
77 struct tm *lt = localtime(&t);
78 hour = lt->tm_hour;
79 min = lt->tm_min;
80 sec = lt->tm_sec;
81 }
82
gsub(const string & pattern,const string & sub,string & str)83 string& gsub(const string &pattern, const string &sub, string &str) {
84 size_t i = str.find(pattern);
85 while(i != string::npos) {
86 str.replace(i, pattern.length(), sub);
87 i = str.find(pattern, i + sub.length());
88 }
89 return str;
90 }
gsub(const char * pattern,const char * sub,string & str)91 string& gsub(const char *pattern, const char *sub, string &str) {
92 size_t i = str.find(pattern);
93 while(i != string::npos) {
94 str.replace(i, strlen(pattern), string(sub));
95 i = str.find(pattern, i + strlen(sub));
96 }
97 return str;
98 }
99
remove_blanks(string & str)100 string& remove_blanks(string &str) {
101 size_t i = str.find_first_of(SPACES, 0);
102 while(i != string::npos) {
103 str.erase(i, 1);
104 i = str.find_first_of(SPACES, i);
105 }
106 return str;
107 }
108
remove_duplicate_blanks(string & str)109 string& remove_duplicate_blanks(string &str) {
110 size_t i = str.find_first_of(SPACES, 0);
111 while(i != string::npos) {
112 if(i != 0 && is_in(SPACES, str[i - 1])) {
113 str.erase(i, 1);
114 } else {
115 str[i] = ' ';
116 i++;
117 }
118 i = str.find_first_of(SPACES, i);
119 }
120 return str;
121 }
122
remove_blank_ends(string & str)123 string& remove_blank_ends(string &str) {
124 size_t i = str.find_first_not_of(SPACES);
125 size_t i2 = str.find_last_not_of(SPACES);
126 if(i != string::npos && i2 != string::npos) {
127 if(i > 0 || i2 < str.length() - 1) {
128 str = str.substr(i, i2 - i + 1);
129 }
130 } else {
131 str.resize(0);
132 }
133 return str;
134 }
remove_parenthesis(string & str)135 string& remove_parenthesis(string &str) {
136 if(str[0] == LEFT_PARENTHESIS_CH && str[str.length() - 1] == RIGHT_PARENTHESIS_CH) {
137 str = str.substr(1, str.length() - 2);
138 return remove_parenthesis(str);
139 }
140 return str;
141 }
142
d2s(double value,int precision)143 string d2s(double value, int precision) {
144 // qgcvt(value, precision, buffer);
145 char buffer[precision + 21];
146 snprintf(buffer, precision + 21, "%.*G", precision, value);
147 string stmp = buffer;
148 // gsub("e", "E", stmp);
149 return stmp;
150 }
151
p2s(void * o)152 string p2s(void *o) {
153 char buffer[21];
154 snprintf(buffer, 21, "%p", o);
155 string stmp = buffer;
156 return stmp;
157 }
i2s(long int value)158 string i2s(long int value) {
159 char buffer[21];
160 snprintf(buffer, 21, "%li", value);
161 string stmp = buffer;
162 return stmp;
163 }
u2s(unsigned long int value)164 string u2s(unsigned long int value) {
165 char buffer[21];
166 snprintf(buffer, 21, "%lu", value);
167 string stmp = buffer;
168 return stmp;
169 }
b2yn(bool b,bool capital)170 const char *b2yn(bool b, bool capital) {
171 if(capital) {
172 if(b) return _("Yes");
173 return _("No");
174 }
175 if(b) return _("yes");
176 return _("no");
177 }
b2tf(bool b,bool capital)178 const char *b2tf(bool b, bool capital) {
179 if(capital) {
180 if(b) return _("True");
181 return _("False");
182 }
183 if(b) return _("true");
184 return _("false");
185 }
b2oo(bool b,bool capital)186 const char *b2oo(bool b, bool capital) {
187 if(capital) {
188 if(b) return _("On");
189 return _("Off");
190 }
191 if(b) return _("on");
192 return _("off");
193 }
s2i(const string & str)194 long int s2i(const string& str) {
195 return strtol(str.c_str(), NULL, 10);
196 }
s2i(const char * str)197 long int s2i(const char *str) {
198 return strtol(str, NULL, 10);
199 }
s2p(const string & str)200 void *s2p(const string& str) {
201 void *p;
202 sscanf(str.c_str(), "%p", &p);
203 return p;
204 }
s2p(const char * str)205 void *s2p(const char *str) {
206 void *p;
207 sscanf(str, "%p", &p);
208 return p;
209 }
210
find_ending_bracket(const string & str,size_t start,int * missing)211 size_t find_ending_bracket(const string &str, size_t start, int *missing) {
212 int i_l = 1;
213 while(true) {
214 start = str.find_first_of(LEFT_PARENTHESIS RIGHT_PARENTHESIS, start);
215 if(start == string::npos) {
216 if(missing) *missing = i_l;
217 return string::npos;
218 }
219 if(str[start] == LEFT_PARENTHESIS_CH) {
220 i_l++;
221 } else {
222 i_l--;
223 if(!i_l) {
224 if(missing) *missing = i_l;
225 return start;
226 }
227 }
228 start++;
229 }
230 }
231
op2ch(MathOperation op)232 char op2ch(MathOperation op) {
233 switch(op) {
234 case OPERATION_ADD: return PLUS_CH;
235 case OPERATION_SUBTRACT: return MINUS_CH;
236 case OPERATION_MULTIPLY: return MULTIPLICATION_CH;
237 case OPERATION_DIVIDE: return DIVISION_CH;
238 case OPERATION_RAISE: return POWER_CH;
239 case OPERATION_EXP10: return EXP_CH;
240 default: return ' ';
241 }
242 }
243
wrap_p(string & str)244 string& wrap_p(string &str) {
245 str.insert(str.begin(), 1, LEFT_PARENTHESIS_CH);
246 str += RIGHT_PARENTHESIS_CH;
247 return str;
248 }
249
is_in(const char * str,char c)250 bool is_in(const char *str, char c) {
251 for(size_t i = 0; i < strlen(str); i++) {
252 if(str[i] == c)
253 return true;
254 }
255 return false;
256 }
is_not_in(const char * str,char c)257 bool is_not_in(const char *str, char c) {
258 for(size_t i = 0; i < strlen(str); i++) {
259 if(str[i] == c)
260 return false;
261 }
262 return true;
263 }
is_in(const string & str,char c)264 bool is_in(const string &str, char c) {
265 for(size_t i = 0; i < str.length(); i++) {
266 if(str[i] == c)
267 return true;
268 }
269 return false;
270 }
is_not_in(const string & str,char c)271 bool is_not_in(const string &str, char c) {
272 for(size_t i = 0; i < str.length(); i++) {
273 if(str[i] == c)
274 return false;
275 }
276 return true;
277 }
278
sign_place(string * str,size_t start)279 int sign_place(string *str, size_t start) {
280 size_t i = str->find_first_of(OPERATORS, start);
281 if(i != string::npos)
282 return i;
283 else
284 return -1;
285 }
286
gcd(int i1,int i2)287 int gcd(int i1, int i2) {
288 if(i1 < 0) i1 = -i1;
289 if(i2 < 0) i2 = -i2;
290 if(i1 == i2) return i2;
291 int i3;
292 if(i2 > i1) {
293 i3 = i2;
294 i2 = i1;
295 i1 = i3;
296 }
297 while((i3 = i1 % i2) != 0) {
298 i1 = i2;
299 i2 = i3;
300 }
301 return i2;
302 }
303
unicode_length(const string & str)304 size_t unicode_length(const string &str) {
305 size_t l = str.length(), l2 = 0;
306 for(size_t i = 0; i < l; i++) {
307 if(str[i] > 0 || (unsigned char) str[i] >= 0xC0) {
308 l2++;
309 }
310 }
311 return l2;
312 }
unicode_length(const char * str)313 size_t unicode_length(const char *str) {
314 size_t l = strlen(str), l2 = 0;
315 for(size_t i = 0; i < l; i++) {
316 if(str[i] > 0 || (unsigned char) str[i] >= 0xC0) {
317 l2++;
318 }
319 }
320 return l2;
321 }
322
text_length_is_one(const string & str)323 bool text_length_is_one(const string &str) {
324 if(str.empty()) return false;
325 if(str.length() == 1) return true;
326 if(str[0] >= 0) return false;
327 for(size_t i = 1; i < str.length(); i++) {
328 if(str[i] > 0 || (unsigned char) str[i] >= 0xC0) {
329 return false;
330 }
331 }
332 return true;
333 }
334
equalsIgnoreCase(const string & str1,const string & str2)335 bool equalsIgnoreCase(const string &str1, const string &str2) {
336 if(str1.empty() || str2.empty()) return false;
337 for(size_t i1 = 0, i2 = 0; i1 < str1.length() || i2 < str2.length(); i1++, i2++) {
338 if(i1 >= str1.length() || i2 >= str2.length()) return false;
339 if((str1[i1] < 0 && i1 + 1 < str1.length()) || (str2[i2] < 0 && i2 + 1 < str2.length())) {
340 size_t iu1 = 1, iu2 = 1;
341 if(str1[i1] < 0) {
342 while(iu1 + i1 < str1.length() && str1[i1 + iu1] < 0) {
343 iu1++;
344 }
345 }
346 if(str2[i2] < 0) {
347 while(iu2 + i2 < str2.length() && str2[i2 + iu2] < 0) {
348 iu2++;
349 }
350 }
351 bool isequal = (iu1 == iu2);
352 if(isequal) {
353 for(size_t i = 0; i < iu1; i++) {
354 if(str1[i1 + i] != str2[i2 + i]) {
355 isequal = false;
356 break;
357 }
358 }
359 }
360 if(!isequal) {
361 char *gstr1 = utf8_strdown(str1.c_str() + (sizeof(char) * i1));
362 char *gstr2 = utf8_strdown(str2.c_str() + (sizeof(char) * i2));
363 if(!gstr1 || !gstr2) return false;
364 bool b = strcmp(gstr1, gstr2) == 0;
365 free(gstr1);
366 free(gstr2);
367 return b;
368 }
369 i1 += iu1 - 1;
370 i2 += iu2 - 1;
371 } else if(str1[i1] != str2[i2] && !((str1[i1] >= 'a' && str1[i1] <= 'z') && str1[i1] - 32 == str2[i2]) && !((str1[i1] <= 'Z' && str1[i1] >= 'A') && str1[i1] + 32 == str2[i2])) {
372 return false;
373 }
374 }
375 return true;
376 }
377
equalsIgnoreCase(const string & str1,const char * str2)378 bool equalsIgnoreCase(const string &str1, const char *str2) {
379 if(str1.empty() || strlen(str2) == 0) return false;
380 for(size_t i1 = 0, i2 = 0; i1 < str1.length() || i2 < strlen(str2); i1++, i2++) {
381 if(i1 >= str1.length() || i2 >= strlen(str2)) return false;
382 if((str1[i1] < 0 && i1 + 1 < str1.length()) || (str2[i2] < 0 && i2 + 1 < strlen(str2))) {
383 size_t iu1 = 1, iu2 = 1;
384 if(str1[i1] < 0) {
385 while(iu1 + i1 < str1.length() && str1[i1 + iu1] < 0) {
386 iu1++;
387 }
388 }
389 if(str2[i2] < 0) {
390 while(iu2 + i2 < strlen(str2) && str2[i2 + iu2] < 0) {
391 iu2++;
392 }
393 }
394 bool isequal = (iu1 == iu2);
395 if(isequal) {
396 for(size_t i = 0; i < iu1; i++) {
397 if(str1[i1 + i] != str2[i2 + i]) {
398 isequal = false;
399 break;
400 }
401 }
402 }
403 if(!isequal) {
404 char *gstr1 = utf8_strdown(str1.c_str() + (sizeof(char) * i1));
405 char *gstr2 = utf8_strdown(str2 + (sizeof(char) * i2));
406 if(!gstr1 || !gstr2) return false;
407 bool b = strcmp(gstr1, gstr2) == 0;
408 free(gstr1);
409 free(gstr2);
410 return b;
411 }
412 i1 += iu1 - 1;
413 i2 += iu2 - 1;
414 } else if(str1[i1] != str2[i2] && !((str1[i1] >= 'a' && str1[i1] <= 'z') && str1[i1] - 32 == str2[i2]) && !((str1[i1] <= 'Z' && str1[i1] >= 'A') && str1[i1] + 32 == str2[i2])) {
415 return false;
416 }
417 }
418 return true;
419 }
420
parse_qalculate_version(string qalculate_version,int * qalculate_version_numbers)421 void parse_qalculate_version(string qalculate_version, int *qalculate_version_numbers) {
422 for(size_t i = 0; i < 3; i++) {
423 size_t dot_i = qalculate_version.find(".");
424 if(dot_i == string::npos) {
425 qalculate_version_numbers[i] = s2i(qalculate_version);
426 break;
427 }
428 qalculate_version_numbers[i] = s2i(qalculate_version.substr(0, dot_i));
429 qalculate_version = qalculate_version.substr(dot_i + 1, qalculate_version.length() - (dot_i + 1));
430 }
431 }
432
433 #ifdef _WIN32
utf8_encode(const std::wstring & wstr)434 string utf8_encode(const std::wstring &wstr) {
435 if(wstr.empty()) return string();
436 int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int) wstr.size(), NULL, 0, NULL, NULL);
437 std::string strTo(size_needed, 0);
438 WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int) wstr.size(), &strTo[0], size_needed, NULL, NULL);
439 return strTo;
440 }
441 #endif
442
getOldLocalDir()443 string getOldLocalDir() {
444 #ifdef _WIN32
445 char path[MAX_PATH];
446 SHGetFolderPathA(NULL, CSIDL_PROFILE, NULL, 0, path);
447 string str = path;
448 return str + "\\Qalculate";
449 #else
450 const char *homedir;
451 if ((homedir = getenv("HOME")) == NULL) {
452 homedir = getpwuid(getuid())->pw_dir;
453 }
454 return string(homedir) + "/.qalculate";
455 #endif
456 }
getLocalDir()457 string getLocalDir() {
458 #ifdef _WIN32
459 # ifdef WIN_PORTABLE
460 char exepath[MAX_PATH];
461 GetModuleFileName(NULL, exepath, MAX_PATH);
462 string str(exepath);
463 str.resize(str.find_last_of('\\'));
464 _mkdir(str.c_str());
465 return str + "\\user";
466 # else
467 char path[MAX_PATH];
468 SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, path);
469 string str = path;
470 return str + "\\Qalculate";
471 # endif
472 #else
473 const char *homedir;
474 if((homedir = getenv("XDG_CONFIG_HOME")) == NULL) {
475 return string(getpwuid(getuid())->pw_dir) + "/.config/qalculate";
476 }
477 return string(homedir) + "/qalculate";
478 #endif
479 }
getLocalDataDir()480 string getLocalDataDir() {
481 #ifdef _WIN32
482 # ifdef WIN_PORTABLE
483 char exepath[MAX_PATH];
484 GetModuleFileName(NULL, exepath, MAX_PATH);
485 string str(exepath);
486 str.resize(str.find_last_of('\\'));
487 _mkdir(str.c_str());
488 return str + "\\user";
489 # else
490 char path[MAX_PATH];
491 SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, path);
492 string str = path;
493 return str + "\\Qalculate";
494 # endif
495 #else
496 const char *homedir;
497 if((homedir = getenv("XDG_DATA_HOME")) == NULL) {
498 return string(getpwuid(getuid())->pw_dir) + "/.local/share/qalculate";
499 }
500 return string(homedir) + "/qalculate";
501 #endif
502 }
getLocalTmpDir()503 string getLocalTmpDir() {
504 #ifdef _WIN32
505 # ifdef WIN_PORTABLE
506 char exepath[MAX_PATH];
507 GetModuleFileName(NULL, exepath, MAX_PATH);
508 string str(exepath);
509 str.resize(str.find_last_of('\\'));
510 _mkdir(str.c_str());
511 return str + "\\tmp";
512 # else
513 char path[MAX_PATH];
514 SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, path);
515 string str = path;
516 str += "\\cache";
517 _mkdir(str.c_str());
518 return str + "\\Qalculate";
519 # endif
520 #else
521 const char *homedir;
522 if((homedir = getenv("XDG_CACHE_HOME")) == NULL) {
523 return string(getpwuid(getuid())->pw_dir) + "/.cache/qalculate";
524 }
525 return string(homedir) + "/qalculate";
526 #endif
527 }
528
move_file(const char * from_file,const char * to_file)529 bool move_file(const char *from_file, const char *to_file) {
530 #ifdef _WIN32
531 return MoveFile(from_file, to_file) != 0;
532 #else
533 ifstream source(from_file);
534 if(source.fail()) {
535 source.close();
536 return false;
537 }
538
539 ofstream dest(to_file);
540 if(dest.fail()) {
541 source.close();
542 dest.close();
543 return false;
544 }
545
546 dest << source.rdbuf();
547
548 source.close();
549 dest.close();
550
551 struct stat stats_from;
552 if(stat(from_file, &stats_from) == 0) {
553 struct utimbuf to_times;
554 to_times.actime = stats_from.st_atime;
555 to_times.modtime = stats_from.st_mtime;
556 utime(to_file, &to_times);
557 }
558
559 remove(from_file);
560
561 return true;
562 #endif
563 }
564
getPackageDataDir()565 string getPackageDataDir() {
566 #ifndef WIN32
567 return PACKAGE_DATA_DIR;
568 #else
569 char exepath[MAX_PATH];
570 GetModuleFileName(NULL, exepath, MAX_PATH);
571 string datadir(exepath);
572 datadir.resize(datadir.find_last_of('\\'));
573 if (datadir.substr(datadir.length() - 4) == "\\bin") {
574 datadir.resize(datadir.find_last_of('\\'));
575 datadir += "\\share";
576 } else if(datadir.substr(datadir.length() - 6) == "\\.libs") {
577 datadir.resize(datadir.find_last_of('\\'));
578 datadir.resize(datadir.find_last_of('\\'));
579 }
580 return datadir;
581 #endif
582 }
583
getGlobalDefinitionsDir()584 string getGlobalDefinitionsDir() {
585 #ifdef COMPILED_DEFINITIONS
586 return "";
587 #else
588 # ifndef WIN32
589 return string(PACKAGE_DATA_DIR) + "/qalculate";
590 # else
591 char exepath[MAX_PATH];
592 GetModuleFileName(NULL, exepath, MAX_PATH);
593 string datadir(exepath);
594 bool is_qalc = datadir.substr(datadir.length() - 8) == "qalc.exe";
595 datadir.resize(datadir.find_last_of('\\'));
596 if(datadir.substr(datadir.length() - 4) == "\\bin") {
597 datadir.resize(datadir.find_last_of('\\'));
598 datadir += "\\share\\qalculate";
599 return datadir;
600 } else if(datadir.substr(datadir.length() - 6) == "\\.libs") {
601 datadir.resize(datadir.find_last_of('\\'));
602 datadir.resize(datadir.find_last_of('\\'));
603 if(!is_qalc) {
604 datadir.resize(datadir.find_last_of('\\'));
605 datadir += "\\libqalculate";
606 if(!dirExists(datadir)) {
607 datadir += "-";
608 datadir += VERSION;
609 }
610 }
611 return datadir + "\\data";
612 }
613 return datadir + "\\definitions";
614 # endif
615 #endif
616 }
617
getPackageLocaleDir()618 string getPackageLocaleDir() {
619 #ifndef WIN32
620 return PACKAGE_LOCALE_DIR;
621 #else
622 char exepath[MAX_PATH];
623 GetModuleFileName(NULL, exepath, MAX_PATH);
624 string datadir(exepath);
625 datadir.resize(datadir.find_last_of('\\'));
626 if(datadir.substr(datadir.length() - 4) == "\\bin" || datadir.substr(datadir.length() - 6) == "\\.libs") {
627 datadir.resize(datadir.find_last_of('\\'));
628 return datadir + "\\share\\locale";
629 }
630 return datadir + "\\locale";
631 #endif
632 }
633
buildPath(string dir,string filename)634 string buildPath(string dir, string filename) {
635 #ifdef WIN32
636 return dir + '\\' + filename;
637 #else
638 return dir + '/' + filename;
639 #endif
640 }
buildPath(string dir1,string dir2,string filename)641 string buildPath(string dir1, string dir2, string filename) {
642 #ifdef WIN32
643 return dir1 + '\\' + dir2 + '\\' + filename;
644 #else
645 return dir1 + '/' + dir2 + '/' + filename;
646 #endif
647 }
buildPath(string dir1,string dir2,string dir3,string filename)648 string buildPath(string dir1, string dir2, string dir3, string filename) {
649 #ifdef WIN32
650 return dir1 + '\\' + dir2 + '\\' + dir3 + '\\' + filename;
651 #else
652 return dir1 + '/' + dir2 + '/' + dir3 + '/' + filename;
653 #endif
654 }
655
dirExists(string dirpath)656 bool dirExists(string dirpath) {
657 return fileExists(dirpath);
658 }
fileExists(string filepath)659 bool fileExists(string filepath) {
660 #ifdef WIN32
661 struct _stat info;
662 return _stat(filepath.c_str(), &info) == 0;
663 #else
664 struct stat info;
665 return stat(filepath.c_str(), &info) == 0;
666 #endif
667 }
makeDir(string dirpath)668 bool makeDir(string dirpath) {
669 #ifdef WIN32
670 return _mkdir(dirpath.c_str()) == 0;
671 #else
672 return mkdir(dirpath.c_str(), S_IRWXU) == 0;
673 #endif
674 }
675
recursiveMakeDir(string dirpath)676 bool recursiveMakeDir(string dirpath) {
677 #ifdef WIN32
678 return _mkdir(dirpath.c_str()) == 0;
679 #else
680 char tmp[256];
681 char *p = NULL;
682 size_t len;
683 snprintf(tmp, sizeof(tmp), "%s", dirpath.c_str());
684 len = strlen(tmp);
685 if(tmp[len - 1] == '/') tmp[len - 1] = 0;
686 for(p = tmp + 1; *p; p++) {
687 if(*p == '/') {
688 *p = 0;
689 if(!dirExists(tmp)) mkdir(tmp, S_IRWXU);
690 *p = '/';
691 }
692 }
693 return mkdir(tmp, S_IRWXU) == 0;
694 #endif
695 }
696
removeDir(string dirpath)697 bool removeDir(string dirpath) {
698 #ifdef WIN32
699 return _rmdir(dirpath.c_str()) == 0;
700 #else
701 return rmdir(dirpath.c_str()) == 0;
702 #endif
703 }
704
locale_from_utf8(const char * str)705 char *locale_from_utf8(const char *str) {
706 iconv_t conv = iconv_open("", "UTF-8");
707 if(conv == (iconv_t) -1) return NULL;
708 size_t inlength = strlen(str);
709 size_t outlength = inlength * 4;
710 char *dest, *buffer;
711 buffer = dest = (char*) malloc((outlength + 4) * sizeof(char));
712 if(!buffer) return NULL;
713 size_t err = iconv(conv, (ICONV_CONST char **) &str, &inlength, &buffer, &outlength);
714 if(err != (size_t) -1) err = iconv(conv, NULL, &inlength, &buffer, &outlength);
715 iconv_close(conv);
716 memset(buffer, 0, 4);
717 if(err == (size_t) -1) {free(dest); return NULL;}
718 return dest;
719 }
locale_to_utf8(const char * str)720 char *locale_to_utf8(const char *str) {
721 iconv_t conv = iconv_open("UTF-8", "");
722 if(conv == (iconv_t) -1) return NULL;
723 size_t inlength = strlen(str);
724 size_t outlength = inlength * 4;
725 char *dest, *buffer;
726 buffer = dest = (char*) malloc((outlength + 4) * sizeof(char));
727 if(!buffer) return NULL;
728 size_t err = iconv(conv, (ICONV_CONST char**) &str, &inlength, &buffer, &outlength);
729 if(err != (size_t) -1) err = iconv(conv, NULL, &inlength, &buffer, &outlength);
730 iconv_close(conv);
731 memset(buffer, 0, 4 * sizeof(char));
732 if(err == (size_t) -1) {free(dest); return NULL;}
733 return dest;
734 }
utf8_strdown(const char * str,int l)735 char *utf8_strdown(const char *str, int l) {
736 #ifdef HAVE_ICU
737 if(!ucm) return NULL;
738 UErrorCode err = U_ZERO_ERROR;
739 size_t inlength = l <= 0 ? strlen(str) : (size_t) l;
740 size_t outlength = inlength + 4;
741 char *buffer = (char*) malloc(outlength * sizeof(char));
742 if(!buffer) return NULL;
743 int32_t length = ucasemap_utf8ToLower(ucm, buffer, outlength, str, inlength, &err);
744 if(U_SUCCESS(err)) {
745 //basic accent removal
746 if(strcmp(buffer, "á") == 0 || strcmp(buffer, "à") == 0) {buffer[0] = 'a'; buffer[1] = '\0';}
747 else if(strcmp(buffer, "é") == 0 || strcmp(buffer, "è") == 0) {buffer[0] = 'e'; buffer[1] = '\0';}
748 else if(strcmp(buffer, "í") == 0 || strcmp(buffer, "ì") == 0) {buffer[0] = 'i'; buffer[1] = '\0';}
749 else if(strcmp(buffer, "ú") == 0 || strcmp(buffer, "ù") == 0) {buffer[0] = 'u'; buffer[1] = '\0';}
750 else if(strcmp(buffer, "ó") == 0 || strcmp(buffer, "ò") == 0 || strcmp(buffer, "õ") == 0) {buffer[0] = 'o'; buffer[1] = '\0';}
751 else if(strcmp(buffer, "ñ") == 0) {buffer[0] = 'n'; buffer[1] = '\0';}
752 return buffer;
753 } else if(err == U_BUFFER_OVERFLOW_ERROR) {
754 outlength = length + 4;
755 char *buffer_realloc = (char*) realloc(buffer, outlength * sizeof(char));
756 if(buffer_realloc) buffer = buffer_realloc;
757 else {free(buffer); return NULL;}
758 err = U_ZERO_ERROR;
759 ucasemap_utf8ToLower(ucm, buffer, outlength, str, inlength, &err);
760 if(U_SUCCESS(err)) {
761 return buffer;
762 }
763 }
764 return NULL;
765 #else
766 return NULL;
767 #endif
768 }
769
770 extern size_t write_data(void *ptr, size_t size, size_t nmemb, string *sbuffer);
checkAvailableVersion(const char * version_id,const char * current_version,string * available_version,int timeout)771 int checkAvailableVersion(const char *version_id, const char *current_version, string *available_version, int timeout) {
772 #ifdef HAVE_LIBCURL
773 string sbuffer;
774 char error_buffer[CURL_ERROR_SIZE];
775 CURL *curl;
776 CURLcode res;
777 long int file_time;
778 curl_global_init(CURL_GLOBAL_DEFAULT);
779 curl = curl_easy_init();
780 if(!curl) {return -1;}
781 curl_easy_setopt(curl, CURLOPT_URL, "https://qalculate.github.io/CURRENT_VERSIONS");
782 curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
783 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
784 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &sbuffer);
785 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer);
786 curl_easy_setopt(curl, CURLOPT_FILETIME, &file_time);
787 #ifdef _WIN32
788 char exepath[MAX_PATH];
789 GetModuleFileName(NULL, exepath, MAX_PATH);
790 string datadir(exepath);
791 datadir.resize(datadir.find_last_of('\\'));
792 if(datadir.substr(datadir.length() - 4) != "\\bin" && datadir.substr(datadir.length() - 6) != "\\.libs") {
793 string cainfo = buildPath(datadir, "ssl", "certs", "ca-bundle.crt");
794 gsub("\\", "/", cainfo);
795 curl_easy_setopt(curl, CURLOPT_CAINFO, cainfo.c_str());
796 }
797 #endif
798 res = curl_easy_perform(curl);
799 curl_easy_cleanup(curl);
800 curl_global_cleanup();
801 if(res != CURLE_OK || sbuffer.empty()) {return -1;}
802 size_t i = sbuffer.find(version_id);
803 if(i == string::npos) return -1;
804 size_t i2 = sbuffer.find('\n', i + strlen(version_id) + 1);
805 string s_version;
806 if(i2 == string::npos) s_version = sbuffer.substr(i + strlen(version_id) + 1);
807 else s_version = sbuffer.substr(i + strlen(version_id) + 1, i2 - (i + strlen(version_id) + 1));
808 remove_blank_ends(s_version);
809 if(s_version.empty()) return -1;
810 if(available_version) *available_version = s_version;
811 if(s_version != current_version) {
812 std::vector<int> version_parts_old, version_parts_new;
813
814 string s_old_version = current_version;
815 while((i = s_old_version.find('.', 0)) != string::npos) {
816 version_parts_old.push_back(s2i(s_old_version.substr(0, i)));
817 s_old_version = s_old_version.substr(i + 1);
818 }
819 i = s_old_version.find_first_not_of("0123456789", 1);
820 if(i != string::npos) {
821 version_parts_old.push_back(s2i(s_old_version.substr(0, i)));
822 s_old_version = s_old_version.substr(i + 1);
823 }
824 version_parts_old.push_back(s2i(s_old_version));
825
826 while((i = s_version.find('.', 0)) != string::npos) {
827 version_parts_new.push_back(s2i(s_version.substr(0, i)));
828 s_version = s_version.substr(i + 1);
829 }
830 i = s_version.find_first_not_of("0123456789", 1);
831 if(i != string::npos) {
832 version_parts_new.push_back(s2i(s_version.substr(0, i)));
833 s_version = s_version.substr(i + 1);
834 }
835 version_parts_new.push_back(s2i(s_version));
836
837 for(i = 0; i < version_parts_new.size(); i++) {
838 if(i == version_parts_old.size() || version_parts_new[i] > version_parts_old[i]) return true;
839 else if(version_parts_new[i] < version_parts_old[i]) return false;
840 }
841
842 }
843
844 return 0;
845 #else
846 return -1;
847 #endif
848 }
849
850 #ifdef _WIN32
851
Thread()852 Thread::Thread() : running(false), m_thread(NULL), m_threadReadyEvent(NULL), m_threadID(0) {
853 m_threadReadyEvent = CreateEvent(NULL, false, false, NULL);
854 }
855
~Thread()856 Thread::~Thread() {
857 cancel();
858 CloseHandle(m_threadReadyEvent);
859 }
860
enableAsynchronousCancel()861 void Thread::enableAsynchronousCancel() {}
862
doRun(void * data)863 DWORD WINAPI Thread::doRun(void *data) {
864 // create thread message queue
865 MSG msg;
866 PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
867
868 Thread *thread = (Thread *) data;
869 SetEvent(thread->m_threadReadyEvent);
870 thread->run();
871 thread->running = false;
872 return 0;
873 }
874
start()875 bool Thread::start() {
876 m_thread = CreateThread(NULL, 0, Thread::doRun, this, 0, &m_threadID);
877 running = (m_thread != NULL);
878 if(!running) return false;
879 WaitForSingleObject(m_threadReadyEvent, INFINITE);
880 return running;
881 }
882
cancel()883 bool Thread::cancel() {
884 if(!running) return true;
885 // FIXME: this is dangerous
886 int ret = TerminateThread(m_thread, 0);
887 if(ret == 0) return false;
888 CloseHandle(m_thread);
889 m_thread = NULL;
890 m_threadID = 0;
891 running = false;
892 return true;
893 }
894
895 #else
896
Thread()897 Thread::Thread() : running(false), m_pipe_r(NULL), m_pipe_w(NULL) {
898 pthread_attr_init(&m_thread_attr);
899 int pipe_wr[] = {0, 0};
900 #ifdef HAVE_PIPE2
901 if(pipe2(pipe_wr, O_CLOEXEC) == 0) {
902 #else
903 if(pipe(pipe_wr) == 0) {
904 #endif
905 m_pipe_r = fdopen(pipe_wr[0], "r");
906 m_pipe_w = fdopen(pipe_wr[1], "w");
907 }
908 }
909
910 Thread::~Thread() {
911 cancel();
912 fclose(m_pipe_r);
913 fclose(m_pipe_w);
914 pthread_attr_destroy(&m_thread_attr);
915 }
916
917 void Thread::doCleanup(void *data) {
918 Thread *thread = (Thread *) data;
919 thread->running = false;
920 }
921
922 void Thread::enableAsynchronousCancel() {
923 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
924 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
925 }
926
927 void *Thread::doRun(void *data) {
928
929 pthread_cleanup_push(&Thread::doCleanup, data);
930
931 Thread *thread = (Thread *) data;
932 thread->run();
933
934 pthread_cleanup_pop(1);
935 return NULL;
936
937 }
938
939 bool Thread::start() {
940 if(running) return true;
941 running = pthread_create(&m_thread, &m_thread_attr, &Thread::doRun, this) == 0;
942 return running;
943 }
944
945 bool Thread::cancel() {
946 if(!running) return true;
947 running = pthread_cancel(m_thread) != 0;
948 return !running;
949 }
950
951
952 #endif
953