Branch data Line data Source code
1 : : /********************************************************************
2 : : * sixtp-utils.c *
3 : : * Copyright (c) 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 : :
24 : : #define __EXTENSIONS__
25 : : #include <guid.hpp>
26 : : #include <config.h>
27 : :
28 : : #include <ctype.h>
29 : : #include <glib.h>
30 : : #include <stdio.h>
31 : : #include <stdlib.h>
32 : : #include <string.h>
33 : :
34 : : #include <time.h>
35 : : #include <errno.h>
36 : :
37 : : #ifndef HAVE_STRPTIME
38 : : #include "strptime.h"
39 : : #endif
40 : : #include <gnc-date.h>
41 : :
42 : : #include "sixtp.h"
43 : : #include "sixtp-utils.h"
44 : :
45 : : #include <charconv>
46 : : #include <cctype>
47 : :
48 : : static QofLogModule log_module = GNC_MOD_IO;
49 : :
50 : : gboolean
51 : 767 : isspace_str (const gchar* str, int nomorethan)
52 : : {
53 : 767 : const gchar* cursor = str;
54 : 3026 : while (*cursor && (nomorethan != 0))
55 : : {
56 : 2259 : if (!isspace (*cursor))
57 : : {
58 : 0 : return (FALSE);
59 : : }
60 : 2259 : cursor++;
61 : 2259 : nomorethan--;
62 : : }
63 : 767 : return (TRUE);
64 : : }
65 : :
66 : : gboolean
67 : 767 : allow_and_ignore_only_whitespace (GSList* sibling_data,
68 : : gpointer parent_data,
69 : : gpointer global_data,
70 : : gpointer* result,
71 : : const char* text,
72 : : int length)
73 : : {
74 : 767 : return (isspace_str (text, length));
75 : : }
76 : :
77 : : gboolean
78 : 0 : generic_accumulate_chars (GSList* sibling_data,
79 : : gpointer parent_data,
80 : : gpointer global_data,
81 : : gpointer* result,
82 : :
83 : : const char* text,
84 : : int length)
85 : : {
86 : 0 : gchar* copytxt = g_strndup (text, length);
87 : 0 : g_return_val_if_fail (result, FALSE);
88 : :
89 : 0 : *result = copytxt;
90 : 0 : return (TRUE);
91 : : }
92 : :
93 : :
94 : : void
95 : 0 : generic_free_data_for_children (gpointer data_for_children,
96 : : GSList* data_from_children,
97 : : GSList* sibling_data,
98 : : gpointer parent_data,
99 : : gpointer global_data,
100 : : gpointer* result,
101 : : const gchar* tag)
102 : : {
103 : 0 : if (data_for_children) g_free (data_for_children);
104 : 0 : }
105 : :
106 : : gchar*
107 : 0 : concatenate_child_result_chars (GSList* data_from_children)
108 : : {
109 : : GSList* lp;
110 : 0 : gchar* name = g_strdup ("");
111 : :
112 : 0 : g_return_val_if_fail (name, NULL);
113 : :
114 : : /* child data lists are in reverse chron order */
115 : 0 : data_from_children = g_slist_reverse (g_slist_copy (data_from_children));
116 : :
117 : 0 : for (lp = data_from_children; lp; lp = lp->next)
118 : : {
119 : 0 : sixtp_child_result* cr = (sixtp_child_result*) lp->data;
120 : 0 : if (cr->type != SIXTP_CHILD_RESULT_CHARS)
121 : : {
122 : 0 : PERR ("result type is not chars");
123 : 0 : g_slist_free (data_from_children);
124 : 0 : g_free (name);
125 : 0 : return (NULL);
126 : : }
127 : : else
128 : : {
129 : : char* temp;
130 : 0 : temp = g_strconcat (name, (gchar*) cr->data, nullptr);
131 : 0 : g_free (name);
132 : 0 : name = temp;
133 : : }
134 : : }
135 : 0 : g_slist_free (data_from_children);
136 : 0 : return (name);
137 : : }
138 : :
139 : : /****************************************************************************/
140 : : /* string to data converters...
141 : : */
142 : :
143 : :
144 : : template <typename T>
145 : 2247 : static bool parse_chars_into_num (const char* str, T* num_ptr)
146 : : {
147 : 2247 : if (!str || !num_ptr)
148 : 0 : return false;
149 : :
150 : 2335 : while (std::isspace (*str))
151 : 88 : ++str;
152 : :
153 : 2247 : const char* end_ptr = str + std::strlen (str);
154 : :
155 : 2247 : auto res = std::from_chars (str, end_ptr, *num_ptr);
156 : 2247 : if (res.ec != std::errc{})
157 : 10 : return false;
158 : :
159 : 2333 : while (std::isspace (*res.ptr))
160 : 96 : ++res.ptr;
161 : :
162 : 2237 : return (res.ptr == end_ptr);
163 : : }
164 : :
165 : : /*********/
166 : : /* double
167 : : */
168 : :
169 : : gboolean
170 : 22 : string_to_double (const char* str, double* result)
171 : : {
172 : : #if __cpp_lib_to_chars >= 201611L
173 : 22 : return parse_chars_into_num<double>(str, result);
174 : : #else
175 : : // because from_chars in cpp < 201611L cannot parse floats
176 : : char* endptr = nullptr;
177 : : g_return_val_if_fail (str && result, false);
178 : : *result = std::strtod (str, &endptr);
179 : : return (endptr != str);
180 : : #endif
181 : : }
182 : :
183 : : /*********/
184 : : /* gint64
185 : : */
186 : : gboolean
187 : 2184 : string_to_gint64 (const gchar* str, gint64* v)
188 : : {
189 : 2184 : return parse_chars_into_num<gint64>(str, v);
190 : : }
191 : :
192 : : /*********/
193 : : /* guint16
194 : : */
195 : : gboolean
196 : 23 : string_to_guint16 (const gchar* str, guint16* v)
197 : : {
198 : 23 : return parse_chars_into_num<guint16>(str, v);
199 : : }
200 : :
201 : : /*********/
202 : : /* guint
203 : : */
204 : : gboolean
205 : 18 : string_to_guint (const gchar* str, guint* v)
206 : : {
207 : 18 : return parse_chars_into_num<guint>(str, v);
208 : : }
209 : :
210 : : /************/
211 : : /* hex string
212 : : */
213 : :
214 : : gboolean
215 : 0 : hex_string_to_binary (const gchar* str, void** v, guint64* data_len)
216 : : {
217 : : /* Convert a hex string to binary. No whitespace allowed. */
218 : 0 : const gchar* cursor = str;
219 : : guint64 str_len;
220 : 0 : gboolean error = FALSE;
221 : :
222 : 0 : g_return_val_if_fail (str, FALSE);
223 : 0 : g_return_val_if_fail (v, FALSE);
224 : 0 : g_return_val_if_fail (data_len, FALSE);
225 : :
226 : 0 : str_len = strlen (str);
227 : : /* Since no whitespace is allowed and hex encoding is 2 text chars
228 : : per binary char, the result must be half the input size and the
229 : : input size must be even. */
230 : 0 : if ((str_len % 2) != 0) return (FALSE);
231 : 0 : *data_len = 0;
232 : 0 : *v = g_new0 (char, str_len / 2);
233 : :
234 : 0 : g_return_val_if_fail (*v, FALSE);
235 : :
236 : 0 : while (*cursor && * (cursor + 1))
237 : : {
238 : : gchar tmpstr[2];
239 : : int tmpint;
240 : :
241 : 0 : if (isspace (*cursor) || isspace (* (cursor + 1)))
242 : : {
243 : 0 : error = TRUE;
244 : : }
245 : : else
246 : : {
247 : : int num_read;
248 : 0 : tmpstr[0] = *cursor;
249 : 0 : tmpstr[0] = * (cursor + 1);
250 : :
251 : 0 : if ((sscanf (tmpstr, "%x%n", &tmpint, &num_read) < 1)
252 : 0 : || (num_read != 2))
253 : : {
254 : 0 : error = TRUE;
255 : : }
256 : : else
257 : : {
258 : 0 : * ((gchar*) (v + *data_len)) = tmpint;
259 : 0 : *data_len += 1;
260 : 0 : cursor += 2;
261 : : }
262 : : }
263 : : }
264 : :
265 : 0 : if (error || (*data_len != (str_len / 2)))
266 : : {
267 : 0 : g_free (*v);
268 : 0 : *v = NULL;
269 : 0 : *data_len = 0;
270 : 0 : return (FALSE);
271 : : }
272 : :
273 : 0 : return (TRUE);
274 : : }
275 : :
276 : : /***************************************************************************/
277 : : /* simple chars only parser - just grabs all it's contained chars and
278 : : does what you specify in the end handler - if you pass NULL as the
279 : : end handler to simple_chars_only_parser_new, the characters are just
280 : : passed to the parent as a new string.
281 : :
282 : : input: NA
283 : : returns: gchar array allocated via g_new, etc.
284 : :
285 : : start: NA
286 : : chars: generic_accumulate_chars.
287 : : end: varies - default is to concatenate all accumulated chars and return.
288 : :
289 : : cleanup-result: g_free (for chars)
290 : : cleanup-chars: g_free (for chars)
291 : : fail: NA
292 : : result-fail: g_free (for chars)
293 : : chars-fail: g_free (for chars)
294 : :
295 : : */
296 : :
297 : : gboolean
298 : 0 : generic_return_chars_end_handler (gpointer data_for_children,
299 : : GSList* data_from_children,
300 : : GSList* sibling_data,
301 : : gpointer parent_data,
302 : : gpointer global_data,
303 : : gpointer* result,
304 : : const gchar* tag)
305 : : {
306 : 0 : gchar* txt = NULL;
307 : :
308 : 0 : txt = concatenate_child_result_chars (data_from_children);
309 : 0 : g_return_val_if_fail (txt, FALSE);
310 : 0 : *result = txt;
311 : 0 : return (TRUE);
312 : : }
313 : :
314 : :
315 : : sixtp*
316 : 0 : simple_chars_only_parser_new (sixtp_end_handler end_handler)
317 : : {
318 : 0 : return sixtp_set_any (
319 : : sixtp_new (), FALSE,
320 : : SIXTP_END_HANDLER_ID, (end_handler
321 : : ? end_handler
322 : : : generic_return_chars_end_handler),
323 : : SIXTP_CHARACTERS_HANDLER_ID, generic_accumulate_chars,
324 : : SIXTP_CLEANUP_RESULT_ID, sixtp_child_free_data,
325 : : SIXTP_CLEANUP_CHARS_ID, sixtp_child_free_data,
326 : : SIXTP_RESULT_FAIL_ID, sixtp_child_free_data,
327 : : SIXTP_CHARS_FAIL_ID, sixtp_child_free_data,
328 : 0 : SIXTP_NO_MORE_HANDLERS);
329 : : }
330 : :
331 : : /****************************************************************************/
332 : : /* generic timespec handler for XML Version 1 files.
333 : :
334 : : A collection of node functions intended to parse a sub-node set
335 : : that looks like this:
336 : :
337 : : <date-posted>
338 : : <s>Mon, 05 Jun 2000 23:16:19 -0500</s>
339 : : <ns>658864000</ns>
340 : : </date-posted>
341 : :
342 : : and produce a time64. The start handler for the top allocates
343 : : the time64 and passes it to the children. The <s> block sets
344 : : the seconds and the <ns> block is ignored. If
345 : : all goes well, returns the time64 as the result.
346 : : */
347 : :
348 : : /* Top level timespec node:
349 : :
350 : : input: user end handler *
351 : : returns: time64
352 : :
353 : : start: Allocates Time64ParseInfo* for data_for_children.
354 : : characters: none (whitespace only).
355 : : end: g_free Time64ParseInfo + any other actions
356 : :
357 : : cleanup-result: NA
358 : : cleanup-chars: NA
359 : : fail: g_free data_for_children.
360 : : result-fail: g_free data_for_children.
361 : : chars-fail: NA
362 : :
363 : : */
364 : :
365 : : gboolean
366 : 0 : generic_timespec_start_handler (GSList* sibling_data, gpointer parent_data,
367 : : gpointer global_data,
368 : : gpointer* data_for_children, gpointer* result,
369 : : const gchar* tag, gchar** attrs)
370 : : {
371 : 0 : Time64ParseInfo* tsp = g_new0 (Time64ParseInfo, 1);
372 : 0 : g_return_val_if_fail (tsp, FALSE);
373 : 0 : *data_for_children = tsp;
374 : 0 : return (TRUE);
375 : : }
376 : :
377 : : /* You can't use this function directly. You have to call it from
378 : : your own end handler. If it returns TRUE, *result will contain the
379 : : new timespec. Otherwise, you can presume that everything's been
380 : : cleaned up properly and return FALSE. */
381 : : gboolean
382 : 0 : timespec_parse_ok (Time64ParseInfo* info)
383 : : {
384 : 0 : return info->s_block_count != 1;
385 : : }
386 : :
387 : : /* generic_timespec_end_handler - must be customized and provided by
388 : : the user. */
389 : :
390 : : /* <s> (parent timespec-node)
391 : :
392 : : input: Time64ParseInfo *
393 : : returns: NA
394 : :
395 : : start: NA
396 : : characters: accumulate.
397 : : end: convert characters to secs part of input time64 and inc s_block_count.
398 : :
399 : : cleanup-result: NA
400 : : cleanup-chars: g_free data.
401 : : fail: NA
402 : : result-fail: NA
403 : : chars-fail: g_free data.
404 : :
405 : : */
406 : :
407 : : gboolean
408 : 0 : generic_timespec_secs_end_handler (gpointer data_for_children,
409 : : GSList* data_from_children, GSList* sibling_data,
410 : : gpointer parent_data, gpointer global_data,
411 : : gpointer* result, const gchar* tag)
412 : : {
413 : 0 : gchar* txt = NULL;
414 : 0 : Time64ParseInfo* info = (Time64ParseInfo*) parent_data;
415 : :
416 : 0 : g_return_val_if_fail (parent_data, FALSE);
417 : :
418 : 0 : txt = concatenate_child_result_chars (data_from_children);
419 : 0 : g_return_val_if_fail (txt, FALSE);
420 : :
421 : 0 : info->time = gnc_iso8601_to_time64_gmt (txt);
422 : 0 : g_free (txt);
423 : :
424 : : // gnc_iso8601_to_time64_gmt returns INT64_MAX on failure.
425 : 0 : g_return_val_if_fail (info->time < INT64_MAX, FALSE);
426 : :
427 : 0 : info->s_block_count++;
428 : 0 : return (TRUE);
429 : : }
430 : :
431 : : /* <s> (parent timespec-node)
432 : :
433 : : input: Time64ParseInfo *
434 : : returns: NA
435 : :
436 : : start: NA
437 : : characters: accumulate.
438 : : end: NA
439 : :
440 : : cleanup-result: NA
441 : : cleanup-chars: NA.
442 : : fail: NA
443 : : result-fail: NA
444 : : chars-fail: g_free data.
445 : :
446 : : */
447 : :
448 : : gboolean
449 : 0 : generic_timespec_nsecs_end_handler (gpointer data_for_children,
450 : : GSList* data_from_children, GSList* sibling_data,
451 : : gpointer parent_data, gpointer global_data,
452 : : gpointer* result, const gchar* tag)
453 : : {
454 : 0 : return TRUE;
455 : : }
456 : :
457 : : static sixtp*
458 : 0 : timespec_sixtp_new (sixtp_end_handler ender)
459 : : {
460 : 0 : return sixtp_set_any (
461 : : sixtp_new (), FALSE,
462 : : SIXTP_CHARACTERS_HANDLER_ID, generic_accumulate_chars,
463 : : SIXTP_END_HANDLER_ID, ender,
464 : : SIXTP_CLEANUP_CHARS_ID, sixtp_child_free_data,
465 : : SIXTP_CHARS_FAIL_ID, sixtp_child_free_data,
466 : 0 : SIXTP_NO_MORE_HANDLERS);
467 : : }
468 : :
469 : : sixtp*
470 : 0 : generic_timespec_parser_new (sixtp_end_handler end_handler)
471 : : {
472 : : sixtp* top_level =
473 : 0 : sixtp_set_any (sixtp_new (), FALSE,
474 : : SIXTP_START_HANDLER_ID, generic_timespec_start_handler,
475 : : SIXTP_CHARACTERS_HANDLER_ID, allow_and_ignore_only_whitespace,
476 : : SIXTP_END_HANDLER_ID, end_handler,
477 : : SIXTP_CLEANUP_RESULT_ID, sixtp_child_free_data,
478 : : SIXTP_FAIL_HANDLER_ID, generic_free_data_for_children,
479 : : SIXTP_RESULT_FAIL_ID, sixtp_child_free_data,
480 : : SIXTP_NO_MORE_HANDLERS);
481 : 0 : g_return_val_if_fail (top_level, NULL);
482 : :
483 : 0 : if (!sixtp_add_some_sub_parsers (
484 : : top_level, TRUE,
485 : : "s", timespec_sixtp_new (generic_timespec_secs_end_handler),
486 : : "ns", timespec_sixtp_new (generic_timespec_nsecs_end_handler),
487 : : NULL, NULL))
488 : : {
489 : 0 : return NULL;
490 : : }
491 : :
492 : 0 : return (top_level);
493 : : }
494 : :
495 : : /****************************************************************************/
496 : : /* <?> generic guid handler...
497 : :
498 : : Attempts to parse the current accumulated characters data as a guid
499 : : and return it.
500 : :
501 : : input: NA
502 : : returns: GncGUID*
503 : :
504 : : start: NA
505 : : characters: return string copy for accumulation in end handler.
506 : : end: concatenate all chars and create and return GncGUID*, if possible.
507 : :
508 : : cleanup-result: g_free the GncGUID*
509 : : cleanup-chars: g_free the result string.
510 : : fail: NA
511 : : result-fail: g_free the GncGUID*
512 : : chars-fail: g_free the result string.
513 : :
514 : : */
515 : :
516 : : gboolean
517 : 0 : generic_guid_end_handler (gpointer data_for_children,
518 : : GSList* data_from_children, GSList* sibling_data,
519 : : gpointer parent_data, gpointer global_data,
520 : : gpointer* result, const gchar* tag)
521 : : {
522 : 0 : gchar* txt = NULL;
523 : : GncGUID* gid;
524 : : gboolean ok;
525 : :
526 : 0 : txt = concatenate_child_result_chars (data_from_children);
527 : 0 : g_return_val_if_fail (txt, FALSE);
528 : :
529 : 0 : gid = g_new (GncGUID, 1);
530 : 0 : if (!gid)
531 : : {
532 : 0 : g_free (txt);
533 : 0 : return (FALSE);
534 : : }
535 : :
536 : 0 : ok = string_to_guid (txt, gid);
537 : 0 : g_free (txt);
538 : :
539 : 0 : if (!ok)
540 : : {
541 : 0 : PERR ("couldn't parse GncGUID");
542 : 0 : g_free (gid);
543 : 0 : return (FALSE);
544 : : }
545 : :
546 : 0 : *result = gid;
547 : 0 : return (TRUE);
548 : : }
549 : :
550 : : sixtp*
551 : 0 : generic_guid_parser_new (void)
552 : : {
553 : 0 : return sixtp_set_any (
554 : : sixtp_new (), FALSE,
555 : : SIXTP_CHARACTERS_HANDLER_ID, generic_accumulate_chars,
556 : : SIXTP_CLEANUP_CHARS_ID, sixtp_child_free_data,
557 : : SIXTP_CHARS_FAIL_ID, sixtp_child_free_data,
558 : : SIXTP_END_HANDLER_ID, generic_guid_end_handler,
559 : : SIXTP_RESULT_FAIL_ID, sixtp_child_free_data,
560 : : SIXTP_CLEANUP_RESULT_ID, sixtp_child_free_data,
561 : 0 : SIXTP_NO_MORE_HANDLERS);
562 : : }
563 : :
564 : : /****************************************************************************/
565 : : /* <?> generic gnc_numeric handler...
566 : :
567 : : Attempts to parse the current accumulated characters data as a
568 : : gnc_numeric and return it.
569 : :
570 : : input: NA
571 : : returns: gnc_numeric*
572 : :
573 : : start: NA
574 : : characters: return string copy for accumulation in end handler.
575 : : end: concatenate all chars and create and return gnc_numeric*, if possible.
576 : :
577 : : cleanup-result: g_free the gnc_numeric*
578 : : cleanup-chars: g_free the result string.
579 : : fail: NA
580 : : result-fail: g_free the gnc_numeric*
581 : : chars-fail: g_free the result string.
582 : :
583 : : */
584 : :
585 : : gboolean
586 : 0 : generic_gnc_numeric_end_handler (gpointer data_for_children,
587 : : GSList* data_from_children, GSList* sibling_data,
588 : : gpointer parent_data, gpointer global_data,
589 : : gpointer* result, const gchar* tag)
590 : : {
591 : 0 : gnc_numeric* num = NULL;
592 : 0 : gchar* txt = NULL;
593 : 0 : gboolean ok = FALSE;
594 : :
595 : 0 : txt = concatenate_child_result_chars (data_from_children);
596 : :
597 : 0 : if (txt)
598 : : {
599 : 0 : num = g_new (gnc_numeric, 1);
600 : 0 : if (num)
601 : : {
602 : 0 : *num = gnc_numeric_from_string (txt);
603 : 0 : if (!gnc_numeric_check (*num))
604 : : {
605 : 0 : ok = TRUE;
606 : 0 : *result = num;
607 : : }
608 : : }
609 : : }
610 : :
611 : 0 : g_free (txt);
612 : 0 : if (!ok)
613 : : {
614 : 0 : PERR ("couldn't parse numeric quantity");
615 : 0 : g_free (num);
616 : : }
617 : :
618 : 0 : return (ok);
619 : : }
620 : :
621 : : sixtp*
622 : 0 : generic_gnc_numeric_parser_new (void)
623 : : {
624 : 0 : return sixtp_set_any (
625 : : sixtp_new (), FALSE,
626 : : SIXTP_CHARACTERS_HANDLER_ID, generic_accumulate_chars,
627 : : SIXTP_CLEANUP_CHARS_ID, sixtp_child_free_data,
628 : : SIXTP_CHARS_FAIL_ID, sixtp_child_free_data,
629 : : SIXTP_END_HANDLER_ID, generic_gnc_numeric_end_handler,
630 : : SIXTP_RESULT_FAIL_ID, sixtp_child_free_data,
631 : : SIXTP_CLEANUP_RESULT_ID, sixtp_child_free_data,
632 : 0 : SIXTP_NO_MORE_HANDLERS);
633 : : }
634 : :
635 : : /***************************************************************************/
636 : :
637 : : sixtp*
638 : 0 : restore_char_generator (sixtp_end_handler ender)
639 : : {
640 : 0 : return sixtp_set_any (
641 : : sixtp_new (), FALSE,
642 : : SIXTP_CHARACTERS_HANDLER_ID, generic_accumulate_chars,
643 : : SIXTP_END_HANDLER_ID, ender,
644 : : SIXTP_CLEANUP_CHARS_ID, sixtp_child_free_data,
645 : : SIXTP_CHARS_FAIL_ID, sixtp_child_free_data,
646 : 0 : SIXTP_NO_MORE_HANDLERS);
647 : : }
648 : :
649 : : /***************************** END OF FILE *********************************/
|