mirror of
https://github.com/Gnucash/gnucash.git
synced 2024-11-29 20:24:25 -06:00
1238b9d8cd
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.
704 lines
20 KiB
C++
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 *********************************/
|