From 8d875a67439e0643f628e41d094190d76d30f6ef Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Sat, 21 Jun 2008 15:08:54 +0300 Subject: [PATCH] Added SILC XML API The SILC XML API provides (or will provide) a simple stream based XML parsing interface. It is wrapper to Expat XML library. Added --with-expat configuration option too in case the automatic detection of expat doesn't find it in the system. --- TODO | 17 +- configure.ad | 24 +++ lib/silcutil/Makefile.ad | 6 +- lib/silcutil/silcruntime.h.in | 3 +- lib/silcutil/silcxml.c | 282 ++++++++++++++++++++++++++++++ lib/silcutil/silcxml.h | 244 ++++++++++++++++++++++++++ lib/silcutil/tests/Makefile.am | 2 +- lib/silcutil/tests/test_silcxml.c | 94 ++++++++++ 8 files changed, 655 insertions(+), 17 deletions(-) create mode 100644 lib/silcutil/silcxml.c create mode 100644 lib/silcutil/silcxml.h create mode 100644 lib/silcutil/tests/test_silcxml.c diff --git a/TODO b/TODO index 7ebae882..0cb16895 100644 --- a/TODO +++ b/TODO @@ -44,6 +44,10 @@ Runtime library, lib/silcutil/ SILC currently supports SOCKS4 and SOCKS5 but it needs to be compiled in separately. + o Add silc_xml_parse_stream to parse SilcStream XML stream. + + o SILC XML API (wrapper to expat). (***DONE) + o Bring silchttp HTTP server library to SRT. (***DONE) o Simple SILC Rand API for pseudo-random numbers. (***DONE) @@ -139,19 +143,6 @@ Runtime library, lib/silcutil/ rwlock implementation using atomic operations.) not for now. -SILC XML Library, lib/silcxml/ -============================== - - o SILC XML API (wrapper to expat). Look at the expat API and simplify - it. The SILC XML API should have at most 8-10 API functions. It should - be possible to create full XML parser with only one function. And, it - should be possible to have a function that is able to parse an entire - XML document. It should also have a parser function to be able to - parse a stream of XML data (SilcStream). It MUST NOT have operations - that require multiple function calls to be able to execute that one - operation (like creating parser). - - Windows Support =============== diff --git a/configure.ad b/configure.ad index cf337358..1fe945ea 100644 --- a/configure.ad +++ b/configure.ad @@ -1193,6 +1193,30 @@ if test x$has_threads = xtrue; then fi +# Check for Expat +AC_ARG_WITH(expat, + [[ --with-expat[=DIR] use Expat XML [search in DIR/include and DIR/lib]]], + [ + case "${withval}" in + no) + ;; + *) + if test -d $withval/include; then + CPPFLAGS="$CPPFLAGS -I$withval/include" + CFLAGS="$CFLAGS -I$withval/include" + fi + if test -d $withval/lib; then + LDFLAGS="$LDFLAGS -L$withval/lib" + fi + ;; + esac + ]) + +AC_CHECK_HEADERS(expat.h, + [ LIBS="$LIBS -lexpat" ], + [ AC_MSG_ERROR(Expat XML Library is required to compile SRT) ]) + + ## ## Native WIN32 compilation under cygwin ## diff --git a/lib/silcutil/Makefile.ad b/lib/silcutil/Makefile.ad index 75df24b2..972b83d6 100644 --- a/lib/silcutil/Makefile.ad +++ b/lib/silcutil/Makefile.ad @@ -68,7 +68,8 @@ libsilcutil_la_SOURCES = \ silcthreadqueue.c \ silcrand.c \ silcglobal.c \ - silcbufferstream.c + silcbufferstream.c \ + silcxml.c include_HEADERS = \ $(SILC_DIST_HEADER) \ @@ -124,7 +125,8 @@ include_HEADERS = \ silcglobal.h \ silcruntime.h \ silcdir.h \ - silcbufferstream.h + silcbufferstream.h \ + silcxml.h SILC_EXTRA_DIST = diff --git a/lib/silcutil/silcruntime.h.in b/lib/silcutil/silcruntime.h.in index 503b3924..2ad645e8 100644 --- a/lib/silcutil/silcruntime.h.in +++ b/lib/silcutil/silcruntime.h.in @@ -198,8 +198,8 @@ extern "C" { #endif /* __SILC_ENABLE_STACKTRACE */ /* SILC Runtime Toolkit includes */ -#include #include +#include #include #include #include @@ -240,6 +240,7 @@ extern "C" { #include #include #include +#include #include #include diff --git a/lib/silcutil/silcxml.c b/lib/silcutil/silcxml.c new file mode 100644 index 00000000..decadb6c --- /dev/null +++ b/lib/silcutil/silcxml.c @@ -0,0 +1,282 @@ +/* + + silcxml.c + + Author: Pekka Riikonen + + Copyright (C) 2008 Pekka Riikonen + + 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; version 2 of the License. + + 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. + +*/ + +#include + +/* XML parser context */ +struct SilcXMLParserStruct { + void *parser; /* Parser implementation */ + SilcXMLParserHandlerStruct handler; /* Handler */ + void *context; /* User context */ + SilcXMLParamsStruct params; /* Parser parameters */ +}; + +#ifdef HAVE_EXPAT_H + +#include + +/* Map expat error to silc_errno */ + +static SilcResult silc_xml_expat_error(XML_Parser parser) +{ + enum XML_Error error = XML_GetErrorCode(parser); + + switch (error) { + case XML_ERROR_NONE: + return SILC_OK; + case XML_ERROR_NO_MEMORY: + return SILC_ERR_OUT_OF_MEMORY; + case XML_ERROR_UNKNOWN_ENCODING: + case XML_ERROR_INCORRECT_ENCODING: + return SILC_ERR_BAD_ENCODING; + case XML_ERROR_ABORTED: + return SILC_ERR_ABORTED; + default: + return SILC_ERR_SYNTAX; + } +} + +/* Return error string */ + +static const char *silc_xml_get_error(SilcXMLParser parser) +{ + return XML_ErrorString(XML_GetErrorCode(parser->parser)); +} + +/* Start element */ + +static void silc_xml_expat_start_element(void *userData, + const XML_Char *name, + const XML_Char **atts) +{ + SilcXMLParser parser = userData; + SilcHashTable t = NULL; + int i; + + if (atts && atts[0]) { + t = silc_hash_table_alloc(NULL, 0, silc_hash_utf8_string, NULL, + silc_hash_utf8_compare, NULL, + NULL, NULL, TRUE); + if (!t) { + silc_set_errno(SILC_ERR_OUT_OF_MEMORY); + silc_set_errno_location(NULL, + XML_GetCurrentLineNumber(parser->parser), + XML_GetCurrentColumnNumber(parser->parser)); + XML_StopParser(parser->parser, FALSE); + return; + } + + for (i = 0; atts[i]; i += 2) + silc_hash_table_add(t, (void *)atts[i], (void *)atts[i + 1]); + } + + if (parser->handler.start_element) + parser->handler.start_element(parser, name, t, parser->context); + + if (t) + silc_hash_table_free(t); +} + +/* End element */ + +static void silc_xml_expat_end_element(void *userData, + const XML_Char *name) +{ + SilcXMLParser parser = userData; + + if (parser->handler.end_element) + parser->handler.end_element(parser, name, parser->context); +} + +/* Characters */ + +static void silc_xml_expat_data(void *userData, + const XML_Char *s, + int len) + +{ + SilcXMLParser parser = userData; + + if (parser->handler.data) + parser->handler.data(parser, (const unsigned char *)s, + (SilcUInt32)len, parser->context); +} + +/* Processing instruction */ + +static void silc_xml_expat_pi(void *userData, + const XML_Char *target, + const XML_Char *data) +{ + SilcXMLParser parser = userData; + + if (parser->handler.pi) + parser->handler.pi(parser, target, data, parser->context); +} + +/* Create parser */ + +SilcXMLParser silc_xml_parser_create(SilcXMLParams params, + SilcXMLParserHandler handler, + void *context) +{ + SilcXMLParser parser; + XML_Parser ep; + + parser = silc_calloc(1, sizeof(*parser)); + if (!parser) + return NULL; + + SILC_LOG_DEBUG(("Allcoated XML parser %p", parser)); + + if (params) + parser->params = *params; + if (handler) + parser->handler = *handler; + parser->context = context; + + /* Allocate expat parser */ + if (parser->params.no_namespace) + ep = XML_ParserCreate("UTF-8"); + else + ep = XML_ParserCreateNS("UTF-8", '\0'); + + if (!ep) { + silc_set_errno(SILC_ERR_OUT_OF_MEMORY); + silc_free(ep); + return NULL; + } + + parser->parser = ep; + + /* Set callbacks */ + XML_SetUserData(ep, parser); + XML_SetElementHandler(ep, silc_xml_expat_start_element, + silc_xml_expat_end_element); + XML_SetCharacterDataHandler(ep, silc_xml_expat_data); + XML_SetProcessingInstructionHandler(ep, silc_xml_expat_pi); + + return parser; +} + +/* Free parser */ + +void silc_xml_parser_free(SilcXMLParser parser) +{ + if (!parser) + return; + + SILC_LOG_DEBUG(("Free XML parser %p", parser)); + + if (parser->parser) + XML_ParserFree(parser->parser); + silc_free(parser); +} + +/* Parse */ + +SilcBool silc_xml_parse(SilcXMLParser parser, + const unsigned char *data, + SilcUInt32 data_len) +{ + int ret; + + SILC_LOG_DEBUG(("Parse XML data with parser %p", parser)); + + if (!parser || !data) { + silc_set_errno(SILC_ERR_INVALID_ARGUMENT); + return FALSE; + } + + /* Parse */ + ret = XML_Parse(parser->parser, (const char *)data, (int)data_len, 1); + if (!ret) { + silc_set_errno_reason(silc_xml_expat_error(parser->parser), + silc_xml_get_error(parser)); + return FALSE; + } + + return TRUE; +} + +/* Parse file */ + +SilcBool silc_xml_parse_file(SilcXMLParser parser, + const char *filename) +{ + unsigned char *data; + SilcUInt32 data_len; + SilcBool ret; + + if (!filename) { + silc_set_errno(SILC_ERR_INVALID_ARGUMENT); + return FALSE; + } + + SILC_LOG_DEBUG(("Parse XML file '%s' with parser %p", filename, parser)); + + data = silc_file_readfile(filename, &data_len, NULL); + if (!data) + return FALSE; + + ret = silc_xml_parse(parser, data, data_len); + if (!ret) { + silc_set_errno_reason(silc_xml_expat_error(parser->parser), + silc_xml_get_error(parser)); + silc_set_errno_location(filename, + XML_GetCurrentLineNumber(parser->parser), + XML_GetCurrentColumnNumber(parser->parser)); + return FALSE; + } + + silc_free(data); + + return ret; +} + +/* Get attribute */ + +const char *silc_xml_get_attribute(SilcXMLParser parser, + SilcHashTable attributes, + const char *name) +{ + char *val; + + if (!attributes) + return NULL; + + if (!silc_hash_table_find(attributes, (void *)name, NULL, (void *)&val)) + return NULL; + + return val; +} + +/* Return current location */ + +void silc_xml_current_location(SilcXMLParser parser, + SilcUInt32 *current_line, + SilcUInt32 *current_column) +{ + if (current_line) + *current_line = XML_GetCurrentLineNumber(parser->parser); + if (current_column) + *current_column = XML_GetCurrentColumnNumber(parser->parser); +} + +#endif /* HAVE_EXPAT_H */ diff --git a/lib/silcutil/silcxml.h b/lib/silcutil/silcxml.h new file mode 100644 index 00000000..e6ccce80 --- /dev/null +++ b/lib/silcutil/silcxml.h @@ -0,0 +1,244 @@ +/* + + silcxml.h + + Author: Pekka Riikonen + + Copyright (C) 2008 Pekka Riikonen + + 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; version 2 of the License. + + 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. + +*/ + +/****h* silcutil/XML Interface + * + * DESCRIPTION + * + * XML parser interface provides simple stream based interface for parsing + * XML data and files. + * + * EXAMPLE + * + * SilcXMLParser parser; + * + * // Create XML parser + * parser = silc_xml_parser_create(NULL, &handler, ctx); + * + * // Parse XML file + * if (!silc_xml_parse_file(parser, filename)) { + * silc_errno_location(NULL, &cur_line, NULL); + * fatal("Error %s:%d: %s", filename, cur_line, silc_errno_reason()); + * } + * + * // Free parser + * silc_xml_parser_free(parser); + * + ***/ + +#ifndef SILCXML_H +#define SILCXML_H + +/****s* silcutil/SilcXMLParser + * + * NAME + * + * typedef struct SilcXMLParserStruct *SilcXMLParser; + * + * DESCRIPTION + * + * The XLM parser context allocated by silc_xml_parser_create. It is + * freed by calling silc_xml_parser_free. + * + ***/ +typedef struct SilcXMLParserStruct *SilcXMLParser; + +/****s* silcutil/SilcXMLParserHandler + * + * NAME + * + * typedef struct SilcXMLParserHandlerObject { ... } + * SilcXMLParserHandler, SilcXMLParserHandlerStruct; + * + * DESCRIPTION + * + * The XML parser handler function callbacks are declared in this + * structure. The structure is given as argument to the + * silc_xml_parser_create. + * + * SOURCE + */ +typedef struct SilcXMLParserHandlerObject { + /* Called at the start of an XML element. The `name' is the element name. + The `attributes' is the element attributes or NULL if there were no + attributes. The `attributes' may be enumerated using the SilcHashTable + API. The silc_xml_get_attribute can be used to retrieve the attribute + values from the `attributes' by their name. */ + void (*start_element)(SilcXMLParser parser, + const char *name, + SilcHashTable attributes, + void *context); + + /* Called and the end of an XML element. The `name' is the element name. */ + void (*end_element)(SilcXMLParser parser, + const char *name, + void *context); + + /* Called to deliver the characters or whatever data is in the element. */ + void (*data)(SilcXMLParser parser, + const unsigned char *data, + SilcUInt32 data_len, + void *context); + + /* Called to deliver a processing instruction. The `target' is the first + word in the processing instruction. The `data' is the rest of the + characters in it skipping all whitespace after the initial word. This + callback may be NULL if it is not needed. */ + void (*pi)(SilcXMLParser parser, + const char *target, + const char *data, + void *context); +} *SilcXMLParserHandler, SilcXMLParserHandlerStruct; +/***/ + +/****s* silcutil/SilcXMLParams + * + * NAME + * + * typedef struct SilcXMLParamsObject { ... } + * *SilcXMLParams, SilcXMLParamsStruct; + * + * DESCRIPTION + * + * The XML parser parameters that can be give as argument to the + * silc_xml_parser_create. + * + * SOURCE + */ +typedef struct SilcXMLParamsObject { + /* Do not process XML namespaces. */ + SilcBool no_namespace; +} *SilcXMLParams, SilcXMLParamsStruct; + +/****f* silcutil/silc_xml_parser_create + * + * SYNOPSIS + * + * SilcXMLParser silc_xml_parser_create(SilcXMLParams params, + * SilcXMLParserHandler handler, + * void *context); + * + * DESCRIPTION + * + * Create XML parser and return in. The `handler' contains the callback + * functions to be called while parsing XML data. The `context' is + * delivered to each callback function. The `params' define parser + * parameters, and may be NULL. The parser parses XML data with UTF-8 + * encoding. All characters delivered to callbacks are in UTF-8 encoding. + * + ***/ +SilcXMLParser silc_xml_parser_create(SilcXMLParams params, + SilcXMLParserHandler handler, + void *context); + +/****f* silcutil/silc_xml_parser_free + * + * SYNOPSIS + * + * void silc_xml_parser_free(SilcXMLParser parser); + * + * DESCRIPTION + * + * Free's XML parser. + * + ***/ +void silc_xml_parser_free(SilcXMLParser parser); + +/****f* silcutil/silc_xml_parse + * + * SYNOPSIS + * + * SilcBool silc_xml_parse(SilcXMLParser parser, + * const unsigned char *data, + * SilcUInt32 data_len); + * + * DESCRIPTION + * + * Parse XML data `data' of length of `data_len' bytes. Returns TRUE + * after the data has been parsed. The handler callback functions set for + * `parser' will be called while parsing the XML data. + * + * Returns FALSE and set silc_errno and silc_errno_reason if error + * occurs. + * + ***/ +SilcBool silc_xml_parse(SilcXMLParser parser, + const unsigned char *data, + SilcUInt32 data_len); + +/****f* silcutil/silc_xml_parse_file + * + * SYNOPSIS + * + * SilcBool silc_xml_parse_file(SilcXMLParser parser, + * const char *filename); + * + * DESCRIPTION + * + * Parse XML file indicated by `filename'. Returns TRUE after the file + * has been parsed. The handler callback functions set for `parser' will + * be called while parsing the XML file. + * + * Returns FALSE and set silc_errno and silc_errno_reason if error + * occurs. The silc_errno_location can be used to retrieve the exact + * location in the file where the error occurred. + * + ***/ +SilcBool silc_xml_parse_file(SilcXMLParser parser, + const char *filename); + +/****f* silcutil/silc_xml_get_attribute + * + * SYNOPSIS + * + * const char *silc_xml_get_attribute(SilcXMLParser parser, + * SilcHashTable attributes, + * const char *name); + * + * DESCRIPTION + * + * Returns the value of the attributes namaed `name' or NULL if no such + * attribute exist in the hash table of `attributes'. + * + ***/ +const char *silc_xml_get_attribute(SilcXMLParser parser, + SilcHashTable attributes, + const char *name); + +/****f* silcutil/silc_xml_get_attribute + * + * SYNOPSIS + * + * void silc_xml_current_location(SilcXMLParser parser, + * SilcUInt32 *current_line, + * SilcUInt32 *current_column); + * + * DESCRIPTION + * + * Return the current location of the parsed XML data. The current line + * number and columns can be returned. This may be used also when an + * error occurs but it is preferred to use silc_errno_location in case + * of error. + * + ***/ +void silc_xml_current_location(SilcXMLParser parser, + SilcUInt32 *current_line, + SilcUInt32 *current_column); + +#endif /* SILCXML_H */ diff --git a/lib/silcutil/tests/Makefile.am b/lib/silcutil/tests/Makefile.am index ceade7d4..bc693704 100644 --- a/lib/silcutil/tests/Makefile.am +++ b/lib/silcutil/tests/Makefile.am @@ -24,7 +24,7 @@ check_PROGRAMS = \ test_silcatomic test_silcmutex test_silctime test_silcthread \ test_silcdll test_silcenv test_silctimer test_silcbitops \ test_silcregex test_silcbuffmt test_silcdir test_silcthreadqueue \ - test_silcrand test_silcglobal test_silcbufferstream + test_silcrand test_silcglobal test_silcbufferstream test_silcxml TESTS = test_silcstrutil test_silcstringprep test_silchashtable \ test_silclist test_silcfsm test_silcasync test_silcschedule \ diff --git a/lib/silcutil/tests/test_silcxml.c b/lib/silcutil/tests/test_silcxml.c new file mode 100644 index 00000000..d24445d4 --- /dev/null +++ b/lib/silcutil/tests/test_silcxml.c @@ -0,0 +1,94 @@ +/* SILC XML tests */ + +#include "silcruntime.h" + +SilcXMLParser parser; +SilcBool success = FALSE; + +static void xml_start_element(SilcXMLParser parser, + const char *name, + SilcHashTable attributes, + void *context) +{ + fprintf(stderr, "<%s", name); + + if (attributes) { + SilcHashTableList htl; + char *att, *val; + + silc_hash_table_list(attributes, &htl); + while (silc_hash_table_get(&htl, (void *)&att, (void *)&val)) + fprintf(stderr, " %s='%s'", att, val); + + silc_hash_table_list_reset(&htl); + } + fprintf(stderr, ">"); +} + +static void xml_end_element(SilcXMLParser parser, + const char *name, + void *context) +{ + fprintf(stderr, "", name); +} + +static void xml_data(SilcXMLParser parser, + const unsigned char *data, + SilcUInt32 data_len, + void *context) +{ + silc_file_write(2, data, data_len); +} + +static void xml_pi(SilcXMLParser parser, + const char *target, + const char *data, + void *context) +{ + fprintf(stderr, "%s %s", target, data); +} + +static SilcXMLParserHandlerStruct handler = +{ + xml_start_element, + xml_end_element, + xml_data, + xml_pi +}; + +int main(int argc, char **argv) +{ + SilcXMLParamsStruct params; + SilcUInt32 cur_line; + char *file; + + silc_runtime_init(); + + if (argc != 2) { + fprintf(stderr, "Usage: test_silcxml \n"); + goto err; + } + + memset(¶ms, 0, sizeof(params)); + parser = silc_xml_parser_create(¶ms, &handler, NULL); + if (!parser) + goto err; + + if (!silc_xml_parse_file(parser, argv[1])) { + silc_errno_location(&file, &cur_line, NULL); + fprintf(stderr, "%s:%d: %s\n", file, cur_line, silc_errno_reason()); + goto err; + } + + silc_xml_parser_free(parser); + + success = TRUE; + + err: + SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE")); + fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE"); + + silc_runtime_uninit(); + + return !success; +} -- 2.24.0