Branch data Line data Source code
1 : : /********************************************************************
2 : : * sixtp-dom-parsers.c *
3 : : * Copyright 2001 Gnumatic, Inc. *
4 : : * *
5 : : * This program is free software; you can redistribute it and/or *
6 : : * modify it under the terms of the GNU General Public License as *
7 : : * published by the Free Software Foundation; either version 2 of *
8 : : * the License, or (at your option) any later version. *
9 : : * *
10 : : * This program is distributed in the hope that it will be useful, *
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 : : * GNU General Public License for more details. *
14 : : * *
15 : : * You should have received a copy of the GNU General Public License*
16 : : * along with this program; if not, contact: *
17 : : * *
18 : : * Free Software Foundation Voice: +1-617-542-5942 *
19 : : * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20 : : * Boston, MA 02110-1301, USA gnu@gnu.org *
21 : : * *
22 : : ********************************************************************/
23 : : #include <glib.h>
24 : :
25 : : #include <config.h>
26 : :
27 : : #include <string.h>
28 : :
29 : : #include <gnc-engine.h>
30 : :
31 : : #include "gnc-xml-helper.h"
32 : : #include "sixtp-utils.h"
33 : : #include "sixtp-dom-parsers.h"
34 : : #include <kvp-frame.hpp>
35 : :
36 : : static QofLogModule log_module = GNC_MOD_IO;
37 : :
38 : : const char*
39 : 37007 : dom_node_to_text (xmlNodePtr node) noexcept
40 : : {
41 : 37007 : if (node && node->children && node->children->type == XML_TEXT_NODE
42 : 36441 : && !node->children->next)
43 : 36441 : return reinterpret_cast<const char*>(node->children->content);
44 : 566 : return nullptr;
45 : : }
46 : :
47 : : std::optional<GncGUID>
48 : 8537 : dom_tree_to_guid (xmlNodePtr node)
49 : : {
50 : 8537 : auto type = xmlGetProp (node, BAD_CAST "type");
51 : 8537 : if (!type)
52 : 0 : return {};
53 : :
54 : 8537 : bool ok = !g_strcmp0 ((char*)type, "guid") || !g_strcmp0 ((char*)type, "new");
55 : :
56 : 8537 : xmlFree (type);
57 : :
58 : 8537 : if (!ok)
59 : 0 : return {};
60 : :
61 : 8537 : auto extract_guid = [](auto str) -> std::optional<GncGUID>
62 : : {
63 : 8537 : if (GncGUID guid; string_to_guid (str, &guid))
64 : 8537 : return guid;
65 : :
66 : 0 : return {};
67 : : };
68 : :
69 : 8537 : return apply_xmlnode_text<std::optional<GncGUID>>(extract_guid, node);
70 : : }
71 : :
72 : : static KvpValue*
73 : 1241 : dom_tree_to_integer_kvp_value (xmlNodePtr node)
74 : : {
75 : 1241 : auto node_to_int_kvp = [](auto txt) -> KvpValue*
76 : : {
77 : 1241 : if (gint64 daint; string_to_gint64 (txt, &daint))
78 : 1241 : return new KvpValue{daint};
79 : :
80 : 0 : return nullptr;
81 : : };
82 : 2482 : return apply_xmlnode_text<KvpValue*> (node_to_int_kvp, node, nullptr);
83 : : }
84 : :
85 : : template <typename T>
86 : : static bool
87 : 719 : dom_tree_to_num (xmlNodePtr node, std::function<bool(const char*, T*)>string_to_num, T* num_ptr)
88 : : {
89 : 1438 : return apply_xmlnode_text<T>([&](auto txt){ return string_to_num (txt, num_ptr);}, node, false);
90 : : }
91 : :
92 : : gboolean
93 : 712 : dom_tree_to_integer (xmlNodePtr node, gint64* daint)
94 : : {
95 : 712 : return dom_tree_to_num<gint64>(node, string_to_gint64, daint);
96 : : }
97 : :
98 : : gboolean
99 : 6 : dom_tree_to_guint16 (xmlNodePtr node, guint16* i)
100 : : {
101 : 6 : return dom_tree_to_num<guint16>(node, string_to_guint16, i);
102 : : }
103 : :
104 : : gboolean
105 : 1 : dom_tree_to_guint (xmlNodePtr node, guint* i)
106 : : {
107 : 1 : return dom_tree_to_num<guint>(node, string_to_guint, i);
108 : : }
109 : :
110 : : gboolean
111 : 0 : dom_tree_to_boolean (xmlNodePtr node, gboolean* b)
112 : : {
113 : 0 : auto set_bool = [b](auto text) -> gboolean
114 : : {
115 : 0 : if (g_ascii_strncasecmp (text, "true", 4) == 0)
116 : : {
117 : 0 : *b = TRUE;
118 : 0 : return TRUE;
119 : : }
120 : 0 : else if (g_ascii_strncasecmp (text, "false", 5) == 0)
121 : : {
122 : 0 : *b = FALSE;
123 : 0 : return TRUE;
124 : : }
125 : : else
126 : : {
127 : 0 : *b = FALSE;
128 : 0 : return FALSE;
129 : : }
130 : 0 : };
131 : 0 : return apply_xmlnode_text<gboolean> (set_bool, node);
132 : : }
133 : :
134 : : static KvpValue*
135 : 0 : dom_tree_to_double_kvp_value (xmlNodePtr node)
136 : : {
137 : 0 : auto node_to_double_kvp = [](auto txt) -> KvpValue*
138 : : {
139 : 0 : if (double dadoub; string_to_double (txt, &dadoub)) return new KvpValue{dadoub};
140 : 0 : return nullptr;
141 : : };
142 : 0 : return apply_xmlnode_text<KvpValue*> (node_to_double_kvp, node, nullptr);
143 : : }
144 : :
145 : : static KvpValue*
146 : 595 : dom_tree_to_numeric_kvp_value (xmlNodePtr node)
147 : : {
148 : 595 : return new KvpValue {dom_tree_to_gnc_numeric (node)};
149 : : }
150 : :
151 : : static KvpValue*
152 : 918 : dom_tree_to_string_kvp_value (xmlNodePtr node)
153 : : {
154 : 918 : auto node_to_string_kvp = [](auto txt) -> KvpValue*
155 : : {
156 : 1836 : return new KvpValue {g_strdup (txt)};
157 : : };
158 : 1836 : return apply_xmlnode_text<KvpValue*> (node_to_string_kvp, node, nullptr);
159 : : }
160 : :
161 : : static KvpValue*
162 : 621 : dom_tree_to_guid_kvp_value (xmlNodePtr node)
163 : : {
164 : 621 : auto daguid = dom_tree_to_guid (node);
165 : 1242 : return daguid ? new KvpValue {guid_copy (&*daguid)} : nullptr;
166 : : }
167 : :
168 : : static KvpValue*
169 : 5 : dom_tree_to_time64_kvp_value (xmlNodePtr node)
170 : : {
171 : 5 : Time64 t{dom_tree_to_time64 (node)};
172 : 10 : return new KvpValue {t};
173 : : }
174 : :
175 : : static KvpValue*
176 : 7 : dom_tree_to_gdate_kvp_value (xmlNodePtr node)
177 : : {
178 : 7 : auto date = dom_tree_to_gdate (node);
179 : 7 : return date ? new KvpValue {*date} : nullptr;
180 : : }
181 : :
182 : : gboolean
183 : 0 : string_to_binary (const gchar* str, void** v, guint64* data_len)
184 : : {
185 : : guint64 str_len;
186 : : guchar* data;
187 : : unsigned int i, j;
188 : :
189 : 0 : g_return_val_if_fail (v != NULL, FALSE);
190 : 0 : g_return_val_if_fail (data_len != NULL, FALSE);
191 : :
192 : 0 : str_len = strlen (str);
193 : :
194 : : /* Since no whitespace is allowed and hex encoding is 2 text chars
195 : : per binary char, the result must be half the input size and the
196 : : input size must be even. */
197 : 0 : if ((str_len % 2) != 0)
198 : 0 : return (FALSE);
199 : 0 : *data_len = str_len / 2;
200 : 0 : data = g_new0 (guchar, *data_len);
201 : :
202 : 0 : for (j = 0, i = 0; i < str_len; i += 2, j++)
203 : : {
204 : : gchar tmpstr[3];
205 : : long int converted;
206 : :
207 : 0 : tmpstr[0] = str[i];
208 : 0 : tmpstr[1] = str[i + 1];
209 : 0 : tmpstr[2] = '\0';
210 : :
211 : 0 : converted = strtol (tmpstr, NULL, 16);
212 : :
213 : 0 : data[j] = (unsigned char)converted;
214 : : }
215 : :
216 : 0 : *v = data;
217 : :
218 : 0 : return (TRUE);
219 : : }
220 : :
221 : : static KvpValue* dom_tree_to_kvp_value (xmlNodePtr node);
222 : : //needed for test access as well as internal use.
223 : : KvpFrame* dom_tree_to_kvp_frame (xmlNodePtr node);
224 : :
225 : : static KvpValue*
226 : 356 : dom_tree_to_list_kvp_value (xmlNodePtr node)
227 : : {
228 : 356 : GList* list = NULL;
229 : : xmlNodePtr mark;
230 : 356 : KvpValue* ret = NULL;
231 : :
232 : 2355 : for (mark = node->xmlChildrenNode; mark; mark = mark->next)
233 : : {
234 : : KvpValue* new_val;
235 : :
236 : 1999 : if (g_strcmp0 ((char*)mark->name, "text") == 0)
237 : 925 : continue;
238 : :
239 : 1074 : new_val = dom_tree_to_kvp_value (mark);
240 : 1074 : if (new_val)
241 : : {
242 : 1074 : list = g_list_prepend (list, (gpointer)new_val);
243 : : }
244 : : }
245 : :
246 : 356 : list = g_list_reverse (list);
247 : :
248 : 356 : ret = new KvpValue {list};
249 : :
250 : 356 : return ret;
251 : : }
252 : :
253 : : static KvpValue*
254 : 368 : dom_tree_to_frame_kvp_value (xmlNodePtr node)
255 : : {
256 : 368 : KvpFrame* frame = dom_tree_to_kvp_frame (node);
257 : 368 : return frame ? new KvpValue {frame} : nullptr;
258 : : }
259 : :
260 : :
261 : : struct kvp_val_converter
262 : : {
263 : : const gchar* tag;
264 : : KvpValue* (*converter) (xmlNodePtr node);
265 : : };
266 : : /* Note: The type attribute must remain 'timespec' to maintain compatibility.
267 : : */
268 : :
269 : : struct kvp_val_converter val_converters[] =
270 : : {
271 : : { "integer", dom_tree_to_integer_kvp_value },
272 : : { "double", dom_tree_to_double_kvp_value },
273 : : { "numeric", dom_tree_to_numeric_kvp_value },
274 : : { "string", dom_tree_to_string_kvp_value },
275 : : { "guid", dom_tree_to_guid_kvp_value },
276 : : { "timespec", dom_tree_to_time64_kvp_value },
277 : : { "gdate", dom_tree_to_gdate_kvp_value },
278 : : { "list", dom_tree_to_list_kvp_value },
279 : : { "frame", dom_tree_to_frame_kvp_value },
280 : : { 0, 0 },
281 : : };
282 : :
283 : : static KvpValue*
284 : 4111 : dom_tree_to_kvp_value (xmlNodePtr node)
285 : : {
286 : : xmlChar* xml_type;
287 : : struct kvp_val_converter* mark;
288 : 4111 : KvpValue* ret = NULL;
289 : :
290 : 4111 : xml_type = xmlGetProp (node, BAD_CAST "type");
291 : :
292 : 41110 : for (mark = val_converters; mark->tag; mark++)
293 : : {
294 : 36999 : if (g_strcmp0 (reinterpret_cast<char*>(xml_type), mark->tag) == 0)
295 : : {
296 : 4111 : ret = (mark->converter) (node);
297 : : }
298 : : }
299 : :
300 : 4111 : if (!mark->tag)
301 : : {
302 : : /* FIXME: deal with unknown type tag here */
303 : : }
304 : :
305 : 4111 : xmlFree (xml_type);
306 : :
307 : 4111 : return ret;
308 : : }
309 : :
310 : : static gboolean
311 : 904 : dom_tree_to_kvp_frame_given (xmlNodePtr node, KvpFrame* frame)
312 : : {
313 : : xmlNodePtr mark;
314 : :
315 : 904 : g_return_val_if_fail (node, FALSE);
316 : 904 : g_return_val_if_fail (frame, FALSE);
317 : :
318 : 6751 : for (mark = node->xmlChildrenNode; mark; mark = mark->next)
319 : : {
320 : 5847 : if (g_strcmp0 ((char*)mark->name, "slot") == 0)
321 : : {
322 : : xmlNodePtr mark2;
323 : 3037 : const gchar* key = NULL;
324 : 3037 : std::optional<std::string> maybe_key;
325 : 3037 : KvpValue* val = NULL;
326 : :
327 : 15459 : for (mark2 = mark->xmlChildrenNode; mark2; mark2 = mark2->next)
328 : : {
329 : 12422 : if (g_strcmp0 ((char*)mark2->name, "slot:key") == 0)
330 : : {
331 : 3037 : key = dom_node_to_text (mark2);
332 : 3037 : if (!key)
333 : : {
334 : 0 : maybe_key = dom_tree_to_text (mark2);
335 : 0 : key = maybe_key ? maybe_key->c_str() : nullptr;
336 : : }
337 : : }
338 : 9385 : else if (g_strcmp0 ((char*)mark2->name, "slot:value") == 0)
339 : : {
340 : 3037 : val = dom_tree_to_kvp_value (mark2);
341 : : }
342 : : else
343 : : {
344 : : /* FIXME: should put some error here.
345 : : * But ignore text type! */
346 : : }
347 : : }
348 : :
349 : 3037 : if (key)
350 : : {
351 : 3037 : if (val)
352 : : {
353 : : //We're deleting the old KvpValue returned by replace_nc().
354 : 9111 : delete frame->set ({key}, val);
355 : : }
356 : : else
357 : : {
358 : : /* FIXME: should put some error here */
359 : : }
360 : : }
361 : 3037 : }
362 : : }
363 : :
364 : 904 : return TRUE;
365 : 9111 : }
366 : :
367 : :
368 : : KvpFrame*
369 : 459 : dom_tree_to_kvp_frame (xmlNodePtr node)
370 : : {
371 : 459 : g_return_val_if_fail (node, NULL);
372 : :
373 : 459 : auto ret = new KvpFrame;
374 : :
375 : 459 : if (dom_tree_to_kvp_frame_given (node, ret))
376 : 459 : return ret;
377 : :
378 : 0 : delete ret;
379 : 0 : return NULL;
380 : : }
381 : :
382 : : gboolean
383 : 445 : dom_tree_create_instance_slots (xmlNodePtr node, QofInstance* inst)
384 : : {
385 : 445 : KvpFrame* frame = qof_instance_get_slots (inst);
386 : 445 : return dom_tree_to_kvp_frame_given (node, frame);
387 : : }
388 : :
389 : : std::optional<std::string>
390 : 1195 : dom_tree_to_text (xmlNodePtr tree)
391 : : {
392 : : /* Expect *only* text and comment sibling nodes in the given tree --
393 : : which actually may only be a "list". i.e. if you're trying to
394 : : extract bar from <foo>bar</foo>, pass in <foo>->xmlChildrenNode
395 : : to this function. This expectation is different from the rest of
396 : : the dom_tree_to_* converters...
397 : :
398 : : Ignores comment nodes and collapse text nodes into one string.
399 : : Returns NULL if expectations are unsatisfied.
400 : : */
401 : 1195 : std::string rv;
402 : : gchar* temp;
403 : :
404 : 1195 : g_return_val_if_fail (tree, std::nullopt);
405 : :
406 : : /* no nodes means it's an empty string text */
407 : 1195 : if (!tree->xmlChildrenNode)
408 : : {
409 : 575 : DEBUG ("No children");
410 : 575 : return "";
411 : : }
412 : :
413 : 620 : temp = (char*)xmlNodeListGetString (NULL, tree->xmlChildrenNode, TRUE);
414 : 620 : if (!temp)
415 : : {
416 : 0 : DEBUG ("Null string");
417 : 0 : return std::nullopt;
418 : : }
419 : :
420 : 620 : DEBUG ("node string [%s]", (temp == NULL ? "(null)" : temp));
421 : 620 : rv = temp;
422 : 620 : xmlFree (temp);
423 : 620 : return rv;
424 : 1195 : }
425 : :
426 : : gnc_numeric
427 : 5162 : dom_tree_to_gnc_numeric (xmlNodePtr node)
428 : : {
429 : 5162 : auto node_to_numeric = [](auto txt)
430 : : {
431 : 5162 : gnc_numeric num = gnc_numeric_from_string(txt);
432 : 10324 : return gnc_numeric_check (num) ? gnc_numeric_zero() : num;
433 : : };
434 : 10324 : return apply_xmlnode_text<gnc_numeric> (node_to_numeric, node, gnc_numeric_zero());
435 : : }
436 : :
437 : :
438 : : time64
439 : 2661 : dom_tree_to_time64 (xmlNodePtr node)
440 : : {
441 : : /* Turn something like this
442 : :
443 : : <date-posted>
444 : : <ts:date>Mon, 05 Jun 2000 23:16:19 -0500</ts:date>
445 : : </date-posted>
446 : :
447 : : into a time64, returning INT64_MAX that we're using to flag an erroneous
448 : : date if there's a problem. Only one ts:date element is permitted for any
449 : : date attribute.
450 : : */
451 : :
452 : 2661 : time64 ret {INT64_MAX};
453 : 2661 : gboolean seen = FALSE;
454 : : xmlNodePtr n;
455 : :
456 : 11852 : for (n = node->xmlChildrenNode; n; n = n->next)
457 : : {
458 : 9191 : switch (n->type)
459 : : {
460 : 5806 : case XML_COMMENT_NODE:
461 : : case XML_TEXT_NODE:
462 : 5806 : break;
463 : 3385 : case XML_ELEMENT_NODE:
464 : 3385 : if (g_strcmp0 ("ts:date", (char*)n->name) == 0)
465 : : {
466 : 2661 : if (seen)
467 : : {
468 : 0 : return INT64_MAX;
469 : : }
470 : 2661 : seen = TRUE;
471 : 2661 : ret = apply_xmlnode_text<time64> (gnc_iso8601_to_time64_gmt, n, INT64_MAX);
472 : : }
473 : 3385 : break;
474 : 0 : default:
475 : 0 : PERR ("unexpected sub-node.");
476 : 0 : return INT64_MAX;
477 : : break;
478 : : }
479 : : }
480 : :
481 : 2661 : if (!seen)
482 : : {
483 : 0 : PERR ("no ts:date node found.");
484 : 0 : return INT64_MAX;
485 : : }
486 : :
487 : 2661 : return ret;
488 : : }
489 : :
490 : : GDate*
491 : 21 : dom_tree_to_gdate (xmlNodePtr node)
492 : : {
493 : : /* Turn something like this
494 : :
495 : : <sx:startdate>
496 : : <gdate>2001-04-03</gdate>
497 : : </sx:startdate>
498 : :
499 : : into a GDate. If the xml is invalid, returns NULL. */
500 : :
501 : : GDate ret;
502 : 21 : gboolean seen_date = FALSE;
503 : : xmlNodePtr n;
504 : :
505 : 21 : auto try_setting_date = [&ret](const char *content) -> bool
506 : : {
507 : 21 : gint year = 0, month = 0, day = 0;
508 : 21 : if (sscanf (content, "%d-%d-%d", &year, &month, &day) != 3) return false;
509 : 21 : g_date_set_dmy (&ret, day, static_cast<GDateMonth>(month), year);
510 : 21 : return (g_date_valid (&ret));
511 : 21 : };
512 : :
513 : : /* creates an invalid date */
514 : 21 : g_date_clear (&ret, 1);
515 : :
516 : 84 : for (n = node->xmlChildrenNode; n; n = n->next)
517 : : {
518 : 63 : switch (n->type)
519 : : {
520 : 42 : case XML_COMMENT_NODE:
521 : : case XML_TEXT_NODE:
522 : 42 : break;
523 : 21 : case XML_ELEMENT_NODE:
524 : 21 : if (g_strcmp0 ("gdate", (char*)n->name) == 0)
525 : : {
526 : 21 : if (seen_date || !apply_xmlnode_text<bool> (try_setting_date, n))
527 : 0 : return NULL;
528 : 21 : seen_date = TRUE;
529 : : }
530 : 21 : break;
531 : 0 : default:
532 : 0 : PERR ("unexpected sub-node.");
533 : 0 : return NULL;
534 : : }
535 : : }
536 : :
537 : 21 : if (!seen_date)
538 : : {
539 : 0 : PWARN ("no gdate node found.");
540 : 0 : return NULL;
541 : : }
542 : :
543 : 21 : return g_date_copy (&ret);
544 : : }
545 : :
546 : : struct CommodityRef
547 : : {
548 : : std::string space;
549 : : std::string id;
550 : : };
551 : :
552 : : std::string
553 : 7462 : gnc_strstrip (std::string_view sv)
554 : : {
555 : 7462 : while (!sv.empty () && g_ascii_isspace (sv.front())) sv.remove_prefix (1);
556 : 7462 : while (!sv.empty () && g_ascii_isspace (sv.back())) sv.remove_suffix (1);
557 : 14924 : return std::string (sv);
558 : : }
559 : :
560 : : static std::optional<CommodityRef>
561 : 3525 : parse_commodity_ref (xmlNodePtr node, QofBook* book)
562 : : {
563 : : /* Turn something like this
564 : :
565 : : <currency>
566 : : <cmdty:space>NASDAQ</cmdty:space>
567 : : <cmdty:id>LNUX</cmdty:space>
568 : : </currency>
569 : :
570 : : into a gnc_commodity*, returning NULL on failure. Both sub-nodes
571 : : are required, though for now, order is irrelevant. */
572 : :
573 : 3525 : CommodityRef rv;
574 : 3525 : bool space_set{false};
575 : 3525 : bool id_set{false};
576 : : xmlNodePtr n;
577 : :
578 : 3525 : if (!node) return {};
579 : 3525 : if (!node->xmlChildrenNode) return {};
580 : :
581 : 21027 : for (n = node->xmlChildrenNode; n; n = n->next)
582 : : {
583 : 17502 : switch (n->type)
584 : : {
585 : 10452 : case XML_COMMENT_NODE:
586 : : case XML_TEXT_NODE:
587 : 10452 : break;
588 : 7050 : case XML_ELEMENT_NODE:
589 : 7050 : if (g_strcmp0 ("cmdty:space", (char*)n->name) == 0)
590 : : {
591 : 3525 : if (space_set)
592 : : {
593 : 0 : return {};
594 : : }
595 : 3525 : rv.space = apply_xmlnode_text<std::string> (gnc_strstrip, n);
596 : 3525 : space_set = true;
597 : : }
598 : 3525 : else if (g_strcmp0 ("cmdty:id", (char*)n->name) == 0)
599 : : {
600 : 3525 : if (id_set)
601 : : {
602 : 0 : return {};
603 : : }
604 : 3525 : rv.id = apply_xmlnode_text<std::string> (gnc_strstrip, n);
605 : 3525 : id_set = true;
606 : : }
607 : 7050 : break;
608 : 0 : default:
609 : 0 : PERR ("unexpected sub-node.");
610 : 0 : return {};
611 : : break;
612 : : }
613 : : }
614 : 3525 : if (space_set && id_set)
615 : 3525 : return rv;
616 : :
617 : 0 : return {};
618 : 3525 : }
619 : :
620 : : gnc_commodity*
621 : 41 : dom_tree_to_commodity_ref_no_engine (xmlNodePtr node, QofBook* book)
622 : : {
623 : 41 : auto ref = parse_commodity_ref (node, book);
624 : :
625 : 41 : if (!ref)
626 : 0 : return nullptr;
627 : :
628 : 41 : return gnc_commodity_new (book, nullptr, ref->space.c_str(), ref->id.c_str(),
629 : 41 : nullptr, 0);
630 : 41 : }
631 : :
632 : : gnc_commodity*
633 : 3484 : dom_tree_to_commodity_ref (xmlNodePtr node, QofBook* book)
634 : : {
635 : : gnc_commodity* ret;
636 : : gnc_commodity_table* table;
637 : :
638 : 3484 : auto ref = parse_commodity_ref (node, book);
639 : :
640 : 3484 : if (!ref)
641 : 0 : return nullptr;
642 : :
643 : 3484 : table = gnc_commodity_table_get_table (book);
644 : :
645 : 3484 : g_return_val_if_fail (table != NULL, NULL);
646 : :
647 : 3484 : ret = gnc_commodity_table_lookup (table, ref->space.c_str(), ref->id.c_str());
648 : :
649 : 3484 : g_return_val_if_fail (ret != NULL, NULL);
650 : :
651 : 3484 : return ret;
652 : 3484 : }
653 : :
654 : : /***********************************************************************/
655 : : /* generic parser */
656 : :
657 : : static inline void
658 : 3806 : dom_tree_handlers_reset (struct dom_tree_handler* handlers)
659 : : {
660 : 48649 : for (; handlers->tag != NULL; handlers++)
661 : : {
662 : 44843 : handlers->gotten = 0;
663 : : }
664 : 3806 : }
665 : :
666 : : static inline gboolean
667 : 3806 : dom_tree_handlers_all_gotten_p (struct dom_tree_handler* handlers)
668 : : {
669 : 3806 : gboolean ret = TRUE;
670 : 48649 : for (; handlers->tag != NULL; handlers++)
671 : : {
672 : 44843 : if (handlers->required && ! handlers->gotten)
673 : : {
674 : 0 : PERR ("Not defined and it should be: %s",
675 : : handlers->tag ? handlers->tag : "(null)");
676 : 0 : ret = FALSE;
677 : : }
678 : : }
679 : 3806 : return ret;
680 : : }
681 : :
682 : :
683 : : static inline gboolean
684 : 22831 : gnc_xml_set_data (const gchar* tag, xmlNodePtr node, gpointer item,
685 : : struct dom_tree_handler* handlers)
686 : : {
687 : 109302 : for (; handlers->tag != NULL; handlers++)
688 : : {
689 : 109302 : if (g_strcmp0 (tag, handlers->tag) == 0)
690 : : {
691 : 22831 : (handlers->handler) (node, item);
692 : 22831 : handlers->gotten = TRUE;
693 : 22831 : break;
694 : : }
695 : : }
696 : :
697 : 22831 : if (!handlers->tag)
698 : : {
699 : 0 : PERR ("Unhandled tag: %s",
700 : : tag ? tag : "(null)");
701 : 0 : return FALSE;
702 : : }
703 : :
704 : 22831 : return TRUE;
705 : : }
706 : :
707 : : gboolean
708 : 3806 : dom_tree_generic_parse (xmlNodePtr node, struct dom_tree_handler* handlers,
709 : : gpointer data)
710 : : {
711 : : xmlNodePtr achild;
712 : 3806 : gboolean successful = TRUE;
713 : :
714 : 3806 : dom_tree_handlers_reset (handlers);
715 : :
716 : 53273 : for (achild = node->xmlChildrenNode; achild; achild = achild->next)
717 : : {
718 : : /* ignore stray text nodes */
719 : 49467 : if (g_strcmp0 ((char*)achild->name, "text") == 0)
720 : 26636 : continue;
721 : :
722 : 22831 : if (!gnc_xml_set_data ((char*)achild->name, achild, data, handlers))
723 : : {
724 : 0 : PERR ("gnc_xml_set_data failed");
725 : 0 : successful = FALSE;
726 : 0 : continue;
727 : : }
728 : : }
729 : :
730 : 3806 : if (!dom_tree_handlers_all_gotten_p (handlers))
731 : : {
732 : 0 : PERR ("didn't find all of the expected tags in the input");
733 : 0 : successful = FALSE;
734 : : }
735 : :
736 : 3806 : return successful;
737 : : }
738 : :
739 : : gboolean
740 : 2556 : dom_tree_valid_time64 (time64 val, const xmlChar * name)
741 : : {
742 : 2556 : if (val != INT64_MAX)
743 : 2556 : return TRUE;
744 : 0 : g_warning ("Invalid timestamp in data file. Look for a '%s' entry "
745 : : "with a year outside of the valid range: 1400..10000", name);
746 : 0 : return FALSE;
747 : : }
|