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 : if (!date) return nullptr;
180 : 7 : auto rv{new KvpValue {*date}};
181 : 7 : g_date_free (date);
182 : 7 : return rv;
183 : : }
184 : :
185 : : gboolean
186 : 0 : string_to_binary (const gchar* str, void** v, guint64* data_len)
187 : : {
188 : : guint64 str_len;
189 : : guchar* data;
190 : : unsigned int i, j;
191 : :
192 : 0 : g_return_val_if_fail (v != NULL, FALSE);
193 : 0 : g_return_val_if_fail (data_len != NULL, FALSE);
194 : :
195 : 0 : str_len = strlen (str);
196 : :
197 : : /* Since no whitespace is allowed and hex encoding is 2 text chars
198 : : per binary char, the result must be half the input size and the
199 : : input size must be even. */
200 : 0 : if ((str_len % 2) != 0)
201 : 0 : return (FALSE);
202 : 0 : *data_len = str_len / 2;
203 : 0 : data = g_new0 (guchar, *data_len);
204 : :
205 : 0 : for (j = 0, i = 0; i < str_len; i += 2, j++)
206 : : {
207 : : gchar tmpstr[3];
208 : : long int converted;
209 : :
210 : 0 : tmpstr[0] = str[i];
211 : 0 : tmpstr[1] = str[i + 1];
212 : 0 : tmpstr[2] = '\0';
213 : :
214 : 0 : converted = strtol (tmpstr, NULL, 16);
215 : :
216 : 0 : data[j] = (unsigned char)converted;
217 : : }
218 : :
219 : 0 : *v = data;
220 : :
221 : 0 : return (TRUE);
222 : : }
223 : :
224 : : static KvpValue* dom_tree_to_kvp_value (xmlNodePtr node);
225 : : //needed for test access as well as internal use.
226 : : KvpFrame* dom_tree_to_kvp_frame (xmlNodePtr node);
227 : :
228 : : static KvpValue*
229 : 356 : dom_tree_to_list_kvp_value (xmlNodePtr node)
230 : : {
231 : 356 : GList* list = NULL;
232 : : xmlNodePtr mark;
233 : 356 : KvpValue* ret = NULL;
234 : :
235 : 2355 : for (mark = node->xmlChildrenNode; mark; mark = mark->next)
236 : : {
237 : : KvpValue* new_val;
238 : :
239 : 1999 : if (g_strcmp0 ((char*)mark->name, "text") == 0)
240 : 925 : continue;
241 : :
242 : 1074 : new_val = dom_tree_to_kvp_value (mark);
243 : 1074 : if (new_val)
244 : : {
245 : 1074 : list = g_list_prepend (list, (gpointer)new_val);
246 : : }
247 : : }
248 : :
249 : 356 : list = g_list_reverse (list);
250 : :
251 : 356 : ret = new KvpValue {list};
252 : :
253 : 356 : return ret;
254 : : }
255 : :
256 : : static KvpValue*
257 : 368 : dom_tree_to_frame_kvp_value (xmlNodePtr node)
258 : : {
259 : 368 : KvpFrame* frame = dom_tree_to_kvp_frame (node);
260 : 368 : return frame ? new KvpValue {frame} : nullptr;
261 : : }
262 : :
263 : :
264 : : struct kvp_val_converter
265 : : {
266 : : const gchar* tag;
267 : : KvpValue* (*converter) (xmlNodePtr node);
268 : : };
269 : : /* Note: The type attribute must remain 'timespec' to maintain compatibility.
270 : : */
271 : :
272 : : struct kvp_val_converter val_converters[] =
273 : : {
274 : : { "integer", dom_tree_to_integer_kvp_value },
275 : : { "double", dom_tree_to_double_kvp_value },
276 : : { "numeric", dom_tree_to_numeric_kvp_value },
277 : : { "string", dom_tree_to_string_kvp_value },
278 : : { "guid", dom_tree_to_guid_kvp_value },
279 : : { "timespec", dom_tree_to_time64_kvp_value },
280 : : { "gdate", dom_tree_to_gdate_kvp_value },
281 : : { "list", dom_tree_to_list_kvp_value },
282 : : { "frame", dom_tree_to_frame_kvp_value },
283 : : { 0, 0 },
284 : : };
285 : :
286 : : static KvpValue*
287 : 4111 : dom_tree_to_kvp_value (xmlNodePtr node)
288 : : {
289 : : xmlChar* xml_type;
290 : : struct kvp_val_converter* mark;
291 : 4111 : KvpValue* ret = NULL;
292 : :
293 : 4111 : xml_type = xmlGetProp (node, BAD_CAST "type");
294 : :
295 : 41110 : for (mark = val_converters; mark->tag; mark++)
296 : : {
297 : 36999 : if (g_strcmp0 (reinterpret_cast<char*>(xml_type), mark->tag) == 0)
298 : : {
299 : 4111 : ret = (mark->converter) (node);
300 : : }
301 : : }
302 : :
303 : 4111 : if (!mark->tag)
304 : : {
305 : : /* FIXME: deal with unknown type tag here */
306 : : }
307 : :
308 : 4111 : xmlFree (xml_type);
309 : :
310 : 4111 : return ret;
311 : : }
312 : :
313 : : static gboolean
314 : 904 : dom_tree_to_kvp_frame_given (xmlNodePtr node, KvpFrame* frame)
315 : : {
316 : : xmlNodePtr mark;
317 : :
318 : 904 : g_return_val_if_fail (node, FALSE);
319 : 904 : g_return_val_if_fail (frame, FALSE);
320 : :
321 : 6751 : for (mark = node->xmlChildrenNode; mark; mark = mark->next)
322 : : {
323 : 5847 : if (g_strcmp0 ((char*)mark->name, "slot") == 0)
324 : : {
325 : : xmlNodePtr mark2;
326 : 3037 : const gchar* key = NULL;
327 : 3037 : std::optional<std::string> maybe_key;
328 : 3037 : KvpValue* val = NULL;
329 : :
330 : 15459 : for (mark2 = mark->xmlChildrenNode; mark2; mark2 = mark2->next)
331 : : {
332 : 12422 : if (g_strcmp0 ((char*)mark2->name, "slot:key") == 0)
333 : : {
334 : 3037 : key = dom_node_to_text (mark2);
335 : 3037 : if (!key)
336 : : {
337 : 0 : maybe_key = dom_tree_to_text (mark2);
338 : 0 : key = maybe_key ? maybe_key->c_str() : nullptr;
339 : : }
340 : : }
341 : 9385 : else if (g_strcmp0 ((char*)mark2->name, "slot:value") == 0)
342 : : {
343 : 3037 : val = dom_tree_to_kvp_value (mark2);
344 : : }
345 : : else
346 : : {
347 : : /* FIXME: should put some error here.
348 : : * But ignore text type! */
349 : : }
350 : : }
351 : :
352 : 3037 : if (key)
353 : : {
354 : 3037 : if (val)
355 : : {
356 : : //We're deleting the old KvpValue returned by replace_nc().
357 : 9111 : delete frame->set ({key}, val);
358 : : }
359 : : else
360 : : {
361 : : /* FIXME: should put some error here */
362 : : }
363 : : }
364 : 3037 : }
365 : : }
366 : :
367 : 904 : return TRUE;
368 : 9111 : }
369 : :
370 : :
371 : : KvpFrame*
372 : 459 : dom_tree_to_kvp_frame (xmlNodePtr node)
373 : : {
374 : 459 : g_return_val_if_fail (node, NULL);
375 : :
376 : 459 : auto ret = new KvpFrame;
377 : :
378 : 459 : if (dom_tree_to_kvp_frame_given (node, ret))
379 : 459 : return ret;
380 : :
381 : 0 : delete ret;
382 : 0 : return NULL;
383 : : }
384 : :
385 : : gboolean
386 : 445 : dom_tree_create_instance_slots (xmlNodePtr node, QofInstance* inst)
387 : : {
388 : 445 : KvpFrame* frame = qof_instance_get_slots (inst);
389 : 445 : return dom_tree_to_kvp_frame_given (node, frame);
390 : : }
391 : :
392 : : std::optional<std::string>
393 : 1195 : dom_tree_to_text (xmlNodePtr tree)
394 : : {
395 : : /* Expect *only* text and comment sibling nodes in the given tree --
396 : : which actually may only be a "list". i.e. if you're trying to
397 : : extract bar from <foo>bar</foo>, pass in <foo>->xmlChildrenNode
398 : : to this function. This expectation is different from the rest of
399 : : the dom_tree_to_* converters...
400 : :
401 : : Ignores comment nodes and collapse text nodes into one string.
402 : : Returns NULL if expectations are unsatisfied.
403 : : */
404 : 1195 : std::string rv;
405 : : gchar* temp;
406 : :
407 : 1195 : g_return_val_if_fail (tree, std::nullopt);
408 : :
409 : : /* no nodes means it's an empty string text */
410 : 1195 : if (!tree->xmlChildrenNode)
411 : : {
412 : 575 : DEBUG ("No children");
413 : 575 : return "";
414 : : }
415 : :
416 : 620 : temp = (char*)xmlNodeListGetString (NULL, tree->xmlChildrenNode, TRUE);
417 : 620 : if (!temp)
418 : : {
419 : 0 : DEBUG ("Null string");
420 : 0 : return std::nullopt;
421 : : }
422 : :
423 : 620 : DEBUG ("node string [%s]", (temp == NULL ? "(null)" : temp));
424 : 620 : rv = temp;
425 : 620 : xmlFree (temp);
426 : 620 : return rv;
427 : 1195 : }
428 : :
429 : : gnc_numeric
430 : 5162 : dom_tree_to_gnc_numeric (xmlNodePtr node)
431 : : {
432 : 5162 : auto node_to_numeric = [](auto txt)
433 : : {
434 : 5162 : gnc_numeric num = gnc_numeric_from_string(txt);
435 : 10324 : return gnc_numeric_check (num) ? gnc_numeric_zero() : num;
436 : : };
437 : 10324 : return apply_xmlnode_text<gnc_numeric> (node_to_numeric, node, gnc_numeric_zero());
438 : : }
439 : :
440 : :
441 : : time64
442 : 2661 : dom_tree_to_time64 (xmlNodePtr node)
443 : : {
444 : : /* Turn something like this
445 : :
446 : : <date-posted>
447 : : <ts:date>Mon, 05 Jun 2000 23:16:19 -0500</ts:date>
448 : : </date-posted>
449 : :
450 : : into a time64, returning INT64_MAX that we're using to flag an erroneous
451 : : date if there's a problem. Only one ts:date element is permitted for any
452 : : date attribute.
453 : : */
454 : :
455 : 2661 : time64 ret {INT64_MAX};
456 : 2661 : gboolean seen = FALSE;
457 : : xmlNodePtr n;
458 : :
459 : 11852 : for (n = node->xmlChildrenNode; n; n = n->next)
460 : : {
461 : 9191 : switch (n->type)
462 : : {
463 : 5806 : case XML_COMMENT_NODE:
464 : : case XML_TEXT_NODE:
465 : 5806 : break;
466 : 3385 : case XML_ELEMENT_NODE:
467 : 3385 : if (g_strcmp0 ("ts:date", (char*)n->name) == 0)
468 : : {
469 : 2661 : if (seen)
470 : : {
471 : 0 : return INT64_MAX;
472 : : }
473 : 2661 : seen = TRUE;
474 : 2661 : ret = apply_xmlnode_text<time64> (gnc_iso8601_to_time64_gmt, n, INT64_MAX);
475 : : }
476 : 3385 : break;
477 : 0 : default:
478 : 0 : PERR ("unexpected sub-node.");
479 : 0 : return INT64_MAX;
480 : : break;
481 : : }
482 : : }
483 : :
484 : 2661 : if (!seen)
485 : : {
486 : 0 : PERR ("no ts:date node found.");
487 : 0 : return INT64_MAX;
488 : : }
489 : :
490 : 2661 : return ret;
491 : : }
492 : :
493 : : GDate*
494 : 21 : dom_tree_to_gdate (xmlNodePtr node)
495 : : {
496 : : /* Turn something like this
497 : :
498 : : <sx:startdate>
499 : : <gdate>2001-04-03</gdate>
500 : : </sx:startdate>
501 : :
502 : : into a GDate. If the xml is invalid, returns NULL. */
503 : :
504 : : GDate ret;
505 : 21 : gboolean seen_date = FALSE;
506 : : xmlNodePtr n;
507 : :
508 : 21 : auto try_setting_date = [&ret](const char *content) -> bool
509 : : {
510 : 21 : gint year = 0, month = 0, day = 0;
511 : 21 : if (sscanf (content, "%d-%d-%d", &year, &month, &day) != 3) return false;
512 : 21 : g_date_set_dmy (&ret, day, static_cast<GDateMonth>(month), year);
513 : 21 : return (g_date_valid (&ret));
514 : 21 : };
515 : :
516 : : /* creates an invalid date */
517 : 21 : g_date_clear (&ret, 1);
518 : :
519 : 84 : for (n = node->xmlChildrenNode; n; n = n->next)
520 : : {
521 : 63 : switch (n->type)
522 : : {
523 : 42 : case XML_COMMENT_NODE:
524 : : case XML_TEXT_NODE:
525 : 42 : break;
526 : 21 : case XML_ELEMENT_NODE:
527 : 21 : if (g_strcmp0 ("gdate", (char*)n->name) == 0)
528 : : {
529 : 21 : if (seen_date || !apply_xmlnode_text<bool> (try_setting_date, n))
530 : 0 : return NULL;
531 : 21 : seen_date = TRUE;
532 : : }
533 : 21 : break;
534 : 0 : default:
535 : 0 : PERR ("unexpected sub-node.");
536 : 0 : return NULL;
537 : : }
538 : : }
539 : :
540 : 21 : if (!seen_date)
541 : : {
542 : 0 : PWARN ("no gdate node found.");
543 : 0 : return NULL;
544 : : }
545 : :
546 : 21 : return g_date_copy (&ret);
547 : : }
548 : :
549 : : struct CommodityRef
550 : : {
551 : : std::string space;
552 : : std::string id;
553 : : };
554 : :
555 : : std::string
556 : 7462 : gnc_strstrip (std::string_view sv)
557 : : {
558 : 7462 : while (!sv.empty () && g_ascii_isspace (sv.front())) sv.remove_prefix (1);
559 : 7462 : while (!sv.empty () && g_ascii_isspace (sv.back())) sv.remove_suffix (1);
560 : 14924 : return std::string (sv);
561 : : }
562 : :
563 : : static std::optional<CommodityRef>
564 : 3525 : parse_commodity_ref (xmlNodePtr node, QofBook* book)
565 : : {
566 : : /* Turn something like this
567 : :
568 : : <currency>
569 : : <cmdty:space>NASDAQ</cmdty:space>
570 : : <cmdty:id>LNUX</cmdty:space>
571 : : </currency>
572 : :
573 : : into a gnc_commodity*, returning NULL on failure. Both sub-nodes
574 : : are required, though for now, order is irrelevant. */
575 : :
576 : 3525 : CommodityRef rv;
577 : 3525 : bool space_set{false};
578 : 3525 : bool id_set{false};
579 : : xmlNodePtr n;
580 : :
581 : 3525 : if (!node) return {};
582 : 3525 : if (!node->xmlChildrenNode) return {};
583 : :
584 : 21027 : for (n = node->xmlChildrenNode; n; n = n->next)
585 : : {
586 : 17502 : switch (n->type)
587 : : {
588 : 10452 : case XML_COMMENT_NODE:
589 : : case XML_TEXT_NODE:
590 : 10452 : break;
591 : 7050 : case XML_ELEMENT_NODE:
592 : 7050 : if (g_strcmp0 ("cmdty:space", (char*)n->name) == 0)
593 : : {
594 : 3525 : if (space_set)
595 : : {
596 : 0 : return {};
597 : : }
598 : 3525 : rv.space = apply_xmlnode_text<std::string> (gnc_strstrip, n);
599 : 3525 : space_set = true;
600 : : }
601 : 3525 : else if (g_strcmp0 ("cmdty:id", (char*)n->name) == 0)
602 : : {
603 : 3525 : if (id_set)
604 : : {
605 : 0 : return {};
606 : : }
607 : 3525 : rv.id = apply_xmlnode_text<std::string> (gnc_strstrip, n);
608 : 3525 : id_set = true;
609 : : }
610 : 7050 : break;
611 : 0 : default:
612 : 0 : PERR ("unexpected sub-node.");
613 : 0 : return {};
614 : : break;
615 : : }
616 : : }
617 : 3525 : if (space_set && id_set)
618 : 3525 : return rv;
619 : :
620 : 0 : return {};
621 : 3525 : }
622 : :
623 : : gnc_commodity*
624 : 41 : dom_tree_to_commodity_ref_no_engine (xmlNodePtr node, QofBook* book)
625 : : {
626 : 41 : auto ref = parse_commodity_ref (node, book);
627 : :
628 : 41 : if (!ref)
629 : 0 : return nullptr;
630 : :
631 : 41 : return gnc_commodity_new (book, nullptr, ref->space.c_str(), ref->id.c_str(),
632 : 41 : nullptr, 0);
633 : 41 : }
634 : :
635 : : gnc_commodity*
636 : 3484 : dom_tree_to_commodity_ref (xmlNodePtr node, QofBook* book)
637 : : {
638 : : gnc_commodity* ret;
639 : : gnc_commodity_table* table;
640 : :
641 : 3484 : auto ref = parse_commodity_ref (node, book);
642 : :
643 : 3484 : if (!ref)
644 : 0 : return nullptr;
645 : :
646 : 3484 : table = gnc_commodity_table_get_table (book);
647 : :
648 : 3484 : g_return_val_if_fail (table != NULL, NULL);
649 : :
650 : 3484 : ret = gnc_commodity_table_lookup (table, ref->space.c_str(), ref->id.c_str());
651 : :
652 : 3484 : g_return_val_if_fail (ret != NULL, NULL);
653 : :
654 : 3484 : return ret;
655 : 3484 : }
656 : :
657 : : /***********************************************************************/
658 : : /* generic parser */
659 : :
660 : : static inline void
661 : 3806 : dom_tree_handlers_reset (struct dom_tree_handler* handlers)
662 : : {
663 : 48649 : for (; handlers->tag != NULL; handlers++)
664 : : {
665 : 44843 : handlers->gotten = 0;
666 : : }
667 : 3806 : }
668 : :
669 : : static inline gboolean
670 : 3806 : dom_tree_handlers_all_gotten_p (struct dom_tree_handler* handlers)
671 : : {
672 : 3806 : gboolean ret = TRUE;
673 : 48649 : for (; handlers->tag != NULL; handlers++)
674 : : {
675 : 44843 : if (handlers->required && ! handlers->gotten)
676 : : {
677 : 0 : PERR ("Not defined and it should be: %s",
678 : : handlers->tag ? handlers->tag : "(null)");
679 : 0 : ret = FALSE;
680 : : }
681 : : }
682 : 3806 : return ret;
683 : : }
684 : :
685 : :
686 : : static inline gboolean
687 : 22831 : gnc_xml_set_data (const gchar* tag, xmlNodePtr node, gpointer item,
688 : : struct dom_tree_handler* handlers)
689 : : {
690 : 109302 : for (; handlers->tag != NULL; handlers++)
691 : : {
692 : 109302 : if (g_strcmp0 (tag, handlers->tag) == 0)
693 : : {
694 : 22831 : (handlers->handler) (node, item);
695 : 22831 : handlers->gotten = TRUE;
696 : 22831 : break;
697 : : }
698 : : }
699 : :
700 : 22831 : if (!handlers->tag)
701 : : {
702 : 0 : PERR ("Unhandled tag: %s",
703 : : tag ? tag : "(null)");
704 : 0 : return FALSE;
705 : : }
706 : :
707 : 22831 : return TRUE;
708 : : }
709 : :
710 : : gboolean
711 : 3806 : dom_tree_generic_parse (xmlNodePtr node, struct dom_tree_handler* handlers,
712 : : gpointer data)
713 : : {
714 : : xmlNodePtr achild;
715 : 3806 : gboolean successful = TRUE;
716 : :
717 : 3806 : dom_tree_handlers_reset (handlers);
718 : :
719 : 53273 : for (achild = node->xmlChildrenNode; achild; achild = achild->next)
720 : : {
721 : : /* ignore stray text nodes */
722 : 49467 : if (g_strcmp0 ((char*)achild->name, "text") == 0)
723 : 26636 : continue;
724 : :
725 : 22831 : if (!gnc_xml_set_data ((char*)achild->name, achild, data, handlers))
726 : : {
727 : 0 : PERR ("gnc_xml_set_data failed");
728 : 0 : successful = FALSE;
729 : 0 : continue;
730 : : }
731 : : }
732 : :
733 : 3806 : if (!dom_tree_handlers_all_gotten_p (handlers))
734 : : {
735 : 0 : PERR ("didn't find all of the expected tags in the input");
736 : 0 : successful = FALSE;
737 : : }
738 : :
739 : 3806 : return successful;
740 : : }
741 : :
742 : : gboolean
743 : 2556 : dom_tree_valid_time64 (time64 val, const xmlChar * name)
744 : : {
745 : 2556 : if (val != INT64_MAX)
746 : 2556 : return TRUE;
747 : 0 : g_warning ("Invalid timestamp in data file. Look for a '%s' entry "
748 : : "with a year outside of the valid range: 1400..10000", name);
749 : 0 : return FALSE;
750 : : }
|