Annotation of embedaddon/php/ext/xmlrpc/libxmlrpc/xml_element.c, revision 1.1
1.1 ! misho 1: /*
! 2: This file is part of libXMLRPC - a C library for xml-encoded function calls.
! 3:
! 4: Author: Dan Libby (dan@libby.com)
! 5: Epinions.com may be contacted at feedback@epinions-inc.com
! 6: */
! 7:
! 8: /*
! 9: Copyright 2000 Epinions, Inc.
! 10:
! 11: Subject to the following 3 conditions, Epinions, Inc. permits you, free
! 12: of charge, to (a) use, copy, distribute, modify, perform and display this
! 13: software and associated documentation files (the "Software"), and (b)
! 14: permit others to whom the Software is furnished to do so as well.
! 15:
! 16: 1) The above copyright notice and this permission notice shall be included
! 17: without modification in all copies or substantial portions of the
! 18: Software.
! 19:
! 20: 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF
! 21: ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY
! 22: IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR
! 23: PURPOSE OR NONINFRINGEMENT.
! 24:
! 25: 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT,
! 26: SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
! 27: OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING
! 28: NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH
! 29: DAMAGES.
! 30:
! 31: */
! 32:
! 33:
! 34: static const char rcsid[] = "#(@) $Id: xml_element.c 271367 2008-12-17 00:30:27Z iliaa $";
! 35:
! 36:
! 37:
! 38: /****h* ABOUT/xml_element
! 39: * NAME
! 40: * xml_element
! 41: * AUTHOR
! 42: * Dan Libby, aka danda (dan@libby.com)
! 43: * CREATION DATE
! 44: * 06/2000
! 45: * HISTORY
! 46: * $Log$
! 47: * Revision 1.9.4.1.2.1 2008/12/09 17:22:12 iliaa
! 48: *
! 49: * MFH: Fixed bug #46746 (xmlrpc_decode_request outputs non-suppressable error
! 50: * when given bad data).
! 51: *
! 52: * Revision 1.9.4.1 2006/07/30 11:34:02 tony2001
! 53: * MFH: fix compile warnings (#38257)
! 54: *
! 55: * Revision 1.9 2005/04/22 11:06:53 jorton
! 56: * Fixed bug #32797 (invalid C code in xmlrpc extension).
! 57: *
! 58: * Revision 1.8 2005/03/28 00:07:24 edink
! 59: * Reshufle includes to make it compile on windows
! 60: *
! 61: * Revision 1.7 2005/03/26 03:13:58 sniper
! 62: * - Made it possible to build ext/xmlrpc with libxml2
! 63: *
! 64: * Revision 1.6 2004/06/01 20:16:06 iliaa
! 65: * Fixed bug #28597 (xmlrpc_encode_request() incorrectly encodes chars in
! 66: * 200-210 range).
! 67: * Patch by: fernando dot nemec at folha dot com dot br
! 68: *
! 69: * Revision 1.5 2003/12/16 21:00:21 sniper
! 70: * Fix some compile warnings (patch by Joe Orton)
! 71: *
! 72: * Revision 1.4 2002/11/26 23:01:16 fmk
! 73: * removing unused variables
! 74: *
! 75: * Revision 1.3 2002/07/05 04:43:53 danda
! 76: * merged in updates from SF project. bring php repository up to date with xmlrpc-epi version 0.51
! 77: *
! 78: * Revision 1.9 2002/07/03 20:54:30 danda
! 79: * root element should not have a parent. patch from anon SF user
! 80: *
! 81: * Revision 1.8 2002/05/23 17:46:51 danda
! 82: * patch from mukund - fix non utf-8 encoding conversions
! 83: *
! 84: * Revision 1.7 2002/02/13 20:58:50 danda
! 85: * patch to make source more windows friendly, contributed by Jeff Lawson
! 86: *
! 87: * Revision 1.6 2002/01/08 01:06:55 danda
! 88: * enable <?xml version="1.0"?> format for parsers that are very picky.
! 89: *
! 90: * Revision 1.5 2001/09/29 21:58:05 danda
! 91: * adding cvs log to history section
! 92: *
! 93: * 10/15/2000 -- danda -- adding robodoc documentation
! 94: * TODO
! 95: * Nicer external API. Get rid of macros. Make opaque types, etc.
! 96: * PORTABILITY
! 97: * Coded on RedHat Linux 6.2. Builds on Solaris x86. Should build on just
! 98: * about anything with minor mods.
! 99: * NOTES
! 100: * This code incorporates ideas from expat-ensor from http://xml.ensor.org.
! 101: *
! 102: * It was coded primarily to act as a go-between for expat and xmlrpc. To this
! 103: * end, it stores xml elements, their sub-elements, and their attributes in an
! 104: * in-memory tree. When expat is done parsing, the tree can be walked, thus
! 105: * retrieving the values. The code can also be used to build a tree via API then
! 106: * write out the tree to a buffer, thus "serializing" the xml.
! 107: *
! 108: * It turns out this is useful for other purposes, such as parsing config files.
! 109: * YMMV.
! 110: *
! 111: * Some Features:
! 112: * - output option for xml escaping data. Choices include no escaping, entity escaping,
! 113: * or CDATA sections.
! 114: * - output option for character encoding. Defaults to (none) utf-8.
! 115: * - output option for verbosity/readability. ultra-compact, newlines, pretty/level indented.
! 116: *
! 117: * BUGS
! 118: * there must be some.
! 119: ******/
! 120:
! 121: #include "ext/xml/expat_compat.h"
! 122: #ifdef _WIN32
! 123: #include "xmlrpc_win32.h"
! 124: #endif
! 125: #include <stdlib.h>
! 126: #include <string.h>
! 127: #include <ctype.h>
! 128:
! 129: #include "xml_element.h"
! 130: #include "queue.h"
! 131: #include "encodings.h"
! 132:
! 133: #define my_free(thing) if(thing) {free(thing); thing = NULL;}
! 134:
! 135: #define XML_DECL_START "<?xml"
! 136: #define XML_DECL_START_LEN sizeof(XML_DECL_START) - 1
! 137: #define XML_DECL_VERSION "version=\"1.0\""
! 138: #define XML_DECL_VERSION_LEN sizeof(XML_DECL_VERSION) - 1
! 139: #define XML_DECL_ENCODING_ATTR "encoding"
! 140: #define XML_DECL_ENCODING_ATTR_LEN sizeof(XML_DECL_ENCODING_ATTR) - 1
! 141: #define XML_DECL_ENCODING_DEFAULT "utf-8"
! 142: #define XML_DECL_ENCODING_DEFAULT_LEN sizeof(XML_DECL_ENCODING_DEFAULT) - 1
! 143: #define XML_DECL_END "?>"
! 144: #define XML_DECL_END_LEN sizeof(XML_DECL_END) - 1
! 145: #define START_TOKEN_BEGIN "<"
! 146: #define START_TOKEN_BEGIN_LEN sizeof(START_TOKEN_BEGIN) - 1
! 147: #define START_TOKEN_END ">"
! 148: #define START_TOKEN_END_LEN sizeof(START_TOKEN_END) - 1
! 149: #define EMPTY_START_TOKEN_END "/>"
! 150: #define EMPTY_START_TOKEN_END_LEN sizeof(EMPTY_START_TOKEN_END) - 1
! 151: #define END_TOKEN_BEGIN "</"
! 152: #define END_TOKEN_BEGIN_LEN sizeof(END_TOKEN_BEGIN) - 1
! 153: #define END_TOKEN_END ">"
! 154: #define END_TOKEN_END_LEN sizeof(END_TOKEN_END) - 1
! 155: #define ATTR_DELIMITER "\""
! 156: #define ATTR_DELIMITER_LEN sizeof(ATTR_DELIMITER) - 1
! 157: #define CDATA_BEGIN "<![CDATA["
! 158: #define CDATA_BEGIN_LEN sizeof(CDATA_BEGIN) - 1
! 159: #define CDATA_END "]]>"
! 160: #define CDATA_END_LEN sizeof(CDATA_END) - 1
! 161: #define EQUALS "="
! 162: #define EQUALS_LEN sizeof(EQUALS) - 1
! 163: #define WHITESPACE " "
! 164: #define WHITESPACE_LEN sizeof(WHITESPACE) - 1
! 165: #define NEWLINE "\n"
! 166: #define NEWLINE_LEN sizeof(NEWLINE) - 1
! 167: #define MAX_VAL_BUF 144
! 168: #define SCALAR_STR "SCALAR"
! 169: #define SCALAR_STR_LEN sizeof(SCALAR_STR) - 1
! 170: #define VECTOR_STR "VECTOR"
! 171: #define VECTOR_STR_LEN sizeof(VECTOR_STR) - 1
! 172: #define RESPONSE_STR "RESPONSE"
! 173: #define RESPONSE_STR_LEN sizeof(RESPONSE_STR) - 1
! 174:
! 175:
! 176: /*-----------------------------
! 177: - Begin xml_element Functions -
! 178: -----------------------------*/
! 179:
! 180: /****f* xml_element/xml_elem_free_non_recurse
! 181: * NAME
! 182: * xml_elem_free_non_recurse
! 183: * SYNOPSIS
! 184: * void xml_elem_free_non_recurse(xml_element* root)
! 185: * FUNCTION
! 186: * free a single xml element. child elements will not be freed.
! 187: * INPUTS
! 188: * root - the element to free
! 189: * RESULT
! 190: * void
! 191: * NOTES
! 192: * SEE ALSO
! 193: * xml_elem_free ()
! 194: * xml_elem_new ()
! 195: * SOURCE
! 196: */
! 197: void xml_elem_free_non_recurse(xml_element* root) {
! 198: if(root) {
! 199: xml_element_attr* attrs = Q_Head(&root->attrs);
! 200: while(attrs) {
! 201: my_free(attrs->key);
! 202: my_free(attrs->val);
! 203: my_free(attrs);
! 204: attrs = Q_Next(&root->attrs);
! 205: }
! 206:
! 207: Q_Destroy(&root->children);
! 208: Q_Destroy(&root->attrs);
! 209: if(root->name) {
! 210: free((char *)root->name);
! 211: root->name = NULL;
! 212: }
! 213: simplestring_free(&root->text);
! 214: my_free(root);
! 215: }
! 216: }
! 217: /******/
! 218:
! 219: /****f* xml_element/xml_elem_free
! 220: * NAME
! 221: * xml_elem_free
! 222: * SYNOPSIS
! 223: * void xml_elem_free(xml_element* root)
! 224: * FUNCTION
! 225: * free an xml element and all of its child elements
! 226: * INPUTS
! 227: * root - the root of an xml tree you would like to free
! 228: * RESULT
! 229: * void
! 230: * NOTES
! 231: * SEE ALSO
! 232: * xml_elem_free_non_recurse ()
! 233: * xml_elem_new ()
! 234: * SOURCE
! 235: */
! 236: void xml_elem_free(xml_element* root) {
! 237: if(root) {
! 238: xml_element* kids = Q_Head(&root->children);
! 239: while(kids) {
! 240: xml_elem_free(kids);
! 241: kids = Q_Next(&root->children);
! 242: }
! 243: xml_elem_free_non_recurse(root);
! 244: }
! 245: }
! 246: /******/
! 247:
! 248: /****f* xml_element/xml_elem_new
! 249: * NAME
! 250: * xml_elem_new
! 251: * SYNOPSIS
! 252: * xml_element* xml_elem_new()
! 253: * FUNCTION
! 254: * allocates and initializes a new xml_element
! 255: * INPUTS
! 256: * none
! 257: * RESULT
! 258: * xml_element* or NULL. NULL indicates an out-of-memory condition.
! 259: * NOTES
! 260: * SEE ALSO
! 261: * xml_elem_free ()
! 262: * xml_elem_free_non_recurse ()
! 263: * SOURCE
! 264: */
! 265: xml_element* xml_elem_new() {
! 266: xml_element* elem = calloc(1, sizeof(xml_element));
! 267: if(elem) {
! 268: Q_Init(&elem->children);
! 269: Q_Init(&elem->attrs);
! 270: simplestring_init(&elem->text);
! 271:
! 272: /* init empty string in case we don't find any char data */
! 273: simplestring_addn(&elem->text, "", 0);
! 274: }
! 275: return elem;
! 276: }
! 277: /******/
! 278:
! 279: static int xml_elem_writefunc(int (*fptr)(void *data, const char *text, int size), const char *text, void *data, int len)
! 280: {
! 281: return fptr && text ? fptr(data, text, len ? len : strlen(text)) : 0;
! 282: }
! 283:
! 284:
! 285:
! 286: static int create_xml_escape(char *pString, unsigned char c)
! 287: {
! 288: int counter = 0;
! 289:
! 290: pString[counter++] = '&';
! 291: pString[counter++] = '#';
! 292: if(c >= 100) {
! 293: pString[counter++] = c / 100 + '0';
! 294: c = c % 100;
! 295: }
! 296: pString[counter++] = c / 10 + '0';
! 297: c = c % 10;
! 298:
! 299: pString[counter++] = c + '0';
! 300: pString[counter++] = ';';
! 301: return counter;
! 302: }
! 303:
! 304: #define non_ascii(c) (c > 127)
! 305: #define non_print(c) (!isprint(c))
! 306: #define markup(c) (c == '&' || c == '\"' || c == '>' || c == '<')
! 307: #define entity_length(c) ( (c >= 100) ? 3 : ((c >= 10) ? 2 : 1) ) + 3; /* "&#" + c + ";" */
! 308:
! 309: /*
! 310: * xml_elem_entity_escape
! 311: *
! 312: * Purpose:
! 313: * escape reserved xml chars and non utf-8 chars as xml entities
! 314: * Comments:
! 315: * The return value may be a new string, or null if no
! 316: * conversion was performed. In the latter case, *newlen will
! 317: * be 0.
! 318: * Flags (to escape)
! 319: * xml_elem_no_escaping = 0x000,
! 320: * xml_elem_entity_escaping = 0x002, // escape xml special chars as entities
! 321: * xml_elem_non_ascii_escaping = 0x008, // escape chars above 127
! 322: * xml_elem_cdata_escaping = 0x010, // wrap in cdata
! 323: */
! 324: static char* xml_elem_entity_escape(const char* buf, int old_len, int *newlen, XML_ELEM_ESCAPING flags) {
! 325: char *pRetval = 0;
! 326: int iNewBufLen=0;
! 327:
! 328: #define should_escape(c, flag) ( ((flag & xml_elem_markup_escaping) && markup(c)) || \
! 329: ((flag & xml_elem_non_ascii_escaping) && non_ascii(c)) || \
! 330: ((flag & xml_elem_non_print_escaping) && non_print(c)) )
! 331:
! 332: if(buf && *buf) {
! 333: const unsigned char *bufcopy;
! 334: char *NewBuffer;
! 335: int ToBeXmlEscaped=0;
! 336: int iLength;
! 337: bufcopy = buf;
! 338: iLength= old_len ? old_len : strlen(buf);
! 339: while(*bufcopy) {
! 340: if( should_escape(*bufcopy, flags) ) {
! 341: /* the length will increase by length of xml escape - the character length */
! 342: iLength += entity_length(*bufcopy);
! 343: ToBeXmlEscaped=1;
! 344: }
! 345: bufcopy++;
! 346: }
! 347:
! 348: if(ToBeXmlEscaped) {
! 349:
! 350: NewBuffer= malloc(iLength+1);
! 351: if(NewBuffer) {
! 352: bufcopy=buf;
! 353: while(*bufcopy) {
! 354: if(should_escape(*bufcopy, flags)) {
! 355: iNewBufLen += create_xml_escape(NewBuffer+iNewBufLen,*bufcopy);
! 356: }
! 357: else {
! 358: NewBuffer[iNewBufLen++]=*bufcopy;
! 359: }
! 360: bufcopy++;
! 361: }
! 362: NewBuffer[iNewBufLen] = 0;
! 363: pRetval = NewBuffer;
! 364: }
! 365: }
! 366: }
! 367:
! 368: if(newlen) {
! 369: *newlen = iNewBufLen;
! 370: }
! 371:
! 372: return pRetval;
! 373: }
! 374:
! 375:
! 376: static void xml_element_serialize(xml_element *el, int (*fptr)(void *data, const char *text, int size), void *data, XML_ELEM_OUTPUT_OPTIONS options, int depth)
! 377: {
! 378: int i;
! 379: static STRUCT_XML_ELEM_OUTPUT_OPTIONS default_opts = {xml_elem_pretty, xml_elem_markup_escaping | xml_elem_non_print_escaping, XML_DECL_ENCODING_DEFAULT};
! 380: static char whitespace[] = " "
! 381: " "
! 382: " ";
! 383: depth++;
! 384:
! 385: if(!el) {
! 386: /* fprintf(stderr, "Nothing to write\n"); */
! 387: return;
! 388: }
! 389: if(!options) {
! 390: options = &default_opts;
! 391: }
! 392:
! 393: /* print xml declaration if at root level */
! 394: if(depth == 1) {
! 395: xml_elem_writefunc(fptr, XML_DECL_START, data, XML_DECL_START_LEN);
! 396: xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
! 397: xml_elem_writefunc(fptr, XML_DECL_VERSION, data, XML_DECL_VERSION_LEN);
! 398: if(options->encoding && *options->encoding) {
! 399: xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
! 400: xml_elem_writefunc(fptr, XML_DECL_ENCODING_ATTR, data, XML_DECL_ENCODING_ATTR_LEN);
! 401: xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN);
! 402: xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
! 403: xml_elem_writefunc(fptr, options->encoding, data, 0);
! 404: xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
! 405: }
! 406: xml_elem_writefunc(fptr, XML_DECL_END, data, XML_DECL_END_LEN);
! 407: if(options->verbosity != xml_elem_no_white_space) {
! 408: xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN);
! 409: }
! 410: }
! 411:
! 412: if(options->verbosity == xml_elem_pretty && depth > 2) {
! 413: xml_elem_writefunc(fptr, whitespace, data, depth - 2);
! 414: }
! 415: /* begin element */
! 416: xml_elem_writefunc(fptr,START_TOKEN_BEGIN, data, START_TOKEN_BEGIN_LEN);
! 417: if(el->name) {
! 418: xml_elem_writefunc(fptr, el->name, data, 0);
! 419:
! 420: /* write attrs, if any */
! 421: if(Q_Size(&el->attrs)) {
! 422: xml_element_attr* iter = Q_Head(&el->attrs);
! 423: while( iter ) {
! 424: xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
! 425: xml_elem_writefunc(fptr, iter->key, data, 0);
! 426: xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN);
! 427: xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
! 428: xml_elem_writefunc(fptr, iter->val, data, 0);
! 429: xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
! 430:
! 431: iter = Q_Next(&el->attrs);
! 432: }
! 433: }
! 434: }
! 435: else {
! 436: xml_elem_writefunc(fptr, "None", data, 0);
! 437: }
! 438: /* if no text and no children, use abbreviated form, eg: <foo/> */
! 439: if(!el->text.len && !Q_Size(&el->children)) {
! 440: xml_elem_writefunc(fptr, EMPTY_START_TOKEN_END, data, EMPTY_START_TOKEN_END_LEN);
! 441: }
! 442: /* otherwise, print element contents */
! 443: else {
! 444: xml_elem_writefunc(fptr, START_TOKEN_END, data, START_TOKEN_END_LEN);
! 445:
! 446: /* print text, if any */
! 447: if(el->text.len) {
! 448: char* escaped_str = el->text.str;
! 449: int buflen = el->text.len;
! 450:
! 451: if(options->escaping && options->escaping != xml_elem_cdata_escaping) {
! 452: escaped_str = xml_elem_entity_escape(el->text.str, buflen, &buflen, options->escaping );
! 453: if(!escaped_str) {
! 454: escaped_str = el->text.str;
! 455: }
! 456: }
! 457:
! 458: if(options->escaping & xml_elem_cdata_escaping) {
! 459: xml_elem_writefunc(fptr, CDATA_BEGIN, data, CDATA_BEGIN_LEN);
! 460: }
! 461:
! 462: xml_elem_writefunc(fptr, escaped_str, data, buflen);
! 463:
! 464: if(escaped_str != el->text.str) {
! 465: my_free(escaped_str);
! 466: }
! 467:
! 468: if(options->escaping & xml_elem_cdata_escaping) {
! 469: xml_elem_writefunc(fptr, CDATA_END, data, CDATA_END_LEN);
! 470: }
! 471: }
! 472: /* no text, so print child elems */
! 473: else {
! 474: xml_element *kids = Q_Head(&el->children);
! 475: i = 0;
! 476: while( kids ) {
! 477: if(i++ == 0) {
! 478: if(options->verbosity != xml_elem_no_white_space) {
! 479: xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN);
! 480: }
! 481: }
! 482: xml_element_serialize(kids, fptr, data, options, depth);
! 483: kids = Q_Next(&el->children);
! 484: }
! 485: if(i) {
! 486: if(options->verbosity == xml_elem_pretty && depth > 2) {
! 487: xml_elem_writefunc(fptr, whitespace, data, depth - 2);
! 488: }
! 489: }
! 490: }
! 491:
! 492: xml_elem_writefunc(fptr, END_TOKEN_BEGIN, data, END_TOKEN_BEGIN_LEN);
! 493: xml_elem_writefunc(fptr,el->name ? el->name : "None", data, 0);
! 494: xml_elem_writefunc(fptr, END_TOKEN_END, data, END_TOKEN_END_LEN);
! 495: }
! 496: if(options->verbosity != xml_elem_no_white_space) {
! 497: xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN);
! 498: }
! 499: }
! 500:
! 501: /* print buf to file */
! 502: static int file_out_fptr(void *f, const char *text, int size)
! 503: {
! 504: fputs(text, (FILE *)f);
! 505: return 0;
! 506: }
! 507:
! 508: /* print buf to simplestring */
! 509: static int simplestring_out_fptr(void *f, const char *text, int size)
! 510: {
! 511: simplestring* buf = (simplestring*)f;
! 512: if(buf) {
! 513: simplestring_addn(buf, text, size);
! 514: }
! 515: return 0;
! 516: }
! 517:
! 518: /****f* xml_element/xml_elem_serialize_to_string
! 519: * NAME
! 520: * xml_elem_serialize_to_string
! 521: * SYNOPSIS
! 522: * void xml_element_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len)
! 523: * FUNCTION
! 524: * writes element tree as XML into a newly allocated buffer
! 525: * INPUTS
! 526: * el - root element of tree
! 527: * options - options determining how output is written. see XML_ELEM_OUTPUT_OPTIONS
! 528: * buf_len - length of returned buffer, if not null.
! 529: * RESULT
! 530: * char* or NULL. Must be free'd by caller.
! 531: * NOTES
! 532: * SEE ALSO
! 533: * xml_elem_serialize_to_stream ()
! 534: * xml_elem_parse_buf ()
! 535: * SOURCE
! 536: */
! 537: char* xml_elem_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len)
! 538: {
! 539: simplestring buf;
! 540: simplestring_init(&buf);
! 541:
! 542: xml_element_serialize(el, simplestring_out_fptr, (void *)&buf, options, 0);
! 543:
! 544: if(buf_len) {
! 545: *buf_len = buf.len;
! 546: }
! 547:
! 548: return buf.str;
! 549: }
! 550: /******/
! 551:
! 552: /****f* xml_element/xml_elem_serialize_to_stream
! 553: * NAME
! 554: * xml_elem_serialize_to_stream
! 555: * SYNOPSIS
! 556: * void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options)
! 557: * FUNCTION
! 558: * writes element tree as XML into a stream (typically an opened file)
! 559: * INPUTS
! 560: * el - root element of tree
! 561: * output - stream handle
! 562: * options - options determining how output is written. see XML_ELEM_OUTPUT_OPTIONS
! 563: * RESULT
! 564: * void
! 565: * NOTES
! 566: * SEE ALSO
! 567: * xml_elem_serialize_to_string ()
! 568: * xml_elem_parse_buf ()
! 569: * SOURCE
! 570: */
! 571: void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options)
! 572: {
! 573: xml_element_serialize(el, file_out_fptr, (void *)output, options, 0);
! 574: }
! 575: /******/
! 576:
! 577: /*--------------------------*
! 578: * End xml_element Functions *
! 579: *--------------------------*/
! 580:
! 581:
! 582: /*----------------------
! 583: * Begin Expat Handlers *
! 584: *---------------------*/
! 585:
! 586: typedef struct _xml_elem_data {
! 587: xml_element* root;
! 588: xml_element* current;
! 589: XML_ELEM_INPUT_OPTIONS input_options;
! 590: int needs_enc_conversion;
! 591: } xml_elem_data;
! 592:
! 593:
! 594: /* expat start of element handler */
! 595: static void _xmlrpc_startElement(void *userData, const char *name, const char **attrs)
! 596: {
! 597: xml_element *c;
! 598: xml_elem_data* mydata = (xml_elem_data*)userData;
! 599: const char** p = attrs;
! 600:
! 601: if(mydata) {
! 602: c = mydata->current;
! 603:
! 604: mydata->current = xml_elem_new();
! 605: mydata->current->name = (char*)strdup(name);
! 606: mydata->current->parent = c;
! 607:
! 608: /* init attrs */
! 609: while(p && *p) {
! 610: xml_element_attr* attr = malloc(sizeof(xml_element_attr));
! 611: if(attr) {
! 612: attr->key = strdup(*p);
! 613: attr->val = strdup(*(p+1));
! 614: Q_PushTail(&mydata->current->attrs, attr);
! 615:
! 616: p += 2;
! 617: }
! 618: }
! 619: }
! 620: }
! 621:
! 622: /* expat end of element handler */
! 623: static void _xmlrpc_endElement(void *userData, const char *name)
! 624: {
! 625: xml_elem_data* mydata = (xml_elem_data*)userData;
! 626:
! 627: if(mydata && mydata->current && mydata->current->parent) {
! 628: Q_PushTail(&mydata->current->parent->children, mydata->current);
! 629:
! 630: mydata->current = mydata->current->parent;
! 631: }
! 632: }
! 633:
! 634: /* expat char data handler */
! 635: static void _xmlrpc_charHandler(void *userData,
! 636: const char *s,
! 637: int len)
! 638: {
! 639: xml_elem_data* mydata = (xml_elem_data*)userData;
! 640: if(mydata && mydata->current) {
! 641:
! 642: /* Check if we need to decode utf-8 parser output to another encoding */
! 643: if(mydata->needs_enc_conversion && mydata->input_options->encoding) {
! 644: int new_len = 0;
! 645: char* add_text = utf8_decode(s, len, &new_len, mydata->input_options->encoding);
! 646: if(add_text) {
! 647: len = new_len;
! 648: simplestring_addn(&mydata->current->text, add_text, len);
! 649: free(add_text);
! 650: return;
! 651: }
! 652: }
! 653: simplestring_addn(&mydata->current->text, s, len);
! 654: }
! 655: }
! 656: /******/
! 657:
! 658: /*-------------------*
! 659: * End Expat Handlers *
! 660: *-------------------*/
! 661:
! 662: /*-------------------*
! 663: * xml_elem_parse_buf *
! 664: *-------------------*/
! 665:
! 666: /****f* xml_element/xml_elem_parse_buf
! 667: * NAME
! 668: * xml_elem_parse_buf
! 669: * SYNOPSIS
! 670: * xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error)
! 671: * FUNCTION
! 672: * parse a buffer containing XML into an xml_element in-memory tree
! 673: * INPUTS
! 674: * in_buf - buffer containing XML document
! 675: * len - length of buffer
! 676: * options - input options. optional
! 677: * error - error result data. optional. check if result is null.
! 678: * RESULT
! 679: * void
! 680: * NOTES
! 681: * The returned data must be free'd by caller
! 682: * SEE ALSO
! 683: * xml_elem_serialize_to_string ()
! 684: * xml_elem_free ()
! 685: * SOURCE
! 686: */
! 687: xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error)
! 688: {
! 689: xml_element* xReturn = NULL;
! 690: char buf[100] = "";
! 691: static STRUCT_XML_ELEM_INPUT_OPTIONS default_opts = {encoding_utf_8};
! 692:
! 693: if(!options) {
! 694: options = &default_opts;
! 695: }
! 696:
! 697: if(in_buf) {
! 698: XML_Parser parser;
! 699: xml_elem_data mydata = {0};
! 700:
! 701: parser = XML_ParserCreate(NULL);
! 702:
! 703: mydata.root = xml_elem_new();
! 704: mydata.current = mydata.root;
! 705: mydata.input_options = options;
! 706: mydata.needs_enc_conversion = options->encoding && strcmp(options->encoding, encoding_utf_8);
! 707:
! 708: XML_SetElementHandler(parser, (XML_StartElementHandler)_xmlrpc_startElement, (XML_EndElementHandler)_xmlrpc_endElement);
! 709: XML_SetCharacterDataHandler(parser, (XML_CharacterDataHandler)_xmlrpc_charHandler);
! 710:
! 711: /* pass the xml_elem_data struct along */
! 712: XML_SetUserData(parser, (void*)&mydata);
! 713:
! 714: if(!len) {
! 715: len = strlen(in_buf);
! 716: }
! 717:
! 718: /* parse the XML */
! 719: if(XML_Parse(parser, in_buf, len, 1) == 0) {
! 720: enum XML_Error err_code = XML_GetErrorCode(parser);
! 721: int line_num = XML_GetCurrentLineNumber(parser);
! 722: int col_num = XML_GetCurrentColumnNumber(parser);
! 723: long byte_idx = XML_GetCurrentByteIndex(parser);
! 724: /* int byte_total = XML_GetCurrentByteCount(parser); */
! 725: const char * error_str = XML_ErrorString(err_code);
! 726: if(byte_idx >= 0) {
! 727: snprintf(buf,
! 728: sizeof(buf),
! 729: "\n\tdata beginning %ld before byte index: %s\n",
! 730: byte_idx > 10 ? 10 : byte_idx,
! 731: in_buf + (byte_idx > 10 ? byte_idx - 10 : byte_idx));
! 732: }
! 733: /*
! 734: fprintf(stderr, "expat reports error code %i\n"
! 735: "\tdescription: %s\n"
! 736: "\tline: %i\n"
! 737: "\tcolumn: %i\n"
! 738: "\tbyte index: %ld\n"
! 739: "\ttotal bytes: %i\n%s ",
! 740: err_code, error_str, line_num,
! 741: col_num, byte_idx, byte_total, buf);
! 742: */
! 743:
! 744: /* error condition */
! 745: if(error) {
! 746: error->parser_code = (long)err_code;
! 747: error->line = line_num;
! 748: error->column = col_num;
! 749: error->byte_index = byte_idx;
! 750: error->parser_error = error_str;
! 751: }
! 752: }
! 753: else {
! 754: xReturn = (xml_element*)Q_Head(&mydata.root->children);
! 755: xReturn->parent = NULL;
! 756: }
! 757:
! 758: XML_ParserFree(parser);
! 759:
! 760:
! 761: xml_elem_free_non_recurse(mydata.root);
! 762: }
! 763:
! 764: return xReturn;
! 765: }
! 766:
! 767: /******/
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>