gnucash/libgnucash/backend/xml/sixtp-utils.cpp
Geert Janssens 1238b9d8cd Prevent gcc from searching config.h in the current directory
This will avoid a ninja-build from picking up a config.h generated by the autotools build
(in the root build directory). Picking up the wrong config.h may lead to all kinds of
subtle issues if the autotools run was done with different options than the cmake run.
2017-10-26 14:05:17 +02:00

704 lines
20 KiB
C++

/********************************************************************
* sixtp-utils.c *
* Copyright (c) 2001 Gnumatic, Inc. *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
* *
********************************************************************/
#define __EXTENSIONS__
#include <guid.hpp>
extern "C"
{
#include <config.h>
#include <ctype.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#ifdef GNUCASH_MAJOR_VERSION
#ifndef HAVE_STRPTIME
#include "strptime.h"
#endif
#include <gnc-date.h>
#endif
}
#include "sixtp.h"
#include "sixtp-utils.h"
static QofLogModule log_module = GNC_MOD_IO;
gboolean
isspace_str (const gchar* str, int nomorethan)
{
const gchar* cursor = str;
while (*cursor && (nomorethan != 0))
{
if (!isspace (*cursor))
{
return (FALSE);
}
cursor++;
nomorethan--;
}
return (TRUE);
}
gboolean
allow_and_ignore_only_whitespace (GSList* sibling_data,
gpointer parent_data,
gpointer global_data,
gpointer* result,
const char* text,
int length)
{
return (isspace_str (text, length));
}
gboolean
generic_accumulate_chars (GSList* sibling_data,
gpointer parent_data,
gpointer global_data,
gpointer* result,
const char* text,
int length)
{
gchar* copytxt = g_strndup (text, length);
g_return_val_if_fail (result, FALSE);
*result = copytxt;
return (TRUE);
}
void
generic_free_data_for_children (gpointer data_for_children,
GSList* data_from_children,
GSList* sibling_data,
gpointer parent_data,
gpointer global_data,
gpointer* result,
const gchar* tag)
{
if (data_for_children) g_free (data_for_children);
}
gchar*
concatenate_child_result_chars (GSList* data_from_children)
{
GSList* lp;
gchar* name = g_strdup ("");
g_return_val_if_fail (name, NULL);
/* child data lists are in reverse chron order */
data_from_children = g_slist_reverse (g_slist_copy (data_from_children));
for (lp = data_from_children; lp; lp = lp->next)
{
sixtp_child_result* cr = (sixtp_child_result*) lp->data;
if (cr->type != SIXTP_CHILD_RESULT_CHARS)
{
PERR ("result type is not chars");
g_slist_free (data_from_children);
g_free (name);
return (NULL);
}
else
{
char* temp;
temp = g_strconcat (name, (gchar*) cr->data, NULL);
g_free (name);
name = temp;
}
}
g_slist_free (data_from_children);
return (name);
}
/****************************************************************************/
/* string to data converters...
*/
/*********/
/* double
*/
gboolean
string_to_double (const char* str, double* result)
{
char* endptr = 0x0;
g_return_val_if_fail (str, FALSE);
g_return_val_if_fail (result, FALSE);
*result = strtod (str, &endptr);
if (endptr == str) return (FALSE);
return (TRUE);
}
/*********/
/* gint64
*/
/* Maybe there should be a comment here explaining why this function
doesn't call g_ascii_strtoull, because it's not so obvious. -CAS */
gboolean
string_to_gint64 (const gchar* str, gint64* v)
{
/* convert a string to a gint64. only whitespace allowed before and after. */
long long int v_in;
int num_read;
g_return_val_if_fail (str, FALSE);
/* must use "<" here because %n's effects aren't well defined */
if (sscanf (str, " " QOF_SCANF_LLD "%n", &v_in, &num_read) < 1)
{
return (FALSE);
}
/*
* Mac OS X version 10.1 and under has a silly bug where scanf
* returns bad values in num_read if there is a space before %n. It
* is fixed in the next release 10.2 afaik
*/
while ((* ((gchar*)str + num_read) != '\0') &&
isspace (* ((unsigned char*)str + num_read)))
num_read++;
if (v)
*v = v_in;
if (!isspace_str (str + num_read, -1)) return (FALSE);
return (TRUE);
}
/*********/
/* gint32
*/
gboolean
string_to_gint32 (const gchar* str, gint32* v)
{
/* convert a string to a gint32. only whitespace allowed before and after. */
int num_read;
int v_in;
/* must use "<" here because %n's effects aren't well defined */
if (sscanf (str, " %d%n", &v_in, &num_read) < 1)
{
return (FALSE);
}
while ((* ((gchar*)str + num_read) != '\0') &&
isspace (* ((unsigned char*)str + num_read)))
num_read++;
if (v)
*v = v_in;
if (!isspace_str (str + num_read, -1)) return (FALSE);
return (TRUE);
}
/************/
/* hex string
*/
gboolean
hex_string_to_binary (const gchar* str, void** v, guint64* data_len)
{
/* Convert a hex string to binary. No whitespace allowed. */
const gchar* cursor = str;
guint64 str_len;
gboolean error = FALSE;
g_return_val_if_fail (str, FALSE);
g_return_val_if_fail (v, FALSE);
g_return_val_if_fail (data_len, FALSE);
str_len = strlen (str);
/* Since no whitespace is allowed and hex encoding is 2 text chars
per binary char, the result must be half the input size and the
input size must be even. */
if ((str_len % 2) != 0) return (FALSE);
*data_len = 0;
*v = g_new0 (char, str_len / 2);
g_return_val_if_fail (*v, FALSE);
while (*cursor && * (cursor + 1))
{
gchar tmpstr[2];
int tmpint;
if (isspace (*cursor) || isspace (* (cursor + 1)))
{
error = TRUE;
}
else
{
int num_read;
tmpstr[0] = *cursor;
tmpstr[0] = * (cursor + 1);
if ((sscanf (tmpstr, "%x%n", &tmpint, &num_read) < 1)
|| (num_read != 2))
{
error = TRUE;
}
else
{
* ((gchar*) (v + *data_len)) = tmpint;
*data_len += 1;
cursor += 2;
}
}
}
if (error || (*data_len != (str_len / 2)))
{
g_free (*v);
*v = NULL;
*data_len = 0;
return (FALSE);
}
return (TRUE);
}
/***************************************************************************/
/* simple chars only parser - just grabs all it's contained chars and
does what you specify in the end handler - if you pass NULL as the
end handler to simple_chars_only_parser_new, the characters are just
passed to the parent as a new string.
input: NA
returns: gchar array allocated via g_new, etc.
start: NA
chars: generic_accumulate_chars.
end: varies - default is to concatenate all accumulated chars and return.
cleanup-result: g_free (for chars)
cleanup-chars: g_free (for chars)
fail: NA
result-fail: g_free (for chars)
chars-fail: g_free (for chars)
*/
gboolean
generic_return_chars_end_handler (gpointer data_for_children,
GSList* data_from_children,
GSList* sibling_data,
gpointer parent_data,
gpointer global_data,
gpointer* result,
const gchar* tag)
{
gchar* txt = NULL;
txt = concatenate_child_result_chars (data_from_children);
g_return_val_if_fail (txt, FALSE);
*result = txt;
return (TRUE);
}
sixtp*
simple_chars_only_parser_new (sixtp_end_handler end_handler)
{
return sixtp_set_any (
sixtp_new (), FALSE,
SIXTP_END_HANDLER_ID, (end_handler
? end_handler
: generic_return_chars_end_handler),
SIXTP_CHARACTERS_HANDLER_ID, generic_accumulate_chars,
SIXTP_CLEANUP_RESULT_ID, sixtp_child_free_data,
SIXTP_CLEANUP_CHARS_ID, sixtp_child_free_data,
SIXTP_RESULT_FAIL_ID, sixtp_child_free_data,
SIXTP_CHARS_FAIL_ID, sixtp_child_free_data,
SIXTP_NO_MORE_HANDLERS);
}
/****************************************************************************/
/* generic timespec handler.
A collection of node functions intended to parse a sub-node set
that looks like this:
<date-posted>
<s>Mon, 05 Jun 2000 23:16:19 -0500</s>
<ns>658864000</ns>
</date-posted>
and produce a Timespec*. The start handler for the top allocates
the Timespec * and passes it to the children. The <s> block sets
the seconds and the <ns> block (if any) sets the nanoseconds. If
all goes well, returns the Timespec* as the result.
*/
gboolean
string_to_timespec_secs (const gchar* str, Timespec* ts)
{
*ts = gnc_iso8601_to_timespec_gmt (str);
return (TRUE);
}
gboolean
string_to_timespec_nsecs (const gchar* str, Timespec* ts)
{
/* We don't do nanoseconds anymore. */
return (TRUE);
}
/* Top level timespec node:
input: user end handler *
returns: Timespec*
start: Allocates TimespecParseInfo* for data_for_children.
characters: none (whitespace only).
end: g_free TimespecParseInfo + any other actions
cleanup-result: NA
cleanup-chars: NA
fail: g_free data_for_children.
result-fail: g_free data_for_children.
chars-fail: NA
*/
gboolean
generic_timespec_start_handler (GSList* sibling_data, gpointer parent_data,
gpointer global_data,
gpointer* data_for_children, gpointer* result,
const gchar* tag, gchar** attrs)
{
TimespecParseInfo* tsp = g_new0 (TimespecParseInfo, 1);
g_return_val_if_fail (tsp, FALSE);
*data_for_children = tsp;
return (TRUE);
}
/* You can't use this function directly. You have to call it from
your own end handler. If it returns TRUE, *result will contain the
new timespec. Otherwise, you can presume that everything's been
cleaned up properly and return FALSE. */
gboolean
timespec_parse_ok (TimespecParseInfo* info)
{
if ((info->s_block_count > 1) || (info->ns_block_count > 1) ||
((info->s_block_count == 0) && (info->ns_block_count == 0)))
{
return (FALSE);
}
else
{
return (TRUE);
}
}
/* generic_timespec_end_handler - must be customized and provided by
the user. */
/* <s> (parent timespec-node)
input: TimespecParseInfo *
returns: NA
start: NA
characters: accumulate.
end: convert characters to secs part of input Timespec and inc s_block_count.
cleanup-result: NA
cleanup-chars: g_free data.
fail: NA
result-fail: NA
chars-fail: g_free data.
*/
gboolean
generic_timespec_secs_end_handler (gpointer data_for_children,
GSList* data_from_children, GSList* sibling_data,
gpointer parent_data, gpointer global_data,
gpointer* result, const gchar* tag)
{
gchar* txt = NULL;
TimespecParseInfo* info = (TimespecParseInfo*) parent_data;
gboolean ok;
g_return_val_if_fail (parent_data, FALSE);
txt = concatenate_child_result_chars (data_from_children);
g_return_val_if_fail (txt, FALSE);
ok = string_to_timespec_secs (txt, & (info->ts));
g_free (txt);
g_return_val_if_fail (ok, FALSE);
info->s_block_count++;
return (TRUE);
}
/* <s> (parent timespec-node)
input: TimespecParseInfo *
returns: NA
start: NA
characters: accumulate.
end: convert characters to secs part of input Timespec and inc s_block_count.
cleanup-result: NA
cleanup-chars: g_free data.
fail: NA
result-fail: NA
chars-fail: g_free data.
*/
gboolean
generic_timespec_nsecs_end_handler (gpointer data_for_children,
GSList* data_from_children, GSList* sibling_data,
gpointer parent_data, gpointer global_data,
gpointer* result, const gchar* tag)
{
gchar* txt = NULL;
TimespecParseInfo* info = (TimespecParseInfo*) parent_data;
gboolean ok;
g_return_val_if_fail (parent_data, FALSE);
txt = concatenate_child_result_chars (data_from_children);
g_return_val_if_fail (txt, FALSE);
ok = string_to_timespec_nsecs (txt, & (info->ts));
g_free (txt);
g_return_val_if_fail (ok, FALSE);
info->ns_block_count++;
return (TRUE);
}
static sixtp*
timespec_sixtp_new (sixtp_end_handler ender)
{
return sixtp_set_any (
sixtp_new (), FALSE,
SIXTP_CHARACTERS_HANDLER_ID, generic_accumulate_chars,
SIXTP_END_HANDLER_ID, ender,
SIXTP_CLEANUP_CHARS_ID, sixtp_child_free_data,
SIXTP_CHARS_FAIL_ID, sixtp_child_free_data,
SIXTP_NO_MORE_HANDLERS);
}
sixtp*
generic_timespec_parser_new (sixtp_end_handler end_handler)
{
sixtp* top_level =
sixtp_set_any (sixtp_new (), FALSE,
SIXTP_START_HANDLER_ID, generic_timespec_start_handler,
SIXTP_CHARACTERS_HANDLER_ID, allow_and_ignore_only_whitespace,
SIXTP_END_HANDLER_ID, end_handler,
SIXTP_CLEANUP_RESULT_ID, sixtp_child_free_data,
SIXTP_FAIL_HANDLER_ID, generic_free_data_for_children,
SIXTP_RESULT_FAIL_ID, sixtp_child_free_data,
SIXTP_NO_MORE_HANDLERS);
g_return_val_if_fail (top_level, NULL);
if (!sixtp_add_some_sub_parsers (
top_level, TRUE,
"s", timespec_sixtp_new (generic_timespec_secs_end_handler),
"ns", timespec_sixtp_new (generic_timespec_nsecs_end_handler),
NULL, NULL))
{
return NULL;
}
return (top_level);
}
/****************************************************************************/
/* <?> generic guid handler...
Attempts to parse the current accumulated characters data as a guid
and return it.
input: NA
returns: GncGUID*
start: NA
characters: return string copy for accumulation in end handler.
end: concatenate all chars and create and return GncGUID*, if possible.
cleanup-result: g_free the GncGUID*
cleanup-chars: g_free the result string.
fail: NA
result-fail: g_free the GncGUID*
chars-fail: g_free the result string.
*/
gboolean
generic_guid_end_handler (gpointer data_for_children,
GSList* data_from_children, GSList* sibling_data,
gpointer parent_data, gpointer global_data,
gpointer* result, const gchar* tag)
{
gchar* txt = NULL;
GncGUID* gid;
gboolean ok;
txt = concatenate_child_result_chars (data_from_children);
g_return_val_if_fail (txt, FALSE);
gid = g_new (GncGUID, 1);
if (!gid)
{
g_free (txt);
return (FALSE);
}
ok = string_to_guid (txt, gid);
g_free (txt);
if (!ok)
{
PERR ("couldn't parse GncGUID");
g_free (gid);
return (FALSE);
}
*result = gid;
return (TRUE);
}
sixtp*
generic_guid_parser_new (void)
{
return sixtp_set_any (
sixtp_new (), FALSE,
SIXTP_CHARACTERS_HANDLER_ID, generic_accumulate_chars,
SIXTP_CLEANUP_CHARS_ID, sixtp_child_free_data,
SIXTP_CHARS_FAIL_ID, sixtp_child_free_data,
SIXTP_END_HANDLER_ID, generic_guid_end_handler,
SIXTP_RESULT_FAIL_ID, sixtp_child_free_data,
SIXTP_CLEANUP_RESULT_ID, sixtp_child_free_data,
SIXTP_NO_MORE_HANDLERS);
}
/****************************************************************************/
/* <?> generic gnc_numeric handler...
Attempts to parse the current accumulated characters data as a
gnc_numeric and return it.
input: NA
returns: gnc_numeric*
start: NA
characters: return string copy for accumulation in end handler.
end: concatenate all chars and create and return gnc_numeric*, if possible.
cleanup-result: g_free the gnc_numeric*
cleanup-chars: g_free the result string.
fail: NA
result-fail: g_free the gnc_numeric*
chars-fail: g_free the result string.
*/
gboolean
generic_gnc_numeric_end_handler (gpointer data_for_children,
GSList* data_from_children, GSList* sibling_data,
gpointer parent_data, gpointer global_data,
gpointer* result, const gchar* tag)
{
gnc_numeric* num = NULL;
gchar* txt = NULL;
gboolean ok = FALSE;
txt = concatenate_child_result_chars (data_from_children);
if (txt)
{
num = g_new (gnc_numeric, 1);
if (num)
{
if (string_to_gnc_numeric (txt, num))
{
ok = TRUE;
*result = num;
}
}
}
g_free (txt);
if (!ok)
{
PERR ("couldn't parse numeric quantity");
g_free (num);
}
return (ok);
}
sixtp*
generic_gnc_numeric_parser_new (void)
{
return sixtp_set_any (
sixtp_new (), FALSE,
SIXTP_CHARACTERS_HANDLER_ID, generic_accumulate_chars,
SIXTP_CLEANUP_CHARS_ID, sixtp_child_free_data,
SIXTP_CHARS_FAIL_ID, sixtp_child_free_data,
SIXTP_END_HANDLER_ID, generic_gnc_numeric_end_handler,
SIXTP_RESULT_FAIL_ID, sixtp_child_free_data,
SIXTP_CLEANUP_RESULT_ID, sixtp_child_free_data,
SIXTP_NO_MORE_HANDLERS);
}
/***************************************************************************/
sixtp*
restore_char_generator (sixtp_end_handler ender)
{
return sixtp_set_any (
sixtp_new (), FALSE,
SIXTP_CHARACTERS_HANDLER_ID, generic_accumulate_chars,
SIXTP_END_HANDLER_ID, ender,
SIXTP_CLEANUP_CHARS_ID, sixtp_child_free_data,
SIXTP_CHARS_FAIL_ID, sixtp_child_free_data,
SIXTP_NO_MORE_HANDLERS);
}
/***************************** END OF FILE *********************************/