Resync
[CMakeLuaTailorHgBridge.git] / CMakeLua / Utilities / cmxmlrpc / xmlrpc_expat.c
blobd7b6e1980644fb176e20ef4a32dc76066810585b
1 /* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
2 **
3 ** Redistribution and use in source and binary forms, with or without
4 ** modification, are permitted provided that the following conditions
5 ** are met:
6 ** 1. Redistributions of source code must retain the above copyright
7 ** notice, this list of conditions and the following disclaimer.
8 ** 2. Redistributions in binary form must reproduce the above copyright
9 ** notice, this list of conditions and the following disclaimer in the
10 ** documentation and/or other materials provided with the distribution.
11 ** 3. The name of the author may not be used to endorse or promote products
12 ** derived from this software without specific prior written permission.
13 **
14 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 ** SUCH DAMAGE. */
26 #include "xmlrpc_config.h"
28 #include <stddef.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <cm_expat.h>
33 #include "xmlrpc.h"
34 #include "xmlrpc_int.h"
35 #include "xmlrpc_xmlparser.h"
37 /* Define the contents of our internal structure. */
38 struct _xml_element {
39 struct _xml_element *_parent;
40 char *_name;
41 xmlrpc_mem_block _cdata; /* char */
42 xmlrpc_mem_block _children; /* xml_element* */
45 /* Check that we're using expat in UTF-8 mode, not wchar_t mode.
46 ** If you need to use expat in wchar_t mode, write a subroutine to
47 ** copy a wchar_t string to a char string & return an error for
48 ** any non-ASCII characters. Then call this subroutine on all
49 ** XML_Char strings passed to our event handlers before using the
50 ** data. */
51 /* #if sizeof(char) != sizeof(XML_Char)
52 ** #error expat must define XML_Char to be a regular char.
53 ** #endif
56 #define XMLRPC_ASSERT_ELEM_OK(elem) \
57 XMLRPC_ASSERT((elem) != NULL && (elem)->_name != XMLRPC_BAD_POINTER)
60 /*=========================================================================
61 ** xml_element_new
62 **=========================================================================
63 ** Create a new xml_element. This routine isn't exported, because the
64 ** arguments are implementation-dependent.
67 static xml_element *xml_element_new (xmlrpc_env *env, char *name)
69 xml_element *retval;
70 int name_valid, cdata_valid, children_valid;
72 XMLRPC_ASSERT_ENV_OK(env);
73 XMLRPC_ASSERT(name != NULL);
75 /* Set up our error-handling preconditions. */
76 name_valid = cdata_valid = children_valid = 0;
78 /* Allocate our xml_element structure. */
79 retval = (xml_element*) malloc(sizeof(xml_element));
80 XMLRPC_FAIL_IF_NULL(retval, env, XMLRPC_INTERNAL_ERROR,
81 "Couldn't allocate memory for XML element");
83 /* Set our parent field to NULL. */
84 retval->_parent = NULL;
86 /* Copy over the element name. */
87 retval->_name = (char*) malloc(strlen(name) + 1);
88 XMLRPC_FAIL_IF_NULL(retval->_name, env, XMLRPC_INTERNAL_ERROR,
89 "Couldn't allocate memory for XML element");
90 name_valid = 1;
91 strcpy(retval->_name, name);
93 /* Initialize a block to hold our CDATA. */
94 XMLRPC_TYPED_MEM_BLOCK_INIT(char, env, &retval->_cdata, 0);
95 XMLRPC_FAIL_IF_FAULT(env);
96 cdata_valid = 1;
98 /* Initialize a block to hold our child elements. */
99 XMLRPC_TYPED_MEM_BLOCK_INIT(xml_element*, env, &retval->_children, 0);
100 XMLRPC_FAIL_IF_FAULT(env);
101 children_valid = 1;
103 cleanup:
104 if (env->fault_occurred) {
105 if (retval) {
106 if (name_valid)
107 free(retval->_name);
108 if (cdata_valid)
109 xmlrpc_mem_block_clean(&retval->_cdata);
110 if (children_valid)
111 xmlrpc_mem_block_clean(&retval->_children);
112 free(retval);
114 return NULL;
115 } else {
116 return retval;
121 /*=========================================================================
122 ** xml_element_free
123 **=========================================================================
124 ** Blow away an existing element & all of its child elements.
127 void xml_element_free (xml_element *elem)
129 xmlrpc_mem_block *children;
130 int size, i;
131 xml_element **contents;
133 XMLRPC_ASSERT_ELEM_OK(elem);
135 free(elem->_name);
136 elem->_name = XMLRPC_BAD_POINTER;
137 xmlrpc_mem_block_clean(&elem->_cdata);
139 /* Deallocate all of our children recursively. */
140 children = &elem->_children;
141 contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(xml_element*, children);
142 size = (int)XMLRPC_TYPED_MEM_BLOCK_SIZE(xml_element*, children);
143 for (i = 0; i < size; i++)
144 xml_element_free(contents[i]);
146 xmlrpc_mem_block_clean(&elem->_children);
147 free(elem);
151 /*=========================================================================
152 ** Miscellaneous Accessors
153 **=========================================================================
154 ** Return the fields of the xml_element. See the header for more
155 ** documentation on each function works.
158 char *xml_element_name (xml_element *elem)
160 XMLRPC_ASSERT_ELEM_OK(elem);
161 return elem->_name;
164 /* The result of this function is NOT VALID until the end_element handler
165 ** has been called! */
166 size_t xml_element_cdata_size (xml_element *elem)
168 XMLRPC_ASSERT_ELEM_OK(elem);
169 return XMLRPC_TYPED_MEM_BLOCK_SIZE(char, &elem->_cdata) - 1;
172 char *xml_element_cdata (xml_element *elem)
174 XMLRPC_ASSERT_ELEM_OK(elem);
175 return XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &elem->_cdata);
178 size_t xml_element_children_size (xml_element *elem)
180 XMLRPC_ASSERT_ELEM_OK(elem);
181 return XMLRPC_TYPED_MEM_BLOCK_SIZE(xml_element*, &elem->_children);
184 xml_element **xml_element_children (xml_element *elem)
186 XMLRPC_ASSERT_ELEM_OK(elem);
187 return XMLRPC_TYPED_MEM_BLOCK_CONTENTS(xml_element*, &elem->_children);
191 /*=========================================================================
192 ** Internal xml_element Utility Functions
193 **=========================================================================
196 static void xml_element_append_cdata (xmlrpc_env *env,
197 xml_element *elem,
198 char *cdata,
199 size_t size)
201 XMLRPC_ASSERT_ENV_OK(env);
202 XMLRPC_ASSERT_ELEM_OK(elem);
204 XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, &elem->_cdata, cdata, size);
207 /* Whether or not this function succeeds, it takes ownership of the 'child'
208 ** argument.
209 ** WARNING - This is the exact opposite of the usual memory ownership
210 ** rules for xmlrpc_value! So please pay attention. */
211 static void xml_element_append_child (xmlrpc_env *env,
212 xml_element *elem,
213 xml_element *child)
215 XMLRPC_ASSERT_ENV_OK(env);
216 XMLRPC_ASSERT_ELEM_OK(elem);
217 XMLRPC_ASSERT_ELEM_OK(child);
218 XMLRPC_ASSERT(child->_parent == NULL);
220 XMLRPC_TYPED_MEM_BLOCK_APPEND(xml_element*, env, &elem->_children,
221 &child, 1);
222 if (!env->fault_occurred)
223 child->_parent = elem;
224 else
225 xml_element_free(child);
229 /*=========================================================================
230 ** Our parse context. We pass this around as expat user data.
231 **=========================================================================
234 typedef struct {
235 xmlrpc_env *env;
236 xml_element *root;
237 xml_element *current;
238 } parse_context;
241 /*=========================================================================
242 ** Expat Event Handler Functions
243 **=========================================================================
246 static void
247 start_element (void *user_data, XML_Char *name, XML_Char **atts ATTR_UNUSED)
249 parse_context *context;
250 xml_element *elem, *new_current;
252 XMLRPC_ASSERT(user_data != NULL && name != NULL);
254 /* Get our context and see if an error has already occured. */
255 context = (parse_context*) user_data;
256 if (!context->env->fault_occurred) {
258 /* Build a new element. */
259 elem = xml_element_new(context->env, name);
260 XMLRPC_FAIL_IF_FAULT(context->env);
262 /* Insert it in the appropriate place. */
263 if (!context->root) {
264 context->root = elem;
265 context->current = elem;
266 elem = NULL;
267 } else {
268 XMLRPC_ASSERT(context->current != NULL);
270 /* (We need to watch our error handling invariants very carefully
271 ** here. Read the docs for xml_element_append_child. */
272 new_current = elem;
273 xml_element_append_child(context->env, context->current, elem);
274 elem = NULL;
275 XMLRPC_FAIL_IF_FAULT(context->env);
276 context->current = new_current;
279 cleanup:
280 if (elem)
281 xml_element_free(elem);
285 static void end_element (void *user_data, XML_Char *name)
287 parse_context *context;
289 XMLRPC_ASSERT(user_data != NULL && name != NULL);
291 /* Get our context and see if an error has already occured. */
292 context = (parse_context*) user_data;
293 if (!context->env->fault_occurred) {
295 /* XXX - I think expat enforces these facts, but I want to be sure.
296 ** If one of these assertion ever fails, it should be replaced by a
297 ** non-assertion runtime error check. */
298 XMLRPC_ASSERT(strcmp(name, context->current->_name) == 0);
299 XMLRPC_ASSERT(context->current->_parent != NULL ||
300 context->current == context->root);
302 /* Add a trailing '\0' to our cdata. */
303 xml_element_append_cdata(context->env, context->current, "\0", 1);
304 XMLRPC_FAIL_IF_FAULT(context->env);
306 /* Pop our "stack" of elements. */
307 context->current = context->current->_parent;
309 cleanup:
310 return;
314 static void character_data (void *user_data, XML_Char *s, int len)
316 parse_context *context;
318 XMLRPC_ASSERT(user_data != NULL && s != NULL && len >= 0);
320 /* Get our context and see if an error has already occured. */
321 context = (parse_context*) user_data;
322 if (!context->env->fault_occurred) {
324 XMLRPC_ASSERT(context->current != NULL);
326 xml_element_append_cdata(context->env, context->current, s, len);
327 XMLRPC_FAIL_IF_FAULT(context->env);
329 cleanup:
330 return;
335 /*=========================================================================
336 ** Expat Driver
337 **=========================================================================
338 ** XXX - We should allow the user to specify the encoding of our xml_data.
341 xml_element *xml_parse (xmlrpc_env *env, const char *xml_data, int xml_len)
343 parse_context context;
344 XML_Parser parser;
345 int ok;
347 XMLRPC_ASSERT_ENV_OK(env);
348 XMLRPC_ASSERT(xml_data != NULL && xml_len >= 0);
350 /* Set up our error-handling preconditions. */
351 context.root = NULL;
353 /* Set up the rest of our parse context. */
354 context.env = env;
355 context.current = NULL;
357 /* Set up our XML parser. */
358 parser = XML_ParserCreate(NULL);
359 XMLRPC_FAIL_IF_NULL(parser, env, XMLRPC_INTERNAL_ERROR,
360 "Could not create expat parser");
361 XML_SetUserData(parser, &context);
362 XML_SetElementHandler(parser,
363 (XML_StartElementHandler) start_element,
364 (XML_EndElementHandler) end_element);
365 XML_SetCharacterDataHandler(parser,
366 (XML_CharacterDataHandler) character_data);
368 /* Parse our data. */
369 ok = XML_Parse(parser, xml_data, xml_len, 1);
370 if (!ok)
371 XMLRPC_FAIL(env, XMLRPC_PARSE_ERROR,
372 (char*) XML_ErrorString(XML_GetErrorCode(parser)));
373 XMLRPC_FAIL_IF_FAULT(env);
375 /* Perform some sanity checks. */
376 XMLRPC_ASSERT(context.root != NULL);
377 XMLRPC_ASSERT(context.current == NULL);
379 cleanup:
380 if (parser)
381 XML_ParserFree(parser);
383 if (env->fault_occurred) {
384 if (context.root)
385 xml_element_free(context.root);
386 return NULL;
387 } else {
388 return context.root;