aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2012-09-20 15:52:18 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2012-09-20 15:52:18 +0300
commit7f3dd0599ac3fb3a69c512b0ecfd043c67ca94ee (patch)
tree422bda7fdce2709ea0b4c67c42e6f9a7cecc4918
parent205d53a8e930d3fd126075ab083d316cc344ebaf (diff)
downloadeclat-7f3dd0599ac3fb3a69c512b0ecfd043c67ca94ee.tar.gz
eclat-7f3dd0599ac3fb3a69c512b0ecfd043c67ca94ee.tar.bz2
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.
-rw-r--r--lib/Makefile.am3
-rw-r--r--lib/libeclat.h16
-rw-r--r--lib/xmltree.c188
-rw-r--r--src/eclat.c73
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/Makefile.am8
-rw-r--r--tests/testsuite.at4
-rw-r--r--tests/txml.c88
-rw-r--r--tests/xml01.at63
9 files changed, 438 insertions, 6 deletions
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 <http://www.gnu.org/licenses/>. */
#include <stddef.h>
+#include <expat.h>
+#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 <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#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 <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#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 = "<stdin>";
+ } 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 <http://www.gnu.org/licenses/>.
+
+AT_SETUP([xml01])
+AT_KEYWORDS([xml xml01])
+
+AT_DATA([input],[<?xml version="1.0" encoding="UTF-8"?>
+<DescribeTagsResponse xmlns="http://ec2.amazonaws.com/doc/2012-08-15/">
+ <requestId>beefdead-57rt-ffea-e4ac-deadbeefbeef</requestId>
+ <tagSet>
+ <item>
+ <resourceId>i-00000000</resourceId>
+ <resourceType>instance</resourceType>
+ <key>foo</key>
+ <value>bar</value>
+ </item>
+ <item>
+ <resourceId>i-11111111</resourceId>
+ <resourceType>instance</resourceType>
+ <key>baz</key>
+ <value>quux</value>
+ </item>
+ <item>
+ <resourceId>i-22222222</resourceId>
+ <resourceType>instance</resourceType>
+ <key>Name</key>
+ <value>test</value>
+ </item>
+ </tagSet>
+</DescribeTagsResponse>
+])
+
+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

Return to:

Send suggestions and report system problems to the System administrator.