You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gnucash/src/engine/sixtp-xml-write-utils.c

457 lines
12 KiB

#include <config.h>
#define _GNU_SOURCE
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gnc-xml-helper.h"
#include "sixtp-xml-write-utils.h"
#include "gnc-numeric.h"
#include "gnc-engine-util.h"
static short module = MOD_IO;
/* ============================================================== */
gboolean
xml_add_str(xmlNodePtr p, const char *tag, const char *str,
gboolean include_if_empty) {
xmlNodePtr child;
g_return_val_if_fail(p, FALSE);
g_return_val_if_fail(tag, FALSE);
if(!str && !include_if_empty) return(TRUE);
if((strlen(str) == 0) && !include_if_empty) return(TRUE);
child = xmlNewTextChild(p, NULL, tag, str);
g_return_val_if_fail(child, FALSE);
return(TRUE);
}
/* ============================================================== */
gboolean
xml_add_character(xmlNodePtr p, const char *tag, const char c) {
char str[2];
str[0] = c;
str[1] = '\0';
return(xml_add_str(p, tag, str, FALSE));
}
/* ============================================================== */
gboolean
xml_add_gint64(xmlNodePtr p, const char *tag, const gint64 value) {
xmlNodePtr val_xml;
char num_string[22];
g_return_val_if_fail(p, FALSE);
g_return_val_if_fail(tag, FALSE);
g_snprintf(num_string, sizeof (num_string), "%lld", value);
val_xml = xmlNewTextChild(p, NULL, tag, num_string);
g_return_val_if_fail(val_xml, FALSE);
return(TRUE);
}
/* ============================================================== */
gboolean
xml_add_gint32(xmlNodePtr p, const char *tag, const gint32 value) {
xmlNodePtr val_xml;
char num_string[22];
g_return_val_if_fail(p, FALSE);
g_return_val_if_fail(tag, FALSE);
g_snprintf(num_string, sizeof (num_string), "%d", value);
val_xml = xmlNewTextChild(p, NULL, tag, num_string);
g_return_val_if_fail(val_xml, FALSE);
return(TRUE);
}
/* ============================================================== */
/*
RLB writes:
We have to use guile because AFAICT, libc, and C in general isn't
smart enough to actually parse it's own output, especially not
portably (big surprise).
Linas writes:
I don't understand the claim; I'm just going to use
atof or strtod to accomplish this.
*/
gboolean
xml_add_double(xmlNodePtr p, const char *tag, const double value)
{
g_return_val_if_fail(p, FALSE);
g_return_val_if_fail(tag, FALSE);
#ifdef USE_GUILE_FOR_DOUBLE_CONVERSION
{
/* FIXME: NOT THREAD SAFE - USES STATIC DATA */
static SCM number_to_string;
static gboolean ready = FALSE;
const char *numstr;
if(!ready) {
number_to_string = gh_eval_str("number->string");
scm_protect_object(number_to_string);
ready = TRUE;
}
numstr = gh_scm2newstr(gh_call1(number_to_string, gh_double2scm(value)),
NULL);
if(!numstr) {
return(FALSE);
} else {
xmlNodePtr child = xmlNewTextChild(p, NULL, tag, numstr);
free((void *) numstr);
g_return_val_if_fail(child, FALSE);
}
}
#else /* don't USE_GUILE_FOR_DOUBLE_CONVERSION */
{
int len;
char prtbuf[80];
xmlNodePtr child;
/* we're just going to use plain-old libc for the double conversion.
* There was some question as to whether libc is accurate enough
* in its printf function for doubles, but I don't understand
* how it couldn't be ...
*/
len = snprintf (prtbuf, 80, "%24.18g", value);
if (80 <=len) return (FALSE);
child = xmlNewTextChild(p, NULL, tag, prtbuf);
g_return_val_if_fail(child, FALSE);
}
#endif /* USE_GUILE_FOR_DOUBLE_CONVERSION */
return(TRUE);
}
/* ============================================================== */
gboolean
xml_add_gnc_numeric(xmlNodePtr p, const char *tag, const gnc_numeric n) {
char *numstr;
xmlNodePtr child;
g_return_val_if_fail(p, FALSE);
g_return_val_if_fail(tag, FALSE);
/* fprintf(stderr, "WRITE GNUM S: %lld/%lld -> ", n.num, n.denom); */
numstr = gnc_numeric_to_string(n);
g_return_val_if_fail(numstr, FALSE);
/* fprintf(stderr, "%s\n", numstr); */
child = xmlNewTextChild(p, NULL, tag, numstr);
g_free(numstr); numstr = FALSE;
g_return_val_if_fail(child, FALSE);
return(TRUE);
}
/* ============================================================== */
gboolean
xml_add_guid(xmlNodePtr p, const char *tag, const GUID *guid) {
g_return_val_if_fail(p, FALSE);
g_return_val_if_fail(tag, FALSE);
g_return_val_if_fail(guid, FALSE);
{
const char *guidstr;
xmlNodePtr child;
if(!guid) {
guidstr = NULL;
} else {
guidstr = guid_to_string(guid);
g_return_val_if_fail(guidstr, FALSE);
}
child = xmlNewTextChild(p, NULL, tag, guidstr);
g_return_val_if_fail(child, FALSE);
if(guidstr) free((void *) guidstr);
}
return(TRUE);
}
/* ============================================================== */
gboolean
xml_add_editable_timespec(xmlNodePtr p,
const char *tag,
const Timespec *ts,
gboolean include_if_zero) {
xmlNodePtr timespec_xml;
xmlNodePtr secs_xml;
size_t num_written;
struct tm parsed_time;
time_t tmp_timet;
char secs_str[512]; /* This should be way bigger than we need.
Still, it's bogus, we ought to have
astrftime... */
g_return_val_if_fail(p, FALSE);
g_return_val_if_fail(tag, FALSE);
g_return_val_if_fail(ts, FALSE);
if(!include_if_zero && (ts->tv_sec == 0) && (ts->tv_nsec == 0)) return TRUE;
tmp_timet = ts->tv_sec;
if(!localtime_r(&tmp_timet, &parsed_time)) return(FALSE);
num_written = strftime(secs_str, sizeof(secs_str),
"%Y-%m-%d %H:%M:%S %z",
&parsed_time);
if(num_written == 0) return(FALSE);
timespec_xml= xmlNewTextChild(p, NULL, tag, NULL);
g_return_val_if_fail(timespec_xml, FALSE);
secs_xml = xmlNewTextChild(timespec_xml, NULL, "s", secs_str);
g_return_val_if_fail(secs_xml, FALSE);
if(ts->tv_nsec) {
xmlNodePtr nsec_xml;
char num_string[22];
g_snprintf(num_string, sizeof (num_string), "%ld", ts->tv_nsec);
nsec_xml = xmlNewTextChild(timespec_xml, NULL, "ns", num_string);
g_return_val_if_fail(nsec_xml, FALSE);
}
return(TRUE);
}
static gboolean
xml_add_binary(xmlNodePtr p,
const char *tag,
const gchar *format,
const void *data,
guint32 size)
{
xmlNodePtr value_xml;
g_return_val_if_fail(p, FALSE);
g_return_val_if_fail(tag, FALSE);
g_return_val_if_fail(format, FALSE);
g_return_val_if_fail(data, FALSE);
value_xml = xmlNewTextChild(p, NULL, tag, NULL);
g_return_val_if_fail(value_xml, FALSE);
if(size == 0) return(TRUE);
if(0 == strcmp(format, "hex")) {
/* Write out the chars as hex, buffering them in max 64 character
lines. I was going to use xmlNewTextChild, and xmlTextConcat,
but that doesn't seem to work, and looking at the source,
xmlNewTextChild doesn't set the node type to a type that
xmlTextConcat will recognize and allow. */
const guint max_line_len = 64;
xmlNodePtr data_xml = NULL;
GString *output;
guint32 i;
output = g_string_sized_new(max_line_len + 2);
for(i = 0; i < size; i++) {
g_string_sprintfa(output, "%x", (int) (((char *) data)[i]));
if(((i + 1) % max_line_len) == 0) {
data_xml = xmlNewTextChild(value_xml, NULL, "hex", output->str);
if(!data_xml) {
return(FALSE);
g_string_free(output, TRUE);
}
g_string_truncate(output, 0);
}
}
if(strlen(output->str) > 0) {
data_xml = xmlNewTextChild(value_xml, NULL, "hex", output->str);
if(!data_xml) {
g_string_free(output, TRUE);
return(FALSE);
}
}
g_string_free(output, TRUE);
} else {
PERR("unknown output format %s.\n", format);
return(FALSE);
}
return(TRUE);
}
/* ============================================================== */
static gboolean xml_add_kvp_value(xmlNodePtr p, kvp_value *val);
static gboolean
xml_add_kvp_glist(xmlNodePtr p, const char *tag, GList *lst) {
xmlNodePtr list_xml;
GList *cursor;
g_return_val_if_fail(p, FALSE);
g_return_val_if_fail(tag, FALSE);
g_return_val_if_fail(lst, FALSE);
list_xml = xmlNewTextChild(p, NULL, tag, NULL);
g_return_val_if_fail(list_xml, FALSE);
for(cursor = lst; cursor; cursor = cursor->next) {
kvp_value * val = (kvp_value *) cursor->data;
if(!xml_add_kvp_value(list_xml, val)) {
return(FALSE);
}
}
return(TRUE);
}
/* ============================================================== */
gboolean
xml_add_kvp_frame(xmlNodePtr p, const char *tag,
const kvp_frame *kvpf,
gboolean add_if_empty);
static gboolean
xml_add_kvp_value(xmlNodePtr p, kvp_value *val) {
g_return_val_if_fail(p, FALSE);
g_return_val_if_fail(val, FALSE);
switch(kvp_value_get_type(val)) {
case KVP_TYPE_GINT64:
return(xml_add_gint64(p, "gint64", kvp_value_get_gint64(val)));
break;
case KVP_TYPE_DOUBLE:
return(xml_add_double(p, "double", kvp_value_get_double(val)));
break;
case KVP_TYPE_NUMERIC:
return(xml_add_gnc_numeric(p, "numeric", kvp_value_get_numeric(val)));
break;
case KVP_TYPE_STRING:
return(xml_add_str(p, "string", kvp_value_get_string(val), TRUE));
break;
case KVP_TYPE_GUID:
return(xml_add_guid(p, "guid", kvp_value_get_guid(val)));
break;
case KVP_TYPE_BINARY:
{
guint64 size;
void *binary_data = kvp_value_get_binary(val, &size);
g_return_val_if_fail(binary_data, FALSE);
return(xml_add_binary(p, "binary", "hex", binary_data, size));
}
break;
case KVP_TYPE_GLIST:
return(xml_add_kvp_glist(p, "glist", kvp_value_get_glist(val)));
break;
case KVP_TYPE_FRAME:
return(xml_add_kvp_frame(p, "frame", kvp_value_get_frame(val), TRUE));
break;
default:
return(FALSE);
break;
};
return(TRUE);
}
/* ============================================================== */
static gboolean
xml_add_kvp_slot(xmlNodePtr p, const char *key, kvp_value *val) {
xmlNodePtr slot_xml;
xmlNodePtr key_xml;
g_return_val_if_fail(p, FALSE);
g_return_val_if_fail(key, FALSE);
g_return_val_if_fail(val, FALSE);
slot_xml = xmlNewTextChild(p, NULL, "s", NULL);
g_return_val_if_fail(slot_xml, FALSE);
key_xml = xmlNewTextChild(slot_xml, NULL, "k", key);
g_return_val_if_fail(key_xml, FALSE);
return(xml_add_kvp_value(slot_xml, val));
}
/* ============================================================== */
typedef struct {
xmlNodePtr node;
gint64 keycount;
} kvp_value_foreach_info;
static void
xml_add_kvp_value_foreach_adapter(const char *key,
kvp_value *value,
gpointer data) {
kvp_value_foreach_info *info = (kvp_value_foreach_info *) data;
xml_add_kvp_slot(info->node, key, value);
info->keycount++;
}
/* ============================================================== */
gboolean
xml_add_kvp_frame(xmlNodePtr p,
const char *tag,
const kvp_frame *kvpf,
gboolean add_if_empty) {
xmlNodePtr kvp_xml;
kvp_value_foreach_info info;
g_return_val_if_fail(p, FALSE);
g_return_val_if_fail(tag, FALSE);
g_return_val_if_fail(kvpf, FALSE);
kvp_xml = xmlNewNode(NULL, tag);
g_return_val_if_fail(kvp_xml, FALSE);
info.node = kvp_xml;
info.keycount = 0;
kvp_frame_for_each_slot((kvp_frame *) kvpf,
xml_add_kvp_value_foreach_adapter,
&info);
if(add_if_empty || info.keycount) {
xmlAddChild(p, kvp_xml);
} else {
xmlFreeNode(kvp_xml);
}
return(TRUE);
}