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