GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
sixtp-utils.c
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 [email protected] *
21  * *
22  ********************************************************************/
23 
24 #define __EXTENSIONS__
25 
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 "sixtp.h"
35 #include "sixtp-utils.h"
36 #include <time.h>
37 #include <errno.h>
38 
39 #ifdef GNUCASH_MAJOR_VERSION
40 #ifndef HAVE_STRPTIME
41 #include "strptime.h"
42 #endif
43 #include <gnc-date.h>
44 #endif
45 
46 static QofLogModule log_module = GNC_MOD_IO;
47 
48 gboolean
49 isspace_str(const gchar *str, int nomorethan)
50 {
51  const gchar *cursor = str;
52  while (*cursor && (nomorethan != 0))
53  {
54  if (!isspace(*cursor))
55  {
56  return(FALSE);
57  }
58  cursor++;
59  nomorethan--;
60  }
61  return(TRUE);
62 }
63 
64 gboolean
65 allow_and_ignore_only_whitespace(GSList *sibling_data,
66  gpointer parent_data,
67  gpointer global_data,
68  gpointer *result,
69  const char *text,
70  int length)
71 {
72  return(isspace_str(text, length));
73 }
74 
75 gboolean
76 generic_accumulate_chars(GSList *sibling_data,
77  gpointer parent_data,
78  gpointer global_data,
79  gpointer *result,
80 
81  const char *text,
82  int length)
83 {
84  gchar *copytxt = g_strndup(text, length);
85  g_return_val_if_fail(result, FALSE);
86 
87  *result = copytxt;
88  return(TRUE);
89 }
90 
91 
92 void
93 generic_free_data_for_children(gpointer data_for_children,
94  GSList* data_from_children,
95  GSList* sibling_data,
96  gpointer parent_data,
97  gpointer global_data,
98  gpointer *result,
99  const gchar *tag)
100 {
101  if (data_for_children) g_free(data_for_children);
102 }
103 
104 gchar *
105 concatenate_child_result_chars(GSList *data_from_children)
106 {
107  GSList *lp;
108  gchar *name = g_strdup("");
109 
110  g_return_val_if_fail(name, NULL);
111 
112  /* child data lists are in reverse chron order */
113  data_from_children = g_slist_reverse(g_slist_copy(data_from_children));
114 
115  for (lp = data_from_children; lp; lp = lp->next)
116  {
117  sixtp_child_result *cr = (sixtp_child_result *) lp->data;
118  if (cr->type != SIXTP_CHILD_RESULT_CHARS)
119  {
120  PERR ("result type is not chars");
121  g_slist_free (data_from_children);
122  g_free(name);
123  return(NULL);
124  }
125  else
126  {
127  char *temp;
128  temp = g_strconcat(name, (gchar *) cr->data, NULL);
129  g_free (name);
130  name = temp;
131  }
132  }
133  g_slist_free (data_from_children);
134  return(name);
135 }
136 
137 /****************************************************************************/
138 /* string to data converters...
139  */
140 
141 
142 /*********/
143 /* double
144  */
145 
146 gboolean
147 string_to_double(const char *str, double *result)
148 {
149  char *endptr = 0x0;
150 
151  g_return_val_if_fail(str, FALSE);
152  g_return_val_if_fail(result, FALSE);
153 
154  *result = strtod (str, &endptr);
155  if (endptr == str) return (FALSE);
156 
157  return(TRUE);
158 }
159 
160 /*********/
161 /* gint64
162  */
163 /* Maybe there should be a comment here explaining why this function
164  doesn't call g_ascii_strtoull, because it's not so obvious. -CAS */
165 gboolean
166 string_to_gint64(const gchar *str, gint64 *v)
167 {
168  /* convert a string to a gint64. only whitespace allowed before and after. */
169  long long int v_in;
170  int num_read;
171 
172  g_return_val_if_fail(str, FALSE);
173 
174  /* must use "<" here because %n's effects aren't well defined */
175  if (sscanf(str, " " QOF_SCANF_LLD "%n", &v_in, &num_read) < 1)
176  {
177  return(FALSE);
178  }
179 
180  /*
181  * Mac OS X version 10.1 and under has a silly bug where scanf
182  * returns bad values in num_read if there is a space before %n. It
183  * is fixed in the next release 10.2 afaik
184  */
185  while ( (*((gchar*)str + num_read) != '\0') &&
186  isspace(*((unsigned char*)str + num_read)))
187  num_read++;
188 
189  if (v)
190  *v = v_in;
191 
192  if (!isspace_str(str + num_read, -1)) return(FALSE);
193  return(TRUE);
194 }
195 
196 /*********/
197 /* gint32
198  */
199 
200 gboolean
201 string_to_gint32(const gchar *str, gint32 *v)
202 {
203  /* convert a string to a gint32. only whitespace allowed before and after. */
204  int num_read;
205  int v_in;
206 
207  /* must use "<" here because %n's effects aren't well defined */
208  if (sscanf(str, " %d%n", &v_in, &num_read) < 1)
209  {
210  return(FALSE);
211  }
212  while ( (*((gchar*)str + num_read) != '\0') &&
213  isspace(*((unsigned char*)str + num_read)))
214  num_read++;
215 
216  if (v)
217  *v = v_in;
218 
219  if (!isspace_str(str + num_read, -1)) return(FALSE);
220  return(TRUE);
221 }
222 
223 /************/
224 /* hex string
225  */
226 
227 gboolean
228 hex_string_to_binary(const gchar *str, void **v, guint64 *data_len)
229 {
230  /* Convert a hex string to binary. No whitespace allowed. */
231  const gchar *cursor = str;
232  guint64 str_len;
233  gboolean error = FALSE;
234 
235  g_return_val_if_fail(str, FALSE);
236  g_return_val_if_fail(v, FALSE);
237  g_return_val_if_fail(data_len, FALSE);
238 
239  str_len = strlen(str);
240  /* Since no whitespace is allowed and hex encoding is 2 text chars
241  per binary char, the result must be half the input size and the
242  input size must be even. */
243  if ((str_len % 2) != 0) return(FALSE);
244  *data_len = 0;
245  *v = g_new0(char, str_len / 2);
246 
247  g_return_val_if_fail(*v, FALSE);
248 
249  while (*cursor && *(cursor + 1))
250  {
251  gchar tmpstr[2];
252  int tmpint;
253 
254  if (isspace(*cursor) || isspace(*(cursor + 1)))
255  {
256  error = TRUE;
257  }
258  else
259  {
260  int num_read;
261  tmpstr[0] = *cursor;
262  tmpstr[0] = *(cursor + 1);
263 
264  if ((sscanf(tmpstr, "%x%n", &tmpint, &num_read) < 1)
265  || (num_read != 2))
266  {
267  error = TRUE;
268  }
269  else
270  {
271  *((gchar *) (v + *data_len)) = tmpint;
272  *data_len += 1;
273  cursor += 2;
274  }
275  }
276  }
277 
278  if (error || (*data_len != (str_len / 2)))
279  {
280  g_free(*v);
281  *v = NULL;
282  *data_len = 0;
283  return(FALSE);
284  }
285 
286  return(TRUE);
287 }
288 
289 /***************************************************************************/
290 /* simple chars only parser - just grabs all it's contained chars and
291  does what you specify in the end handler - if you pass NULL as the
292  end handler to simple_chars_only_parser_new, the characters are just
293  passed to the parent as a new string.
294 
295  input: NA
296  returns: gchar array allocated via g_new, etc.
297 
298  start: NA
299  chars: generic_accumulate_chars.
300  end: varies - default is to concatenate all accumulated chars and return.
301 
302  cleanup-result: g_free (for chars)
303  cleanup-chars: g_free (for chars)
304  fail: NA
305  result-fail: g_free (for chars)
306  chars-fail: g_free (for chars)
307 
308  */
309 
310 gboolean
311 generic_return_chars_end_handler(gpointer data_for_children,
312  GSList* data_from_children,
313  GSList* sibling_data,
314  gpointer parent_data,
315  gpointer global_data,
316  gpointer *result,
317  const gchar *tag)
318 {
319  gchar *txt = NULL;
320 
321  txt = concatenate_child_result_chars(data_from_children);
322  g_return_val_if_fail(txt, FALSE);
323  *result = txt;
324  return(TRUE);
325 }
326 
327 
328 sixtp*
329 simple_chars_only_parser_new(sixtp_end_handler end_handler)
330 {
331  return sixtp_set_any(
332  sixtp_new(), FALSE,
333  SIXTP_END_HANDLER_ID, (end_handler
334  ? end_handler
335  : generic_return_chars_end_handler),
336  SIXTP_CHARACTERS_HANDLER_ID, generic_accumulate_chars,
337  SIXTP_CLEANUP_RESULT_ID, sixtp_child_free_data,
338  SIXTP_CLEANUP_CHARS_ID, sixtp_child_free_data,
339  SIXTP_RESULT_FAIL_ID, sixtp_child_free_data,
340  SIXTP_CHARS_FAIL_ID, sixtp_child_free_data,
341  SIXTP_NO_MORE_HANDLERS);
342 }
343 
344 /****************************************************************************/
345 /* generic timespec handler.
346 
347  A collection of node functions intended to parse a sub-node set
348  that looks like this:
349 
350  <date-posted>
351  <s>Mon, 05 Jun 2000 23:16:19 -0500</s>
352  <ns>658864000</ns>
353  </date-posted>
354 
355  and produce a Timespec*. The start handler for the top allocates
356  the Timespec * and passes it to the children. The <s> block sets
357  the seconds and the <ns> block (if any) sets the nanoseconds. If
358  all goes well, returns the Timespec* as the result.
359 */
360 
361 gboolean
362 string_to_timespec_secs(const gchar *str, Timespec *ts)
363 {
364 
365  struct tm parsed_time;
366  const gchar *strpos;
367  time64 parsed_secs;
368  long int gmtoff;
369 
370  if (!str || !ts) return FALSE;
371 
372  memset(&parsed_time, 0, sizeof(struct tm));
373 
374  /* If you change this, make sure you also change the output code, if
375  necessary. */
376  /*fprintf(stderr, "parsing (%s)\n", str);*/
377  strpos = strptime(str, TIMESPEC_PARSE_TIME_FORMAT, &parsed_time);
378 
379  g_return_val_if_fail(strpos, FALSE);
380 
381  {
382  char sign;
383  int h1;
384  int h2;
385  int m1;
386  int m2;
387  int num_read;
388 
389  /* must use "<" here because %n's effects aren't well defined */
390  if (sscanf(strpos, " %c%1d%1d%1d%1d%n",
391  &sign,
392  &h1,
393  &h2,
394  &m1,
395  &m2,
396  &num_read) < 5)
397  {
398  return(FALSE);
399  }
400 
401  if ((sign != '+') && (sign != '-')) return(FALSE);
402  if (!isspace_str(strpos + num_read, -1)) return(FALSE);
403 
404  gmtoff = (h1 * 10 + h2) * 60 * 60;
405  gmtoff += (m1 * 10 + m2) * 60;
406  if (sign == '-') gmtoff = - gmtoff;
407 
408  parsed_time.tm_isdst = -1;
409  }
410 
411  parsed_secs = gnc_timegm(&parsed_time);
412 
413  parsed_secs -= gmtoff;
414 
415  ts->tv_sec = parsed_secs;
416 
417  return(TRUE);
418 }
419 
420 gboolean
421 string_to_timespec_nsecs(const gchar *str, Timespec *ts)
422 {
423  long int nanosecs;
424  unsigned int charcount;
425 
426  if (!str || !ts) return FALSE;
427 
428  /* The '%n' doesn't count as a conversion. */
429  if (1 != sscanf(str, " %ld%n", &nanosecs, &charcount))
430  return FALSE;
431 
432  while ( (*((gchar*)str + charcount) != '\0') &&
433  isspace(*((unsigned char*)str + charcount)))
434  charcount++;
435 
436  if (charcount != strlen(str)) return(FALSE);
437 
438  ts->tv_nsec = nanosecs;
439 
440  return(TRUE);
441 }
442 
443 /* Top level timespec node:
444 
445  input: user end handler *
446  returns: Timespec*
447 
448  start: Allocates TimespecParseInfo* for data_for_children.
449  characters: none (whitespace only).
450  end: g_free TimespecParseInfo + any other actions
451 
452  cleanup-result: NA
453  cleanup-chars: NA
454  fail: g_free data_for_children.
455  result-fail: g_free data_for_children.
456  chars-fail: NA
457 
458  */
459 
460 gboolean
461 generic_timespec_start_handler(GSList* sibling_data, gpointer parent_data,
462  gpointer global_data,
463  gpointer *data_for_children, gpointer *result,
464  const gchar *tag, gchar **attrs)
465 {
466  TimespecParseInfo *tsp = g_new0(TimespecParseInfo, 1);
467  g_return_val_if_fail(tsp, FALSE);
468  *data_for_children = tsp;
469  return(TRUE);
470 }
471 
472 /* You can't use this function directly. You have to call it from
473  your own end handler. If it returns TRUE, *result will contain the
474  new timespec. Otherwise, you can presume that everything's been
475  cleaned up properly and return FALSE. */
476 gboolean
477 timespec_parse_ok(TimespecParseInfo *info)
478 {
479 
480  if ((info->s_block_count > 1) || (info->ns_block_count > 1) ||
481  ((info->s_block_count == 0) && (info->ns_block_count == 0)))
482  {
483  return(FALSE);
484  }
485  else
486  {
487  return(TRUE);
488  }
489 }
490 
491 /* generic_timespec_end_handler - must be customized and provided by
492  the user. */
493 
494 /* <s> (parent timespec-node)
495 
496  input: TimespecParseInfo *
497  returns: NA
498 
499  start: NA
500  characters: accumulate.
501  end: convert characters to secs part of input Timespec and inc s_block_count.
502 
503  cleanup-result: NA
504  cleanup-chars: g_free data.
505  fail: NA
506  result-fail: NA
507  chars-fail: g_free data.
508 
509  */
510 
511 gboolean
512 generic_timespec_secs_end_handler(gpointer data_for_children,
513  GSList *data_from_children, GSList *sibling_data,
514  gpointer parent_data, gpointer global_data,
515  gpointer *result, const gchar *tag)
516 {
517  gchar *txt = NULL;
518  TimespecParseInfo *info = (TimespecParseInfo *) parent_data;
519  gboolean ok;
520 
521  g_return_val_if_fail(parent_data, FALSE);
522 
523  txt = concatenate_child_result_chars(data_from_children);
524  g_return_val_if_fail(txt, FALSE);
525 
526  ok = string_to_timespec_secs(txt, &(info->ts));
527  g_free(txt);
528 
529  g_return_val_if_fail(ok, FALSE);
530 
531  info->s_block_count++;
532  return(TRUE);
533 }
534 
535 /* <s> (parent timespec-node)
536 
537  input: TimespecParseInfo *
538  returns: NA
539 
540  start: NA
541  characters: accumulate.
542  end: convert characters to secs part of input Timespec and inc s_block_count.
543 
544  cleanup-result: NA
545  cleanup-chars: g_free data.
546  fail: NA
547  result-fail: NA
548  chars-fail: g_free data.
549 
550  */
551 
552 gboolean
553 generic_timespec_nsecs_end_handler(gpointer data_for_children,
554  GSList *data_from_children, GSList *sibling_data,
555  gpointer parent_data, gpointer global_data,
556  gpointer *result, const gchar *tag)
557 {
558  gchar *txt = NULL;
559  TimespecParseInfo *info = (TimespecParseInfo *) parent_data;
560  gboolean ok;
561 
562  g_return_val_if_fail(parent_data, FALSE);
563 
564  txt = concatenate_child_result_chars(data_from_children);
565  g_return_val_if_fail(txt, FALSE);
566 
567  ok = string_to_timespec_nsecs(txt, &(info->ts));
568  g_free(txt);
569 
570  g_return_val_if_fail(ok, FALSE);
571 
572  info->ns_block_count++;
573  return(TRUE);
574 }
575 
576 static sixtp*
577 timespec_sixtp_new(sixtp_end_handler ender)
578 {
579  return sixtp_set_any(
580  sixtp_new(), FALSE,
581  SIXTP_CHARACTERS_HANDLER_ID, generic_accumulate_chars,
582  SIXTP_END_HANDLER_ID, ender,
583  SIXTP_CLEANUP_CHARS_ID, sixtp_child_free_data,
584  SIXTP_CHARS_FAIL_ID, sixtp_child_free_data,
585  SIXTP_NO_MORE_HANDLERS);
586 }
587 
588 sixtp *
589 generic_timespec_parser_new(sixtp_end_handler end_handler)
590 {
591  sixtp *top_level =
592  sixtp_set_any(sixtp_new(), FALSE,
593  SIXTP_START_HANDLER_ID, generic_timespec_start_handler,
594  SIXTP_CHARACTERS_HANDLER_ID, allow_and_ignore_only_whitespace,
595  SIXTP_END_HANDLER_ID, end_handler,
596  SIXTP_CLEANUP_RESULT_ID, sixtp_child_free_data,
597  SIXTP_FAIL_HANDLER_ID, generic_free_data_for_children,
598  SIXTP_RESULT_FAIL_ID, sixtp_child_free_data,
599  SIXTP_NO_MORE_HANDLERS);
600  g_return_val_if_fail(top_level, NULL);
601 
602  if (!sixtp_add_some_sub_parsers(
603  top_level, TRUE,
604  "s", timespec_sixtp_new(generic_timespec_secs_end_handler),
605  "ns", timespec_sixtp_new(generic_timespec_nsecs_end_handler),
606  NULL, NULL))
607  {
608  return NULL;
609  }
610 
611  return(top_level);
612 }
613 
614 /****************************************************************************/
615 /* <?> generic guid handler...
616 
617  Attempts to parse the current accumulated characters data as a guid
618  and return it.
619 
620  input: NA
621  returns: GncGUID*
622 
623  start: NA
624  characters: return string copy for accumulation in end handler.
625  end: concatenate all chars and create and return GncGUID*, if possible.
626 
627  cleanup-result: g_free the GncGUID*
628  cleanup-chars: g_free the result string.
629  fail: NA
630  result-fail: g_free the GncGUID*
631  chars-fail: g_free the result string.
632 
633  */
634 
635 gboolean
636 generic_guid_end_handler(gpointer data_for_children,
637  GSList *data_from_children, GSList *sibling_data,
638  gpointer parent_data, gpointer global_data,
639  gpointer *result, const gchar *tag)
640 {
641  gchar *txt = NULL;
642  GncGUID *gid;
643  gboolean ok;
644 
645  txt = concatenate_child_result_chars(data_from_children);
646  g_return_val_if_fail(txt, FALSE);
647 
648  gid = g_new(GncGUID, 1);
649  if (!gid)
650  {
651  g_free(txt);
652  return(FALSE);
653  }
654 
655  ok = string_to_guid(txt, gid);
656  g_free(txt);
657 
658  if (!ok)
659  {
660  PERR ("couldn't parse GncGUID");
661  g_free(gid);
662  return(FALSE);
663  }
664 
665  *result = gid;
666  return(TRUE);
667 }
668 
669 sixtp*
670 generic_guid_parser_new(void)
671 {
672  return sixtp_set_any(
673  sixtp_new(), FALSE,
674  SIXTP_CHARACTERS_HANDLER_ID, generic_accumulate_chars,
675  SIXTP_CLEANUP_CHARS_ID, sixtp_child_free_data,
676  SIXTP_CHARS_FAIL_ID, sixtp_child_free_data,
677  SIXTP_END_HANDLER_ID, generic_guid_end_handler,
678  SIXTP_RESULT_FAIL_ID, sixtp_child_free_data,
679  SIXTP_CLEANUP_RESULT_ID, sixtp_child_free_data,
680  SIXTP_NO_MORE_HANDLERS);
681 }
682 
683 /****************************************************************************/
684 /* <?> generic gnc_numeric handler...
685 
686  Attempts to parse the current accumulated characters data as a
687  gnc_numeric and return it.
688 
689  input: NA
690  returns: gnc_numeric*
691 
692  start: NA
693  characters: return string copy for accumulation in end handler.
694  end: concatenate all chars and create and return gnc_numeric*, if possible.
695 
696  cleanup-result: g_free the gnc_numeric*
697  cleanup-chars: g_free the result string.
698  fail: NA
699  result-fail: g_free the gnc_numeric*
700  chars-fail: g_free the result string.
701 
702  */
703 
704 gboolean
705 generic_gnc_numeric_end_handler(gpointer data_for_children,
706  GSList *data_from_children, GSList *sibling_data,
707  gpointer parent_data, gpointer global_data,
708  gpointer *result, const gchar *tag)
709 {
710  gnc_numeric *num = NULL;
711  gchar *txt = NULL;
712  gboolean ok = FALSE;
713 
714  txt = concatenate_child_result_chars(data_from_children);
715 
716  if (txt)
717  {
718  num = g_new(gnc_numeric, 1);
719  if (num)
720  {
721  if (string_to_gnc_numeric(txt, num))
722  {
723  ok = TRUE;
724  *result = num;
725  }
726  }
727  }
728 
729  g_free(txt);
730  if (!ok)
731  {
732  PERR ("couldn't parse numeric quantity");
733  g_free(num);
734  }
735 
736  return(ok);
737 }
738 
739 sixtp*
740 generic_gnc_numeric_parser_new(void)
741 {
742  return sixtp_set_any(
743  sixtp_new(), FALSE,
744  SIXTP_CHARACTERS_HANDLER_ID, generic_accumulate_chars,
745  SIXTP_CLEANUP_CHARS_ID, sixtp_child_free_data,
746  SIXTP_CHARS_FAIL_ID, sixtp_child_free_data,
747  SIXTP_END_HANDLER_ID, generic_gnc_numeric_end_handler,
748  SIXTP_RESULT_FAIL_ID, sixtp_child_free_data,
749  SIXTP_CLEANUP_RESULT_ID, sixtp_child_free_data,
750  SIXTP_NO_MORE_HANDLERS);
751 }
752 
753 /***************************************************************************/
754 
755 sixtp*
756 restore_char_generator(sixtp_end_handler ender)
757 {
758  return sixtp_set_any(
759  sixtp_new(), FALSE,
760  SIXTP_CHARACTERS_HANDLER_ID, generic_accumulate_chars,
761  SIXTP_END_HANDLER_ID, ender,
762  SIXTP_CLEANUP_CHARS_ID, sixtp_child_free_data,
763  SIXTP_CHARS_FAIL_ID, sixtp_child_free_data,
764  SIXTP_NO_MORE_HANDLERS);
765 }
766 
767 /***************************** END OF FILE *********************************/
Definition: sixtp.h:93
Date and Time handling routines.
gboolean string_to_guid(const gchar *string, GncGUID *guid)
Use a 64-bit unsigned int timespec.
Definition: gnc-date.h:299
#define PERR(format, args...)
Definition: qoflog.h:237
gboolean string_to_gnc_numeric(const gchar *str, gnc_numeric *n)
Definition: guid.h:65
time64 gnc_timegm(struct tm *time)
calculate seconds from the epoch given a time struct
gint64 time64
Definition: gnc-date.h:83
const gchar * QofLogModule
Definition: qofid.h:89