GnuCash  5.6-150-g038405b370+
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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 
47 static QofLogModule log_module = GNC_MOD_IO;
48 
49 gboolean
50 isspace_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 
65 gboolean
66 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  return (isspace_str (text, length));
74 }
75 
76 gboolean
77 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  gchar* copytxt = g_strndup (text, length);
86  g_return_val_if_fail (result, FALSE);
87 
88  *result = copytxt;
89  return (TRUE);
90 }
91 
92 
93 void
94 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  if (data_for_children) g_free (data_for_children);
103 }
104 
105 gchar*
106 concatenate_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 
143 template <typename T>
144 static bool parse_chars_into_num (const char* str, T* num_ptr)
145 {
146  if (!str || !num_ptr)
147  return false;
148 
149  while (std::isspace (*str))
150  ++str;
151 
152  const char* end_ptr = str + std::strlen (str);
153 
154  auto res = std::from_chars (str, end_ptr, *num_ptr);
155  if (res.ec != std::errc{})
156  return false;
157 
158  while (std::isspace (*res.ptr))
159  ++res.ptr;
160 
161  return (res.ptr == end_ptr);
162 }
163 
164 /*********/
165 /* double
166  */
167 
168 gboolean
169 string_to_double (const char* str, double* result)
170 {
171 #if __cpp_lib_to_chars >= 201611L
172  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 string_to_gint64 (const gchar* str, gint64* v)
187 {
188  return parse_chars_into_num<gint64>(str, v);
189 }
190 
191 /*********/
192 /* guint16
193  */
194 gboolean
195 string_to_guint16 (const gchar* str, guint16* v)
196 {
197  return parse_chars_into_num<guint16>(str, v);
198 }
199 
200 /*********/
201 /* guint
202  */
203 gboolean
204 string_to_guint (const gchar* str, guint* v)
205 {
206  return parse_chars_into_num<guint>(str, v);
207 }
208 
209 /************/
210 /* hex string
211  */
212 
213 gboolean
214 hex_string_to_binary (const gchar* str, void** v, guint64* data_len)
215 {
216  /* Convert a hex string to binary. No whitespace allowed. */
217  const gchar* cursor = str;
218  guint64 str_len;
219  gboolean error = FALSE;
220 
221  g_return_val_if_fail (str, FALSE);
222  g_return_val_if_fail (v, FALSE);
223  g_return_val_if_fail (data_len, FALSE);
224 
225  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  if ((str_len % 2) != 0) return (FALSE);
230  *data_len = 0;
231  *v = g_new0 (char, str_len / 2);
232 
233  g_return_val_if_fail (*v, FALSE);
234 
235  while (*cursor && * (cursor + 1))
236  {
237  gchar tmpstr[2];
238  int tmpint;
239 
240  if (isspace (*cursor) || isspace (* (cursor + 1)))
241  {
242  error = TRUE;
243  }
244  else
245  {
246  int num_read;
247  tmpstr[0] = *cursor;
248  tmpstr[0] = * (cursor + 1);
249 
250  if ((sscanf (tmpstr, "%x%n", &tmpint, &num_read) < 1)
251  || (num_read != 2))
252  {
253  error = TRUE;
254  }
255  else
256  {
257  * ((gchar*) (v + *data_len)) = tmpint;
258  *data_len += 1;
259  cursor += 2;
260  }
261  }
262  }
263 
264  if (error || (*data_len != (str_len / 2)))
265  {
266  g_free (*v);
267  *v = NULL;
268  *data_len = 0;
269  return (FALSE);
270  }
271 
272  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 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  gchar* txt = NULL;
306 
307  txt = concatenate_child_result_chars (data_from_children);
308  g_return_val_if_fail (txt, FALSE);
309  *result = txt;
310  return (TRUE);
311 }
312 
313 
314 sixtp*
315 simple_chars_only_parser_new (sixtp_end_handler end_handler)
316 {
317  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  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 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  Time64ParseInfo* tsp = g_new0 (Time64ParseInfo, 1);
371  g_return_val_if_fail (tsp, FALSE);
372  *data_for_children = tsp;
373  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 timespec_parse_ok (Time64ParseInfo* info)
382 {
383  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 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  gchar* txt = NULL;
413  Time64ParseInfo* info = (Time64ParseInfo*) parent_data;
414 
415  g_return_val_if_fail (parent_data, FALSE);
416 
417  txt = concatenate_child_result_chars (data_from_children);
418  g_return_val_if_fail (txt, FALSE);
419 
420  info->time = gnc_iso8601_to_time64_gmt (txt);
421  g_free (txt);
422 
423 // gnc_iso8601_to_time64_gmt returns INT64_MAX on failure.
424  g_return_val_if_fail (info->time < INT64_MAX, FALSE);
425 
426  info->s_block_count++;
427  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 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  return TRUE;
454 }
455 
456 static sixtp*
457 timespec_sixtp_new (sixtp_end_handler ender)
458 {
459  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  SIXTP_NO_MORE_HANDLERS);
466 }
467 
468 sixtp*
469 generic_timespec_parser_new (sixtp_end_handler end_handler)
470 {
471  sixtp* top_level =
472  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  g_return_val_if_fail (top_level, NULL);
481 
482  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  return NULL;
489  }
490 
491  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 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  gchar* txt = NULL;
522  GncGUID* gid;
523  gboolean ok;
524 
525  txt = concatenate_child_result_chars (data_from_children);
526  g_return_val_if_fail (txt, FALSE);
527 
528  gid = g_new (GncGUID, 1);
529  if (!gid)
530  {
531  g_free (txt);
532  return (FALSE);
533  }
534 
535  ok = string_to_guid (txt, gid);
536  g_free (txt);
537 
538  if (!ok)
539  {
540  PERR ("couldn't parse GncGUID");
541  g_free (gid);
542  return (FALSE);
543  }
544 
545  *result = gid;
546  return (TRUE);
547 }
548 
549 sixtp*
550 generic_guid_parser_new (void)
551 {
552  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  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 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  gnc_numeric* num = NULL;
591  gchar* txt = NULL;
592  gboolean ok = FALSE;
593 
594  txt = concatenate_child_result_chars (data_from_children);
595 
596  if (txt)
597  {
598  num = g_new (gnc_numeric, 1);
599  if (num)
600  {
601  *num = gnc_numeric_from_string (txt);
602  if (!gnc_numeric_check (*num))
603  {
604  ok = TRUE;
605  *result = num;
606  }
607  }
608  }
609 
610  g_free (txt);
611  if (!ok)
612  {
613  PERR ("couldn't parse numeric quantity");
614  g_free (num);
615  }
616 
617  return (ok);
618 }
619 
620 sixtp*
621 generic_gnc_numeric_parser_new (void)
622 {
623  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  SIXTP_NO_MORE_HANDLERS);
632 }
633 
634 /***************************************************************************/
635 
636 sixtp*
637 restore_char_generator (sixtp_end_handler ender)
638 {
639  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  SIXTP_NO_MORE_HANDLERS);
646 }
647 
648 /***************************** END OF FILE *********************************/
time64 gnc_iso8601_to_time64_gmt(const gchar *)
The gnc_iso8601_to_time64_gmt() routine converts an ISO-8601 style date/time string to time64...
Definition: sixtp.h:129
Date and Time handling routines.
gboolean string_to_guid(const gchar *string, GncGUID *guid)
Given a string, replace the given guid with the parsed one unless the given value is null...
#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.
GNCNumericErrorCode gnc_numeric_check(gnc_numeric a)
Check for error signal in value.
The type used to store guids in C.
Definition: guid.h:75