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 = ⊤
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 = ⊤
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 = ⊤
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 = ⊤
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