1 /*
2     Qalculate
3 
4     Copyright (C) 2003-2007, 2008, 2016-2021  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 "Calculator.h"
15 #include "BuiltinFunctions.h"
16 #include "util.h"
17 #include "MathStructure.h"
18 #include "Unit.h"
19 #include "Variable.h"
20 #include "Function.h"
21 #include "DataSet.h"
22 #include "ExpressionItem.h"
23 #include "Prefix.h"
24 #include "Number.h"
25 #include "QalculateDateTime.h"
26 
27 #include <locale.h>
28 #include <libxml/xmlmemory.h>
29 #include <libxml/parser.h>
30 #ifdef COMPILED_DEFINITIONS
31 #	include "definitions.h"
32 #endif
33 #include <unistd.h>
34 #include <time.h>
35 #include <utime.h>
36 #include <errno.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <dirent.h>
40 #include <queue>
41 #include <iostream>
42 #include <sstream>
43 #include <fstream>
44 
45 #include "MathStructure-support.h"
46 
47 #ifdef HAVE_LIBCURL
48 #	include <curl/curl.h>
49 #endif
50 
51 using std::string;
52 using std::cout;
53 using std::vector;
54 using std::ostream;
55 using std::ofstream;
56 using std::endl;
57 using std::ios;
58 using std::ifstream;
59 using std::iterator;
60 using std::list;
61 using std::queue;
62 
63 #include "Calculator_p.h"
64 
65 #define XML_GET_PREC_FROM_PROP(node, i)			value = xmlGetProp(node, (xmlChar*) "precision"); if(value) {i = s2i((char*) value); xmlFree(value);} else {i = -1;}
66 #define XML_GET_APPROX_FROM_PROP(node, b)		value = xmlGetProp(node, (xmlChar*) "approximate"); if(value) {b = !xmlStrcmp(value, (const xmlChar*) "true");} else {value = xmlGetProp(node, (xmlChar*) "precise"); if(value) {b = xmlStrcmp(value, (const xmlChar*) "true");} else {b = false;}} if(value) xmlFree(value);
67 #define XML_GET_FALSE_FROM_PROP(node, name, b)		value = xmlGetProp(node, (xmlChar*) name); if(value && !xmlStrcmp(value, (const xmlChar*) "false")) {b = false;} else {b = true;} if(value) xmlFree(value);
68 #define XML_GET_TRUE_FROM_PROP(node, name, b)		value = xmlGetProp(node, (xmlChar*) name); if(value && !xmlStrcmp(value, (const xmlChar*) "true")) {b = true;} else {b = false;} if(value) xmlFree(value);
69 #define XML_GET_BOOL_FROM_PROP(node, name, b)		value = xmlGetProp(node, (xmlChar*) name); if(value && !xmlStrcmp(value, (const xmlChar*) "false")) {b = false;} else if(value && !xmlStrcmp(value, (const xmlChar*) "true")) {b = true;} if(value) xmlFree(value);
70 #define XML_GET_FALSE_FROM_TEXT(node, b)		value = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); if(value && !xmlStrcmp(value, (const xmlChar*) "false")) {b = false;} else {b = true;} if(value) xmlFree(value);
71 #define XML_GET_TRUE_FROM_TEXT(node, b)			value = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); if(value && !xmlStrcmp(value, (const xmlChar*) "true")) {b = true;} else {b = false;} if(value) xmlFree(value);
72 #define XML_GET_BOOL_FROM_TEXT(node, b)			value = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); if(value && !xmlStrcmp(value, (const xmlChar*) "false")) {b = false;} else if(value && !xmlStrcmp(value, (const xmlChar*) "true")) {b = true;} if(value) xmlFree(value);
73 #define XML_GET_STRING_FROM_PROP(node, name, str)	value = xmlGetProp(node, (xmlChar*) name); if(value) {str = (char*) value; remove_blank_ends(str); xmlFree(value);} else str = "";
74 #define XML_GET_STRING_FROM_TEXT(node, str)		value = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); if(value) {str = (char*) value; remove_blank_ends(str); xmlFree(value);} else str = "";
75 #define XML_DO_FROM_PROP(node, name, action)		value = xmlGetProp(node, (xmlChar*) name); if(value) action((char*) value); else action(""); if(value) xmlFree(value);
76 #define XML_DO_FROM_TEXT(node, action)			value = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); if(value) {action((char*) value); xmlFree(value);} else action("");
77 #define XML_GET_INT_FROM_PROP(node, name, i)		value = xmlGetProp(node, (xmlChar*) name); if(value) {i = s2i((char*) value); xmlFree(value);}
78 #define XML_GET_INT_FROM_TEXT(node, i)			value = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); if(value) {i = s2i((char*) value); xmlFree(value);}
79 #define XML_GET_LOCALE_STRING_FROM_TEXT(node, str, best, next_best)		value = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); lang = xmlNodeGetLang(node); if(!best) {if(!lang) {if(!next_best) {if(value) {str = (char*) value; remove_blank_ends(str);} else str = ""; if(locale.empty()) {best = true;}}} else {if(locale == (char*) lang) {best = true; if(value) {str = (char*) value; remove_blank_ends(str);} else str = "";} else if(!next_best && strlen((char*) lang) >= 2 && fulfilled_translation == 0 && lang[0] == localebase[0] && lang[1] == localebase[1]) {next_best = true; if(value) {str = (char*) value; remove_blank_ends(str);} else str = "";} else if(!next_best && str.empty() && value) {str = (char*) value; remove_blank_ends(str);}}} if(value) xmlFree(value); if(lang) xmlFree(lang);
80 #define XML_GET_LOCALE_STRING_FROM_TEXT_REQ(node, str, best, next_best)		value = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); lang = xmlNodeGetLang(node); if(!best) {if(!lang) {if(!next_best) {if(value) {str = (char*) value; remove_blank_ends(str);} else str = ""; if(locale.empty()) {best = true;}}} else {if(locale == (char*) lang) {best = true; if(value) {str = (char*) value; remove_blank_ends(str);} else str = "";} else if(!next_best && strlen((char*) lang) >= 2 && fulfilled_translation == 0 && lang[0] == localebase[0] && lang[1] == localebase[1]) {next_best = true; if(value) {str = (char*) value; remove_blank_ends(str);} else str = "";} else if(!next_best && str.empty() && value && !require_translation) {str = (char*) value; remove_blank_ends(str);}}} if(value) xmlFree(value); if(lang) xmlFree(lang);
81 
82 #define VERSION_BEFORE(i1, i2, i3) (version_numbers[0] < i1 || (version_numbers[0] == i1 && (version_numbers[1] < i2 || (version_numbers[1] == i2 && version_numbers[2] < i3))))
83 
84 #ifdef COMPILED_DEFINITIONS
85 
loadGlobalDefinitions()86 bool Calculator::loadGlobalDefinitions() {
87 	bool b = true;
88 	if(!loadDefinitions(prefixes_xml, false)) b = false;
89 	if(!loadDefinitions(currencies_xml, false)) b = false;
90 	if(!loadDefinitions(units_xml, false)) b = false;
91 	if(!loadDefinitions(functions_xml, false)) b = false;
92 	if(!loadDefinitions(datasets_xml, false)) b = false;
93 	if(!loadDefinitions(variables_xml, false)) b = false;
94 	return b;
95 }
loadGlobalDefinitions(string filename)96 bool Calculator::loadGlobalDefinitions(string filename) {
97 	return loadDefinitions(buildPath(getGlobalDefinitionsDir(), filename).c_str(), false);
98 }
loadGlobalPrefixes()99 bool Calculator::loadGlobalPrefixes() {
100 	return loadDefinitions(prefixes_xml, false);
101 }
loadGlobalCurrencies()102 bool Calculator::loadGlobalCurrencies() {
103 	return loadDefinitions(currencies_xml, false);
104 }
loadGlobalUnits()105 bool Calculator::loadGlobalUnits() {
106 	bool b = loadDefinitions(currencies_xml, false);
107 	return loadDefinitions(units_xml, false) && b;
108 }
loadGlobalVariables()109 bool Calculator::loadGlobalVariables() {
110 	return loadDefinitions(variables_xml, false);
111 }
loadGlobalFunctions()112 bool Calculator::loadGlobalFunctions() {
113 	return loadDefinitions(functions_xml, false);
114 }
loadGlobalDataSets()115 bool Calculator::loadGlobalDataSets() {
116 	return loadDefinitions(datasets_xml, false);
117 }
118 #else
loadGlobalDefinitions()119 bool Calculator::loadGlobalDefinitions() {
120 	bool b = true;
121 	if(!loadDefinitions(buildPath(getGlobalDefinitionsDir(), "prefixes.xml").c_str(), false)) b = false;
122 	if(!loadDefinitions(buildPath(getGlobalDefinitionsDir(), "currencies.xml").c_str(), false)) b = false;
123 	if(!loadDefinitions(buildPath(getGlobalDefinitionsDir(), "units.xml").c_str(), false)) b = false;
124 	if(!loadDefinitions(buildPath(getGlobalDefinitionsDir(), "functions.xml").c_str(), false)) b = false;
125 	if(!loadDefinitions(buildPath(getGlobalDefinitionsDir(), "datasets.xml").c_str(), false)) b = false;
126 	if(!loadDefinitions(buildPath(getGlobalDefinitionsDir(), "variables.xml").c_str(), false)) b = false;
127 	return b;
128 }
loadGlobalDefinitions(string filename)129 bool Calculator::loadGlobalDefinitions(string filename) {
130 	return loadDefinitions(buildPath(getGlobalDefinitionsDir(), filename).c_str(), false);
131 }
loadGlobalPrefixes()132 bool Calculator::loadGlobalPrefixes() {
133 	return loadGlobalDefinitions("prefixes.xml");
134 }
loadGlobalCurrencies()135 bool Calculator::loadGlobalCurrencies() {
136 	return loadGlobalDefinitions("currencies.xml");
137 }
loadGlobalUnits()138 bool Calculator::loadGlobalUnits() {
139 	bool b = loadGlobalDefinitions("currencies.xml");
140 	return loadGlobalDefinitions("units.xml") && b;
141 }
loadGlobalVariables()142 bool Calculator::loadGlobalVariables() {
143 	return loadGlobalDefinitions("variables.xml");
144 }
loadGlobalFunctions()145 bool Calculator::loadGlobalFunctions() {
146 	return loadGlobalDefinitions("functions.xml");
147 }
loadGlobalDataSets()148 bool Calculator::loadGlobalDataSets() {
149 	return loadGlobalDefinitions("datasets.xml");
150 }
151 #endif
loadLocalDefinitions()152 bool Calculator::loadLocalDefinitions() {
153 	string homedir = buildPath(getLocalDataDir(), "definitions");
154 	if(!dirExists(homedir)) {
155 		string homedir_old = buildPath(getOldLocalDir(), "definitions");
156 		if(dirExists(homedir)) {
157 			if(!dirExists(getLocalDataDir())) {
158 				recursiveMakeDir(getLocalDataDir());
159 			}
160 			if(makeDir(homedir)) {
161 				list<string> eps_old;
162 				struct dirent *ep_old;
163 				DIR *dp_old = opendir(homedir_old.c_str());
164 				if(dp_old) {
165 					while((ep_old = readdir(dp_old))) {
166 #ifdef _DIRENT_HAVE_D_TYPE
167 						if(ep_old->d_type != DT_DIR) {
168 #endif
169 							if(strcmp(ep_old->d_name, "..") != 0 && strcmp(ep_old->d_name, ".") != 0 && strcmp(ep_old->d_name, "datasets") != 0) {
170 								eps_old.push_back(ep_old->d_name);
171 							}
172 #ifdef _DIRENT_HAVE_D_TYPE
173 						}
174 #endif
175 					}
176 					closedir(dp_old);
177 				}
178 				for(list<string>::iterator it = eps_old.begin(); it != eps_old.end(); ++it) {
179 					move_file(buildPath(homedir_old, *it).c_str(), buildPath(homedir, *it).c_str());
180 				}
181 				if(removeDir(homedir_old)) {
182 					removeDir(getOldLocalDir());
183 				}
184 			}
185 		}
186 	}
187 	list<string> eps;
188 	struct dirent *ep;
189 	DIR *dp = opendir(homedir.c_str());
190 	if(dp) {
191 		while((ep = readdir(dp))) {
192 #ifdef _DIRENT_HAVE_D_TYPE
193 			if(ep->d_type != DT_DIR) {
194 #endif
195 				if(strcmp(ep->d_name, "..") != 0 && strcmp(ep->d_name, ".") != 0 && strcmp(ep->d_name, "datasets") != 0) {
196 					eps.push_back(ep->d_name);
197 				}
198 #ifdef _DIRENT_HAVE_D_TYPE
199 			}
200 #endif
201 		}
202 		closedir(dp);
203 	}
204 	eps.sort();
205 	for(list<string>::iterator it = eps.begin(); it != eps.end(); ++it) {
206 		loadDefinitions(buildPath(homedir, *it).c_str(), (*it) == "functions.xml" || (*it) == "variables.xml" || (*it) == "units.xml" || (*it) == "datasets.xml", true);
207 	}
208 	for(size_t i = 0; i < variables.size(); i++) {
209 		if(!variables[i]->isLocal() && !variables[i]->isActive() && !getActiveExpressionItem(variables[i])) variables[i]->setActive(true);
210 	}
211 	for(size_t i = 0; i < units.size(); i++) {
212 		if(!units[i]->isLocal() && !units[i]->isActive() && !getActiveExpressionItem(units[i])) units[i]->setActive(true);
213 	}
214 	for(size_t i = 0; i < functions.size(); i++) {
215 		if(!functions[i]->isLocal() && !functions[i]->isActive() && !getActiveExpressionItem(functions[i])) functions[i]->setActive(true);
216 	}
217 	return true;
218 }
219 
220 #define ITEM_SAVE_BUILTIN_NAMES\
221 	if(!is_user_defs) {item->setRegistered(false);} \
222 	for(size_t i = 1; i <= item->countNames(); i++) { \
223 		if(item->getName(i).reference) { \
224 			for(size_t i2 = 0; i2 < 10; i2++) { \
225 				if(ref_names[i2].name.empty()) { \
226 					ref_names[i2] = item->getName(i); \
227 					break; \
228 				} \
229 			} \
230 		} \
231 	} \
232 	item->clearNames();
233 
234 #define ITEM_SET_BEST_NAMES(validation) \
235 	size_t names_i = 0, i2 = 0; \
236 	string *str_names; \
237 	if(best_names == "-") {best_names = ""; nextbest_names = "";} \
238 	if(!best_names.empty()) {str_names = &best_names;} \
239 	else if(!nextbest_names.empty()) {str_names = &nextbest_names;} \
240 	else {str_names = &default_names;} \
241 	if(!str_names->empty() && (*str_names)[0] == '!') { \
242 		names_i = str_names->find('!', 1) + 1; \
243 	} \
244 	while(true) { \
245 		size_t i3 = names_i; \
246 		names_i = str_names->find(",", i3); \
247 		if(i2 == 0) { \
248 			i2 = str_names->find(":", i3); \
249 		} \
250 		bool case_set = false; \
251 		ename.unicode = false; \
252 		ename.abbreviation = false; \
253 		ename.case_sensitive = false; \
254 		ename.suffix = false; \
255 		ename.avoid_input = false; \
256 		ename.completion_only = false; \
257 		ename.reference = false; \
258 		ename.plural = false; \
259 		if(i2 < names_i) { \
260 			bool b = true; \
261 			for(; i3 < i2; i3++) { \
262 				switch((*str_names)[i3]) { \
263 					case '-': {b = false; break;} \
264 					case 'a': {ename.abbreviation = b; b = true; break;} \
265 					case 'c': {ename.case_sensitive = b; b = true; case_set = true; break;} \
266 					case 'i': {ename.avoid_input = b; b = true; break;} \
267 					case 'p': {ename.plural = b; b = true; break;} \
268 					case 'r': {ename.reference = b; b = true; break;} \
269 					case 's': {ename.suffix = b; b = true; break;} \
270 					case 'u': {ename.unicode = b; b = true; break;} \
271 					case 'o': {ename.completion_only = b; b = true; break;} \
272 				} \
273 			} \
274 			i3++; \
275 			i2 = 0; \
276 		} \
277 		if(names_i == string::npos) {ename.name = str_names->substr(i3, str_names->length() - i3);} \
278 		else {ename.name = str_names->substr(i3, names_i - i3);} \
279 		remove_blank_ends(ename.name); \
280 		if(!ename.name.empty() && validation(ename.name, version_numbers, is_user_defs)) { \
281 			if(!case_set) { \
282 				ename.case_sensitive = ename.abbreviation || text_length_is_one(ename.name); \
283 			} \
284 			item->addName(ename); \
285 		} \
286 		if(names_i == string::npos) {break;} \
287 		names_i++; \
288 	}
289 
290 #define ITEM_SET_BUILTIN_NAMES \
291 	for(size_t i = 0; i < 10; i++) { \
292 		if(ref_names[i].name.empty()) { \
293 			break; \
294 		} else { \
295 			size_t i4 = item->hasName(ref_names[i].name, ref_names[i].case_sensitive); \
296 			if(i4 > 0) { \
297 				const ExpressionName *enameptr = &item->getName(i4); \
298 				ref_names[i].case_sensitive = enameptr->case_sensitive; \
299 				ref_names[i].abbreviation = enameptr->abbreviation; \
300 				ref_names[i].avoid_input = enameptr->avoid_input; \
301 				ref_names[i].completion_only = enameptr->completion_only; \
302 				ref_names[i].plural = enameptr->plural; \
303 				ref_names[i].suffix = enameptr->suffix; \
304 				item->setName(ref_names[i], i4); \
305 			} else { \
306 				item->addName(ref_names[i]); \
307 			} \
308 			ref_names[i].name = ""; \
309 		} \
310 	} \
311 	if(!is_user_defs) { \
312 		item->setRegistered(true); \
313 		nameChanged(item); \
314 	}
315 
316 #define ITEM_SET_REFERENCE_NAMES(validation) \
317 	if(str_names != &default_names && !default_names.empty()) { \
318 		if(default_names[0] == '!') { \
319 			names_i = default_names.find('!', 1) + 1; \
320 		} else { \
321 			names_i = 0; \
322 		} \
323 		i2 = 0; \
324 		while(true) { \
325 			size_t i3 = names_i; \
326 			names_i = default_names.find(",", i3); \
327 			if(i2 == 0) { \
328 				i2 = default_names.find(":", i3); \
329 			} \
330 			bool case_set = false; \
331 			ename.unicode = false; \
332 			ename.abbreviation = false; \
333 			ename.case_sensitive = false; \
334 			ename.suffix = false; \
335 			ename.avoid_input = false; \
336 			ename.completion_only = false; \
337 			ename.reference = false; \
338 			ename.plural = false; \
339 			if(i2 < names_i) { \
340 				bool b = true; \
341 				for(; i3 < i2; i3++) { \
342 					switch(default_names[i3]) { \
343 						case '-': {b = false; break;} \
344 						case 'a': {ename.abbreviation = b; b = true; break;} \
345 						case 'c': {ename.case_sensitive = b; b = true; case_set = true; break;} \
346 						case 'i': {ename.avoid_input = b; b = true; break;} \
347 						case 'p': {ename.plural = b; b = true; break;} \
348 						case 'r': {ename.reference = b; b = true; break;} \
349 						case 's': {ename.suffix = b; b = true; break;} \
350 						case 'u': {ename.unicode = b; b = true; break;} \
351 						case 'o': {ename.completion_only = b; b = true; break;} \
352 					} \
353 				} \
354 				i3++; \
355 				i2 = 0; \
356 			} \
357 			if(ename.reference) { \
358 				if(names_i == string::npos) {ename.name = default_names.substr(i3, default_names.length() - i3);} \
359 				else {ename.name = default_names.substr(i3, names_i - i3);} \
360 				remove_blank_ends(ename.name); \
361 				size_t i4 = item->hasName(ename.name, ename.case_sensitive); \
362 				if(i4 > 0) { \
363 					const ExpressionName *enameptr = &item->getName(i4); \
364 					ename.suffix = enameptr->suffix; \
365 					ename.abbreviation = enameptr->abbreviation; \
366 					ename.avoid_input = enameptr->avoid_input; \
367 					ename.completion_only = enameptr->completion_only; \
368 					ename.plural = enameptr->plural; \
369 					ename.case_sensitive = enameptr->case_sensitive; \
370 					item->setName(ename, i4); \
371 				} else if(!ename.name.empty() && validation(ename.name, version_numbers, is_user_defs)) { \
372 					if(!case_set) { \
373 						ename.case_sensitive = ename.abbreviation || text_length_is_one(ename.name); \
374 					} \
375 					item->addName(ename); \
376 				} \
377 			} \
378 			if(names_i == string::npos) {break;} \
379 			names_i++; \
380 		} \
381 	}
382 
383 
384 #define ITEM_READ_NAME(validation)\
385 					if(!new_names && (!xmlStrcmp(child->name, (const xmlChar*) "name") || !xmlStrcmp(child->name, (const xmlChar*) "abbreviation") || !xmlStrcmp(child->name, (const xmlChar*) "plural"))) {\
386 						name_index = 1;\
387 						XML_GET_INT_FROM_PROP(child, "index", name_index)\
388 						if(name_index > 0 && name_index <= 10) {\
389 							name_index--;\
390 							names[name_index] = empty_expression_name;\
391 							ref_names[name_index] = empty_expression_name;\
392 							value2 = NULL;\
393 							bool case_set = false;\
394 							if(child->name[0] == 'a') {\
395 								names[name_index].abbreviation = true;\
396 								ref_names[name_index].abbreviation = true;\
397 							} else if(child->name[0] == 'p') {\
398 								names[name_index].plural = true;\
399 								ref_names[name_index].plural = true;\
400 							}\
401 							child2 = child->xmlChildrenNode;\
402 							while(child2 != NULL) {\
403 								if((!best_name[name_index] || (ref_names[name_index].name.empty() && !locale.empty())) && !xmlStrcmp(child2->name, (const xmlChar*) "name")) {\
404 									lang = xmlNodeGetLang(child2);\
405 									if(!lang) {\
406 										value2 = xmlNodeListGetString(doc, child2->xmlChildrenNode, 1);\
407 										if(!value2 || validation((char*) value2, version_numbers, is_user_defs)) {\
408 											if(locale.empty()) {\
409 												best_name[name_index] = true;\
410 												if(value2) names[name_index].name = (char*) value2;\
411 												else names[name_index].name = "";\
412 											} else if(!require_translation) {\
413 												if(!best_name[name_index] && !nextbest_name[name_index]) {\
414 													if(value2) names[name_index].name = (char*) value2;\
415 													else names[name_index].name = "";\
416 												}\
417 												if(value2) ref_names[name_index].name = (char*) value2;\
418 												else ref_names[name_index].name = "";\
419 											}\
420 										}\
421 									} else if(!best_name[name_index] && !locale.empty()) {\
422 										if(locale == (char*) lang) {\
423 											value2 = xmlNodeListGetString(doc, child2->xmlChildrenNode, 1);\
424 											if(!value2 || validation((char*) value2, version_numbers, is_user_defs)) {\
425 												best_name[name_index] = true;\
426 												if(value2) names[name_index].name = (char*) value2;\
427 												else names[name_index].name = "";\
428 											}\
429 										} else if(!nextbest_name[name_index] && strlen((char*) lang) >= 2 && fulfilled_translation == 0 && lang[0] == localebase[0] && lang[1] == localebase[1]) {\
430 											value2 = xmlNodeListGetString(doc, child2->xmlChildrenNode, 1);\
431 											if(!value2 || validation((char*) value2, version_numbers, is_user_defs)) {\
432 												nextbest_name[name_index] = true; \
433 												if(value2) names[name_index].name = (char*) value2;\
434 												else names[name_index].name = "";\
435 											}\
436 										}\
437 									}\
438 									if(value2) xmlFree(value2);\
439 									if(lang) xmlFree(lang);\
440 									value2 = NULL; lang = NULL;\
441 								} else if(!xmlStrcmp(child2->name, (const xmlChar*) "unicode")) {\
442 									XML_GET_BOOL_FROM_TEXT(child2, names[name_index].unicode)\
443 									ref_names[name_index].unicode = names[name_index].unicode;\
444 								} else if(!xmlStrcmp(child2->name, (const xmlChar*) "reference")) {\
445 									XML_GET_BOOL_FROM_TEXT(child2, names[name_index].reference)\
446 									ref_names[name_index].reference = names[name_index].reference;\
447 								} else if(!xmlStrcmp(child2->name, (const xmlChar*) "suffix")) {\
448 									XML_GET_BOOL_FROM_TEXT(child2, names[name_index].suffix)\
449 									ref_names[name_index].suffix = names[name_index].suffix;\
450 								} else if(!xmlStrcmp(child2->name, (const xmlChar*) "avoid_input")) {\
451 									XML_GET_BOOL_FROM_TEXT(child2, names[name_index].avoid_input)\
452 									ref_names[name_index].avoid_input = names[name_index].avoid_input;\
453 								} else if(!xmlStrcmp(child2->name, (const xmlChar*) "completion_only")) {\
454 									XML_GET_BOOL_FROM_TEXT(child2, names[name_index].completion_only)\
455 									ref_names[name_index].completion_only = names[name_index].completion_only;\
456 								} else if(!xmlStrcmp(child2->name, (const xmlChar*) "plural")) {\
457 									XML_GET_BOOL_FROM_TEXT(child2, names[name_index].plural)\
458 									ref_names[name_index].plural = names[name_index].plural;\
459 								} else if(!xmlStrcmp(child2->name, (const xmlChar*) "abbreviation")) {\
460 									XML_GET_BOOL_FROM_TEXT(child2, names[name_index].abbreviation)\
461 									ref_names[name_index].abbreviation = names[name_index].abbreviation;\
462 								} else if(!xmlStrcmp(child2->name, (const xmlChar*) "case_sensitive")) {\
463 									XML_GET_BOOL_FROM_TEXT(child2, names[name_index].case_sensitive)\
464 									ref_names[name_index].case_sensitive = names[name_index].case_sensitive;\
465 									case_set = true;\
466 								}\
467 								child2 = child2->next;\
468 							}\
469 							if(!case_set) {\
470 								ref_names[name_index].case_sensitive = ref_names[name_index].abbreviation || text_length_is_one(ref_names[name_index].name);\
471 								names[name_index].case_sensitive = names[name_index].abbreviation || text_length_is_one(names[name_index].name);\
472 							}\
473 							if(names[name_index].reference) {\
474 								if(!ref_names[name_index].name.empty()) {\
475 									if(ref_names[name_index].name == names[name_index].name) {\
476 										ref_names[name_index].name = "";\
477 									} else {\
478 										names[name_index].reference = false;\
479 									}\
480 								}\
481 							} else if(!ref_names[name_index].name.empty()) {\
482 								ref_names[name_index].name = "";\
483 							}\
484 						}\
485 					}
486 
487 #define ITEM_READ_DTH \
488 					if(!xmlStrcmp(child->name, (const xmlChar*) "description")) {\
489 						XML_GET_LOCALE_STRING_FROM_TEXT(child, description, best_description, next_best_description)\
490 					} else if(!xmlStrcmp(child->name, (const xmlChar*) "title")) {\
491 						XML_GET_LOCALE_STRING_FROM_TEXT_REQ(child, title, best_title, next_best_title)\
492 					} else if(!xmlStrcmp(child->name, (const xmlChar*) "hidden")) {\
493 						XML_GET_TRUE_FROM_TEXT(child, hidden);\
494 					}
495 
496 #define ITEM_READ_NAMES \
497 					if(new_names && ((best_names.empty() && fulfilled_translation != 2) || default_names.empty()) && !xmlStrcmp(child->name, (const xmlChar*) "names")) {\
498 						value = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);\
499  						lang = xmlNodeGetLang(child);\
500 						if(!lang) {\
501 							if(default_names.empty()) {\
502 								if(value) {\
503 									default_names = (char*) value;\
504 									remove_blank_ends(default_names);\
505 								} else {\
506 									default_names = "";\
507 								}\
508 							}\
509 						} else if(best_names.empty()) {\
510 							if(locale == (char*) lang) {\
511 								if(value) {\
512 									best_names = (char*) value;\
513 									remove_blank_ends(best_names);\
514 								} else {\
515 									best_names = " ";\
516 								}\
517 							} else if(nextbest_names.empty() && strlen((char*) lang) >= 2 && fulfilled_translation == 0 && lang[0] == localebase[0] && lang[1] == localebase[1]) {\
518 								if(value) {\
519 									nextbest_names = (char*) value;\
520 									remove_blank_ends(nextbest_names);\
521 								} else {\
522 									nextbest_names = " ";\
523 								}\
524 							} else if(nextbest_names.empty() && default_names.empty() && value && !require_translation) {\
525 								nextbest_names = (char*) value;\
526 								remove_blank_ends(nextbest_names);\
527 							}\
528 						}\
529 						if(value) xmlFree(value);\
530 						if(lang) xmlFree(lang);\
531 					}
532 
533 #define ITEM_INIT_DTH \
534 					hidden = -1;\
535 					title = ""; best_title = false; next_best_title = false;\
536 					description = ""; best_description = false; next_best_description = false;\
537 					if(fulfilled_translation > 0) require_translation = false; \
538 					else {XML_GET_TRUE_FROM_PROP(cur, "require_translation", require_translation)}
539 
540 #define ITEM_INIT_NAME \
541 					if(new_names) {\
542 						best_names = "";\
543 						nextbest_names = "";\
544 						default_names = "";\
545 					} else {\
546 						for(size_t i = 0; i < 10; i++) {\
547 							best_name[i] = false;\
548 							nextbest_name[i] = false;\
549 						}\
550 					}
551 
552 
553 #define ITEM_SET_NAME_1(validation)\
554 					if(!name.empty() && validation(name, version_numbers, is_user_defs)) {\
555 						ename.name = name;\
556 						ename.unicode = false;\
557 						ename.abbreviation = false;\
558 						ename.case_sensitive = text_length_is_one(ename.name);\
559 						ename.suffix = false;\
560 						ename.avoid_input = false;\
561 						ename.completion_only = false;\
562 						ename.reference = true;\
563 						ename.plural = false;\
564 						item->addName(ename);\
565 					}
566 
567 #define ITEM_SET_NAME_2\
568 					for(size_t i = 0; i < 10; i++) {\
569 						if(!names[i].name.empty()) {\
570 							item->addName(names[i], i + 1);\
571 							names[i].name = "";\
572 						} else if(!ref_names[i].name.empty()) {\
573 							item->addName(ref_names[i], i + 1);\
574 							ref_names[i].name = "";\
575 						}\
576 					}
577 
578 #define ITEM_SET_NAME_3\
579 					for(size_t i = 0; i < 10; i++) {\
580 						if(!ref_names[i].name.empty()) {\
581 							item->addName(ref_names[i]);\
582 							ref_names[i].name = "";\
583 						}\
584 					}
585 
586 #define ITEM_SET_DTH\
587 					item->setDescription(description);\
588 					if(!title.empty() && title[0] == '!') {\
589 						size_t i = title.find('!', 1);\
590 						if(i == string::npos) {\
591 							item->setTitle(title);\
592 						} else if(i + 1 == title.length()) {\
593 							item->setTitle("");\
594 						} else {\
595 							item->setTitle(title.substr(i + 1, title.length() - (i + 1)));\
596 						}\
597 					} else {\
598 						item->setTitle(title);\
599 					}\
600 					if(hidden >= 0) item->setHidden(hidden);
601 
602 #define ITEM_SET_SHORT_NAME\
603 					if(!name.empty() && unitNameIsValid(name, version_numbers, is_user_defs)) {\
604 						ename.name = name;\
605 						ename.unicode = false;\
606 						ename.abbreviation = true;\
607 						ename.case_sensitive = true;\
608 						ename.suffix = false;\
609 						ename.avoid_input = false;\
610 						ename.completion_only = false;\
611 						ename.reference = true;\
612 						ename.plural = false;\
613 						item->addName(ename);\
614 					}
615 
616 #define ITEM_SET_SINGULAR\
617 					if(!singular.empty()) {\
618 						ename.name = singular;\
619 						ename.unicode = false;\
620 						ename.abbreviation = false;\
621 						ename.case_sensitive = text_length_is_one(ename.name);\
622 						ename.suffix = false;\
623 						ename.avoid_input = false;\
624 						ename.completion_only = false;\
625 						ename.reference = false;\
626 						ename.plural = false;\
627 						item->addName(ename);\
628 					}
629 
630 #define ITEM_SET_PLURAL\
631 					if(!plural.empty()) {\
632 						ename.name = plural;\
633 						ename.unicode = false;\
634 						ename.abbreviation = false;\
635 						ename.case_sensitive = text_length_is_one(ename.name);\
636 						ename.suffix = false;\
637 						ename.avoid_input = false;\
638 						ename.completion_only = false;\
639 						ename.reference = false;\
640 						ename.plural = true;\
641 						item->addName(ename);\
642 					}
643 
644 #define BUILTIN_NAMES_1\
645 				if(!is_user_defs) item->setRegistered(false);\
646 					bool has_ref_name;\
647 					for(size_t i = 1; i <= item->countNames(); i++) {\
648 						if(item->getName(i).reference) {\
649 							has_ref_name = false;\
650 							for(size_t i2 = 0; i2 < 10; i2++) {\
651 								if(names[i2].name == item->getName(i).name || ref_names[i2].name == item->getName(i).name) {\
652 									has_ref_name = true;\
653 									break;\
654 								}\
655 							}\
656 							if(!has_ref_name) {\
657 								for(int i2 = 9; i2 >= 0; i2--) {\
658 									if(ref_names[i2].name.empty()) {\
659 										ref_names[i2] = item->getName(i);\
660 										break;\
661 									}\
662 								}\
663 							}\
664 						}\
665 					}\
666 					item->clearNames();
667 
668 #define BUILTIN_UNIT_NAMES_1\
669 				if(!is_user_defs) item->setRegistered(false);\
670 					bool has_ref_name;\
671 					for(size_t i = 1; i <= item->countNames(); i++) {\
672 						if(item->getName(i).reference) {\
673 							has_ref_name = item->getName(i).name == singular || item->getName(i).name == plural;\
674 							for(size_t i2 = 0; !has_ref_name && i2 < 10; i2++) {\
675 								if(names[i2].name == item->getName(i).name || ref_names[i2].name == item->getName(i).name) {\
676 									has_ref_name = true;\
677 									break;\
678 								}\
679 							}\
680 							if(!has_ref_name) {\
681 								for(int i2 = 9; i2 >= 0; i2--) {\
682 									if(ref_names[i2].name.empty()) {\
683 										ref_names[i2] = item->getName(i);\
684 										break;\
685 									}\
686 								}\
687 							}\
688 						}\
689 					}\
690 					item->clearNames();
691 
692 #define BUILTIN_NAMES_2\
693 				if(!is_user_defs) {\
694 					item->setRegistered(true);\
695 					nameChanged(item);\
696 				}
697 
698 #define ITEM_CLEAR_NAMES\
699 					for(size_t i = 0; i < 10; i++) {\
700 						if(!names[i].name.empty()) {\
701 							names[i].name = "";\
702 						}\
703 						if(!ref_names[i].name.empty()) {\
704 							ref_names[i].name = "";\
705 						}\
706 					}
707 
loadDefinitions(const char * file_name,bool is_user_defs,bool check_duplicates)708 int Calculator::loadDefinitions(const char* file_name, bool is_user_defs, bool check_duplicates) {
709 
710 	xmlDocPtr doc;
711 	xmlNodePtr cur, child, child2, child3;
712 	string version, stmp, name, uname, type, svalue, sexp, plural, countries, singular, category_title, category, description, title, inverse, suncertainty, base, argname, usystem;
713 	bool unc_rel;
714 	bool best_title, next_best_title, best_category_title, next_best_category_title, best_description, next_best_description;
715 	bool best_plural, next_best_plural, best_singular, next_best_singular, best_argname, next_best_argname, best_countries, next_best_countries;
716 	bool best_proptitle, next_best_proptitle, best_propdescr, next_best_propdescr;
717 	string proptitle, propdescr;
718 	ExpressionName names[10];
719 	ExpressionName ref_names[10];
720 	string prop_names[10];
721 	string ref_prop_names[10];
722 	bool best_name[10];
723 	bool nextbest_name[10];
724 	string best_names, nextbest_names, default_names;
725 	string best_prop_names, nextbest_prop_names, default_prop_names;
726 	int name_index, prec;
727 	ExpressionName ename;
728 
729 	string locale;
730 #ifdef _WIN32
731 	WCHAR wlocale[LOCALE_NAME_MAX_LENGTH];
732 	if(LCIDToLocaleName(LOCALE_USER_DEFAULT, wlocale, LOCALE_NAME_MAX_LENGTH, 0) != 0) locale = utf8_encode(wlocale);
733 	gsub("-", "_", locale);
734 #else
735 	char *clocale = setlocale(LC_MESSAGES, NULL);
736 	if(clocale) locale = clocale;
737 #endif
738 
739 	if(b_ignore_locale || locale == "POSIX" || locale == "C") {
740 		locale = "";
741 	} else {
742 		size_t i = locale.find('.');
743 		if(i != string::npos) locale = locale.substr(0, i);
744 	}
745 
746 	int fulfilled_translation = 0;
747 	string localebase;
748 	if(locale.length() > 2) {
749 		localebase = locale.substr(0, 2);
750 		if(locale == "en_US") {
751 			fulfilled_translation = 2;
752 		} else if(localebase == "en") {
753 			fulfilled_translation = 1;
754 		}
755 	} else {
756 		localebase = locale;
757 		if(locale == "en") {
758 			fulfilled_translation = 2;
759 		}
760 	}
761 	while(localebase.length() < 2) {
762 		localebase += " ";
763 		fulfilled_translation = 2;
764 	}
765 
766 	int exponent = 1, litmp = 0, mix_priority = 0, mix_min = 0;
767 	bool active = false, b = false, require_translation = false, use_with_prefixes = false, use_with_prefixes_set = false;
768 	int hidden = -1;
769 	Number nr;
770 	ExpressionItem *item;
771 	MathFunction *f;
772 	Variable *v;
773 	Unit *u;
774 	AliasUnit *au;
775 	CompositeUnit *cu;
776 	Prefix *p;
777 	Argument *arg;
778 	DataSet *dc;
779 	DataProperty *dp;
780 	int itmp;
781 	IntegerArgument *iarg;
782 	NumberArgument *farg;
783 	xmlChar *value, *lang, *value2;
784 	int in_unfinished = 0;
785 	bool done_something = false;
786 #ifdef COMPILED_DEFINITIONS
787 	if(strlen(file_name) > 1 && file_name[0] == '<') {
788 		doc = xmlParseMemory(file_name, strlen(file_name));
789 	} else {
790 		doc = xmlParseFile(file_name);
791 	}
792 #else
793 	doc = xmlParseFile(file_name);
794 #endif
795 	if(doc == NULL) {
796 		return false;
797 	}
798 	cur = xmlDocGetRootElement(doc);
799 	if(cur == NULL) {
800 		xmlFreeDoc(doc);
801 		return false;
802 	}
803 	while(cur != NULL) {
804 		if(!xmlStrcmp(cur->name, (const xmlChar*) "QALCULATE")) {
805 			XML_GET_STRING_FROM_PROP(cur, "version", version)
806 			break;
807 		}
808 		cur = cur->next;
809 	}
810 	if(cur == NULL) {
811 		error(true, _("File not identified as Qalculate! definitions file: %s."), file_name, NULL);
812 		xmlFreeDoc(doc);
813 		return false;
814 	}
815 	int version_numbers[] = {3, 17, 0};
816 	parse_qalculate_version(version, version_numbers);
817 
818 	bool new_names = version_numbers[0] > 0 || version_numbers[1] > 9 || (version_numbers[1] == 9 && version_numbers[2] >= 4);
819 
820 	ParseOptions po;
821 
822 	vector<xmlNodePtr> unfinished_nodes;
823 	vector<string> unfinished_cats;
824 	queue<xmlNodePtr> sub_items;
825 	vector<queue<xmlNodePtr> > nodes;
826 
827 	category = "";
828 	nodes.resize(1);
829 
830 	Unit *u_usd = getUnit("USD");
831 	Unit *u_gbp = getUnit("GBP");
832 	bool b_remove_cent = false;
833 
834 	while(true) {
835 		if(!in_unfinished) {
836 			category_title = ""; best_category_title = false; next_best_category_title = false;
837 			child = cur->xmlChildrenNode;
838 			while(child != NULL) {
839 				if(!xmlStrcmp(child->name, (const xmlChar*) "title")) {
840 					XML_GET_LOCALE_STRING_FROM_TEXT(child, category_title, best_category_title, next_best_category_title)
841 				} else if(!xmlStrcmp(child->name, (const xmlChar*) "category")) {
842 					nodes.back().push(child);
843 				} else {
844 					sub_items.push(child);
845 				}
846 				child = child->next;
847 			}
848 			if(!category.empty()) {
849 				category += "/";
850 			}
851 			if(!category_title.empty() && category_title[0] == '!') {\
852 				size_t i = category_title.find('!', 1);
853 				if(i == string::npos) {
854 					category += category_title;
855 				} else if(i + 1 < category_title.length()) {
856 					category += category_title.substr(i + 1, category_title.length() - (i + 1));
857 				}
858 			} else {
859 				category += category_title;
860 			}
861 		}
862 		while(!sub_items.empty() || (in_unfinished && cur)) {
863 			if(!in_unfinished) {
864 				cur = sub_items.front();
865 				sub_items.pop();
866 			}
867 			if(!xmlStrcmp(cur->name, (const xmlChar*) "activate")) {
868 				XML_GET_STRING_FROM_TEXT(cur, name)
869 				ExpressionItem *item = getInactiveExpressionItem(name);
870 				if(item && !item->isLocal()) {
871 					item->setActive(true);
872 					done_something = true;
873 				}
874 			} else if(!xmlStrcmp(cur->name, (const xmlChar*) "deactivate")) {
875 				XML_GET_STRING_FROM_TEXT(cur, name)
876 				ExpressionItem *item = getActiveExpressionItem(name);
877 				if(item && !item->isLocal()) {
878 					item->setActive(false);
879 					done_something = true;
880 				}
881 			} else if(!xmlStrcmp(cur->name, (const xmlChar*) "function")) {
882 				if(VERSION_BEFORE(0, 6, 3)) {
883 					XML_GET_STRING_FROM_PROP(cur, "name", name)
884 				} else {
885 					name = "";
886 				}
887 				XML_GET_FALSE_FROM_PROP(cur, "active", active)
888 				f = new UserFunction(category, "", "", is_user_defs, 0, "", "", 0, active);
889 				item = f;
890 				done_something = true;
891 				child = cur->xmlChildrenNode;
892 				ITEM_INIT_DTH
893 				ITEM_INIT_NAME
894 				while(child != NULL) {
895 					if(!xmlStrcmp(child->name, (const xmlChar*) "expression")) {
896 						XML_DO_FROM_TEXT(child, ((UserFunction*) f)->setFormula);
897 						XML_GET_PREC_FROM_PROP(child, prec)
898 						f->setPrecision(prec);
899 						XML_GET_APPROX_FROM_PROP(child, b)
900 						f->setApproximate(b);
901 					} else if(!xmlStrcmp(child->name, (const xmlChar*) "condition")) {
902 						XML_DO_FROM_TEXT(child, f->setCondition);
903 					} else if(!xmlStrcmp(child->name, (const xmlChar*) "subfunction")) {
904 						XML_GET_FALSE_FROM_PROP(child, "precalculate", b);
905 						value = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
906 						if(value) ((UserFunction*) f)->addSubfunction((char*) value, b);
907 						else ((UserFunction*) f)->addSubfunction("", true);
908 						if(value) xmlFree(value);
909 					} else if(!xmlStrcmp(child->name, (const xmlChar*) "argument")) {
910 						farg = NULL; iarg = NULL;
911 						XML_GET_STRING_FROM_PROP(child, "type", type);
912 						if(type == "text") {
913 							arg = new TextArgument();
914 						} else if(type == "symbol") {
915 							arg = new SymbolicArgument();
916 						} else if(type == "date") {
917 							arg = new DateArgument();
918 						} else if(type == "integer") {
919 							iarg = new IntegerArgument();
920 							arg = iarg;
921 						} else if(type == "number") {
922 							farg = new NumberArgument();
923 							arg = farg;
924 						} else if(type == "vector") {
925 							arg = new VectorArgument();
926 						} else if(type == "matrix") {
927 							arg = new MatrixArgument();
928 						} else if(type == "boolean") {
929 							arg = new BooleanArgument();
930 						} else if(type == "function") {
931 							arg = new FunctionArgument();
932 						} else if(type == "unit") {
933 							arg = new UnitArgument();
934 						} else if(type == "variable") {
935 							arg = new VariableArgument();
936 						} else if(type == "object") {
937 							arg = new ExpressionItemArgument();
938 						} else if(type == "angle") {
939 							arg = new AngleArgument();
940 						} else if(type == "data-object") {
941 							arg = new DataObjectArgument(NULL, "");
942 						} else if(type == "data-property") {
943 							arg = new DataPropertyArgument(NULL, "");
944 						} else {
945 							arg = new Argument();
946 						}
947 						child2 = child->xmlChildrenNode;
948 						argname = ""; best_argname = false; next_best_argname = false;
949 						while(child2 != NULL) {
950 							if(!xmlStrcmp(child2->name, (const xmlChar*) "title")) {
951 								XML_GET_LOCALE_STRING_FROM_TEXT(child2, argname, best_argname, next_best_argname)
952 							} else if(!xmlStrcmp(child2->name, (const xmlChar*) "min")) {
953 								if(farg) {
954 									XML_DO_FROM_TEXT(child2, nr.set);
955 									farg->setMin(&nr);
956 									XML_GET_FALSE_FROM_PROP(child, "include_equals", b)
957 									farg->setIncludeEqualsMin(b);
958 								} else if(iarg) {
959 									XML_GET_STRING_FROM_TEXT(child2, stmp);
960 									Number integ(stmp);
961 									iarg->setMin(&integ);
962 								}
963 							} else if(!xmlStrcmp(child2->name, (const xmlChar*) "max")) {
964 								if(farg) {
965 									XML_DO_FROM_TEXT(child2, nr.set);
966 									farg->setMax(&nr);
967 									XML_GET_FALSE_FROM_PROP(child, "include_equals", b)
968 									farg->setIncludeEqualsMax(b);
969 								} else if(iarg) {
970 									XML_GET_STRING_FROM_TEXT(child2, stmp);
971 									Number integ(stmp);
972 									iarg->setMax(&integ);
973 								}
974 							} else if(farg && !xmlStrcmp(child2->name, (const xmlChar*) "complex_allowed")) {
975 								XML_GET_FALSE_FROM_TEXT(child2, b);
976 								farg->setComplexAllowed(b);
977 							} else if(!xmlStrcmp(child2->name, (const xmlChar*) "condition")) {
978 								XML_DO_FROM_TEXT(child2, arg->setCustomCondition);
979 							} else if(!xmlStrcmp(child2->name, (const xmlChar*) "matrix_allowed")) {
980 								XML_GET_TRUE_FROM_TEXT(child2, b);
981 								arg->setMatrixAllowed(b);
982 							} else if(!xmlStrcmp(child2->name, (const xmlChar*) "zero_forbidden")) {
983 								XML_GET_TRUE_FROM_TEXT(child2, b);
984 								arg->setZeroForbidden(b);
985 							} else if(!xmlStrcmp(child2->name, (const xmlChar*) "test")) {
986 								XML_GET_FALSE_FROM_TEXT(child2, b);
987 								arg->setTests(b);
988 							} else if(!xmlStrcmp(child2->name, (const xmlChar*) "handle_vector")) {
989 								XML_GET_FALSE_FROM_TEXT(child2, b);
990 								arg->setHandleVector(b);
991 							} else if(!xmlStrcmp(child2->name, (const xmlChar*) "alert")) {
992 								XML_GET_FALSE_FROM_TEXT(child2, b);
993 								arg->setAlerts(b);
994 							}
995 							child2 = child2->next;
996 						}
997 						if(!argname.empty() && argname[0] == '!') {
998 							size_t i = argname.find('!', 1);
999 							if(i == string::npos) {
1000 								arg->setName(argname);
1001 							} else if(i + 1 < argname.length()) {
1002 								arg->setName(argname.substr(i + 1, argname.length() - (i + 1)));
1003 							}
1004 						} else {
1005 							arg->setName(argname);
1006 						}
1007 						itmp = 1;
1008 						XML_GET_INT_FROM_PROP(child, "index", itmp);
1009 						f->setArgumentDefinition(itmp, arg);
1010 					} else if(!xmlStrcmp(child->name, (const xmlChar*) "example")) {
1011 						XML_DO_FROM_TEXT(child, f->setExample);
1012 					} else ITEM_READ_NAME(functionNameIsValid)
1013 					 else ITEM_READ_DTH
1014 					 else {
1015 						ITEM_READ_NAMES
1016 					}
1017 					child = child->next;
1018 				}
1019 				if(new_names) {
1020 					ITEM_SET_BEST_NAMES(functionNameIsValid)
1021 					ITEM_SET_REFERENCE_NAMES(functionNameIsValid)
1022 				} else {
1023 					ITEM_SET_NAME_1(functionNameIsValid)
1024 					ITEM_SET_NAME_2
1025 					ITEM_SET_NAME_3
1026 				}
1027 				ITEM_SET_DTH
1028 				if(check_duplicates && !is_user_defs) {
1029 					for(size_t i = 1; i <= f->countNames();) {
1030 						if(getActiveFunction(f->getName(i).name)) f->removeName(i);
1031 						else i++;
1032 					}
1033 				}
1034 				if(f->countNames() == 0) {
1035 					f->destroy();
1036 					f = NULL;
1037 				} else {
1038 					f->setChanged(false);
1039 					addFunction(f, true, is_user_defs);
1040 				}
1041 			} else if(!xmlStrcmp(cur->name, (const xmlChar*) "dataset") || !xmlStrcmp(cur->name, (const xmlChar*) "builtin_dataset")) {
1042 				bool builtin = !xmlStrcmp(cur->name, (const xmlChar*) "builtin_dataset");
1043 				XML_GET_FALSE_FROM_PROP(cur, "active", active)
1044 				if(builtin) {
1045 					XML_GET_STRING_FROM_PROP(cur, "name", name)
1046 					dc = getDataSet(name);
1047 					if(!dc) {
1048 						goto after_load_object;
1049 					}
1050 					dc->setCategory(category);
1051 				} else {
1052 					dc = new DataSet(category, "", "", "", "", is_user_defs);
1053 				}
1054 				item = dc;
1055 				done_something = true;
1056 				child = cur->xmlChildrenNode;
1057 				ITEM_INIT_DTH
1058 				ITEM_INIT_NAME
1059 				while(child != NULL) {
1060 					if(!xmlStrcmp(child->name, (const xmlChar*) "property")) {
1061 						dp = new DataProperty(dc);
1062 						child2 = child->xmlChildrenNode;
1063 						if(new_names) {
1064 							default_prop_names = ""; best_prop_names = ""; nextbest_prop_names = "";
1065 						} else {
1066 							for(size_t i = 0; i < 10; i++) {
1067 								best_name[i] = false;
1068 								nextbest_name[i] = false;
1069 							}
1070 						}
1071 						proptitle = ""; best_proptitle = false; next_best_proptitle = false;
1072 						propdescr = ""; best_propdescr = false; next_best_propdescr = false;
1073 						while(child2 != NULL) {
1074 							if(!xmlStrcmp(child2->name, (const xmlChar*) "title")) {
1075 								XML_GET_LOCALE_STRING_FROM_TEXT(child2, proptitle, best_proptitle, next_best_proptitle)
1076 							} else if(!new_names && !xmlStrcmp(child2->name, (const xmlChar*) "name")) {
1077 								name_index = 1;
1078 								XML_GET_INT_FROM_PROP(child2, "index", name_index)
1079 								if(name_index > 0 && name_index <= 10) {
1080 									name_index--;
1081 									prop_names[name_index] = "";
1082 									ref_prop_names[name_index] = "";
1083 									value2 = NULL;
1084 									child3 = child2->xmlChildrenNode;
1085 									while(child3 != NULL) {
1086 										if((!best_name[name_index] || (ref_prop_names[name_index].empty() && !locale.empty())) && !xmlStrcmp(child3->name, (const xmlChar*) "name")) {
1087 											lang = xmlNodeGetLang(child3);
1088 											if(!lang) {
1089 												value2 = xmlNodeListGetString(doc, child3->xmlChildrenNode, 1);
1090 												if(locale.empty()) {
1091 													best_name[name_index] = true;
1092 													if(value2) prop_names[name_index] = (char*) value2;
1093 													else prop_names[name_index] = "";
1094 												} else {
1095 													if(!best_name[name_index] && !nextbest_name[name_index]) {
1096 														if(value2) prop_names[name_index] = (char*) value2;
1097 														else prop_names[name_index] = "";
1098 													}
1099 													if(value2) ref_prop_names[name_index] = (char*) value2;
1100 													else ref_prop_names[name_index] = "";
1101 												}
1102 											} else if(!best_name[name_index] && !locale.empty()) {
1103 												if(locale == (char*) lang) {
1104 													value2 = xmlNodeListGetString(doc, child3->xmlChildrenNode, 1);
1105 													best_name[name_index] = true;
1106 													if(value2) prop_names[name_index] = (char*) value2;
1107 													else prop_names[name_index] = "";
1108 												} else if(!nextbest_name[name_index] && strlen((char*) lang) >= 2 && fulfilled_translation == 0 && lang[0] == localebase[0] && lang[1] == localebase[1]) {
1109 													value2 = xmlNodeListGetString(doc, child3->xmlChildrenNode, 1);
1110 													nextbest_name[name_index] = true;
1111 													if(value2) prop_names[name_index] = (char*) value2;
1112 													else prop_names[name_index] = "";
1113 												}
1114 											}
1115 											if(value2) xmlFree(value2);
1116 											if(lang) xmlFree(lang);
1117 											value2 = NULL; lang = NULL;
1118 										}
1119 										child3 = child3->next;
1120 									}
1121 									if(!ref_prop_names[name_index].empty() && ref_prop_names[name_index] == prop_names[name_index]) {
1122 										ref_prop_names[name_index] = "";
1123 									}
1124 								}
1125 							} else if(new_names && !xmlStrcmp(child2->name, (const xmlChar*) "names") && ((best_prop_names.empty() && fulfilled_translation != 2) || default_prop_names.empty())) {
1126 									value2 = xmlNodeListGetString(doc, child2->xmlChildrenNode, 1);
1127  									lang = xmlNodeGetLang(child2);
1128 									if(!lang) {
1129 										if(default_prop_names.empty()) {
1130 											if(value2) {
1131 												default_prop_names = (char*) value2;
1132 												remove_blank_ends(default_prop_names);
1133 											} else {
1134 												default_prop_names = "";
1135 											}
1136 										}
1137 									} else {
1138 										if(locale == (char*) lang) {
1139 											if(value2) {
1140 												best_prop_names = (char*) value2;
1141 												remove_blank_ends(best_prop_names);
1142 											} else {
1143 												best_prop_names = " ";
1144 											}
1145 									} else if(nextbest_prop_names.empty() && strlen((char*) lang) >= 2 && fulfilled_translation == 0 && lang[0] == localebase[0] && lang[1] == localebase[1]) {
1146 										if(value2) {
1147 											nextbest_prop_names = (char*) value2;
1148 											remove_blank_ends(nextbest_prop_names);
1149 										} else {
1150 											nextbest_prop_names = " ";
1151 										}
1152 									} else if(nextbest_prop_names.empty() && default_prop_names.empty() && value2 && !require_translation) {
1153 										nextbest_prop_names = (char*) value2;
1154 										remove_blank_ends(nextbest_prop_names);
1155 									}
1156 								}
1157 								if(value2) xmlFree(value2);
1158 								if(lang) xmlFree(lang);
1159 							} else if(!xmlStrcmp(child2->name, (const xmlChar*) "description")) {
1160 								XML_GET_LOCALE_STRING_FROM_TEXT(child2, propdescr, best_propdescr, next_best_propdescr)
1161 							} else if(!xmlStrcmp(child2->name, (const xmlChar*) "unit")) {
1162 								XML_DO_FROM_TEXT(child2, dp->setUnit)
1163 							} else if(!xmlStrcmp(child2->name, (const xmlChar*) "key")) {
1164 								XML_GET_TRUE_FROM_TEXT(child2, b)
1165 								dp->setKey(b);
1166 							} else if(!xmlStrcmp(child2->name, (const xmlChar*) "hidden")) {
1167 								XML_GET_TRUE_FROM_TEXT(child2, b)
1168 								dp->setHidden(b);
1169 							} else if(!xmlStrcmp(child2->name, (const xmlChar*) "brackets")) {
1170 								XML_GET_TRUE_FROM_TEXT(child2, b)
1171 								dp->setUsesBrackets(b);
1172 							} else if(!xmlStrcmp(child2->name, (const xmlChar*) "approximate")) {
1173 								XML_GET_TRUE_FROM_TEXT(child2, b)
1174 								dp->setApproximate(b);
1175 							} else if(!xmlStrcmp(child2->name, (const xmlChar*) "case_sensitive")) {
1176 								XML_GET_TRUE_FROM_TEXT(child2, b)
1177 								dp->setCaseSensitive(b);
1178 							} else if(!xmlStrcmp(child2->name, (const xmlChar*) "type")) {
1179 								XML_GET_STRING_FROM_TEXT(child2, stmp)
1180 								if(stmp == "text") {
1181 									dp->setPropertyType(PROPERTY_STRING);
1182 								} else if(stmp == "number") {
1183 									dp->setPropertyType(PROPERTY_NUMBER);
1184 								} else if(stmp == "expression") {
1185 									dp->setPropertyType(PROPERTY_EXPRESSION);
1186 								}
1187 							}
1188 							child2 = child2->next;
1189 						}
1190 						if(!proptitle.empty() && proptitle[0] == '!') {\
1191 							size_t i = proptitle.find('!', 1);
1192 							if(i == string::npos) {
1193 								dp->setTitle(proptitle);
1194 							} else if(i + 1 < proptitle.length()) {
1195 								dp->setTitle(proptitle.substr(i + 1, proptitle.length() - (i + 1)));
1196 							}
1197 						} else {
1198 							dp->setTitle(proptitle);
1199 						}
1200 						dp->setDescription(propdescr);
1201 						if(new_names) {
1202 							size_t names_i = 0, i2 = 0;
1203 							string *str_names;
1204 							bool had_ref = false;
1205 							if(best_prop_names == "-") {best_prop_names = ""; nextbest_prop_names = "";}
1206 							if(!best_prop_names.empty()) {str_names = &best_prop_names;}
1207 							else if(!nextbest_prop_names.empty()) {str_names = &nextbest_prop_names;}
1208 							else {str_names = &default_prop_names;}
1209 							if(!str_names->empty() && (*str_names)[0] == '!') {
1210 								names_i = str_names->find('!', 1) + 1;
1211 							}
1212 							while(true) {
1213 								size_t i3 = names_i;
1214 								names_i = str_names->find(",", i3);
1215 								if(i2 == 0) {
1216 									i2 = str_names->find(":", i3);
1217 								}
1218 								bool b_prop_ref = false;
1219 								if(i2 < names_i) {
1220 									bool b = true;
1221 									for(; i3 < i2; i3++) {
1222 										switch((*str_names)[i3]) {
1223 											case '-': {b = false; break;}
1224 											case 'r': {b_prop_ref = b; b = true; break;}
1225 										}
1226 									}
1227 									i3++;
1228 									i2 = 0;
1229 								}
1230 								if(names_i == string::npos) {stmp = str_names->substr(i3, str_names->length() - i3);}
1231 								else {stmp = str_names->substr(i3, names_i - i3);}
1232 								remove_blank_ends(stmp);
1233 								if(!stmp.empty()) {
1234 									if(b_prop_ref) had_ref = true;
1235 									dp->addName(stmp, b_prop_ref);
1236 								}
1237 								if(names_i == string::npos) {break;}
1238 								names_i++;
1239 							}
1240 							if(str_names != &default_prop_names && !default_prop_names.empty()) {
1241 								if(default_prop_names[0] == '!') {
1242 									names_i = default_prop_names.find('!', 1) + 1;
1243 								} else {
1244 									names_i = 0;
1245 								}
1246 								i2 = 0;
1247 								while(true) {
1248 									size_t i3 = names_i;
1249 									names_i = default_prop_names.find(",", i3);
1250 									if(i2 == 0) {
1251 										i2 = default_prop_names.find(":", i3);
1252 									}
1253 									bool b_prop_ref = false;
1254 									if(i2 < names_i) {
1255 										bool b = true;
1256 										for(; i3 < i2; i3++) {
1257 											switch(default_prop_names[i3]) {
1258 												case '-': {b = false; break;}
1259 												case 'r': {b_prop_ref = b; b = true; break;}
1260 											}
1261 										}
1262 										i3++;
1263 										i2 = 0;
1264 									}
1265 									if(b_prop_ref || (!had_ref && names_i == string::npos)) {
1266 										had_ref = true;
1267 										if(names_i == string::npos) {stmp = default_prop_names.substr(i3, default_prop_names.length() - i3);}
1268 										else {stmp = default_prop_names.substr(i3, names_i - i3);}
1269 										remove_blank_ends(stmp);
1270 										size_t i4 = dp->hasName(stmp);
1271 										if(i4 > 0) {
1272 											dp->setNameIsReference(i4, true);
1273 										} else if(!stmp.empty()) {
1274 											dp->addName(stmp, true);
1275 										}
1276 									}
1277 									if(names_i == string::npos) {break;}
1278 									names_i++;
1279 								}
1280 							}
1281 							if(!had_ref && dp->countNames() > 0) dp->setNameIsReference(1, true);
1282 						} else {
1283 							bool b = false;
1284 							for(size_t i = 0; i < 10; i++) {
1285 								if(!prop_names[i].empty()) {
1286 									if(!b && ref_prop_names[i].empty()) {
1287 										dp->addName(prop_names[i], true, i + 1);
1288 										b = true;
1289 									} else {
1290 										dp->addName(prop_names[i], false, i + 1);
1291 									}
1292 									prop_names[i] = "";
1293 								}
1294 							}
1295 							for(size_t i = 0; i < 10; i++) {
1296 								if(!ref_prop_names[i].empty()) {
1297 									if(!b) {
1298 										dp->addName(ref_prop_names[i], true);
1299 										b = true;
1300 									} else {
1301 										dp->addName(ref_prop_names[i], false);
1302 									}
1303 									ref_prop_names[i] = "";
1304 								}
1305 							}
1306 						}
1307 						dp->setUserModified(is_user_defs);
1308 						dc->addProperty(dp);
1309 					} else if(!xmlStrcmp(child->name, (const xmlChar*) "argument")) {
1310 						child2 = child->xmlChildrenNode;
1311 						argname = ""; best_argname = false; next_best_argname = false;
1312 						while(child2 != NULL) {
1313 							if(!xmlStrcmp(child2->name, (const xmlChar*) "title")) {
1314 								XML_GET_LOCALE_STRING_FROM_TEXT(child2, argname, best_argname, next_best_argname)
1315 							}
1316 							child2 = child2->next;
1317 						}
1318 						itmp = 1;
1319 						XML_GET_INT_FROM_PROP(child, "index", itmp);
1320 						if(dc->getArgumentDefinition(itmp)) {
1321 							dc->getArgumentDefinition(itmp)->setName(argname);
1322 						}
1323 					} else if(!xmlStrcmp(child->name, (const xmlChar*) "object_argument")) {
1324 						child2 = child->xmlChildrenNode;
1325 						argname = ""; best_argname = false; next_best_argname = false;
1326 						while(child2 != NULL) {
1327 							if(!xmlStrcmp(child2->name, (const xmlChar*) "title")) {
1328 								XML_GET_LOCALE_STRING_FROM_TEXT(child2, argname, best_argname, next_best_argname)
1329 							}
1330 							child2 = child2->next;
1331 						}
1332 						itmp = 1;
1333 						if(dc->getArgumentDefinition(itmp)) {
1334 							if(!argname.empty() && argname[0] == '!') {
1335 								size_t i = argname.find('!', 1);
1336 								if(i == string::npos) {
1337 									dc->getArgumentDefinition(itmp)->setName(argname);
1338 								} else if(i + 1 < argname.length()) {
1339 									dc->getArgumentDefinition(itmp)->setName(argname.substr(i + 1, argname.length() - (i + 1)));
1340 								}
1341 							} else {
1342 								dc->getArgumentDefinition(itmp)->setName(argname);
1343 							}
1344 						}
1345 					} else if(!xmlStrcmp(child->name, (const xmlChar*) "property_argument")) {
1346 						child2 = child->xmlChildrenNode;
1347 						argname = ""; best_argname = false; next_best_argname = false;
1348 						while(child2 != NULL) {
1349 							if(!xmlStrcmp(child2->name, (const xmlChar*) "title")) {
1350 								XML_GET_LOCALE_STRING_FROM_TEXT(child2, argname, best_argname, next_best_argname)
1351 							}
1352 							child2 = child2->next;
1353 						}
1354 						itmp = 2;
1355 						if(dc->getArgumentDefinition(itmp)) {
1356 							if(!argname.empty() && argname[0] == '!') {
1357 								size_t i = argname.find('!', 1);
1358 								if(i == string::npos) {
1359 									dc->getArgumentDefinition(itmp)->setName(argname);
1360 								} else if(i + 1 < argname.length()) {
1361 									dc->getArgumentDefinition(itmp)->setName(argname.substr(i + 1, argname.length() - (i + 1)));
1362 								}
1363 							} else {
1364 								dc->getArgumentDefinition(itmp)->setName(argname);
1365 							}
1366 						}
1367 					} else if(!xmlStrcmp(child->name, (const xmlChar*) "default_property")) {
1368 						XML_DO_FROM_TEXT(child, dc->setDefaultProperty)
1369 					} else if(!builtin && !xmlStrcmp(child->name, (const xmlChar*) "copyright")) {
1370 						XML_DO_FROM_TEXT(child, dc->setCopyright)
1371 					} else if(!builtin && !xmlStrcmp(child->name, (const xmlChar*) "datafile")) {
1372 						XML_DO_FROM_TEXT(child, dc->setDefaultDataFile)
1373 					} else if(!xmlStrcmp(child->name, (const xmlChar*) "example")) {
1374 						XML_DO_FROM_TEXT(child, dc->setExample);
1375 					} else ITEM_READ_NAME(functionNameIsValid)
1376 					 else ITEM_READ_DTH
1377 					 else {
1378 						ITEM_READ_NAMES
1379 					}
1380 					child = child->next;
1381 				}
1382 				if(new_names) {
1383 					if(builtin) {
1384 						ITEM_SAVE_BUILTIN_NAMES
1385 					}
1386 					ITEM_SET_BEST_NAMES(functionNameIsValid)
1387 					ITEM_SET_REFERENCE_NAMES(functionNameIsValid)
1388 					if(builtin) {
1389 						ITEM_SET_BUILTIN_NAMES
1390 					}
1391 				} else {
1392 					if(builtin) {
1393 						BUILTIN_NAMES_1
1394 					}
1395 					ITEM_SET_NAME_2
1396 					ITEM_SET_NAME_3
1397 					if(builtin) {
1398 						BUILTIN_NAMES_2
1399 					}
1400 				}
1401 				ITEM_SET_DTH
1402 				if(check_duplicates && !is_user_defs) {
1403 					for(size_t i = 1; i <= dc->countNames();) {
1404 						if(getActiveFunction(dc->getName(i).name)) dc->removeName(i);
1405 						else i++;
1406 					}
1407 				}
1408 				if(!builtin && dc->countNames() == 0) {
1409 					dc->destroy();
1410 					dc = NULL;
1411 				} else {
1412 					dc->setChanged(builtin && is_user_defs);
1413 					if(!builtin) addDataSet(dc, true, is_user_defs);
1414 				}
1415 				done_something = true;
1416 			} else if(!xmlStrcmp(cur->name, (const xmlChar*) "builtin_function")) {
1417 				XML_GET_STRING_FROM_PROP(cur, "name", name)
1418 				f = getFunction(name);
1419 				if(f) {
1420 					XML_GET_FALSE_FROM_PROP(cur, "active", active)
1421 					f->setLocal(is_user_defs, active);
1422 					f->setCategory(category);
1423 					item = f;
1424 					child = cur->xmlChildrenNode;
1425 					ITEM_INIT_DTH
1426 					ITEM_INIT_NAME
1427 					while(child != NULL) {
1428 						if(!xmlStrcmp(child->name, (const xmlChar*) "argument")) {
1429 							child2 = child->xmlChildrenNode;
1430 							argname = ""; best_argname = false; next_best_argname = false;
1431 							while(child2 != NULL) {
1432 								if(!xmlStrcmp(child2->name, (const xmlChar*) "title")) {
1433 									XML_GET_LOCALE_STRING_FROM_TEXT(child2, argname, best_argname, next_best_argname)
1434 								}
1435 								child2 = child2->next;
1436 							}
1437 							itmp = 1;
1438 							XML_GET_INT_FROM_PROP(child, "index", itmp);
1439 							if(f->getArgumentDefinition(itmp)) {
1440 								if(!argname.empty() && argname[0] == '!') {
1441 									size_t i = argname.find('!', 1);
1442 									if(i == string::npos) {
1443 										f->getArgumentDefinition(itmp)->setName(argname);
1444 									} else if(i + 1 < argname.length()) {
1445 										f->getArgumentDefinition(itmp)->setName(argname.substr(i + 1, argname.length() - (i + 1)));
1446 									}
1447 								} else {
1448 									f->getArgumentDefinition(itmp)->setName(argname);
1449 								}
1450 							} else if(itmp <= f->maxargs() || itmp <= f->minargs()) {
1451 								if(!argname.empty() && argname[0] == '!') {
1452 									size_t i = argname.find('!', 1);
1453 									if(i == string::npos) {
1454 										f->setArgumentDefinition(itmp, new Argument(argname, false));
1455 									} else if(i + 1 < argname.length()) {
1456 										f->setArgumentDefinition(itmp, new Argument(argname.substr(i + 1, argname.length() - (i + 1)), false));
1457 									}
1458 								} else {
1459 									f->setArgumentDefinition(itmp, new Argument(argname, false));
1460 								}
1461 							}
1462 						} else if(!xmlStrcmp(child->name, (const xmlChar*) "example")) {
1463 							XML_DO_FROM_TEXT(child, f->setExample);
1464 						} else ITEM_READ_NAME(functionNameIsValid)
1465 						 else ITEM_READ_DTH
1466 						 else {
1467 							ITEM_READ_NAMES
1468 						}
1469 						child = child->next;
1470 					}
1471 					if(new_names) {
1472 						ITEM_SAVE_BUILTIN_NAMES
1473 						ITEM_SET_BEST_NAMES(functionNameIsValid)
1474 						ITEM_SET_REFERENCE_NAMES(functionNameIsValid)
1475 						ITEM_SET_BUILTIN_NAMES
1476 					} else {
1477 						BUILTIN_NAMES_1
1478 						ITEM_SET_NAME_2
1479 						ITEM_SET_NAME_3
1480 						BUILTIN_NAMES_2
1481 					}
1482 					ITEM_SET_DTH
1483 					f->setChanged(false);
1484 					done_something = true;
1485 				}
1486 			} else if(!xmlStrcmp(cur->name, (const xmlChar*) "unknown")) {
1487 				if(VERSION_BEFORE(0, 6, 3)) {
1488 					XML_GET_STRING_FROM_PROP(cur, "name", name)
1489 				} else {
1490 					name = "";
1491 				}
1492 				XML_GET_FALSE_FROM_PROP(cur, "active", active)
1493 				svalue = "";
1494 				v = new UnknownVariable(category, "", "", is_user_defs, false, active);
1495 				item = v;
1496 				done_something = true;
1497 				child = cur->xmlChildrenNode;
1498 				b = true;
1499 				ITEM_INIT_DTH
1500 				ITEM_INIT_NAME
1501 				while(child != NULL) {
1502 					if(!xmlStrcmp(child->name, (const xmlChar*) "type")) {
1503 						XML_GET_STRING_FROM_TEXT(child, stmp);
1504 						if(!((UnknownVariable*) v)->assumptions()) ((UnknownVariable*) v)->setAssumptions(new Assumptions());
1505 						if(stmp == "integer") ((UnknownVariable*) v)->assumptions()->setType(ASSUMPTION_TYPE_INTEGER);
1506 						else if(stmp == "rational") ((UnknownVariable*) v)->assumptions()->setType(ASSUMPTION_TYPE_RATIONAL);
1507 						else if(stmp == "real") ((UnknownVariable*) v)->assumptions()->setType(ASSUMPTION_TYPE_REAL);
1508 						else if(stmp == "complex") ((UnknownVariable*) v)->assumptions()->setType(ASSUMPTION_TYPE_COMPLEX);
1509 						else if(stmp == "number") ((UnknownVariable*) v)->assumptions()->setType(ASSUMPTION_TYPE_NUMBER);
1510 						else if(stmp == "non-matrix") {
1511 							if(VERSION_BEFORE(0, 9, 13)) {
1512 								((UnknownVariable*) v)->assumptions()->setType(ASSUMPTION_TYPE_NUMBER);
1513 							} else {
1514 								((UnknownVariable*) v)->assumptions()->setType(ASSUMPTION_TYPE_NONMATRIX);
1515 							}
1516 						} else if(stmp == "none") {
1517 							if(VERSION_BEFORE(0, 9, 13)) {
1518 								((UnknownVariable*) v)->assumptions()->setType(ASSUMPTION_TYPE_NUMBER);
1519 							} else {
1520 								((UnknownVariable*) v)->assumptions()->setType(ASSUMPTION_TYPE_NONE);
1521 							}
1522 						}
1523 					} else if(!xmlStrcmp(child->name, (const xmlChar*) "sign")) {
1524 						XML_GET_STRING_FROM_TEXT(child, stmp);
1525 						if(!((UnknownVariable*) v)->assumptions()) ((UnknownVariable*) v)->setAssumptions(new Assumptions());
1526 						if(stmp == "non-zero") ((UnknownVariable*) v)->assumptions()->setSign(ASSUMPTION_SIGN_NONZERO);
1527 						else if(stmp == "non-positive") ((UnknownVariable*) v)->assumptions()->setSign(ASSUMPTION_SIGN_NONPOSITIVE);
1528 						else if(stmp == "negative") ((UnknownVariable*) v)->assumptions()->setSign(ASSUMPTION_SIGN_NEGATIVE);
1529 						else if(stmp == "non-negative") ((UnknownVariable*) v)->assumptions()->setSign(ASSUMPTION_SIGN_NONNEGATIVE);
1530 						else if(stmp == "positive") ((UnknownVariable*) v)->assumptions()->setSign(ASSUMPTION_SIGN_POSITIVE);
1531 						else if(stmp == "unknown") ((UnknownVariable*) v)->assumptions()->setSign(ASSUMPTION_SIGN_UNKNOWN);
1532 					} else ITEM_READ_NAME(variableNameIsValid)
1533 					 else ITEM_READ_DTH
1534 					 else {
1535 						ITEM_READ_NAMES
1536 					}
1537 					child = child->next;
1538 				}
1539 				if(new_names) {
1540 					ITEM_SET_BEST_NAMES(variableNameIsValid)
1541 					ITEM_SET_REFERENCE_NAMES(variableNameIsValid)
1542 				} else {
1543 					ITEM_SET_NAME_1(variableNameIsValid)
1544 					ITEM_SET_NAME_2
1545 					ITEM_SET_NAME_3
1546 				}
1547 				ITEM_SET_DTH
1548 				if(check_duplicates && !is_user_defs) {
1549 					for(size_t i = 1; i <= v->countNames();) {
1550 						if(getActiveVariable(v->getName(i).name) || getActiveUnit(v->getName(i).name) || getCompositeUnit(v->getName(i).name)) v->removeName(i);
1551 						else i++;
1552 					}
1553 				}
1554 				for(size_t i = 1; i <= v->countNames(); i++) {
1555 					if(v->getName(i).name == "x") {v_x->destroy(); v_x = (UnknownVariable*) v; break;}
1556 					if(v->getName(i).name == "y") {v_y->destroy(); v_y = (UnknownVariable*) v; break;}
1557 					if(v->getName(i).name == "z") {v_z->destroy(); v_z = (UnknownVariable*) v; break;}
1558 				}
1559 				if(v->countNames() == 0) {
1560 					v->destroy();
1561 					v = NULL;
1562 				} else {
1563 					addVariable(v, true, is_user_defs);
1564 					v->setChanged(false);
1565 				}
1566 			} else if(!xmlStrcmp(cur->name, (const xmlChar*) "variable")) {
1567 				if(VERSION_BEFORE(0, 6, 3)) {
1568 					XML_GET_STRING_FROM_PROP(cur, "name", name)
1569 				} else {
1570 					name = "";
1571 				}
1572 				XML_GET_FALSE_FROM_PROP(cur, "active", active)
1573 				svalue = "";
1574 				v = new KnownVariable(category, "", "", "", is_user_defs, false, active);
1575 				item = v;
1576 				done_something = true;
1577 				child = cur->xmlChildrenNode;
1578 				b = true;
1579 				ITEM_INIT_DTH
1580 				ITEM_INIT_NAME
1581 				while(child != NULL) {
1582 					if(!xmlStrcmp(child->name, (const xmlChar*) "value")) {
1583 						XML_DO_FROM_TEXT(child, ((KnownVariable*) v)->set);
1584 						XML_GET_STRING_FROM_PROP(child, "relative_uncertainty", suncertainty)
1585 						unc_rel = false;
1586 						if(suncertainty.empty()) {XML_GET_STRING_FROM_PROP(child, "uncertainty", suncertainty)}
1587 						else unc_rel = true;
1588 						((KnownVariable*) v)->setUncertainty(suncertainty, unc_rel);
1589 						XML_DO_FROM_PROP(child, "unit", ((KnownVariable*) v)->setUnit)
1590 						XML_GET_PREC_FROM_PROP(child, prec)
1591 						v->setPrecision(prec);
1592 						XML_GET_APPROX_FROM_PROP(child, b);
1593 						if(b) v->setApproximate(true);
1594 					} else ITEM_READ_NAME(variableNameIsValid)
1595 					 else ITEM_READ_DTH
1596 					 else {
1597 						ITEM_READ_NAMES
1598 					}
1599 					child = child->next;
1600 				}
1601 				if(new_names) {
1602 					ITEM_SET_BEST_NAMES(variableNameIsValid)
1603 					ITEM_SET_REFERENCE_NAMES(variableNameIsValid)
1604 				} else {
1605 					ITEM_SET_NAME_1(variableNameIsValid)
1606 					ITEM_SET_NAME_2
1607 					ITEM_SET_NAME_3
1608 				}
1609 				ITEM_SET_DTH
1610 				if(check_duplicates && !is_user_defs) {
1611 					for(size_t i = 1; i <= v->countNames();) {
1612 						if(getActiveVariable(v->getName(i).name) || getActiveUnit(v->getName(i).name) || getCompositeUnit(v->getName(i).name)) v->removeName(i);
1613 						else i++;
1614 					}
1615 				}
1616 				if(v->countNames() == 0) {
1617 					v->destroy();
1618 					v = NULL;
1619 				} else {
1620 					addVariable(v, true, is_user_defs);
1621 					item->setChanged(false);
1622 				}
1623 			} else if(!xmlStrcmp(cur->name, (const xmlChar*) "builtin_variable")) {
1624 				XML_GET_STRING_FROM_PROP(cur, "name", name)
1625 				v = getVariable(name);
1626 				if(v) {
1627 					XML_GET_FALSE_FROM_PROP(cur, "active", active)
1628 					v->setLocal(is_user_defs, active);
1629 					v->setCategory(category);
1630 					item = v;
1631 					child = cur->xmlChildrenNode;
1632 					ITEM_INIT_DTH
1633 					ITEM_INIT_NAME
1634 					while(child != NULL) {
1635 						ITEM_READ_NAME(variableNameIsValid)
1636 						 else ITEM_READ_DTH
1637 						 else {
1638 							ITEM_READ_NAMES
1639 						}
1640 						child = child->next;
1641 					}
1642 					if(new_names) {
1643 						ITEM_SAVE_BUILTIN_NAMES
1644 						ITEM_SET_BEST_NAMES(variableNameIsValid)
1645 						ITEM_SET_REFERENCE_NAMES(variableNameIsValid)
1646 						ITEM_SET_BUILTIN_NAMES
1647 					} else {
1648 						BUILTIN_NAMES_1
1649 						ITEM_SET_NAME_2
1650 						ITEM_SET_NAME_3
1651 						BUILTIN_NAMES_2
1652 					}
1653 					ITEM_SET_DTH
1654 					v->setChanged(false);
1655 					done_something = true;
1656 				}
1657 			} else if(!xmlStrcmp(cur->name, (const xmlChar*) "unit")) {
1658 				XML_GET_STRING_FROM_PROP(cur, "type", type)
1659 				if(type == "base") {
1660 					if(VERSION_BEFORE(0, 6, 3)) {
1661 						XML_GET_STRING_FROM_PROP(cur, "name", name)
1662 					} else {
1663 						name = "";
1664 					}
1665 					XML_GET_FALSE_FROM_PROP(cur, "active", active)
1666 					u = new Unit(category, "", "", "", "", is_user_defs, false, active);
1667 					item = u;
1668 					child = cur->xmlChildrenNode;
1669 					singular = ""; best_singular = false; next_best_singular = false;
1670 					plural = ""; best_plural = false; next_best_plural = false;
1671 					countries = "", best_countries = false, next_best_countries = false;
1672 					use_with_prefixes_set = false;
1673 					ITEM_INIT_DTH
1674 					ITEM_INIT_NAME
1675 					while(child != NULL) {
1676 						if(!xmlStrcmp(child->name, (const xmlChar*) "system")) {
1677 							XML_DO_FROM_TEXT(child, u->setSystem)
1678 						} else if(!xmlStrcmp(child->name, (const xmlChar*) "use_with_prefixes")) {
1679 							XML_GET_TRUE_FROM_TEXT(child, use_with_prefixes)
1680 							use_with_prefixes_set = true;
1681 						} else if((VERSION_BEFORE(0, 6, 3)) && !xmlStrcmp(child->name, (const xmlChar*) "singular")) {
1682 							XML_GET_LOCALE_STRING_FROM_TEXT(child, singular, best_singular, next_best_singular)
1683 							if(!unitNameIsValid(singular, version_numbers, is_user_defs)) {
1684 								singular = "";
1685 							}
1686 						} else if((VERSION_BEFORE(0, 6, 3)) && !xmlStrcmp(child->name, (const xmlChar*) "plural") && !xmlGetProp(child, (xmlChar*) "index")) {
1687 							XML_GET_LOCALE_STRING_FROM_TEXT(child, plural, best_plural, next_best_plural)
1688 							if(!unitNameIsValid(plural, version_numbers, is_user_defs)) {
1689 								plural = "";
1690 							}
1691 						} else if(!xmlStrcmp(child->name, (const xmlChar*) "countries")) {
1692 							XML_GET_LOCALE_STRING_FROM_TEXT(child, countries, best_countries, next_best_countries)
1693 						} else ITEM_READ_NAME(unitNameIsValid)
1694 						 else ITEM_READ_DTH
1695 						 else {
1696 							ITEM_READ_NAMES
1697 						}
1698 						child = child->next;
1699 					}
1700 					u->setCountries(countries);
1701 					if(new_names) {
1702 						ITEM_SET_BEST_NAMES(unitNameIsValid)
1703 						ITEM_SET_REFERENCE_NAMES(unitNameIsValid)
1704 					} else {
1705 						ITEM_SET_SHORT_NAME
1706 						ITEM_SET_SINGULAR
1707 						ITEM_SET_PLURAL
1708 						ITEM_SET_NAME_2
1709 						ITEM_SET_NAME_3
1710 					}
1711 					ITEM_SET_DTH
1712 					if(use_with_prefixes_set) {
1713 						u->setUseWithPrefixesByDefault(use_with_prefixes);
1714 					}
1715 					if(check_duplicates && !is_user_defs) {
1716 						for(size_t i = 1; i <= u->countNames();) {
1717 							if(getActiveVariable(u->getName(i).name) || getActiveUnit(u->getName(i).name) || getCompositeUnit(u->getName(i).name)) u->removeName(i);
1718 							else i++;
1719 						}
1720 					}
1721 					if(u->countNames() == 0) {
1722 						u->destroy();
1723 						u = NULL;
1724 					} else {
1725 						if(!is_user_defs && u->referenceName() == "s") u_second = u;
1726 						else if(!is_user_defs && u->referenceName() == "K") priv->u_kelvin = u;
1727 						addUnit(u, true, is_user_defs);
1728 						u->setChanged(false);
1729 					}
1730 					done_something = true;
1731 				} else if(type == "alias") {
1732 					if(VERSION_BEFORE(0, 6, 3)) {
1733 						XML_GET_STRING_FROM_PROP(cur, "name", name)
1734 					} else {
1735 						name = "";
1736 					}
1737 					XML_GET_FALSE_FROM_PROP(cur, "active", active)
1738 					u = NULL;
1739 					child = cur->xmlChildrenNode;
1740 					singular = ""; best_singular = false; next_best_singular = false;
1741 					plural = ""; best_plural = false; next_best_plural = false;
1742 					countries = "", best_countries = false, next_best_countries = false;
1743 					bool b_currency = false;
1744 					use_with_prefixes_set = false;
1745 					usystem = "";
1746 					prec = -1;
1747 					ITEM_INIT_DTH
1748 					ITEM_INIT_NAME
1749 					unc_rel = false;
1750 					while(child != NULL) {
1751 						if(!xmlStrcmp(child->name, (const xmlChar*) "base")) {
1752 							child2 = child->xmlChildrenNode;
1753 							exponent = 1;
1754 							mix_priority = 0;
1755 							mix_min = 0;
1756 							svalue = "";
1757 							inverse = "";
1758 							suncertainty = "";
1759 							b = true;
1760 							while(child2 != NULL) {
1761 								if(!xmlStrcmp(child2->name, (const xmlChar*) "unit")) {
1762 									XML_GET_STRING_FROM_TEXT(child2, base);
1763 									u = getUnit(base);
1764 									b_currency = (!is_user_defs && u && u == u_euro);
1765 									if(!u) {
1766 										u = getCompositeUnit(base);
1767 									}
1768 								} else if(!xmlStrcmp(child2->name, (const xmlChar*) "relation")) {
1769 									XML_GET_STRING_FROM_TEXT(child2, svalue);
1770 									XML_GET_APPROX_FROM_PROP(child2, b)
1771 									XML_GET_PREC_FROM_PROP(child2, prec)
1772 									XML_GET_STRING_FROM_PROP(child2, "relative_uncertainty", suncertainty)
1773 									if(suncertainty.empty()) {XML_GET_STRING_FROM_PROP(child2, "uncertainty", suncertainty)}
1774 									else unc_rel = true;
1775 								} else if(!xmlStrcmp(child2->name, (const xmlChar*) "reverse_relation")) {
1776 									XML_GET_STRING_FROM_TEXT(child2, inverse);
1777 								} else if(!xmlStrcmp(child2->name, (const xmlChar*) "inverse_relation")) {
1778 									XML_GET_STRING_FROM_TEXT(child2, inverse);
1779 								} else if(!xmlStrcmp(child2->name, (const xmlChar*) "exponent")) {
1780 									XML_GET_STRING_FROM_TEXT(child2, stmp);
1781 									if(stmp.empty()) {
1782 										exponent = 1;
1783 									} else {
1784 										exponent = s2i(stmp);
1785 									}
1786 								} else if(!xmlStrcmp(child2->name, (const xmlChar*) "mix")) {
1787 									XML_GET_INT_FROM_PROP(child2, "min", mix_min);
1788 									XML_GET_STRING_FROM_TEXT(child2, stmp);
1789 									if(stmp.empty()) {
1790 										mix_priority = 0;
1791 									} else {
1792 										mix_priority = s2i(stmp);
1793 									}
1794 								}
1795 								child2 = child2->next;
1796 							}
1797 						} else if(!xmlStrcmp(child->name, (const xmlChar*) "system")) {
1798 							XML_GET_STRING_FROM_TEXT(child, usystem);
1799 						} else if(!xmlStrcmp(child->name, (const xmlChar*) "use_with_prefixes")) {
1800 							XML_GET_TRUE_FROM_TEXT(child, use_with_prefixes)
1801 							use_with_prefixes_set = true;
1802 						} else if((VERSION_BEFORE(0, 6, 3)) && !xmlStrcmp(child->name, (const xmlChar*) "singular")) {
1803 							XML_GET_LOCALE_STRING_FROM_TEXT(child, singular, best_singular, next_best_singular)
1804 							if(!unitNameIsValid(singular, version_numbers, is_user_defs)) {
1805 								singular = "";
1806 							}
1807 						} else if((VERSION_BEFORE(0, 6, 3)) && !xmlStrcmp(child->name, (const xmlChar*) "plural") && !xmlGetProp(child, (xmlChar*) "index")) {
1808 							XML_GET_LOCALE_STRING_FROM_TEXT(child, plural, best_plural, next_best_plural)
1809 							if(!unitNameIsValid(plural, version_numbers, is_user_defs)) {
1810 								plural = "";
1811 							}
1812 						} else if(!xmlStrcmp(child->name, (const xmlChar*) "countries")) {
1813 							XML_GET_LOCALE_STRING_FROM_TEXT(child, countries, best_countries, next_best_countries)
1814 						} else ITEM_READ_NAME(unitNameIsValid)
1815 						 else ITEM_READ_DTH
1816 						 else {
1817 							ITEM_READ_NAMES
1818 						}
1819 						child = child->next;
1820 					}
1821 					if(!u) {
1822 						ITEM_CLEAR_NAMES
1823 						if(!in_unfinished) {
1824 							unfinished_nodes.push_back(cur);
1825 							unfinished_cats.push_back(category);
1826 						}
1827 					} else {
1828 						au = new AliasUnit(category, name, plural, singular, title, u, svalue, exponent, inverse, is_user_defs, false, active);
1829 						au->setCountries(countries);
1830 						if(mix_priority > 0) {
1831 							au->setMixWithBase(mix_priority);
1832 							au->setMixWithBaseMinimum(mix_min);
1833 						}
1834 						au->setDescription(description);
1835 						au->setPrecision(prec);
1836 						if(b) au->setApproximate(true);
1837 						au->setUncertainty(suncertainty, unc_rel);
1838 						if(hidden >= 0) au->setHidden(hidden);
1839 						au->setSystem(usystem);
1840 						if(use_with_prefixes_set) {
1841 							au->setUseWithPrefixesByDefault(use_with_prefixes);
1842 						}
1843 						item = au;
1844 						if(new_names) {
1845 							ITEM_SET_BEST_NAMES(unitNameIsValid)
1846 							ITEM_SET_REFERENCE_NAMES(unitNameIsValid)
1847 						} else {
1848 							ITEM_SET_NAME_2
1849 							ITEM_SET_NAME_3
1850 						}
1851 						if(b_currency && !au->referenceName().empty()) {
1852 							u = getUnit(au->referenceName());
1853 							if(u && u->subtype() == SUBTYPE_ALIAS_UNIT && ((AliasUnit*) u)->baseUnit() == u_euro) u->destroy();
1854 						}
1855 						if(check_duplicates && !is_user_defs) {
1856 							for(size_t i = 1; i <= au->countNames();) {
1857 								if(getActiveVariable(au->getName(i).name) || getActiveUnit(au->getName(i).name) || getCompositeUnit(au->getName(i).name)) au->removeName(i);
1858 								else i++;
1859 							}
1860 						}
1861 						if(au->countNames() == 0 || (b_remove_cent && au->firstBaseUnit() == u_usd && au->hasName("cent"))) {
1862 							au->destroy();
1863 							au = NULL;
1864 						} else {
1865 							if(!is_user_defs && au->baseUnit() == u_second) {
1866 								if(au->referenceName() == "d" || au->referenceName() == "day") u_day = au;
1867 								else if(au->referenceName() == "year") u_year = au;
1868 								else if(au->referenceName() == "month") u_month = au;
1869 								else if(au->referenceName() == "min") u_minute = au;
1870 								else if(au->referenceName() == "h") u_hour = au;
1871 							} else if(!is_user_defs && au->baseUnit() == priv->u_kelvin) {
1872 								if(au->referenceName() == "oR") priv->u_rankine = au;
1873 								else if(au->referenceName() == "oC") priv->u_celsius = au;
1874 								else if(au->referenceName() == "oF") priv->u_fahrenheit = au;
1875 							}
1876 							addUnit(au, true, is_user_defs);
1877 							au->setChanged(false);
1878 						}
1879 						done_something = true;
1880 					}
1881 				} else if(type == "composite") {
1882 					if(VERSION_BEFORE(0, 6, 3)) {
1883 						XML_GET_STRING_FROM_PROP(cur, "name", name)
1884 					} else {
1885 						name = "";
1886 					}
1887 					XML_GET_FALSE_FROM_PROP(cur, "active", active)
1888 					child = cur->xmlChildrenNode;
1889 					usystem = "";
1890 					cu = NULL;
1891 					ITEM_INIT_DTH
1892 					ITEM_INIT_NAME
1893 					b = true;
1894 					while(child != NULL) {
1895 						u = NULL;
1896 						if(!xmlStrcmp(child->name, (const xmlChar*) "part")) {
1897 							child2 = child->xmlChildrenNode;
1898 							p = NULL;
1899 							exponent = 1;
1900 							while(child2 != NULL) {
1901 								if(!xmlStrcmp(child2->name, (const xmlChar*) "unit")) {
1902 									XML_GET_STRING_FROM_TEXT(child2, base);
1903 									u = getUnit(base);
1904 									if(!u) {
1905 										u = getCompositeUnit(base);
1906 									}
1907 								} else if(!xmlStrcmp(child2->name, (const xmlChar*) "prefix")) {
1908 									XML_GET_STRING_FROM_PROP(child2, "type", stmp)
1909 									XML_GET_STRING_FROM_TEXT(child2, svalue);
1910 									p = NULL;
1911 									if(stmp == "binary") {
1912 										litmp = s2i(svalue);
1913 										if(litmp != 0) {
1914 											p = getExactBinaryPrefix(litmp);
1915 											if(!p) b = false;
1916 										}
1917 									} else if(stmp == "number") {
1918 										nr.set(stmp);
1919 										if(!nr.isZero()) {
1920 											p = getExactPrefix(stmp);
1921 											if(!p) b = false;
1922 										}
1923 									} else {
1924 										litmp = s2i(svalue);
1925 										if(litmp != 0) {
1926 											p = getExactDecimalPrefix(litmp);
1927 											if(!p) b = false;
1928 										}
1929 									}
1930 									if(!b) {
1931 										if(cu) {
1932 											delete cu;
1933 										}
1934 										cu = NULL;
1935 										break;
1936 									}
1937 								} else if(!xmlStrcmp(child2->name, (const xmlChar*) "exponent")) {
1938 									XML_GET_STRING_FROM_TEXT(child2, stmp);
1939 									if(stmp.empty()) {
1940 										exponent = 1;
1941 									} else {
1942 										exponent = s2i(stmp);
1943 									}
1944 								}
1945 								child2 = child2->next;
1946 							}
1947 							if(!b) break;
1948 							if(u) {
1949 								if(!cu) {
1950 									cu = new CompositeUnit("", "", "", "", is_user_defs, false, active);
1951 								}
1952 								cu->add(u, exponent, p);
1953 							} else {
1954 								if(cu) delete cu;
1955 								cu = NULL;
1956 								if(!in_unfinished) {
1957 									unfinished_nodes.push_back(cur);
1958 									unfinished_cats.push_back(category);
1959 								}
1960 								break;
1961 							}
1962 						} else if(!xmlStrcmp(child->name, (const xmlChar*) "system")) {
1963 							XML_GET_STRING_FROM_TEXT(child, usystem);
1964 						} else if(!xmlStrcmp(child->name, (const xmlChar*) "use_with_prefixes")) {
1965 							XML_GET_TRUE_FROM_TEXT(child, use_with_prefixes)
1966 							use_with_prefixes_set = true;
1967 						} else ITEM_READ_NAME(unitNameIsValid)
1968 						 else ITEM_READ_DTH
1969 						 else {
1970 							ITEM_READ_NAMES
1971 						}
1972 						child = child->next;
1973 					}
1974 					if(cu) {
1975 						item = cu;
1976 						cu->setCategory(category);
1977 						cu->setSystem(usystem);
1978 						/*if(use_with_prefixes_set) {
1979 							cu->setUseWithPrefixesByDefault(use_with_prefixes);
1980 						}*/
1981 						if(new_names) {
1982 							ITEM_SET_BEST_NAMES(unitNameIsValid)
1983 							ITEM_SET_REFERENCE_NAMES(unitNameIsValid)
1984 						} else {
1985 							ITEM_SET_NAME_1(unitNameIsValid)
1986 							ITEM_SET_NAME_2
1987 							ITEM_SET_NAME_3
1988 						}
1989 						ITEM_SET_DTH
1990 						if(check_duplicates && !is_user_defs) {
1991 							for(size_t i = 1; i <= cu->countNames();) {
1992 								if(getActiveVariable(cu->getName(i).name) || getActiveUnit(cu->getName(i).name) || getCompositeUnit(cu->getName(i).name)) cu->removeName(i);
1993 								else i++;
1994 							}
1995 						}
1996 						if(cu->countNames() == 0) {
1997 							cu->destroy();
1998 							cu = NULL;
1999 						} else {
2000 							addUnit(cu, true, is_user_defs);
2001 							cu->setChanged(false);
2002 						}
2003 						done_something = true;
2004 					} else {
2005 						ITEM_CLEAR_NAMES
2006 					}
2007 				}
2008 			} else if(!xmlStrcmp(cur->name, (const xmlChar*) "builtin_unit")) {
2009 				XML_GET_STRING_FROM_PROP(cur, "name", name)
2010 				u = getUnit(name);
2011 				if(!u) {
2012 					u = getCompositeUnit(name);
2013 				}
2014 				if(u) {
2015 					XML_GET_FALSE_FROM_PROP(cur, "active", active)
2016 					u->setLocal(is_user_defs, active);
2017 					u->setCategory(category);
2018 					item = u;
2019 					child = cur->xmlChildrenNode;
2020 					singular = ""; best_singular = false; next_best_singular = false;
2021 					plural = ""; best_plural = false; next_best_plural = false;
2022 					countries = "", best_countries = false, next_best_countries = false;
2023 					use_with_prefixes_set = false;
2024 					ITEM_INIT_DTH
2025 					ITEM_INIT_NAME
2026 					while(child != NULL) {
2027 						if(!xmlStrcmp(child->name, (const xmlChar*) "singular")) {
2028 							XML_GET_LOCALE_STRING_FROM_TEXT(child, singular, best_singular, next_best_singular)
2029 							if(!unitNameIsValid(singular, version_numbers, is_user_defs)) {
2030 								singular = "";
2031 							}
2032 						} else if(!xmlStrcmp(child->name, (const xmlChar*) "plural") && !xmlGetProp(child, (xmlChar*) "index")) {
2033 							XML_GET_LOCALE_STRING_FROM_TEXT(child, plural, best_plural, next_best_plural)
2034 							if(!unitNameIsValid(plural, version_numbers, is_user_defs)) {
2035 								plural = "";
2036 							}
2037 						} else if(!xmlStrcmp(child->name, (const xmlChar*) "system")) {
2038 							XML_DO_FROM_TEXT(child, u->setSystem)
2039 						} else if(!xmlStrcmp(child->name, (const xmlChar*) "use_with_prefixes")) {
2040 							XML_GET_TRUE_FROM_TEXT(child, use_with_prefixes)
2041 							use_with_prefixes_set = true;
2042 						} else if(!xmlStrcmp(child->name, (const xmlChar*) "countries")) {
2043 							XML_GET_LOCALE_STRING_FROM_TEXT(child, countries, best_countries, next_best_countries)
2044 						} else ITEM_READ_NAME(unitNameIsValid)
2045 						 else ITEM_READ_DTH
2046 						 else {
2047 							ITEM_READ_NAMES
2048 						}
2049 						child = child->next;
2050 					}
2051 					if(use_with_prefixes_set) {
2052 						u->setUseWithPrefixesByDefault(use_with_prefixes);
2053 					}
2054 					u->setCountries(countries);
2055 					if(new_names) {
2056 						ITEM_SAVE_BUILTIN_NAMES
2057 						ITEM_SET_BEST_NAMES(unitNameIsValid)
2058 						ITEM_SET_REFERENCE_NAMES(unitNameIsValid)
2059 						ITEM_SET_BUILTIN_NAMES
2060 					} else {
2061 						BUILTIN_UNIT_NAMES_1
2062 						ITEM_SET_SINGULAR
2063 						ITEM_SET_PLURAL
2064 						ITEM_SET_NAME_2
2065 						ITEM_SET_NAME_3
2066 						BUILTIN_NAMES_2
2067 					}
2068 					ITEM_SET_DTH
2069 					if((u == u_usd || u == u_gbp) && !is_user_defs && !b_ignore_locale) {
2070 						struct lconv *lc = localeconv();
2071 						if(lc) {
2072 							string local_currency = lc->int_curr_symbol;
2073 							remove_blank_ends(local_currency);
2074 							if(local_currency.length() > 3) local_currency = local_currency.substr(0, 3);
2075 							if(!u->hasName(local_currency)) {
2076 								local_currency = lc->currency_symbol;
2077 								remove_blank_ends(local_currency);
2078 								if(u == u_usd && local_currency == "$") {
2079 									b_remove_cent = true;
2080 									size_t index = u->hasName("$");
2081 									if(index > 0) u->removeName(index);
2082 									index = u->hasName("dollar");
2083 									if(index > 0) u->removeName(index);
2084 									index = u->hasName("dollars");
2085 									if(index > 0) u->removeName(index);
2086 								} else if(u == u_gbp && local_currency == "£") {
2087 									size_t index = u->hasName("£");
2088 									if(index > 0) u->removeName(index);
2089 								}
2090 							}
2091 						}
2092 					}
2093 					if(u_usd && u->subtype() == SUBTYPE_ALIAS_UNIT && ((AliasUnit*) u)->firstBaseUnit() == u_usd) u->setHidden(true);
2094 					u->setChanged(false);
2095 					done_something = true;
2096 				}
2097 			} else if(!xmlStrcmp(cur->name, (const xmlChar*) "prefix")) {
2098 				child = cur->xmlChildrenNode;
2099 				XML_GET_STRING_FROM_PROP(cur, "type", type)
2100 				uname = ""; sexp = ""; svalue = ""; name = "";
2101 				bool b_best = false;
2102 				while(child != NULL) {
2103 					if(!xmlStrcmp(child->name, (const xmlChar*) "name")) {
2104 						lang = xmlNodeGetLang(child);
2105 						if(!lang) {
2106 							if(name.empty()) {
2107 								XML_GET_STRING_FROM_TEXT(child, name);
2108 							}
2109 						} else {
2110 							if(!b_best && !locale.empty()) {
2111 								if(locale == (char*) lang) {
2112 									XML_GET_STRING_FROM_TEXT(child, name);
2113 									b_best = true;
2114 								} else if(strlen((char*) lang) >= 2 && lang[0] == localebase[0] && lang[1] == localebase[1]) {
2115 									XML_GET_STRING_FROM_TEXT(child, name);
2116 								}
2117 							}
2118 							xmlFree(lang);
2119 						}
2120 					} else if(!xmlStrcmp(child->name, (const xmlChar*) "abbreviation")) {
2121 						XML_GET_STRING_FROM_TEXT(child, stmp);
2122 					} else if(!xmlStrcmp(child->name, (const xmlChar*) "unicode")) {
2123 						XML_GET_STRING_FROM_TEXT(child, uname);
2124 					} else if(!xmlStrcmp(child->name, (const xmlChar*) "exponent")) {
2125 						XML_GET_STRING_FROM_TEXT(child, sexp);
2126 					} else if(!xmlStrcmp(child->name, (const xmlChar*) "value")) {
2127 						XML_GET_STRING_FROM_TEXT(child, svalue);
2128 					}
2129 					child = child->next;
2130 				}
2131 				if(type == "decimal") {
2132 					addPrefix(new DecimalPrefix(s2i(sexp), name, stmp, uname));
2133 				} else if(type == "number") {
2134 					addPrefix(new NumberPrefix(svalue, name, stmp, uname));
2135 				} else if(type == "binary") {
2136 					addPrefix(new BinaryPrefix(s2i(sexp), name, stmp, uname));
2137 				} else {
2138 					if(svalue.empty()) {
2139 						addPrefix(new DecimalPrefix(s2i(sexp), name, stmp, uname));
2140 					} else {
2141 						addPrefix(new NumberPrefix(svalue, name, stmp, uname));
2142 					}
2143 				}
2144 				done_something = true;
2145 			}
2146 			after_load_object:
2147 			cur = NULL;
2148 			if(in_unfinished) {
2149 				if(done_something) {
2150 					in_unfinished--;
2151 					unfinished_nodes.erase(unfinished_nodes.begin() + in_unfinished);
2152 					unfinished_cats.erase(unfinished_cats.begin() + in_unfinished);
2153 				}
2154 				if((int) unfinished_nodes.size() > in_unfinished) {
2155 					cur = unfinished_nodes[in_unfinished];
2156 					category = unfinished_cats[in_unfinished];
2157 				} else if(done_something && unfinished_nodes.size() > 0) {
2158 					cur = unfinished_nodes[0];
2159 					category = unfinished_cats[0];
2160 					in_unfinished = 0;
2161 					done_something = false;
2162 				}
2163 				in_unfinished++;
2164 				done_something = false;
2165 			}
2166 		}
2167 		if(in_unfinished) {
2168 			break;
2169 		}
2170 		while(!nodes.empty() && nodes.back().empty()) {
2171 			size_t cat_i = category.rfind("/");
2172 			if(cat_i == string::npos) {
2173 				category = "";
2174 			} else {
2175 				category = category.substr(0, cat_i);
2176 			}
2177 			nodes.pop_back();
2178 		}
2179 		if(!nodes.empty()) {
2180 			cur = nodes.back().front();
2181 			nodes.back().pop();
2182 			nodes.resize(nodes.size() + 1);
2183 		} else {
2184 			if(unfinished_nodes.size() > 0) {
2185 				cur = unfinished_nodes[0];
2186 				category = unfinished_cats[0];
2187 				in_unfinished = 1;
2188 				done_something = false;
2189 			} else {
2190 				cur = NULL;
2191 			}
2192 		}
2193 		if(cur == NULL) {
2194 			break;
2195 		}
2196 	}
2197 	xmlFreeDoc(doc);
2198 	return true;
2199 }
saveDefinitions()2200 bool Calculator::saveDefinitions() {
2201 
2202 	recursiveMakeDir(getLocalDataDir());
2203 	string homedir = buildPath(getLocalDataDir(), "definitions");
2204 	makeDir(homedir);
2205 	bool b = true;
2206 	if(!saveFunctions(buildPath(homedir, "functions.xml").c_str())) b = false;
2207 	if(!saveUnits(buildPath(homedir, "units.xml").c_str())) b = false;
2208 	if(!saveVariables(buildPath(homedir, "variables.xml").c_str())) b = false;
2209 	if(!saveDataSets(buildPath(homedir, "datasets.xml").c_str())) b = false;
2210 	if(!saveDataObjects()) b = false;
2211 	return b;
2212 }
2213 
2214 struct node_tree_item {
2215 	xmlNodePtr node;
2216 	string category;
2217 	vector<node_tree_item> items;
2218 };
2219 
saveDataObjects()2220 int Calculator::saveDataObjects() {
2221 	int returnvalue = 1;
2222 	for(size_t i = 0; i < data_sets.size(); i++) {
2223 		int rv = data_sets[i]->saveObjects(NULL, false);
2224 		if(rv <= 0) returnvalue = rv;
2225 	}
2226 	return returnvalue;
2227 }
2228 
savePrefixes(const char * file_name,bool save_global)2229 int Calculator::savePrefixes(const char* file_name, bool save_global) {
2230 	if(!save_global) {
2231 		return true;
2232 	}
2233 	xmlDocPtr doc = xmlNewDoc((xmlChar*) "1.0");
2234 	xmlNodePtr cur, newnode;
2235 	doc->children = xmlNewDocNode(doc, NULL, (xmlChar*) "QALCULATE", NULL);
2236 	xmlNewProp(doc->children, (xmlChar*) "version", (xmlChar*) VERSION);
2237 	cur = doc->children;
2238 	for(size_t i = 0; i < prefixes.size(); i++) {
2239 		newnode = xmlNewTextChild(cur, NULL, (xmlChar*) "prefix", NULL);
2240 		if(!prefixes[i]->longName(false).empty()) xmlNewTextChild(newnode, NULL, (xmlChar*) "name", (xmlChar*) prefixes[i]->longName(false).c_str());
2241 		if(!prefixes[i]->shortName(false).empty()) xmlNewTextChild(newnode, NULL, (xmlChar*) "abbreviation", (xmlChar*) prefixes[i]->shortName(false).c_str());
2242 		if(!prefixes[i]->unicodeName(false).empty()) xmlNewTextChild(newnode, NULL, (xmlChar*) "unicode", (xmlChar*) prefixes[i]->unicodeName(false).c_str());
2243 		switch(prefixes[i]->type()) {
2244 			case PREFIX_DECIMAL: {
2245 				xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "decimal");
2246 				xmlNewTextChild(newnode, NULL, (xmlChar*) "exponent", (xmlChar*) i2s(((DecimalPrefix*) prefixes[i])->exponent()).c_str());
2247 				break;
2248 			}
2249 			case PREFIX_BINARY: {
2250 				xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "binary");
2251 				xmlNewTextChild(newnode, NULL, (xmlChar*) "exponent", (xmlChar*) i2s(((BinaryPrefix*) prefixes[i])->exponent()).c_str());
2252 				break;
2253 			}
2254 			case PREFIX_NUMBER: {
2255 				xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "number");
2256 				xmlNewTextChild(newnode, NULL, (xmlChar*) "value", (xmlChar*) prefixes[i]->value().print(save_printoptions).c_str());
2257 				break;
2258 			}
2259 		}
2260 	}
2261 	int returnvalue = xmlSaveFormatFile(file_name, doc, 1);
2262 	xmlFreeDoc(doc);
2263 	return returnvalue;
2264 }
2265 
2266 #define SAVE_NAMES(o)\
2267 				str = "";\
2268 				for(size_t i2 = 1;;)  {\
2269 					ename = &o->getName(i2);\
2270 					if(ename->abbreviation) {str += 'a';}\
2271 					bool b_cs = (ename->abbreviation || text_length_is_one(ename->name));\
2272 					if(ename->case_sensitive && !b_cs) {str += 'c';}\
2273 					if(!ename->case_sensitive && b_cs) {str += "-c";}\
2274 					if(ename->avoid_input) {str += 'i';}\
2275 					if(ename->completion_only) {str += 'o';}\
2276 					if(ename->plural) {str += 'p';}\
2277 					if(ename->reference) {str += 'r';}\
2278 					if(ename->suffix) {str += 's';}\
2279 					if(ename->unicode) {str += 'u';}\
2280 					if(str.empty() || str[str.length() - 1] == ',') {\
2281 						if(i2 == 1 && o->countNames() == 1) {\
2282 							if(save_global) {\
2283 								xmlNewTextChild(newnode, NULL, (xmlChar*) "_names", (xmlChar*) ename->name.c_str());\
2284 							} else {\
2285 								xmlNewTextChild(newnode, NULL, (xmlChar*) "names", (xmlChar*) ename->name.c_str());\
2286 							}\
2287 							break;\
2288 						}\
2289 					} else {\
2290 						str += ':';\
2291 					}\
2292 					str += ename->name;\
2293 					i2++;\
2294 					if(i2 > o->countNames()) {\
2295 						if(save_global) {\
2296 							xmlNewTextChild(newnode, NULL, (xmlChar*) "_names", (xmlChar*) str.c_str());\
2297 						} else {\
2298 							xmlNewTextChild(newnode, NULL, (xmlChar*) "names", (xmlChar*) str.c_str());\
2299 						}\
2300 						break;\
2301 					}\
2302 					str += ',';\
2303 				}
2304 
temporaryCategory() const2305 string Calculator::temporaryCategory() const {
2306 	return _("Temporary");
2307 }
2308 
saveVariables(const char * file_name,bool save_global)2309 int Calculator::saveVariables(const char* file_name, bool save_global) {
2310 	string str;
2311 	const ExpressionName *ename;
2312 	xmlDocPtr doc = xmlNewDoc((xmlChar*) "1.0");
2313 	xmlNodePtr cur, newnode, newnode2;
2314 	doc->children = xmlNewDocNode(doc, NULL, (xmlChar*) "QALCULATE", NULL);
2315 	xmlNewProp(doc->children, (xmlChar*) "version", (xmlChar*) VERSION);
2316 	node_tree_item top;
2317 	top.category = "";
2318 	top.node = doc->children;
2319 	node_tree_item *item;
2320 	string cat, cat_sub;
2321 	for(size_t i = 0; i < variables.size(); i++) {
2322 		if((save_global || variables[i]->isLocal() || variables[i]->hasChanged()) && variables[i]->category() != _("Temporary") && variables[i]->category() != "Temporary") {
2323 			item = &top;
2324 			if(!variables[i]->category().empty()) {
2325 				cat = variables[i]->category();
2326 				size_t cat_i = cat.find("/"); size_t cat_i_prev = 0;
2327 				bool b = false;
2328 				while(true) {
2329 					if(cat_i == string::npos) {
2330 						cat_sub = cat.substr(cat_i_prev, cat.length() - cat_i_prev);
2331 					} else {
2332 						cat_sub = cat.substr(cat_i_prev, cat_i - cat_i_prev);
2333 					}
2334 					b = false;
2335 					for(size_t i2 = 0; i2 < item->items.size(); i2++) {
2336 						if(cat_sub == item->items[i2].category) {
2337 							item = &item->items[i2];
2338 							b = true;
2339 							break;
2340 						}
2341 					}
2342 					if(!b) {
2343 						item->items.resize(item->items.size() + 1);
2344 						item->items[item->items.size() - 1].node = xmlNewTextChild(item->node, NULL, (xmlChar*) "category", NULL);
2345 						item = &item->items[item->items.size() - 1];
2346 						item->category = cat_sub;
2347 						if(save_global) {
2348 							xmlNewTextChild(item->node, NULL, (xmlChar*) "_title", (xmlChar*) item->category.c_str());
2349 						} else {
2350 							xmlNewTextChild(item->node, NULL, (xmlChar*) "title", (xmlChar*) item->category.c_str());
2351 						}
2352 					}
2353 					if(cat_i == string::npos) {
2354 						break;
2355 					}
2356 					cat_i_prev = cat_i + 1;
2357 					cat_i = cat.find("/", cat_i_prev);
2358 				}
2359 			}
2360 			cur = item->node;
2361 			if(!save_global && !variables[i]->isLocal() && variables[i]->hasChanged()) {
2362 				if(variables[i]->isActive()) {
2363 					xmlNewTextChild(cur, NULL, (xmlChar*) "activate", (xmlChar*) variables[i]->referenceName().c_str());
2364 				} else {
2365 					xmlNewTextChild(cur, NULL, (xmlChar*) "deactivate", (xmlChar*) variables[i]->referenceName().c_str());
2366 				}
2367 			} else if(save_global || variables[i]->isLocal()) {
2368 				if(variables[i]->isBuiltin()) {
2369 					if(variables[i]->isKnown()) {
2370 						newnode = xmlNewTextChild(cur, NULL, (xmlChar*) "builtin_variable", NULL);
2371 					} else {
2372 						newnode = xmlNewTextChild(cur, NULL, (xmlChar*) "builtin_unknown", NULL);
2373 					}
2374 					xmlNewProp(newnode, (xmlChar*) "name", (xmlChar*) variables[i]->referenceName().c_str());
2375 				} else {
2376 					if(variables[i]->isKnown()) {
2377 						newnode = xmlNewTextChild(cur, NULL, (xmlChar*) "variable", NULL);
2378 					} else {
2379 						newnode = xmlNewTextChild(cur, NULL, (xmlChar*) "unknown", NULL);
2380 					}
2381 				}
2382 				if(!variables[i]->isActive()) xmlNewProp(newnode, (xmlChar*) "active", (xmlChar*) "false");
2383 				if(variables[i]->isHidden()) xmlNewTextChild(newnode, NULL, (xmlChar*) "hidden", (xmlChar*) "true");
2384 				if(!variables[i]->title(false).empty()) {
2385 					if(save_global) {
2386 						xmlNewTextChild(newnode, NULL, (xmlChar*) "_title", (xmlChar*) variables[i]->title(false).c_str());
2387 					} else {
2388 						xmlNewTextChild(newnode, NULL, (xmlChar*) "title", (xmlChar*) variables[i]->title(false).c_str());
2389 					}
2390 				}
2391 				SAVE_NAMES(variables[i])
2392 				if(!variables[i]->description().empty()) {
2393 					str = variables[i]->description();
2394 					if(save_global) {
2395 						xmlNewTextChild(newnode, NULL, (xmlChar*) "_description", (xmlChar*) str.c_str());
2396 					} else {
2397 						xmlNewTextChild(newnode, NULL, (xmlChar*) "description", (xmlChar*) str.c_str());
2398 					}
2399 				}
2400 				if(!variables[i]->isBuiltin()) {
2401 					if(variables[i]->isKnown()) {
2402 						bool is_approx = false;
2403 						save_printoptions.is_approximate = &is_approx;
2404 						if(((KnownVariable*) variables[i])->isExpression()) {
2405 							newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "value", (xmlChar*) ((KnownVariable*) variables[i])->expression().c_str());
2406 							bool unc_rel = false;
2407 							if(!((KnownVariable*) variables[i])->uncertainty(&unc_rel).empty()) xmlNewProp(newnode2, (xmlChar*) (unc_rel ? "relative_uncertainty" : "uncertainty"), (xmlChar*) ((KnownVariable*) variables[i])->uncertainty().c_str());
2408 							if(!((KnownVariable*) variables[i])->unit().empty()) xmlNewProp(newnode2, (xmlChar*) "unit", (xmlChar*) ((KnownVariable*) variables[i])->unit().c_str());
2409 						} else {
2410 							newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "value", (xmlChar*) ((KnownVariable*) variables[i])->get().print(save_printoptions).c_str());
2411 						}
2412 						save_printoptions.is_approximate = NULL;
2413 						if(variables[i]->isApproximate() || is_approx) xmlNewProp(newnode2, (xmlChar*) "approximate", (xmlChar*) "true");
2414 						if(variables[i]->precision() >= 0) xmlNewProp(newnode2, (xmlChar*) "precision", (xmlChar*) i2s(variables[i]->precision()).c_str());
2415 					} else {
2416 						if(((UnknownVariable*) variables[i])->assumptions()) {
2417 							switch(((UnknownVariable*) variables[i])->assumptions()->type()) {
2418 								case ASSUMPTION_TYPE_INTEGER: {
2419 									newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "type", (xmlChar*) "integer");
2420 									break;
2421 								}
2422 								case ASSUMPTION_TYPE_RATIONAL: {
2423 									newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "type", (xmlChar*) "rational");
2424 									break;
2425 								}
2426 								case ASSUMPTION_TYPE_REAL: {
2427 									newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "type", (xmlChar*) "real");
2428 									break;
2429 								}
2430 								case ASSUMPTION_TYPE_COMPLEX: {
2431 									newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "type", (xmlChar*) "complex");
2432 									break;
2433 								}
2434 								case ASSUMPTION_TYPE_NUMBER: {
2435 									newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "type", (xmlChar*) "number");
2436 									break;
2437 								}
2438 								case ASSUMPTION_TYPE_NONMATRIX: {
2439 									newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "type", (xmlChar*) "non-matrix");
2440 									break;
2441 								}
2442 								case ASSUMPTION_TYPE_NONE: {
2443 									newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "type", (xmlChar*) "none");
2444 									break;
2445 								}
2446 							}
2447 							switch(((UnknownVariable*) variables[i])->assumptions()->sign()) {
2448 								case ASSUMPTION_SIGN_NONZERO: {
2449 									newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "sign", (xmlChar*) "non-zero");
2450 									break;
2451 								}
2452 								case ASSUMPTION_SIGN_NONPOSITIVE: {
2453 									newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "sign", (xmlChar*) "non-positive");
2454 									break;
2455 								}
2456 								case ASSUMPTION_SIGN_NEGATIVE: {
2457 									newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "sign", (xmlChar*) "negative");
2458 									break;
2459 								}
2460 								case ASSUMPTION_SIGN_NONNEGATIVE: {
2461 									newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "sign", (xmlChar*) "non-negative");
2462 									break;
2463 								}
2464 								case ASSUMPTION_SIGN_POSITIVE: {
2465 									newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "sign", (xmlChar*) "positive");
2466 									break;
2467 								}
2468 								case ASSUMPTION_SIGN_UNKNOWN: {
2469 									newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "sign", (xmlChar*) "unknown");
2470 									break;
2471 								}
2472 							}
2473 						}
2474 					}
2475 				}
2476 			}
2477 		}
2478 	}
2479 	int returnvalue = xmlSaveFormatFile(file_name, doc, 1);
2480 	xmlFreeDoc(doc);
2481 	return returnvalue;
2482 }
2483 
saveUnits(const char * file_name,bool save_global)2484 int Calculator::saveUnits(const char* file_name, bool save_global) {
2485 	string str;
2486 	xmlDocPtr doc = xmlNewDoc((xmlChar*) "1.0");
2487 	xmlNodePtr cur, newnode, newnode2, newnode3;
2488 	doc->children = xmlNewDocNode(doc, NULL, (xmlChar*) "QALCULATE", NULL);
2489 	xmlNewProp(doc->children, (xmlChar*) "version", (xmlChar*) VERSION);
2490 	const ExpressionName *ename;
2491 	CompositeUnit *cu = NULL;
2492 	AliasUnit *au = NULL;
2493 	Unit *u;
2494 	node_tree_item top;
2495 	top.category = "";
2496 	top.node = doc->children;
2497 	node_tree_item *item;
2498 	string cat, cat_sub;
2499 	for(size_t i = 0; i < units.size(); i++) {
2500 		u = units[i];
2501 		if(save_global || u->isLocal() || u->hasChanged()) {
2502 			item = &top;
2503 			if(!u->category().empty()) {
2504 				cat = u->category();
2505 				size_t cat_i = cat.find("/"); size_t cat_i_prev = 0;
2506 				bool b = false;
2507 				while(true) {
2508 					if(cat_i == string::npos) {
2509 						cat_sub = cat.substr(cat_i_prev, cat.length() - cat_i_prev);
2510 					} else {
2511 						cat_sub = cat.substr(cat_i_prev, cat_i - cat_i_prev);
2512 					}
2513 					b = false;
2514 					for(size_t i2 = 0; i2 < item->items.size(); i2++) {
2515 						if(cat_sub == item->items[i2].category) {
2516 							item = &item->items[i2];
2517 							b = true;
2518 							break;
2519 						}
2520 					}
2521 					if(!b) {
2522 						item->items.resize(item->items.size() + 1);
2523 						item->items[item->items.size() - 1].node = xmlNewTextChild(item->node, NULL, (xmlChar*) "category", NULL);
2524 						item = &item->items[item->items.size() - 1];
2525 						item->category = cat_sub;
2526 						if(save_global) {
2527 							xmlNewTextChild(item->node, NULL, (xmlChar*) "_title", (xmlChar*) item->category.c_str());
2528 						} else {
2529 							xmlNewTextChild(item->node, NULL, (xmlChar*) "title", (xmlChar*) item->category.c_str());
2530 						}
2531 					}
2532 					if(cat_i == string::npos) {
2533 						break;
2534 					}
2535 					cat_i_prev = cat_i + 1;
2536 					cat_i = cat.find("/", cat_i_prev);
2537 				}
2538 			}
2539 			cur = item->node;
2540 			if(!save_global && !u->isLocal() && u->hasChanged()) {
2541 				if(u->isActive()) {
2542 					xmlNewTextChild(cur, NULL, (xmlChar*) "activate", (xmlChar*) u->referenceName().c_str());
2543 				} else {
2544 					xmlNewTextChild(cur, NULL, (xmlChar*) "deactivate", (xmlChar*) u->referenceName().c_str());
2545 				}
2546 			} else if(save_global || u->isLocal()) {
2547 				if(u->isBuiltin()) {
2548 					newnode = xmlNewTextChild(cur, NULL, (xmlChar*) "builtin_unit", NULL);
2549 					xmlNewProp(newnode, (xmlChar*) "name", (xmlChar*) u->referenceName().c_str());
2550 				} else {
2551 					newnode = xmlNewTextChild(cur, NULL, (xmlChar*) "unit", NULL);
2552 					switch(u->subtype()) {
2553 						case SUBTYPE_BASE_UNIT: {
2554 							xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "base");
2555 							break;
2556 						}
2557 						case SUBTYPE_ALIAS_UNIT: {
2558 							au = (AliasUnit*) u;
2559 							xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "alias");
2560 							break;
2561 						}
2562 						case SUBTYPE_COMPOSITE_UNIT: {
2563 							cu = (CompositeUnit*) u;
2564 							xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "composite");
2565 							break;
2566 						}
2567 					}
2568 				}
2569 				if(!u->isActive()) xmlNewProp(newnode, (xmlChar*) "active", (xmlChar*) "false");
2570 				if(u->isHidden()) xmlNewTextChild(newnode, NULL, (xmlChar*) "hidden", (xmlChar*) "true");
2571 				if(!u->system().empty()) {
2572 					xmlNewTextChild(newnode, NULL, (xmlChar*) "system", (xmlChar*) u->system().c_str());
2573 				}
2574 				if(!u->isSIUnit() || !u->useWithPrefixesByDefault()) {
2575 					xmlNewTextChild(newnode, NULL, (xmlChar*) "use_with_prefixes", u->useWithPrefixesByDefault() ? (xmlChar*) "true" : (xmlChar*) "false");
2576 				}
2577 				if(!u->title(false).empty()) {
2578 					if(save_global) {
2579 						xmlNewTextChild(newnode, NULL, (xmlChar*) "_title", (xmlChar*) u->title(false).c_str());
2580 					} else {
2581 						xmlNewTextChild(newnode, NULL, (xmlChar*) "title", (xmlChar*) u->title(false).c_str());
2582 					}
2583 				}
2584 				if(save_global && u->subtype() == SUBTYPE_COMPOSITE_UNIT) {
2585 					save_global = false;
2586 					SAVE_NAMES(u)
2587 					save_global = true;
2588 				} else {
2589 					SAVE_NAMES(u)
2590 				}
2591 				if(!u->description().empty()) {
2592 					str = u->description();
2593 					if(save_global) {
2594 						xmlNewTextChild(newnode, NULL, (xmlChar*) "_description", (xmlChar*) str.c_str());
2595 					} else {
2596 						xmlNewTextChild(newnode, NULL, (xmlChar*) "description", (xmlChar*) str.c_str());
2597 					}
2598 				}
2599 				if(!u->isBuiltin()) {
2600 					if(u->subtype() == SUBTYPE_COMPOSITE_UNIT) {
2601 						for(size_t i2 = 1; i2 <= cu->countUnits(); i2++) {
2602 							newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "part", NULL);
2603 							int exp = 1;
2604 							Prefix *p = NULL;
2605 							Unit *u = cu->get(i2, &exp, &p);
2606 							xmlNewTextChild(newnode2, NULL, (xmlChar*) "unit", (xmlChar*) u->referenceName().c_str());
2607 							if(p) {
2608 								switch(p->type()) {
2609 									case PREFIX_DECIMAL: {
2610 										xmlNewTextChild(newnode2, NULL, (xmlChar*) "prefix", (xmlChar*) i2s(((DecimalPrefix*) p)->exponent()).c_str());
2611 										break;
2612 									}
2613 									case PREFIX_BINARY: {
2614 										newnode3 = xmlNewTextChild(newnode2, NULL, (xmlChar*) "prefix", (xmlChar*) i2s(((BinaryPrefix*) p)->exponent()).c_str());
2615 										xmlNewProp(newnode3, (xmlChar*) "type", (xmlChar*) "binary");
2616 										break;
2617 									}
2618 									case PREFIX_NUMBER: {
2619 										newnode3 = xmlNewTextChild(newnode2, NULL, (xmlChar*) "prefix", (xmlChar*) p->value().print(save_printoptions).c_str());
2620 										xmlNewProp(newnode3, (xmlChar*) "type", (xmlChar*) "number");
2621 										break;
2622 									}
2623 								}
2624 							}
2625 							xmlNewTextChild(newnode2, NULL, (xmlChar*) "exponent", (xmlChar*) i2s(exp).c_str());
2626 						}
2627 					}
2628 					if(u->subtype() == SUBTYPE_ALIAS_UNIT) {
2629 						newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "base", NULL);
2630 						xmlNewTextChild(newnode2, NULL, (xmlChar*) "unit", (xmlChar*) au->firstBaseUnit()->referenceName().c_str());
2631 						newnode3 = xmlNewTextChild(newnode2, NULL, (xmlChar*) "relation", (xmlChar*) au->expression().c_str());
2632 						if(au->isApproximate()) xmlNewProp(newnode3, (xmlChar*) "approximate", (xmlChar*) "true");
2633 						if(au->precision() >= 0) xmlNewProp(newnode3, (xmlChar*) "precision", (xmlChar*) i2s(u->precision()).c_str());
2634 						bool unc_rel = false;
2635 						if(!au->uncertainty(&unc_rel).empty()) xmlNewProp(newnode3, (xmlChar*) (unc_rel ? "relative_uncertainty" : "uncertainty"), (xmlChar*) au->uncertainty().c_str());
2636 						if(!au->inverseExpression().empty()) {
2637 							xmlNewTextChild(newnode2, NULL, (xmlChar*) "inverse_relation", (xmlChar*) au->inverseExpression().c_str());
2638 						}
2639 						xmlNewTextChild(newnode2, NULL, (xmlChar*) "exponent", (xmlChar*) i2s(au->firstBaseExponent()).c_str());
2640 						if(au->mixWithBase() > 0) {
2641 							newnode3 = xmlNewTextChild(newnode2, NULL, (xmlChar*) "mix", (xmlChar*) i2s(au->mixWithBase()).c_str());
2642 							if(au->mixWithBaseMinimum() > 1) xmlNewProp(newnode3, (xmlChar*) "min", (xmlChar*) i2s(au->mixWithBaseMinimum()).c_str());
2643 						}
2644 					}
2645 				}
2646 			}
2647 		}
2648 	}
2649 	int returnvalue = xmlSaveFormatFile(file_name, doc, 1);
2650 	xmlFreeDoc(doc);
2651 	return returnvalue;
2652 }
2653 
saveFunctions(const char * file_name,bool save_global)2654 int Calculator::saveFunctions(const char* file_name, bool save_global) {
2655 	xmlDocPtr doc = xmlNewDoc((xmlChar*) "1.0");
2656 	xmlNodePtr cur, newnode, newnode2;
2657 	doc->children = xmlNewDocNode(doc, NULL, (xmlChar*) "QALCULATE", NULL);
2658 	xmlNewProp(doc->children, (xmlChar*) "version", (xmlChar*) VERSION);
2659 	const ExpressionName *ename;
2660 	node_tree_item top;
2661 	top.category = "";
2662 	top.node = doc->children;
2663 	node_tree_item *item;
2664 	string cat, cat_sub;
2665 	Argument *arg;
2666 	IntegerArgument *iarg;
2667 	NumberArgument *farg;
2668 	string str;
2669 	for(size_t i = 0; i < functions.size(); i++) {
2670 		if(functions[i]->subtype() != SUBTYPE_DATA_SET && (save_global || functions[i]->isLocal() || functions[i]->hasChanged())) {
2671 			item = &top;
2672 			if(!functions[i]->category().empty()) {
2673 				cat = functions[i]->category();
2674 				size_t cat_i = cat.find("/"); size_t cat_i_prev = 0;
2675 				bool b = false;
2676 				while(true) {
2677 					if(cat_i == string::npos) {
2678 						cat_sub = cat.substr(cat_i_prev, cat.length() - cat_i_prev);
2679 					} else {
2680 						cat_sub = cat.substr(cat_i_prev, cat_i - cat_i_prev);
2681 					}
2682 					b = false;
2683 					for(size_t i2 = 0; i2 < item->items.size(); i2++) {
2684 						if(cat_sub == item->items[i2].category) {
2685 							item = &item->items[i2];
2686 							b = true;
2687 							break;
2688 						}
2689 					}
2690 					if(!b) {
2691 						item->items.resize(item->items.size() + 1);
2692 						item->items[item->items.size() - 1].node = xmlNewTextChild(item->node, NULL, (xmlChar*) "category", NULL);
2693 						item = &item->items[item->items.size() - 1];
2694 						item->category = cat_sub;
2695 						if(save_global) {
2696 							xmlNewTextChild(item->node, NULL, (xmlChar*) "_title", (xmlChar*) item->category.c_str());
2697 						} else {
2698 							xmlNewTextChild(item->node, NULL, (xmlChar*) "title", (xmlChar*) item->category.c_str());
2699 						}
2700 					}
2701 					if(cat_i == string::npos) {
2702 						break;
2703 					}
2704 					cat_i_prev = cat_i + 1;
2705 					cat_i = cat.find("/", cat_i_prev);
2706 				}
2707 			}
2708 			cur = item->node;
2709 			if(!save_global && !functions[i]->isLocal() && functions[i]->hasChanged()) {
2710 				if(functions[i]->isActive()) {
2711 					xmlNewTextChild(cur, NULL, (xmlChar*) "activate", (xmlChar*) functions[i]->referenceName().c_str());
2712 				} else {
2713 					xmlNewTextChild(cur, NULL, (xmlChar*) "deactivate", (xmlChar*) functions[i]->referenceName().c_str());
2714 				}
2715 			} else if(save_global || functions[i]->isLocal()) {
2716 				if(functions[i]->isBuiltin()) {
2717 					newnode = xmlNewTextChild(cur, NULL, (xmlChar*) "builtin_function", NULL);
2718 					xmlNewProp(newnode, (xmlChar*) "name", (xmlChar*) functions[i]->referenceName().c_str());
2719 				} else {
2720 					newnode = xmlNewTextChild(cur, NULL, (xmlChar*) "function", NULL);
2721 				}
2722 				if(!functions[i]->isActive()) xmlNewProp(newnode, (xmlChar*) "active", (xmlChar*) "false");
2723 				if(functions[i]->isHidden()) xmlNewTextChild(newnode, NULL, (xmlChar*) "hidden", (xmlChar*) "true");
2724 				if(!functions[i]->title(false).empty()) {
2725 					if(save_global) {
2726 						xmlNewTextChild(newnode, NULL, (xmlChar*) "_title", (xmlChar*) functions[i]->title(false).c_str());
2727 					} else {
2728 						xmlNewTextChild(newnode, NULL, (xmlChar*) "title", (xmlChar*) functions[i]->title(false).c_str());
2729 					}
2730 				}
2731 				SAVE_NAMES(functions[i])
2732 				if(!functions[i]->description().empty()) {
2733 					str = functions[i]->description();
2734 					if(save_global) {
2735 						xmlNewTextChild(newnode, NULL, (xmlChar*) "_description", (xmlChar*) str.c_str());
2736 					} else {
2737 						xmlNewTextChild(newnode, NULL, (xmlChar*) "description", (xmlChar*) str.c_str());
2738 					}
2739 				}
2740 				if(!functions[i]->example(true).empty()) newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "example", (xmlChar*) functions[i]->example(true).c_str());
2741 				if(functions[i]->isBuiltin()) {
2742 					cur = newnode;
2743 					for(size_t i2 = 1; i2 <= functions[i]->lastArgumentDefinitionIndex(); i2++) {
2744 						arg = functions[i]->getArgumentDefinition(i2);
2745 						if(arg && !arg->name().empty()) {
2746 							newnode = xmlNewTextChild(cur, NULL, (xmlChar*) "argument", NULL);
2747 							if(save_global) {
2748 								xmlNewTextChild(newnode, NULL, (xmlChar*) "_title", (xmlChar*) arg->name().c_str());
2749 							} else {
2750 								xmlNewTextChild(newnode, NULL, (xmlChar*) "title", (xmlChar*) arg->name().c_str());
2751 							}
2752 							xmlNewProp(newnode, (xmlChar*) "index", (xmlChar*) i2s(i2).c_str());
2753 						}
2754 					}
2755 				} else {
2756 					for(size_t i2 = 1; i2 <= ((UserFunction*) functions[i])->countSubfunctions(); i2++) {
2757 						newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "subfunction", (xmlChar*) ((UserFunction*) functions[i])->getSubfunction(i2).c_str());
2758 						if(((UserFunction*) functions[i])->subfunctionPrecalculated(i2)) xmlNewProp(newnode2, (xmlChar*) "precalculate", (xmlChar*) "true");
2759 						else xmlNewProp(newnode2, (xmlChar*) "precalculate", (xmlChar*) "false");
2760 
2761 					}
2762 					newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "expression", (xmlChar*) ((UserFunction*) functions[i])->formula().c_str());
2763 					if(functions[i]->isApproximate()) xmlNewProp(newnode2, (xmlChar*) "approximate", (xmlChar*) "true");
2764 					if(functions[i]->precision() >= 0) xmlNewProp(newnode2, (xmlChar*) "precision", (xmlChar*) i2s(functions[i]->precision()).c_str());
2765 					if(!functions[i]->condition().empty()) {
2766 						xmlNewTextChild(newnode, NULL, (xmlChar*) "condition", (xmlChar*) functions[i]->condition().c_str());
2767 					}
2768 					cur = newnode;
2769 					for(size_t i2 = 1; i2 <= functions[i]->lastArgumentDefinitionIndex(); i2++) {
2770 						arg = functions[i]->getArgumentDefinition(i2);
2771 						if(arg) {
2772 							newnode = xmlNewTextChild(cur, NULL, (xmlChar*) "argument", NULL);
2773 							if(!arg->name().empty()) {
2774 								if(save_global) {
2775 									xmlNewTextChild(newnode, NULL, (xmlChar*) "_title", (xmlChar*) arg->name().c_str());
2776 								} else {
2777 									xmlNewTextChild(newnode, NULL, (xmlChar*) "title", (xmlChar*) arg->name().c_str());
2778 								}
2779 							}
2780 							switch(arg->type()) {
2781 								case ARGUMENT_TYPE_TEXT: {xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "text"); break;}
2782 								case ARGUMENT_TYPE_SYMBOLIC: {xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "symbol"); break;}
2783 								case ARGUMENT_TYPE_DATE: {xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "date"); break;}
2784 								case ARGUMENT_TYPE_INTEGER: {xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "integer"); break;}
2785 								case ARGUMENT_TYPE_NUMBER: {xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "number"); break;}
2786 								case ARGUMENT_TYPE_VECTOR: {xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "vector"); break;}
2787 								case ARGUMENT_TYPE_MATRIX: {xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "matrix"); break;}
2788 								case ARGUMENT_TYPE_BOOLEAN: {xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "boolean"); break;}
2789 								case ARGUMENT_TYPE_FUNCTION: {xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "function"); break;}
2790 								case ARGUMENT_TYPE_UNIT: {xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "unit"); break;}
2791 								case ARGUMENT_TYPE_VARIABLE: {xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "variable"); break;}
2792 								case ARGUMENT_TYPE_EXPRESSION_ITEM: {xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "object"); break;}
2793 								case ARGUMENT_TYPE_ANGLE: {xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "angle"); break;}
2794 								case ARGUMENT_TYPE_DATA_OBJECT: {xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "data-object"); break;}
2795 								case ARGUMENT_TYPE_DATA_PROPERTY: {xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "data-property"); break;}
2796 								default: {xmlNewProp(newnode, (xmlChar*) "type", (xmlChar*) "free");}
2797 							}
2798 							xmlNewProp(newnode, (xmlChar*) "index", (xmlChar*) i2s(i2).c_str());
2799 							bool default_hv = arg->tests() && (arg->type() == ARGUMENT_TYPE_NUMBER || arg->type() == ARGUMENT_TYPE_INTEGER);
2800 							if(!default_hv && arg->handlesVector()) {
2801 								xmlNewTextChild(newnode, NULL, (xmlChar*) "handle_vector", (xmlChar*) "true");
2802 							} else if(default_hv && !arg->handlesVector()) {
2803 								xmlNewTextChild(newnode, NULL, (xmlChar*) "handle_vector", (xmlChar*) "false");
2804 							}
2805 							if(!arg->tests()) {
2806 								xmlNewTextChild(newnode, NULL, (xmlChar*) "test", (xmlChar*) "false");
2807 							}
2808 							if(!arg->alerts()) {
2809 								xmlNewTextChild(newnode, NULL, (xmlChar*) "alert", (xmlChar*) "false");
2810 							}
2811 							if(arg->zeroForbidden()) {
2812 								xmlNewTextChild(newnode, NULL, (xmlChar*) "zero_forbidden", (xmlChar*) "true");
2813 							}
2814 							if(arg->matrixAllowed()) {
2815 								xmlNewTextChild(newnode, NULL, (xmlChar*) "matrix_allowed", (xmlChar*) "true");
2816 							}
2817 							switch(arg->type()) {
2818 								case ARGUMENT_TYPE_INTEGER: {
2819 									iarg = (IntegerArgument*) arg;
2820 									if(iarg->min()) {
2821 										xmlNewTextChild(newnode, NULL, (xmlChar*) "min", (xmlChar*) iarg->min()->print(save_printoptions).c_str());
2822 									}
2823 									if(iarg->max()) {
2824 										xmlNewTextChild(newnode, NULL, (xmlChar*) "max", (xmlChar*) iarg->max()->print(save_printoptions).c_str());
2825 									}
2826 									break;
2827 								}
2828 								case ARGUMENT_TYPE_NUMBER: {
2829 									farg = (NumberArgument*) arg;
2830 									if(farg->min()) {
2831 										newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "min", (xmlChar*) farg->min()->print(save_printoptions).c_str());
2832 										if(farg->includeEqualsMin()) {
2833 											xmlNewProp(newnode2, (xmlChar*) "include_equals", (xmlChar*) "true");
2834 										} else {
2835 											xmlNewProp(newnode2, (xmlChar*) "include_equals", (xmlChar*) "false");
2836 										}
2837 									}
2838 									if(farg->max()) {
2839 										newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "max", (xmlChar*) farg->max()->print(save_printoptions).c_str());
2840 										if(farg->includeEqualsMax()) {
2841 											xmlNewProp(newnode2, (xmlChar*) "include_equals", (xmlChar*) "true");
2842 										} else {
2843 											xmlNewProp(newnode2, (xmlChar*) "include_equals", (xmlChar*) "false");
2844 										}
2845 									}
2846 									if(!farg->complexAllowed()) {
2847 										xmlNewTextChild(newnode, NULL, (xmlChar*) "complex_allowed", (xmlChar*) "false");
2848 									}
2849 									break;
2850 								}
2851 							}
2852 							if(!arg->getCustomCondition().empty()) {
2853 								xmlNewTextChild(newnode, NULL, (xmlChar*) "condition", (xmlChar*) arg->getCustomCondition().c_str());
2854 							}
2855 						}
2856 					}
2857 				}
2858 			}
2859 		}
2860 	}
2861 	int returnvalue = xmlSaveFormatFile(file_name, doc, 1);
2862 	xmlFreeDoc(doc);
2863 	return returnvalue;
2864 }
saveDataSets(const char * file_name,bool save_global)2865 int Calculator::saveDataSets(const char* file_name, bool save_global) {
2866 	xmlDocPtr doc = xmlNewDoc((xmlChar*) "1.0");
2867 	xmlNodePtr cur, newnode, newnode2;
2868 	doc->children = xmlNewDocNode(doc, NULL, (xmlChar*) "QALCULATE", NULL);
2869 	xmlNewProp(doc->children, (xmlChar*) "version", (xmlChar*) VERSION);
2870 	const ExpressionName *ename;
2871 	node_tree_item top;
2872 	top.category = "";
2873 	top.node = doc->children;
2874 	node_tree_item *item;
2875 	string cat, cat_sub;
2876 	Argument *arg;
2877 	DataSet *ds;
2878 	DataProperty *dp;
2879 	string str;
2880 	for(size_t i = 0; i < functions.size(); i++) {
2881 		if(functions[i]->subtype() == SUBTYPE_DATA_SET && (save_global || functions[i]->isLocal() || functions[i]->hasChanged())) {
2882 			item = &top;
2883 			ds = (DataSet*) functions[i];
2884 			if(!ds->category().empty()) {
2885 				cat = ds->category();
2886 				size_t cat_i = cat.find("/"); size_t cat_i_prev = 0;
2887 				bool b = false;
2888 				while(true) {
2889 					if(cat_i == string::npos) {
2890 						cat_sub = cat.substr(cat_i_prev, cat.length() - cat_i_prev);
2891 					} else {
2892 						cat_sub = cat.substr(cat_i_prev, cat_i - cat_i_prev);
2893 					}
2894 					b = false;
2895 					for(size_t i2 = 0; i2 < item->items.size(); i2++) {
2896 						if(cat_sub == item->items[i2].category) {
2897 							item = &item->items[i2];
2898 							b = true;
2899 							break;
2900 						}
2901 					}
2902 					if(!b) {
2903 						item->items.resize(item->items.size() + 1);
2904 						item->items[item->items.size() - 1].node = xmlNewTextChild(item->node, NULL, (xmlChar*) "category", NULL);
2905 						item = &item->items[item->items.size() - 1];
2906 						item->category = cat_sub;
2907 						if(save_global) {
2908 							xmlNewTextChild(item->node, NULL, (xmlChar*) "_title", (xmlChar*) item->category.c_str());
2909 						} else {
2910 							xmlNewTextChild(item->node, NULL, (xmlChar*) "title", (xmlChar*) item->category.c_str());
2911 						}
2912 					}
2913 					if(cat_i == string::npos) {
2914 						break;
2915 					}
2916 					cat_i_prev = cat_i + 1;
2917 					cat_i = cat.find("/", cat_i_prev);
2918 				}
2919 			}
2920 			cur = item->node;
2921 			if(save_global || ds->isLocal() || ds->hasChanged()) {
2922 				if(save_global || ds->isLocal()) {
2923 					newnode = xmlNewTextChild(cur, NULL, (xmlChar*) "dataset", NULL);
2924 				} else {
2925 					newnode = xmlNewTextChild(cur, NULL, (xmlChar*) "builtin_dataset", NULL);
2926 					xmlNewProp(newnode, (xmlChar*) "name", (xmlChar*) ds->referenceName().c_str());
2927 				}
2928 				if(!ds->isActive()) xmlNewProp(newnode, (xmlChar*) "active", (xmlChar*) "false");
2929 				if(ds->isHidden()) xmlNewTextChild(newnode, NULL, (xmlChar*) "hidden", (xmlChar*) "true");
2930 				if(!ds->title(false).empty()) {
2931 					if(save_global) {
2932 						xmlNewTextChild(newnode, NULL, (xmlChar*) "_title", (xmlChar*) ds->title(false).c_str());
2933 					} else {
2934 						xmlNewTextChild(newnode, NULL, (xmlChar*) "title", (xmlChar*) ds->title(false).c_str());
2935 					}
2936 				}
2937 				if((save_global || ds->isLocal()) && !ds->defaultDataFile().empty()) {
2938 					xmlNewTextChild(newnode, NULL, (xmlChar*) "datafile", (xmlChar*) ds->defaultDataFile().c_str());
2939 				}
2940 				SAVE_NAMES(ds)
2941 				if(!ds->description().empty()) {
2942 					str = ds->description();
2943 					if(save_global) {
2944 						xmlNewTextChild(newnode, NULL, (xmlChar*) "_description", (xmlChar*) str.c_str());
2945 					} else {
2946 						xmlNewTextChild(newnode, NULL, (xmlChar*) "description", (xmlChar*) str.c_str());
2947 					}
2948 				}
2949 				if((save_global || ds->isLocal()) && !ds->copyright().empty()) {
2950 					if(save_global) {
2951 						xmlNewTextChild(newnode, NULL, (xmlChar*) "_copyright", (xmlChar*) ds->copyright().c_str());
2952 					} else {
2953 						xmlNewTextChild(newnode, NULL, (xmlChar*) "copyright", (xmlChar*) ds->copyright().c_str());
2954 					}
2955 				}
2956 				arg = ds->getArgumentDefinition(1);
2957 				if(arg && ((!save_global && !ds->isLocal()) || arg->name() != _("Object"))) {
2958 					newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "object_argument", NULL);
2959 					if(save_global) {
2960 						xmlNewTextChild(newnode2, NULL, (xmlChar*) "_title", (xmlChar*) arg->name().c_str());
2961 					} else {
2962 						xmlNewTextChild(newnode2, NULL, (xmlChar*) "title", (xmlChar*) arg->name().c_str());
2963 					}
2964 				}
2965 				arg = ds->getArgumentDefinition(2);
2966 				if(arg && ((!save_global && !ds->isLocal()) || arg->name() != _("Property"))) {
2967 					newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "property_argument", NULL);
2968 					if(save_global) {
2969 						xmlNewTextChild(newnode2, NULL, (xmlChar*) "_title", (xmlChar*) arg->name().c_str());
2970 					} else {
2971 						xmlNewTextChild(newnode2, NULL, (xmlChar*) "title", (xmlChar*) arg->name().c_str());
2972 					}
2973 				}
2974 				if((!save_global && !ds->isLocal()) || ds->getDefaultValue(2) != _("info")) {
2975 					xmlNewTextChild(newnode, NULL, (xmlChar*) "default_property", (xmlChar*) ds->getDefaultValue(2).c_str());
2976 				}
2977 				DataPropertyIter it;
2978 				dp = ds->getFirstProperty(&it);
2979 				while(dp) {
2980 					if(save_global || ds->isLocal() || dp->isUserModified()) {
2981 						newnode2 = xmlNewTextChild(newnode, NULL, (xmlChar*) "property", NULL);
2982 						if(!dp->title(false).empty()) {
2983 							if(save_global) {
2984 								xmlNewTextChild(newnode2, NULL, (xmlChar*) "_title", (xmlChar*) dp->title().c_str());
2985 							} else {
2986 								xmlNewTextChild(newnode2, NULL, (xmlChar*) "title", (xmlChar*) dp->title().c_str());
2987 							}
2988 						}
2989 						switch(dp->propertyType()) {
2990 							case PROPERTY_STRING: {
2991 								xmlNewTextChild(newnode2, NULL, (xmlChar*) "type", (xmlChar*) "text");
2992 								break;
2993 							}
2994 							case PROPERTY_NUMBER: {
2995 								xmlNewTextChild(newnode2, NULL, (xmlChar*) "type", (xmlChar*) "number");
2996 								break;
2997 							}
2998 							case PROPERTY_EXPRESSION: {
2999 								xmlNewTextChild(newnode2, NULL, (xmlChar*) "type", (xmlChar*) "expression");
3000 								break;
3001 							}
3002 						}
3003 						if(dp->isHidden()) {
3004 							xmlNewTextChild(newnode2, NULL, (xmlChar*) "hidden", (xmlChar*) "true");
3005 						}
3006 						if(dp->isKey()) {
3007 							xmlNewTextChild(newnode2, NULL, (xmlChar*) "key", (xmlChar*) "true");
3008 						}
3009 						if(dp->isApproximate()) {
3010 							xmlNewTextChild(newnode2, NULL, (xmlChar*) "approximate", (xmlChar*) "true");
3011 						}
3012 						if(dp->usesBrackets()) {
3013 							xmlNewTextChild(newnode2, NULL, (xmlChar*) "brackets", (xmlChar*) "true");
3014 						}
3015 						if(dp->isCaseSensitive()) {
3016 							xmlNewTextChild(newnode2, NULL, (xmlChar*) "case_sensitive", (xmlChar*) "true");
3017 						}
3018 						if(!dp->getUnitString().empty()) {
3019 							xmlNewTextChild(newnode2, NULL, (xmlChar*) "unit", (xmlChar*) dp->getUnitString().c_str());
3020 						}
3021 						str = "";
3022 						for(size_t i2 = 1;;)  {
3023 							if(dp->nameIsReference(i2)) {str += 'r';}
3024 							if(str.empty() || str[str.length() - 1] == ',') {
3025 								if(i2 == 1 && dp->countNames() == 1) {
3026 									if(save_global) {
3027 										xmlNewTextChild(newnode2, NULL, (xmlChar*) "_names", (xmlChar*) dp->getName(i2).c_str());
3028 									} else {
3029 										xmlNewTextChild(newnode2, NULL, (xmlChar*) "names", (xmlChar*) dp->getName(i2).c_str());
3030 									}
3031 									break;
3032 								}
3033 							} else {
3034 								str += ':';
3035 							}
3036 							str += dp->getName(i2);
3037 							i2++;
3038 							if(i2 > dp->countNames()) {
3039 								if(save_global) {
3040 									xmlNewTextChild(newnode2, NULL, (xmlChar*) "_names", (xmlChar*) str.c_str());
3041 								} else {
3042 									xmlNewTextChild(newnode2, NULL, (xmlChar*) "names", (xmlChar*) str.c_str());
3043 								}
3044 								break;
3045 							}
3046 							str += ',';
3047 						}
3048 						if(!dp->description().empty()) {
3049 							str = dp->description();
3050 							if(save_global) {
3051 								xmlNewTextChild(newnode2, NULL, (xmlChar*) "_description", (xmlChar*) str.c_str());
3052 							} else {
3053 								xmlNewTextChild(newnode2, NULL, (xmlChar*) "description", (xmlChar*) str.c_str());
3054 							}
3055 						}
3056 					}
3057 					dp = ds->getNextProperty(&it);
3058 				}
3059 			}
3060 		}
3061 	}
3062 	int returnvalue = xmlSaveFormatFile(file_name, doc, 1);
3063 	xmlFreeDoc(doc);
3064 	return returnvalue;
3065 }
3066 
importCSV(MathStructure & mstruct,const char * file_name,int first_row,string delimiter,vector<string> * headers)3067 bool Calculator::importCSV(MathStructure &mstruct, const char *file_name, int first_row, string delimiter, vector<string> *headers) {
3068 	FILE *file = fopen(file_name, "r");
3069 	if(file == NULL) {
3070 		return false;
3071 	}
3072 	if(first_row < 1) {
3073 		first_row = 1;
3074 	}
3075 	char line[10000];
3076 	string stmp, str1, str2;
3077 	long int row = 0, rows = 1;
3078 	int columns = 1;
3079 	int column;
3080 	mstruct = m_empty_matrix;
3081 	size_t is, is_n;
3082 	bool v_added = false;
3083 	while(fgets(line, 10000, file)) {
3084 		row++;
3085 		if(row >= first_row) {
3086 			stmp = line;
3087 			remove_blank_ends(stmp);
3088 			if(row == first_row) {
3089 				if(stmp.empty()) {
3090 					row--;
3091 				} else {
3092 					is = 0;
3093 					while((is_n = stmp.find(delimiter, is)) != string::npos) {
3094 						columns++;
3095 						if(headers) {
3096 							str1 = stmp.substr(is, is_n - is);
3097 							remove_blank_ends(str1);
3098 							headers->push_back(str1);
3099 						}
3100 						is = is_n + delimiter.length();
3101 					}
3102 					if(headers) {
3103 						str1 = stmp.substr(is, stmp.length() - is);
3104 						remove_blank_ends(str1);
3105 						headers->push_back(str1);
3106 					}
3107 					mstruct.resizeMatrix(1, columns, m_undefined);
3108 				}
3109 			}
3110 			if((!headers || row > first_row) && !stmp.empty()) {
3111 				is = 0;
3112 				column = 1;
3113 				if(v_added) {
3114 					mstruct.addRow(m_undefined);
3115 					rows++;
3116 				}
3117 				while(column <= columns) {
3118 					is_n = stmp.find(delimiter, is);
3119 					if(is_n == string::npos) {
3120 						str1 = stmp.substr(is, stmp.length() - is);
3121 					} else {
3122 						str1 = stmp.substr(is, is_n - is);
3123 						is = is_n + delimiter.length();
3124 					}
3125 					parse(&mstruct[rows - 1][column - 1], str1);
3126 					column++;
3127 					if(is_n == string::npos) {
3128 						break;
3129 					}
3130 				}
3131 				v_added = true;
3132 			}
3133 		}
3134 	}
3135 	return true;
3136 }
3137 
importCSV(const char * file_name,int first_row,bool headers,string delimiter,bool to_matrix,string name,string title,string category)3138 bool Calculator::importCSV(const char *file_name, int first_row, bool headers, string delimiter, bool to_matrix, string name, string title, string category) {
3139 	FILE *file = fopen(file_name, "r");
3140 	if(file == NULL) {
3141 		return false;
3142 	}
3143 	if(first_row < 1) {
3144 		first_row = 1;
3145 	}
3146 	string filestr = file_name;
3147 	remove_blank_ends(filestr);
3148 	size_t i = filestr.find_last_of("/");
3149 	if(i != string::npos) {
3150 		filestr = filestr.substr(i + 1, filestr.length() - (i + 1));
3151 	}
3152 	remove_blank_ends(name);
3153 	if(name.empty()) {
3154 		name = filestr;
3155 		i = name.find_last_of("/");
3156 		if(i != string::npos) name = name.substr(i + 1, name.length() - i);
3157 		i = name.find_last_of(".");
3158 		if(i != string::npos) name = name.substr(0, i);
3159 	}
3160 
3161 	char line[10000];
3162 	string stmp, str1, str2;
3163 	int row = 0;
3164 	int columns = 1, rows = 1;
3165 	int column;
3166 	vector<string> header;
3167 	vector<MathStructure> vectors;
3168 	MathStructure mstruct = m_empty_matrix;
3169 	size_t is, is_n;
3170 	bool v_added = false;
3171 	while(fgets(line, 10000, file)) {
3172 		row++;
3173 		if(row >= first_row) {
3174 			stmp = line;
3175 			remove_blank_ends(stmp);
3176 			if(row == first_row) {
3177 				if(stmp.empty()) {
3178 					row--;
3179 				} else {
3180 					is = 0;
3181 					while((is_n = stmp.find(delimiter, is)) != string::npos) {
3182 						columns++;
3183 						if(headers) {
3184 							str1 = stmp.substr(is, is_n - is);
3185 							remove_blank_ends(str1);
3186 							header.push_back(str1);
3187 						}
3188 						if(!to_matrix) {
3189 							vectors.push_back(m_empty_vector);
3190 						}
3191 						is = is_n + delimiter.length();
3192 					}
3193 					if(headers) {
3194 						str1 = stmp.substr(is, stmp.length() - is);
3195 						remove_blank_ends(str1);
3196 						header.push_back(str1);
3197 					}
3198 					if(to_matrix) {
3199 						mstruct.resizeMatrix(1, columns, m_undefined);
3200 					} else {
3201 						vectors.push_back(m_empty_vector);
3202 					}
3203 				}
3204 			}
3205 			if((!headers || row > first_row) && !stmp.empty()) {
3206 				if(to_matrix && v_added) {
3207 					mstruct.addRow(m_undefined);
3208 					rows++;
3209 				}
3210 				is = 0;
3211 				column = 1;
3212 				while(column <= columns) {
3213 					is_n = stmp.find(delimiter, is);
3214 					if(is_n == string::npos) {
3215 						str1 = stmp.substr(is, stmp.length() - is);
3216 					} else {
3217 						str1 = stmp.substr(is, is_n - is);
3218 						is = is_n + delimiter.length();
3219 					}
3220 					if(to_matrix) {
3221 						parse(&mstruct[rows - 1][column - 1], str1);
3222 					} else {
3223 						vectors[column - 1].addChild(parse(str1));
3224 					}
3225 					column++;
3226 					if(is_n == string::npos) {
3227 						break;
3228 					}
3229 				}
3230 				for(; column <= columns; column++) {
3231 					if(!to_matrix) {
3232 						vectors[column - 1].addChild(m_undefined);
3233 					}
3234 				}
3235 				v_added = true;
3236 			}
3237 		}
3238 	}
3239 	if(to_matrix) {
3240 		addVariable(new KnownVariable(category, name, mstruct, title));
3241 	} else {
3242 		if(vectors.size() > 1) {
3243 			if(!category.empty()) {
3244 				category += "/";
3245 			}
3246 			category += name;
3247 		}
3248 		for(size_t i = 0; i < vectors.size(); i++) {
3249 			str1 = "";
3250 			str2 = "";
3251 			if(vectors.size() > 1) {
3252 				str1 += name;
3253 				str1 += "_";
3254 				if(title.empty()) {
3255 					str2 += name;
3256 					str2 += " ";
3257 				} else {
3258 					str2 += title;
3259 					str2 += " ";
3260 				}
3261 				if(i < header.size()) {
3262 					str1 += header[i];
3263 					str2 += header[i];
3264 				} else {
3265 					str1 += _("column");
3266 					str1 += "_";
3267 					str1 += i2s(i + 1);
3268 					str2 += _("Column ");
3269 					str2 += i2s(i + 1);
3270 				}
3271 				gsub(" ", "_", str1);
3272 			} else {
3273 				str1 = name;
3274 				str2 = title;
3275 				if(i < header.size()) {
3276 					str2 += " (";
3277 					str2 += header[i];
3278 					str2 += ")";
3279 				}
3280 			}
3281 			addVariable(new KnownVariable(category, str1, vectors[i], str2));
3282 		}
3283 	}
3284 	return true;
3285 }
exportCSV(const MathStructure & mstruct,const char * file_name,string delimiter)3286 bool Calculator::exportCSV(const MathStructure &mstruct, const char *file_name, string delimiter) {
3287 	FILE *file = fopen(file_name, "w+");
3288 	if(file == NULL) {
3289 		return false;
3290 	}
3291 	MathStructure mcsv(mstruct);
3292 	PrintOptions po;
3293 	po.number_fraction_format = FRACTION_DECIMAL;
3294 	po.interval_display = INTERVAL_DISPLAY_SIGNIFICANT_DIGITS;
3295 	po.decimalpoint_sign = ".";
3296 	po.comma_sign = ",";
3297 	if(mcsv.isMatrix()) {
3298 		for(size_t i = 0; i < mcsv.size(); i++) {
3299 			for(size_t i2 = 0; i2 < mcsv[i].size(); i2++) {
3300 				if(i2 > 0) fputs(delimiter.c_str(), file);
3301 				mcsv[i][i2].format(po);
3302 				fputs(mcsv[i][i2].print(po).c_str(), file);
3303 			}
3304 			fputs("\n", file);
3305 		}
3306 	} else if(mcsv.isVector()) {
3307 		for(size_t i = 0; i < mcsv.size(); i++) {
3308 			mcsv[i].format(po);
3309 			fputs(mcsv[i].print(po).c_str(), file);
3310 			fputs("\n", file);
3311 		}
3312 	} else {
3313 		mcsv.format(po);
3314 		fputs(mcsv.print(po).c_str(), file);
3315 		fputs("\n", file);
3316 	}
3317 	fclose(file);
3318 	return true;
3319 }
3320 
3321 
loadExchangeRates()3322 bool Calculator::loadExchangeRates() {
3323 
3324 	xmlDocPtr doc = NULL;
3325 	xmlNodePtr cur = NULL;
3326 	xmlChar *value;
3327 	bool global_file = false;
3328 	string currency, rate, sdate;
3329 
3330 	unordered_map<Unit*, bool> cunits;
3331 
3332 	string filename = buildPath(getLocalDataDir(), "custom_exchange_rates");
3333 	ifstream cfile(filename.c_str());
3334 	if(cfile.is_open()) {
3335 		char linebuffer[1000];
3336 		string sbuffer, s1, s2;
3337 		string nr1, nr2;
3338 		Unit *u1, *u2;
3339 		while((cfile.rdstate() & std::ifstream::eofbit) == 0) {
3340 			cfile.getline(linebuffer, 1000);
3341 			if((cfile.rdstate() & std::ifstream::failbit) != 0) break;
3342 			sbuffer = linebuffer;
3343 			size_t i = sbuffer.find("=");
3344 			if(i != string::npos && i > 0 && i < sbuffer.length() - 1) {
3345 				s1 = sbuffer.substr(0, i);
3346 				s2 = sbuffer.substr(i + 1, sbuffer.length() - (i + 1));
3347 				remove_blanks(s1);
3348 				remove_blanks(s2);
3349 				if(!s1.empty() && !s2.empty()) {
3350 					i = s1.find_first_not_of(ILLEGAL_IN_UNITNAMES);
3351 					if(i == 0) {
3352 						i = s1.find_first_of(ILLEGAL_IN_UNITNAMES);
3353 						if(i == string::npos) {
3354 							nr1 = "1";
3355 						} else {
3356 							nr1 = s1.substr(i, s1.length() - i);
3357 							if(i == 0) s1 = "";
3358 							else s1 = s1.substr(0, i);
3359 						}
3360 					} else {
3361 						if(i == s1.length() - 1) nr1 = "1";
3362 						else nr1 = s1.substr(0, i);
3363 						s1 = s1.substr(i, s1.length() - i);
3364 					}
3365 					i = s2.find_first_not_of(ILLEGAL_IN_UNITNAMES);
3366 					if(i == 0) {
3367 						i = s2.find_first_of(ILLEGAL_IN_UNITNAMES);
3368 						if(i == string::npos) {
3369 							nr2 = "1";
3370 						} else {
3371 							nr2 = s2.substr(i, s2.length() - i);
3372 							if(i == 0) s2 = "";
3373 							else s2 = s2.substr(0, i);
3374 						}
3375 					} else {
3376 						if(i == s2.length() - 1) nr2 = "1";
3377 						else nr2 = s2.substr(0, i);
3378 						s2 = s2.substr(i, s2.length() - i);
3379 					}
3380 				}
3381 				if(!s1.empty() && !s2.empty()) {
3382 					u2 = CALCULATOR->getActiveUnit(s2);
3383 					if(!u2) {
3384 						u2 = addUnit(new AliasUnit(_("Currency"), s2, "", "", "", u_euro, "1", 1, "", false, true));
3385 					}
3386 					if(u2 && u2->isCurrency()) {
3387 						u1 = CALCULATOR->getActiveUnit(s1);
3388 						if(!u1) {
3389 							u1 = addUnit(new AliasUnit(_("Currency"), s1, "", "", "", u2, nr1 == "1" ? nr2 : nr2 + string("/") + nr1, 1, "", false, true));
3390 							u1->setApproximate();
3391 							u1->setPrecision(-2);
3392 							u1->setChanged(false);
3393 							cunits[u1] = true;
3394 						} else if(u1->isCurrency()) {
3395 							if(u1 == u_euro) {
3396 								((AliasUnit*) u2)->setBaseUnit(u1);
3397 								((AliasUnit*) u2)->setExpression(nr2 == "1" ? nr1 : nr1 + string("/") + nr2);
3398 								u2->setApproximate();
3399 								u2->setPrecision(-2);
3400 								u2->setChanged(false);
3401 								cunits[u2] = true;
3402 							} else {
3403 								((AliasUnit*) u1)->setBaseUnit(u2);
3404 								((AliasUnit*) u1)->setExpression(nr1 == "1" ? nr2 : nr2 + string("/") + nr1);
3405 								u1->setApproximate();
3406 								u1->setPrecision(-2);
3407 								u1->setChanged(false);
3408 								cunits[u1] = true;
3409 							}
3410 						}
3411 					}
3412 				}
3413 			}
3414 		}
3415 		cfile.close();
3416 	}
3417 
3418 	filename = buildPath(getLocalDataDir(), "eurofxref-daily.xml");
3419 	if(fileExists(filename)) {
3420 		doc = xmlParseFile(filename.c_str());
3421 	} else {
3422 #ifndef _WIN32
3423 		string filename_old = buildPath(getOldLocalDir(), "eurofxref-daily.xml");
3424 		if(fileExists(filename)) {
3425 			doc = xmlParseFile(filename_old.c_str());
3426 			if(doc) {
3427 				recursiveMakeDir(getLocalDataDir());
3428 				move_file(filename_old.c_str(), filename.c_str());
3429 				removeDir(getOldLocalDir());
3430 			}
3431 		}
3432 #endif
3433 	}
3434 	if(doc) cur = xmlDocGetRootElement(doc);
3435 	if(!cur) {
3436 		if(doc) xmlFreeDoc(doc);
3437 #ifdef COMPILED_DEFINITIONS
3438 		doc = xmlParseMemory(eurofxref_daily_xml, strlen(eurofxref_daily_xml));
3439 #else
3440 		filename = buildPath(getGlobalDefinitionsDir(), "eurofxref-daily.xml");
3441 		doc = xmlParseFile(filename.c_str());
3442 #endif
3443 		if(!doc) return false;
3444 		cur = xmlDocGetRootElement(doc);
3445 		if(!cur) return false;
3446 		global_file = true;
3447 	}
3448 	Unit *u;
3449 	while(cur) {
3450 		if(!xmlStrcmp(cur->name, (const xmlChar*) "Cube")) {
3451 			if(global_file && sdate.empty()) {
3452 				XML_GET_STRING_FROM_PROP(cur, "time", sdate);
3453 				QalculateDateTime qdate;
3454 				if(qdate.set(sdate)) {
3455 					exchange_rates_time[0] = (time_t) qdate.timestamp().ulintValue();
3456 					if(exchange_rates_time[0] > exchange_rates_check_time[0]) exchange_rates_check_time[0] = exchange_rates_time[0];
3457 				} else {
3458 					sdate.clear();
3459 				}
3460 			}
3461 			XML_GET_STRING_FROM_PROP(cur, "currency", currency);
3462 			if(!currency.empty()) {
3463 				XML_GET_STRING_FROM_PROP(cur, "rate", rate);
3464 				if(!rate.empty()) {
3465 					rate = "1/" + rate;
3466 					u = getUnit(currency);
3467 					if(!u) {
3468 						u = addUnit(new AliasUnit(_("Currency"), currency, "", "", "", u_euro, rate, 1, "", false, true));
3469 					} else if(u->subtype() == SUBTYPE_ALIAS_UNIT) {
3470 						if(cunits.find(u) != cunits.end()) {
3471 							u = NULL;
3472 						} else {
3473 							((AliasUnit*) u)->setBaseUnit(u_euro);
3474 							((AliasUnit*) u)->setExpression(rate);
3475 						}
3476 					}
3477 					if(u) {
3478 						u->setApproximate();
3479 						u->setPrecision(-2);
3480 						u->setChanged(false);
3481 					}
3482 				}
3483 			}
3484 		}
3485 		if(cur->children) {
3486 			cur = cur->children;
3487 		} else if(cur->next) {
3488 			cur = cur->next;
3489 		} else {
3490 			cur = cur->parent;
3491 			if(cur) {
3492 				cur = cur->next;
3493 			}
3494 		}
3495 	}
3496 	xmlFreeDoc(doc);
3497 	if(sdate.empty()) {
3498 		struct stat stats;
3499 		if(stat(filename.c_str(), &stats) == 0) {
3500 			if(exchange_rates_time[0] >= stats.st_mtime) {
3501 #ifdef _WIN32
3502 				struct _utimbuf new_times;
3503 #else
3504 				struct utimbuf new_times;
3505 #endif
3506 				struct tm *temptm = localtime(&exchange_rates_time[0]);
3507 				if(temptm) {
3508 					struct tm extm = *temptm;
3509 					time_t time_now = time(NULL);
3510 					struct tm *newtm = localtime(&time_now);
3511 					if(newtm && newtm->tm_mday != extm.tm_mday) {
3512 						newtm->tm_hour = extm.tm_hour;
3513 						newtm->tm_min = extm.tm_min;
3514 						newtm->tm_sec = extm.tm_sec;
3515 						exchange_rates_time[0] = mktime(newtm);
3516 					} else {
3517 						time(&exchange_rates_time[0]);
3518 					}
3519 				} else {
3520 					time(&exchange_rates_time[0]);
3521 				}
3522 				new_times.modtime = exchange_rates_time[0];
3523 				new_times.actime = exchange_rates_time[0];
3524 #ifdef _WIN32
3525 				_utime(filename.c_str(), &new_times);
3526 #else
3527 				utime(filename.c_str(), &new_times);
3528 #endif
3529 			} else {
3530 				exchange_rates_time[0] = stats.st_mtime;
3531 				if(exchange_rates_time[0] > exchange_rates_check_time[0]) exchange_rates_check_time[0] = exchange_rates_time[0];
3532 			}
3533 		}
3534 	}
3535 
3536 	if(cunits.find(u_btc) == cunits.end()) {
3537 		filename = getExchangeRatesFileName(2);
3538 		ifstream file2(filename.c_str());
3539 		if(file2.is_open()) {
3540 			std::stringstream ssbuffer2;
3541 			ssbuffer2 << file2.rdbuf();
3542 			string sbuffer = ssbuffer2.str();
3543 			size_t i = sbuffer.find("\"amount\":");
3544 			if(i != string::npos) {
3545 				i = sbuffer.find("\"", i + 9);
3546 				if(i != string::npos) {
3547 					size_t i2 = sbuffer.find("\"", i + 1);
3548 					((AliasUnit*) u_btc)->setBaseUnit(u_euro);
3549 					((AliasUnit*) u_btc)->setExpression(sbuffer.substr(i + 1, i2 - (i + 1)));
3550 					u_btc->setApproximate();
3551 					u_btc->setPrecision(-2);
3552 					u_btc->setChanged(false);
3553 				}
3554 			}
3555 			file2.close();
3556 			struct stat stats;
3557 			if(stat(filename.c_str(), &stats) == 0) {
3558 				exchange_rates_time[1] = stats.st_mtime;
3559 				if(exchange_rates_time[1] > exchange_rates_check_time[1]) exchange_rates_check_time[1] = exchange_rates_time[1];
3560 			}
3561 		}
3562 	} else {
3563 		exchange_rates_time[1] = exchange_rates_time[0];
3564 		if(exchange_rates_time[1] > exchange_rates_check_time[1]) exchange_rates_check_time[1] = exchange_rates_time[1];
3565 	}
3566 
3567 	if(cunits.find(priv->u_byn) == cunits.end()) {
3568 		filename = getExchangeRatesFileName(4);
3569 		ifstream file3(filename.c_str());
3570 		if(file3.is_open()) {
3571 			std::stringstream ssbuffer3;
3572 			ssbuffer3 << file3.rdbuf();
3573 			string sbuffer = ssbuffer3.str();
3574 			size_t i = sbuffer.find("\"Cur_OfficialRate\":");
3575 			if(i != string::npos) {
3576 				i = sbuffer.find_first_of(NUMBER_ELEMENTS, i + 19);
3577 				if(i != string::npos) {
3578 					size_t i2 = sbuffer.find_first_not_of(NUMBER_ELEMENTS, i);
3579 					if(i2 == string::npos) i2 = sbuffer.length();
3580 					((AliasUnit*) priv->u_byn)->setBaseUnit(u_euro);
3581 					((AliasUnit*) priv->u_byn)->setExpression(string("1/") + sbuffer.substr(i, i2 - i));
3582 					priv->u_byn->setApproximate();
3583 					priv->u_byn->setPrecision(-2);
3584 					priv->u_byn->setChanged(false);
3585 				}
3586 			}
3587 			i = sbuffer.find("\"Date\":");
3588 			if(i != string::npos) {
3589 				i = sbuffer.find("\"", i + 7);
3590 				if(i != string::npos) {
3591 					size_t i2 = sbuffer.find("\"", i + 1);
3592 					QalculateDateTime qdate;
3593 					if(qdate.set(sbuffer.substr(i + 1, i2 - (i + 1)))) {
3594 						priv->exchange_rates_time2[0] = (time_t) qdate.timestamp().ulintValue();
3595 						if(priv->exchange_rates_time2[0] > priv->exchange_rates_check_time2[0]) priv->exchange_rates_check_time2[0] = priv->exchange_rates_time2[0];
3596 					}
3597 				}
3598 			}
3599 			file3.close();
3600 		}
3601 	} else {
3602 		priv->exchange_rates_time2[0] = exchange_rates_time[0];
3603 		if(priv->exchange_rates_time2[0] > priv->exchange_rates_check_time2[0]) priv->exchange_rates_check_time2[0] = priv->exchange_rates_time2[0];
3604 	}
3605 
3606 	Unit *u_usd = getUnit("USD");
3607 	if(!u_usd) return true;
3608 
3609 	string sbuffer;
3610 	filename = getExchangeRatesFileName(3);
3611 	ifstream file(filename.c_str());
3612 	if(file.is_open()) {
3613 		std::stringstream ssbuffer;
3614 		ssbuffer << file.rdbuf();
3615 		sbuffer = ssbuffer.str();
3616 	}
3617 	if(sbuffer.empty()) {
3618 		if(file.is_open()) file.close();
3619 		file.clear();
3620 #ifdef COMPILED_DEFINITIONS
3621 		sbuffer = rates_json;
3622 #else
3623 		filename = buildPath(getGlobalDefinitionsDir(), "rates.json");
3624 		file.open(filename.c_str());
3625 		if(!file.is_open()) return true;
3626 		std::stringstream ssbuffer;
3627 		ssbuffer << file.rdbuf();
3628 		sbuffer = ssbuffer.str();
3629 #endif
3630 		string sname;
3631 		size_t i = sbuffer.find("\"currency_code\":");
3632 		while(i != string::npos) {
3633 			i += 16;
3634 			size_t i2 = sbuffer.find("\"", i);
3635 			if(i2 == string::npos) break;
3636 			size_t i3 = sbuffer.find("\"", i2 + 1);
3637 			if(i3 != string::npos && i3 - (i2 + 1) == 3) {
3638 				currency = sbuffer.substr(i2 + 1, i3 - (i2 + 1));
3639 				if(currency.length() == 3 && currency[0] >= 'A' && currency[0] <= 'Z') {
3640 					u = getUnit(currency);
3641 					if(!u || (u->subtype() == SUBTYPE_ALIAS_UNIT && ((AliasUnit*) u)->firstBaseUnit() == u_usd)) {
3642 						i2 = sbuffer.find("\"rate\":", i3 + 1);
3643 						size_t i4 = sbuffer.find("}", i3 + 1);
3644 						if(i2 != string::npos && i2 < i4) {
3645 							i3 = sbuffer.find(",", i2 + 7);
3646 							rate = sbuffer.substr(i2 + 7, i3 - (i2 + 7));
3647 							rate = "1/" + rate;
3648 							if(!u) {
3649 								i2 = sbuffer.find("\"name\":\"", i3 + 1);
3650 								if(i2 != string::npos && i2 < i4) {
3651 									i3 = sbuffer.find("\"", i2 + 8);
3652 									if(i3 != string::npos) {
3653 										sname = sbuffer.substr(i2 + 8, i3 - (i2 + 8));
3654 										remove_blank_ends(sname);
3655 									}
3656 								} else {
3657 									sname = "";
3658 								}
3659 								u = addUnit(new AliasUnit(_("Currency"), currency, "", "", sname, u_usd, rate, 1, "", false, true), false, true);
3660 								if(u) u->setHidden(true);
3661 							} else {
3662 								if(cunits.find(u) != cunits.end()) {
3663 									u = NULL;
3664 								} else {
3665 									((AliasUnit*) u)->setBaseUnit(u_usd);
3666 									((AliasUnit*) u)->setExpression(rate);
3667 								}
3668 							}
3669 							if(u) {
3670 								u->setApproximate();
3671 								u->setPrecision(-2);
3672 								u->setChanged(false);
3673 							}
3674 						}
3675 					}
3676 				}
3677 			}
3678 			i = sbuffer.find("\"currency_code\":", i);
3679 		}
3680 		if(file.is_open()) file.close();
3681 		exchange_rates_time[2] = ((time_t) 1527199L) * 1000;
3682 		if(exchange_rates_time[2] > exchange_rates_check_time[2]) exchange_rates_check_time[2] = exchange_rates_time[2];
3683 	} else {
3684 		string sname;
3685 		size_t i = sbuffer.find("class=\'country\'");
3686 		while(i != string::npos) {
3687 			currency = ""; sname = ""; rate = "";
3688 			i += 15;
3689 			size_t i2 = sbuffer.find("data-currency-code=\"", i);
3690 			if(i2 != string::npos) {
3691 				i2 += 19;
3692 				size_t i3 = sbuffer.find("\"", i2 + 1);
3693 				if(i3 != string::npos) {
3694 					currency = sbuffer.substr(i2 + 1, i3 - (i2 + 1));
3695 					remove_blank_ends(currency);
3696 				}
3697 			}
3698 			i2 = sbuffer.find("data-currency-name=\'", i);
3699 			if(i2 != string::npos) {
3700 				i2 += 19;
3701 				size_t i3 = sbuffer.find("|", i2 + 1);
3702 				if(i3 != string::npos) {
3703 					sname = sbuffer.substr(i2 + 1, i3 - (i2 + 1));
3704 					remove_blank_ends(sname);
3705 				}
3706 			}
3707 			i2 = sbuffer.find("data-rate=\'", i);
3708 			if(i2 != string::npos) {
3709 				i2 += 10;
3710 				size_t i3 = sbuffer.find("'", i2 + 1);
3711 				if(i3 != string::npos) {
3712 					rate = sbuffer.substr(i2 + 1, i3 - (i2 + 1));
3713 					remove_blank_ends(rate);
3714 				}
3715 			}
3716 			if(currency.length() == 3 && currency[0] >= 'A' && currency[0] <= 'Z' && !rate.empty()) {
3717 				u = getUnit(currency);
3718 				if(!u || (u->subtype() == SUBTYPE_ALIAS_UNIT && ((AliasUnit*) u)->firstBaseUnit() == u_usd)) {
3719 					rate = "1/" + rate;
3720 					if(!u) {
3721 						u = addUnit(new AliasUnit(_("Currency"), currency, "", "", sname, u_usd, rate, 1, "", false, true), false, true);
3722 						if(u) u->setHidden(true);
3723 					} else {
3724 						if(cunits.find(u) != cunits.end()) {
3725 							u = NULL;
3726 						} else {
3727 							((AliasUnit*) u)->setBaseUnit(u_usd);
3728 							((AliasUnit*) u)->setExpression(rate);
3729 						}
3730 					}
3731 					if(u) {
3732 						u->setApproximate();
3733 						u->setPrecision(-2);
3734 						u->setChanged(false);
3735 					}
3736 				}
3737 			}
3738 			i = sbuffer.find("class=\'country\'", i);
3739 		}
3740 		file.close();
3741 		struct stat stats;
3742 		if(stat(filename.c_str(), &stats) == 0) {
3743 			exchange_rates_time[2] = stats.st_mtime;
3744 			if(exchange_rates_time[2] > exchange_rates_check_time[2]) exchange_rates_check_time[2] = exchange_rates_time[2];
3745 		}
3746 	}
3747 
3748 	return true;
3749 
3750 }
hasGVFS()3751 bool Calculator::hasGVFS() {
3752 	return false;
3753 }
hasGnomeVFS()3754 bool Calculator::hasGnomeVFS() {
3755 	return hasGVFS();
3756 }
canFetch()3757 bool Calculator::canFetch() {
3758 #ifdef HAVE_LIBCURL
3759 	return true;
3760 #else
3761 	return false;
3762 #endif
3763 }
getExchangeRatesFileName(int index)3764 string Calculator::getExchangeRatesFileName(int index) {
3765 	switch(index) {
3766 		case 1: {return buildPath(getLocalDataDir(), "eurofxref-daily.xml");}
3767 		case 2: {return buildPath(getLocalDataDir(), "btc.json");}
3768 		//case 3: {return buildPath(getLocalDataDir(), "rates.json");}
3769 		case 3: {return buildPath(getLocalDataDir(), "rates.html");}
3770 		case 4: {return buildPath(getLocalDataDir(), "nrby.json");}
3771 		default: {}
3772 	}
3773 	return "";
3774 }
getExchangeRatesTime(int index)3775 time_t Calculator::getExchangeRatesTime(int index) {
3776 	if(index > 5) index = 5;
3777 	if(index < 1) {
3778 		time_t extime = exchange_rates_time[0];
3779 		for(int i = 1; i < 4; i++) {
3780 			if(i > 2 && priv->exchange_rates_time2[i - 3] < extime) extime = priv->exchange_rates_time2[i - 3];
3781 			else if(i <= 2 && exchange_rates_time[i] < extime) extime = exchange_rates_time[i];
3782 		}
3783 		return extime;
3784 	}
3785 	index--;
3786 	if(index == 5) {
3787 		if(exchange_rates_time[2] < priv->exchange_rates_time2[0]) return exchange_rates_time[2];
3788 		return priv->exchange_rates_time2[0];
3789 	}
3790 	if(index > 2) return priv->exchange_rates_time2[index - 3];
3791 	return exchange_rates_time[index];
3792 }
3793 
getExchangeRatesUrl(int index)3794 string Calculator::getExchangeRatesUrl(int index) {
3795 	switch(index) {
3796 		case 1: {return "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml";}
3797 		case 2: {return "https://api.coinbase.com/v2/prices/spot?currency=EUR";}
3798 		//case 2: {return "http://www.mycurrency.net/US.json";}
3799 		case 3: {return "https://www.mycurrency.net/=US";}
3800 		case 4: {return "https://www.nbrb.by/api/exrates/rates/eur?parammode=2";}
3801 		default: {}
3802 	}
3803 	return "";
3804 }
fetchExchangeRates(int timeout,string)3805 bool Calculator::fetchExchangeRates(int timeout, string) {return fetchExchangeRates(timeout);}
write_data(void * ptr,size_t size,size_t nmemb,string * sbuffer)3806 size_t write_data(void *ptr, size_t size, size_t nmemb, string *sbuffer) {
3807 	sbuffer->append((char*) ptr, size * nmemb);
3808 	return size * nmemb;
3809 }
3810 #define FETCH_FAIL_CLEANUP curl_easy_cleanup(curl); curl_global_cleanup(); time(&exchange_rates_check_time[0]); time(&exchange_rates_check_time[1]); time(&exchange_rates_check_time[2]); time(&priv->exchange_rates_check_time2[0]);
fetchExchangeRates(int timeout,int n)3811 bool Calculator::fetchExchangeRates(int timeout, int n) {
3812 #ifdef HAVE_LIBCURL
3813 
3814 	recursiveMakeDir(getLocalDataDir());
3815 	string sbuffer;
3816 	char error_buffer[CURL_ERROR_SIZE];
3817 	CURL *curl;
3818 	CURLcode res;
3819 	long int file_time = 0;
3820 	curl_global_init(CURL_GLOBAL_DEFAULT);
3821 	curl = curl_easy_init();
3822 	if(!curl) {return false;}
3823 	curl_easy_setopt(curl, CURLOPT_URL, getExchangeRatesUrl(1).c_str());
3824 	curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
3825 	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
3826 	curl_easy_setopt(curl, CURLOPT_WRITEDATA, &sbuffer);
3827 	curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer);
3828 	error_buffer[0] = 0;
3829 	curl_easy_setopt(curl, CURLOPT_FILETIME, &file_time);
3830 #ifdef _WIN32
3831 	char exepath[MAX_PATH];
3832 	GetModuleFileName(NULL, exepath, MAX_PATH);
3833 	string datadir(exepath);
3834 	datadir.resize(datadir.find_last_of('\\'));
3835 	if(datadir.substr(datadir.length() - 4) != "\\bin" && datadir.substr(datadir.length() - 6) != "\\.libs") {
3836 		string cainfo = buildPath(datadir, "ssl", "certs", "ca-bundle.crt");
3837 		gsub("\\", "/", cainfo);
3838 		curl_easy_setopt(curl, CURLOPT_CAINFO, cainfo.c_str());
3839 	}
3840 #endif
3841 	res = curl_easy_perform(curl);
3842 
3843 	if(res != CURLE_OK) {
3844 		if(strlen(error_buffer)) error(true, _("Failed to download exchange rates from %s: %s."), "ECB", error_buffer, NULL);
3845 		else error(true, _("Failed to download exchange rates from %s: %s."), "ECB", curl_easy_strerror(res), NULL);
3846 		FETCH_FAIL_CLEANUP;
3847 		return false;
3848 	}
3849 	if(sbuffer.empty()) {error(true, _("Failed to download exchange rates from %s: %s."), "ECB", "Document empty", NULL); FETCH_FAIL_CLEANUP; return false;}
3850 	ofstream file(getExchangeRatesFileName(1).c_str(), ios::out | ios::trunc | ios::binary);
3851 	if(!file.is_open()) {
3852 		error(true, _("Failed to download exchange rates from %s: %s."), "ECB", strerror(errno), NULL);
3853 		FETCH_FAIL_CLEANUP
3854 		return false;
3855 	}
3856 	file << sbuffer;
3857 	file.close();
3858 	if(file_time > 0) {
3859 #ifdef _WIN32
3860 		struct _utimbuf new_times;
3861 #else
3862 		struct utimbuf new_times;
3863 #endif
3864 		new_times.modtime = (time_t) file_time;
3865 		new_times.actime = (time_t) file_time;
3866 #ifdef _WIN32
3867 		_utime(getExchangeRatesFileName(1).c_str(), &new_times);
3868 #else
3869 		utime(getExchangeRatesFileName(1).c_str(), &new_times);
3870 #endif
3871 	}
3872 
3873 	if(n <= 0 || n >= 2) {
3874 
3875 		sbuffer = "";
3876 		curl_easy_setopt(curl, CURLOPT_URL, getExchangeRatesUrl(2).c_str());
3877 		curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
3878 		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
3879 		curl_easy_setopt(curl, CURLOPT_WRITEDATA, &sbuffer);
3880 		curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer);
3881 
3882 		res = curl_easy_perform(curl);
3883 
3884 		if(res != CURLE_OK) {error(true, _("Failed to download exchange rates from %s: %s."), "coinbase.com", error_buffer, NULL); FETCH_FAIL_CLEANUP; return false;}
3885 		if(sbuffer.empty()) {error(true, _("Failed to download exchange rates from %s: %s."), "coinbase.com", "Document empty", NULL); FETCH_FAIL_CLEANUP; return false;}
3886 		ofstream file3(getExchangeRatesFileName(2).c_str(), ios::out | ios::trunc | ios::binary);
3887 		if(!file3.is_open()) {
3888 			error(true, _("Failed to download exchange rates from %s: %s."), "coinbase.com", strerror(errno), NULL);
3889 			FETCH_FAIL_CLEANUP
3890 			return false;
3891 		}
3892 		file3 << sbuffer;
3893 		file3.close();
3894 
3895 	}
3896 
3897 	if(n <= 0 || (n >= 3 && n != 4)) {
3898 
3899 		sbuffer = "";
3900 		curl_easy_setopt(curl, CURLOPT_URL, getExchangeRatesUrl(3).c_str());
3901 		curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
3902 		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
3903 		curl_easy_setopt(curl, CURLOPT_WRITEDATA, &sbuffer);
3904 		curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer);
3905 		res = curl_easy_perform(curl);
3906 
3907 		if(res != CURLE_OK) {error(true, _("Failed to download exchange rates from %s: %s."), "mycurrency.net", error_buffer, NULL); FETCH_FAIL_CLEANUP; return false;}
3908 		if(sbuffer.empty() || sbuffer.find("Internal Server Error") != string::npos) {error(true, _("Failed to download exchange rates from %s: %s."), "mycurrency.net", "Document empty", NULL); FETCH_FAIL_CLEANUP; return false;}
3909 		ofstream file2(getExchangeRatesFileName(3).c_str(), ios::out | ios::trunc | ios::binary);
3910 		if(!file2.is_open()) {
3911 			error(true, _("Failed to download exchange rates from %s: %s."), "mycurrency.net", strerror(errno), NULL);
3912 			FETCH_FAIL_CLEANUP
3913 			return false;
3914 		}
3915 		file2 << sbuffer;
3916 		file2.close();
3917 
3918 	}
3919 
3920 	if(n <= 0 || n >= 4) {
3921 
3922 		sbuffer = "";
3923 		curl_easy_setopt(curl, CURLOPT_URL, getExchangeRatesUrl(4).c_str());
3924 		curl_easy_setopt(curl, CURLOPT_TIMEOUT, (timeout > 4 && n <= 0) ? 4 : timeout);
3925 		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
3926 		curl_easy_setopt(curl, CURLOPT_WRITEDATA, &sbuffer);
3927 		curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer);
3928 
3929 		res = curl_easy_perform(curl);
3930 
3931 		if(res != CURLE_OK) {error(true, _("Failed to download exchange rates from %s: %s."), "nbrb.by", error_buffer, NULL); FETCH_FAIL_CLEANUP; return false;}
3932 		if(sbuffer.empty()) {error(true, _("Failed to download exchange rates from %s: %s."), "nbrb.by", "Document empty", NULL); FETCH_FAIL_CLEANUP; return false;}
3933 		ofstream file4(getExchangeRatesFileName(4).c_str(), ios::out | ios::trunc | ios::binary);
3934 		if(!file4.is_open()) {
3935 			error(true, _("Failed to download exchange rates from %s: %s."), "nbrb.by", strerror(errno), NULL);
3936 			FETCH_FAIL_CLEANUP
3937 			return false;
3938 		}
3939 		file4 << sbuffer;
3940 		file4.close();
3941 
3942 	}
3943 
3944 	curl_easy_cleanup(curl); curl_global_cleanup();
3945 
3946 	return true;
3947 #else
3948 	return false;
3949 #endif
3950 }
checkExchangeRatesDate(unsigned int n_days,bool force_check,bool send_warning,int n)3951 bool Calculator::checkExchangeRatesDate(unsigned int n_days, bool force_check, bool send_warning, int n) {
3952 	if(n <= 0) n = 5;
3953 	time_t extime = exchange_rates_time[0];
3954 	for(int i = 1; i < (n > 5 ? 4 : n); i++) {
3955 		if(i > 2 && priv->exchange_rates_time2[i - 3] < extime) extime = priv->exchange_rates_time2[i - 3];
3956 		else if(i <= 2 && (i != 2 || n != 4) && exchange_rates_time[i] < extime) extime = exchange_rates_time[i];
3957 	}
3958 	time_t cextime = exchange_rates_check_time[0];
3959 	for(int i = 1; i < (n > 5 ? 4 : n); i++) {
3960 		if(i > 2 && priv->exchange_rates_check_time2[i - 3] < cextime) cextime = priv->exchange_rates_check_time2[i - 3];
3961 		else if(i <= 2 && (i != 2 || n != 4) && exchange_rates_check_time[i] < cextime) cextime = exchange_rates_check_time[i];
3962 	}
3963 	if(extime > 0 && ((!force_check && cextime > 0 && difftime(time(NULL), cextime) < 86400 * n_days) || difftime(time(NULL), extime) < (86400 * n_days) + 3600)) return true;
3964 	for(int i = 0; i < (n > 5 ? 4 : n); i++) {
3965 		if(i <= 2 && (i != 2 || n != 4)) time(&exchange_rates_check_time[i]);
3966 		else if(i > 2) time(&priv->exchange_rates_check_time2[i - 3]);
3967 	}
3968 	if(send_warning) error(false, _("It has been %s day(s) since the exchange rates last were updated."), i2s((int) floor(difftime(time(NULL), extime) / 86400)).c_str(), NULL);
3969 	return false;
3970 }
setExchangeRatesWarningEnabled(bool enable)3971 void Calculator::setExchangeRatesWarningEnabled(bool enable) {
3972 	b_exchange_rates_warning_enabled = enable;
3973 }
exchangeRatesWarningEnabled() const3974 bool Calculator::exchangeRatesWarningEnabled() const {
3975 	return b_exchange_rates_warning_enabled;
3976 }
exchangeRatesUsed() const3977 int Calculator::exchangeRatesUsed() const {
3978 	return b_exchange_rates_used;
3979 }
resetExchangeRatesUsed()3980 void Calculator::resetExchangeRatesUsed() {
3981 	b_exchange_rates_used = 0;
3982 }
setExchangeRatesUsed(int index)3983 void Calculator::setExchangeRatesUsed(int index) {
3984 	if(index > b_exchange_rates_used) b_exchange_rates_used = index;
3985 	if(b_exchange_rates_warning_enabled) checkExchangeRatesDate(7, false, true);
3986 }
3987 
3988