GnuCash c935c2f+
Loading...
Searching...
No Matches
sixtp-utils.cpp
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
47static QofLogModule log_module = GNC_MOD_IO;
48
49gboolean
50isspace_str (const gchar* str, int nomorethan)
51{
52 const gchar* cursor = str;
53 while (*cursor && (nomorethan != 0))
54 {
55 if (!isspace (*cursor))
56 {
57 return (FALSE);
58 }
59 cursor++;
60 nomorethan--;
61 }
62 return (TRUE);
63}
64
65gboolean
66allow_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 return (isspace_str (text, length));
74}
75
76gboolean
77generic_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 gchar* copytxt = g_strndup (text, length);
86 g_return_val_if_fail (result, FALSE);
87
88 *result = copytxt;
89 return (TRUE);
90}
91
92
93void
94generic_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 if (data_for_children) g_free (data_for_children);
103}
104
105gchar*
106concatenate_child_result_chars (GSList* data_from_children)
107{
108 GSList* lp;
109 gchar* name = g_strdup ("");
110
111 g_return_val_if_fail (name, NULL);
112
113 /* child data lists are in reverse chron order */
114 data_from_children = g_slist_reverse (g_slist_copy (data_from_children));
115
116 for (lp = data_from_children; lp; lp = lp->next)
117 {
118 sixtp_child_result* cr = (sixtp_child_result*) lp->data;
119 if (cr->type != SIXTP_CHILD_RESULT_CHARS)
120 {
121 PERR ("result type is not chars");
122 g_slist_free (data_from_children);
123 g_free (name);
124 return (NULL);
125 }
126 else
127 {
128 char* temp;
129 temp = g_strconcat (name, (gchar*) cr->data, nullptr);
130 g_free (name);
131 name = temp;
132 }
133 }
134 g_slist_free (data_from_children);
135 return (name);
136}
137
138/****************************************************************************/
139/* string to data converters...
140 */
141
142
143template <typename T>
144static bool parse_chars_into_num (std::string_view sv, T* num_ptr)
145{
146 if (!num_ptr)
147 return false;
148
149 while (!sv.empty() && std::isspace (sv.front ())) sv.remove_prefix (1);
150 while (!sv.empty() && std::isspace (sv.back ())) sv.remove_suffix (1);
151
152 auto res = std::from_chars (sv.begin(), sv.end(), *num_ptr);
153 return (res.ec == std::errc{} && res.ptr == sv.end());
154}
155
156/*********/
157/* double
158 */
159
160gboolean
161string_to_double (std::string_view sv, double* result)
162{
163#if __cpp_lib_to_chars >= 201611L
164 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 */
178gboolean
179string_to_gint64 (std::string_view sv, gint64* v)
180{
181 return parse_chars_into_num<gint64>(sv, v);
182}
183
184/*********/
185/* guint16
186 */
187gboolean
188string_to_guint16 (std::string_view sv, guint16* v)
189{
190 return parse_chars_into_num<guint16>(sv, v);
191}
192
193/*********/
194/* guint
195 */
196gboolean
197string_to_guint (std::string_view sv, guint* v)
198{
199 return parse_chars_into_num<guint>(sv, v);
200}
201
202/************/
203/* hex string
204 */
205
206gboolean
207hex_string_to_binary (const gchar* str, void** v, guint64* data_len)
208{
209 /* Convert a hex string to binary. No whitespace allowed. */
210 const gchar* cursor = str;
211 guint64 str_len;
212 gboolean error = FALSE;
213
214 g_return_val_if_fail (str, FALSE);
215 g_return_val_if_fail (v, FALSE);
216 g_return_val_if_fail (data_len, FALSE);
217
218 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 if ((str_len % 2) != 0) return (FALSE);
223 *data_len = 0;
224 *v = g_new0 (char, str_len / 2);
225
226 g_return_val_if_fail (*v, FALSE);
227
228 while (*cursor && * (cursor + 1))
229 {
230 gchar tmpstr[2];
231 int tmpint;
232
233 if (isspace (*cursor) || isspace (* (cursor + 1)))
234 {
235 error = TRUE;
236 }
237 else
238 {
239 int num_read;
240 tmpstr[0] = *cursor;
241 tmpstr[0] = * (cursor + 1);
242
243 if ((sscanf (tmpstr, "%x%n", &tmpint, &num_read) < 1)
244 || (num_read != 2))
245 {
246 error = TRUE;
247 }
248 else
249 {
250 * ((gchar*) (v + *data_len)) = tmpint;
251 *data_len += 1;
252 cursor += 2;
253 }
254 }
255 }
256
257 if (error || (*data_len != (str_len / 2)))
258 {
259 g_free (*v);
260 *v = NULL;
261 *data_len = 0;
262 return (FALSE);
263 }
264
265 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
289gboolean
290generic_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 gchar* txt = NULL;
299
300 txt = concatenate_child_result_chars (data_from_children);
301 g_return_val_if_fail (txt, FALSE);
302 *result = txt;
303 return (TRUE);
304}
305
306
307sixtp*
308simple_chars_only_parser_new (sixtp_end_handler end_handler)
309{
310 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 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
357gboolean
358generic_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 Time64ParseInfo* tsp = g_new0 (Time64ParseInfo, 1);
364 g_return_val_if_fail (tsp, FALSE);
365 *data_for_children = tsp;
366 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. */
373gboolean
374timespec_parse_ok (Time64ParseInfo* info)
375{
376 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
399gboolean
400generic_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 gchar* txt = NULL;
406 Time64ParseInfo* info = (Time64ParseInfo*) parent_data;
407
408 g_return_val_if_fail (parent_data, FALSE);
409
410 txt = concatenate_child_result_chars (data_from_children);
411 g_return_val_if_fail (txt, FALSE);
412
413 info->time = gnc_iso8601_to_time64_gmt (txt);
414 g_free (txt);
415
416// gnc_iso8601_to_time64_gmt returns INT64_MAX on failure.
417 g_return_val_if_fail (info->time < INT64_MAX, FALSE);
418
419 info->s_block_count++;
420 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
440gboolean
441generic_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 return TRUE;
447}
448
449static sixtp*
450timespec_sixtp_new (sixtp_end_handler ender)
451{
452 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 SIXTP_NO_MORE_HANDLERS);
459}
460
461sixtp*
462generic_timespec_parser_new (sixtp_end_handler end_handler)
463{
464 sixtp* top_level =
465 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 g_return_val_if_fail (top_level, NULL);
474
475 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 return NULL;
482 }
483
484 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
508gboolean
509generic_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 gchar* txt = NULL;
515 GncGUID* gid;
516 gboolean ok;
517
518 txt = concatenate_child_result_chars (data_from_children);
519 g_return_val_if_fail (txt, FALSE);
520
521 gid = g_new (GncGUID, 1);
522 if (!gid)
523 {
524 g_free (txt);
525 return (FALSE);
526 }
527
528 ok = string_to_guid (txt, gid);
529 g_free (txt);
530
531 if (!ok)
532 {
533 PERR ("couldn't parse GncGUID");
534 g_free (gid);
535 return (FALSE);
536 }
537
538 *result = gid;
539 return (TRUE);
540}
541
542sixtp*
543generic_guid_parser_new (void)
544{
545 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 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
577gboolean
578generic_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 gnc_numeric* num = NULL;
584 gchar* txt = NULL;
585 gboolean ok = FALSE;
586
587 txt = concatenate_child_result_chars (data_from_children);
588
589 if (txt)
590 {
591 num = g_new (gnc_numeric, 1);
592 if (num)
593 {
594 *num = gnc_numeric_from_string (txt);
595 if (!gnc_numeric_check (*num))
596 {
597 ok = TRUE;
598 *result = num;
599 }
600 }
601 }
602
603 g_free (txt);
604 if (!ok)
605 {
606 PERR ("couldn't parse numeric quantity");
607 g_free (num);
608 }
609
610 return (ok);
611}
612
613sixtp*
614generic_gnc_numeric_parser_new (void)
615{
616 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 SIXTP_NO_MORE_HANDLERS);
625}
626
627/***************************************************************************/
628
629sixtp*
630restore_char_generator (sixtp_end_handler ender)
631{
632 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 SIXTP_NO_MORE_HANDLERS);
639}
640
641/***************************** END OF FILE *********************************/
Date and Time handling routines.
#define PERR(format, args...)
Log a serious error.
Definition qoflog.h:244
gnc_numeric gnc_numeric_from_string(const gchar *str)
Read a gnc_numeric from str, skipping any leading whitespace.
The type used to store guids in C.
Definition guid.h:75
Definition sixtp.h:130