From 7f3dd0599ac3fb3a69c512b0ecfd043c67ca94ee Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Thu, 20 Sep 2012 15:52:18 +0300 Subject: Parse returned XML into a grecs tree structure. * lib/xmltree.c: New file. * lib/Makefile.am: Add new file. * lib/libeclat.h: Include expat.h and grecs.h (eclat_partial_tree_t): New typedef. (eclat_partial_tree_create,eclat_partial_tree_destroy) (eclat_partial_tree_finish,eclat_partial_tree_data_handler) (eclat_partial_tree_start_handler) (eclat_partial_tree_end_handler): New protos. * src/eclat.c (main): Initialize XML parser with eclat_partial_tree handlers and bind it to the CURL output handler. * tests/.gitignore: Add txml * tests/Makefile.am (TESTSUITE_AT): Add xml01.at Build txml * tests/testsuite.at: Include xml01.at * tests/txml.c: New file. * tests/xml01.at: New file. --- lib/Makefile.am | 3 +- lib/libeclat.h | 16 ++++- lib/xmltree.c | 188 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/eclat.c | 73 +++++++++++++++++++++ tests/.gitignore | 1 + tests/Makefile.am | 8 ++- tests/testsuite.at | 4 +- tests/txml.c | 88 +++++++++++++++++++++++++ tests/xml01.at | 63 ++++++++++++++++++ 9 files changed, 438 insertions(+), 6 deletions(-) create mode 100644 lib/xmltree.c create mode 100644 tests/txml.c create mode 100644 tests/xml01.at diff --git a/lib/Makefile.am b/lib/Makefile.am index 7843433..2c1d3a8 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -28,7 +28,8 @@ libeclat_a_SOURCES=\ reqsign.c\ sha1.c\ sha1.h\ - urlencode.c + urlencode.c\ + xmltree.c AM_LDFLAGS = $(CURL_LIBS) INCLUDES = -I$(top_srcdir)/grecs/src/ $(CURL_CFLAGS) diff --git a/lib/libeclat.h b/lib/libeclat.h index 59ff0f6..670c51b 100644 --- a/lib/libeclat.h +++ b/lib/libeclat.h @@ -15,6 +15,8 @@ along with Eclat. If not, see . */ #include +#include +#include "grecs.h" void hmac_sha1(const void *text, size_t textlen, const void *key, size_t keylen, @@ -56,4 +58,16 @@ int eclat_query_signature(struct ec2_query *req, char *secret); char *eclat_query_to_url(struct ec2_query *req, char **post_params); void eclat_query_encode(struct ec2_query *q); - + +typedef struct eclat_partial_tree *eclat_partial_tree_t; + +eclat_partial_tree_t eclat_partial_tree_create(void); +void eclat_partial_tree_destroy(eclat_partial_tree_t); +struct grecs_node *eclat_partial_tree_finish(eclat_partial_tree_t); +void eclat_partial_tree_data_handler(void *data, const XML_Char *s, int len); +void eclat_partial_tree_start_handler(void *data, const XML_Char *name, + const XML_Char **atts); +void eclat_partial_tree_end_handler(void *data, const XML_Char *name); + + + diff --git a/lib/xmltree.c b/lib/xmltree.c new file mode 100644 index 0000000..e5d4772 --- /dev/null +++ b/lib/xmltree.c @@ -0,0 +1,188 @@ +/* This file is part of Eclat + Copyright (C) 2012 Sergey Poznyakoff + + Eclat 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 3, or (at your option) + any later version. + + Eclat 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 Eclat. If not, see . */ + +#include +#include "libeclat.h" + +#define NODEREF_TEXT 0x01 /* Text is being constructed */ + +struct eclat_partial_tree { + int flags; + struct grecs_txtacc *acc; + struct grecs_symtab *texttab; + struct grecs_list *stack; /* Node stack */ +}; + +static void +pt_free_node(void *ptr) +{ + struct grecs_node *node = ptr; + grecs_node_free(node); +} + +struct eclat_partial_tree * +eclat_partial_tree_create() +{ + struct eclat_partial_tree *pt = grecs_zalloc(sizeof(*pt)); + pt->acc = grecs_txtacc_create(); + pt->stack = grecs_list_create(); + pt->stack->free_entry = pt_free_node; + return pt; +} + +void +eclat_partial_tree_destroy(struct eclat_partial_tree *pt) +{ + grecs_txtacc_free(pt->acc); + if (pt->texttab) + grecs_symtab_free(pt->texttab); + grecs_list_free(pt->stack); + free(pt); +} + +struct grecs_node * +eclat_partial_tree_finish(struct eclat_partial_tree *pt) +{ + struct grecs_node *root = grecs_node_create(grecs_node_root, NULL); + root->v.texttab = pt->texttab; + pt->texttab = NULL; + grecs_node_bind(root, grecs_list_pop(pt->stack), 1); + eclat_partial_tree_destroy(pt); + return root; +} + + +static char * +install_text(struct eclat_partial_tree *nref, const char *str) +{ + struct grecs_syment key; + struct grecs_syment *ent; + int install = 1; + + if (!nref->texttab) { + nref->texttab = grecs_symtab_create_default( + sizeof(struct grecs_syment)); + if (!nref->texttab) + grecs_alloc_die(); + } + + key.name = (char*) str; + ent = grecs_symtab_lookup_or_install(nref->texttab, &key, &install); + if (!ent) + grecs_alloc_die(); + return ent->name; +} + +static struct grecs_node * +gettos(struct eclat_partial_tree *nref) +{ + if (nref->stack->head) + return nref->stack->head->data; + return NULL; +} + +void +eclat_partial_tree_data_handler(void *data, const XML_Char *s, int len) +{ + struct eclat_partial_tree *noderef = data; + + grecs_txtacc_grow(noderef->acc, s, len); + noderef->flags |= NODEREF_TEXT; +} + +/* Start handler: create a node and push it on stack */ +void +eclat_partial_tree_start_handler(void *data, const XML_Char *name, + const XML_Char **atts) +{ + struct eclat_partial_tree *noderef = data; + struct grecs_node *node; + + if (noderef->flags & NODEREF_TEXT) { + grecs_txtacc_free_string(noderef->acc, + grecs_txtacc_finish(noderef->acc, 0)); + noderef->flags &= ~NODEREF_TEXT; + } + + node = grecs_node_create((enum grecs_node_type)-1, NULL); + node->ident = grecs_strdup(name); + grecs_list_push(noderef->stack, node); + noderef->flags &= ~NODEREF_TEXT; +} + +void +eclat_partial_tree_end_handler(void *data, const XML_Char *name) +{ + struct eclat_partial_tree *noderef = data; + struct grecs_node *node = grecs_list_pop(noderef->stack); + struct grecs_node *tos = gettos(noderef); + + if (noderef->flags & NODEREF_TEXT) { + char *s, *p; + grecs_value_t *ptr; + + grecs_txtacc_grow_char(noderef->acc, 0); + s = grecs_txtacc_finish(noderef->acc, 1); + + for (p = s; *p && isspace(*p); p++) + ; + if (!*p) { + free(s); + if (node->type == -1) + node->type = grecs_node_block; + } else { + switch (node->type) { + default: + node->type = grecs_node_stmt; + ptr = grecs_malloc(sizeof(*ptr)); + ptr->type = GRECS_TYPE_STRING; + ptr->v.string = s; + node->v.value = ptr; + break; + + case grecs_node_root: + abort(); /* should not happen */ + + case grecs_node_stmt: + case grecs_node_block: + /* FIXME: error message */ + /* ignoring additional textual data */ + free(s); + } + } + noderef->flags &= ~NODEREF_TEXT; + } + + if (!tos) + grecs_list_push(noderef->stack, node); + else { + switch (tos->type) { + default: + tos->type = grecs_node_block; + break; + case grecs_node_root: + abort(); /* should not happen */ + case grecs_node_stmt: + /* FIXME: error message */ + /* ignoring additional textual data */ + grecs_value_free(tos->v.value); + tos->type = grecs_node_block; + break; + } + + grecs_node_bind(tos, node, 1); + } +} diff --git a/src/eclat.c b/src/eclat.c index c5994fe..3703376 100644 --- a/src/eclat.c +++ b/src/eclat.c @@ -169,7 +169,52 @@ eclat_trace_fun(CURL *handle, curl_infotype type, dump(text, stderr, (unsigned char *)data, size); return 0; } + +static void +dump_text(FILE *stream, int line, int column, const char *text, size_t len) +{ + fprintf(stream, "%02d:%02d: ", line, column); + while (len--) { + char c = *text++; + if (c == '\r') + continue; + fputc(c, stream); + ++column; + if (c == '\n') { + ++line; + column = 0; + fprintf(stream, "%02d:%02d: ", line, column); + } + } + fputc('\n', stream); +} +static size_t +write_callback(void *ptr, size_t size, size_t nmemb, void *data) +{ + size_t realsize = size * nmemb; + XML_Parser parser = data; + enum XML_Status status; + int line = XML_GetCurrentLineNumber(parser); + int column = XML_GetCurrentColumnNumber(parser); + + /* FIXME: Debugging level. */ + if (debug_level[ECLAT_DEBCAT_MAIN] > 10) { + dump_text(stderr, line, column, ptr, realsize); + } + status = XML_Parse(parser, ptr, realsize, 0); + if (status == XML_STATUS_ERROR) { + enum XML_Error error = XML_GetErrorCode(parser); + + line = XML_GetCurrentLineNumber(parser); + column = XML_GetCurrentColumnNumber(parser); + + /* FIXME: better diagnostics. */ + die(EX_SOFTWARE, "XML parse error at %d:%d: %s", + line, column, XML_ErrorString(error)); + } + return realsize; +} #include "cmdline.h" @@ -187,6 +232,9 @@ main(int argc, char **argv) int index, rc; struct grecs_node *tree; CURL *curl; + XML_Parser parser; + eclat_partial_tree_t part; + struct grecs_node *xmltree; set_program_name(argv[0]); config_init(); @@ -250,8 +298,33 @@ main(int argc, char **argv) curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, eclat_trace_fun); } + + + /* Create XML parser */ + parser = XML_ParserCreate("UTF-8"); + if (!parser) + die(EX_SOFTWARE, "cannot create XML parser"); + + XML_SetElementHandler(parser, + eclat_partial_tree_start_handler, + eclat_partial_tree_end_handler); + XML_SetCharacterDataHandler(parser, + eclat_partial_tree_data_handler); + part = eclat_partial_tree_create(); + XML_SetUserData(parser, part); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, parser); rc = handler_tab[eclat_command](curl, argc, argv); curl_easy_cleanup(curl); + XML_Parse(parser, "", 0, 1); + + xmltree = eclat_partial_tree_finish(part); + + grecs_print_node(xmltree, GRECS_NODE_FLAG_DEFAULT, stdout); + fputc('\n', stdout); + + exit(rc); } diff --git a/tests/.gitignore b/tests/.gitignore index 9a2f830..99d3c9c 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -6,3 +6,4 @@ testsuite.dir testsuite.log thmac turlenc +txml diff --git a/tests/Makefile.am b/tests/Makefile.am index 6d95607..82cae7e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -44,7 +44,8 @@ TESTSUITE_AT = \ hmac02.at\ hmac03.at\ testsuite.at\ - urlenc01.at + urlenc01.at\ + xml01.at TESTSUITE = $(srcdir)/testsuite M4=m4 @@ -69,10 +70,11 @@ check-local: atconfig atlocal $(TESTSUITE) noinst_PROGRAMS = \ thmac\ - turlenc + turlenc\ + txml LDADD = ../lib/libeclat.a ../grecs/src/libgrecs.a -INCLUDES = -I$(top_srcdir)/lib +INCLUDES = -I$(top_srcdir)/grecs/src/ -I$(top_srcdir)/lib diff --git a/tests/testsuite.at b/tests/testsuite.at index c368490..f40b8a7 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -24,7 +24,9 @@ m4_include(hmac01.at) m4_include(hmac02.at) m4_include(hmac03.at) -AT_BANNER([URL encode]) +AT_BANNER([URL Encode]) m4_include([urlenc01.at]) +AT_BANNER([XML Processing]) +m4_include([xml01.at]) # End of testsuite.at diff --git a/tests/txml.c b/tests/txml.c new file mode 100644 index 0000000..e00abc2 --- /dev/null +++ b/tests/txml.c @@ -0,0 +1,88 @@ +/* This file is part of Eclat. + Copyright (C) 2012 Sergey Poznyakoff. + + Eclat 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 3, or (at your option) + any later version. + + Eclat 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 Eclat. If not, see . */ + +#include +#include +#include +#include +#include "libeclat.h" + +int +main(int argc, char **argv) +{ + XML_Parser parser; + eclat_partial_tree_t part; + FILE *fp; + char buffer[128]; + size_t size; + const char *filename; + struct grecs_node *tree; + + if (argc > 2) { + fprintf(stderr, "usage: %s [string]\n", argv[0]); + return 1; + } + + if (argc == 1 || strcmp(argv[1], "-") == 0) { + fp = stdin; + filename = ""; + } else { + filename = argv[1]; + fp = fopen(filename, "r"); + if (!fp) { + perror(filename); + return 1; + } + } + + parser = XML_ParserCreate("UTF-8"); + if (!parser) { + fprintf(stderr, "cannot create XML parser\n"); + return 1; + } + + XML_SetElementHandler(parser, + eclat_partial_tree_start_handler, + eclat_partial_tree_end_handler); + XML_SetCharacterDataHandler(parser, + eclat_partial_tree_data_handler); + part = eclat_partial_tree_create(); + XML_SetUserData(parser, part); + + while ((size = fread(buffer, 1, sizeof(buffer), fp)) > 0) { + enum XML_Status status; + + status = XML_Parse(parser, buffer, size, 0); + if (status == XML_STATUS_ERROR) { + enum XML_Error error = XML_GetErrorCode(parser); + int line = XML_GetCurrentLineNumber(parser); + int column = XML_GetCurrentColumnNumber(parser); + + fprintf(stderr, "%s:%d:%d: %s\n", + filename, line, column, XML_ErrorString(error)); + return 1; + } + } + XML_Parse(parser, "", 0, 1); + + tree = eclat_partial_tree_finish(part); + + grecs_print_node(tree, GRECS_NODE_FLAG_DEFAULT, stdout); + fputc('\n', stdout); + grecs_tree_free(tree); + + return 0; +} diff --git a/tests/xml01.at b/tests/xml01.at new file mode 100644 index 0000000..01d7df4 --- /dev/null +++ b/tests/xml01.at @@ -0,0 +1,63 @@ +# This file is part of Eclat -*- Autotest -*- +# Copyright (C) 2012 Sergey Poznyakoff +# +# Eclat 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 3, or (at your option) +# any later version. +# +# Eclat 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 Eclat. If not, see . + +AT_SETUP([xml01]) +AT_KEYWORDS([xml xml01]) + +AT_DATA([input],[ + + beefdead-57rt-ffea-e4ac-deadbeefbeef + + + i-00000000 + instance + foo + bar + + + i-11111111 + instance + baz + quux + + + i-22222222 + instance + Name + test + + + +]) + +AT_CHECK([txml input], +[0], +[.DescribeTagsResponse.requestId: "beefdead-57rt-ffea-e4ac-deadbeefbeef" +.DescribeTagsResponse.tagSet.item.resourceId: "i-00000000" +.DescribeTagsResponse.tagSet.item.resourceType: "instance" +.DescribeTagsResponse.tagSet.item.key: "foo" +.DescribeTagsResponse.tagSet.item.value: "bar" +.DescribeTagsResponse.tagSet.item.resourceId: "i-11111111" +.DescribeTagsResponse.tagSet.item.resourceType: "instance" +.DescribeTagsResponse.tagSet.item.key: "baz" +.DescribeTagsResponse.tagSet.item.value: "quux" +.DescribeTagsResponse.tagSet.item.resourceId: "i-22222222" +.DescribeTagsResponse.tagSet.item.resourceType: "instance" +.DescribeTagsResponse.tagSet.item.key: "Name" +.DescribeTagsResponse.tagSet.item.value: "test" +]) + +AT_CLEANUP -- cgit v1.2.1