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