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