Back to index

php5  5.3.10
xml_element.c
Go to the documentation of this file.
00001 /*
00002   This file is part of libXMLRPC - a C library for xml-encoded function calls.
00003 
00004   Author: Dan Libby (dan@libby.com)
00005   Epinions.com may be contacted at feedback@epinions-inc.com
00006 */
00007 
00008 /*  
00009   Copyright 2000 Epinions, Inc. 
00010 
00011   Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
00012   of charge, to (a) use, copy, distribute, modify, perform and display this 
00013   software and associated documentation files (the "Software"), and (b) 
00014   permit others to whom the Software is furnished to do so as well.  
00015 
00016   1) The above copyright notice and this permission notice shall be included 
00017   without modification in all copies or substantial portions of the 
00018   Software.  
00019 
00020   2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
00021   ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
00022   IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
00023   PURPOSE OR NONINFRINGEMENT.  
00024 
00025   3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
00026   SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
00027   OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
00028   NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
00029   DAMAGES.    
00030 
00031 */
00032 
00033 
00034 static const char rcsid[] = "#(@) $Id: xml_element.c 271367 2008-12-17 00:30:27Z iliaa $";
00035 
00036 
00037 
00038 /****h* ABOUT/xml_element
00039  * NAME
00040  *   xml_element
00041  * AUTHOR
00042  *   Dan Libby, aka danda  (dan@libby.com)
00043  * CREATION DATE
00044  *   06/2000
00045  * HISTORY
00046  *   $Log$
00047  *   Revision 1.9.4.1.2.1  2008/12/09 17:22:12  iliaa
00048  *
00049  *   MFH: Fixed bug #46746 (xmlrpc_decode_request outputs non-suppressable error
00050  *   when given bad data).
00051  *
00052  *   Revision 1.9.4.1  2006/07/30 11:34:02  tony2001
00053  *   MFH: fix compile warnings (#38257)
00054  *
00055  *   Revision 1.9  2005/04/22 11:06:53  jorton
00056  *   Fixed bug #32797 (invalid C code in xmlrpc extension).
00057  *
00058  *   Revision 1.8  2005/03/28 00:07:24  edink
00059  *   Reshufle includes to make it compile on windows
00060  *
00061  *   Revision 1.7  2005/03/26 03:13:58  sniper
00062  *   - Made it possible to build ext/xmlrpc with libxml2
00063  *
00064  *   Revision 1.6  2004/06/01 20:16:06  iliaa
00065  *   Fixed bug #28597 (xmlrpc_encode_request() incorrectly encodes chars in
00066  *   200-210 range).
00067  *   Patch by: fernando dot nemec at folha dot com dot br
00068  *
00069  *   Revision 1.5  2003/12/16 21:00:21  sniper
00070  *   Fix some compile warnings (patch by Joe Orton)
00071  *
00072  *   Revision 1.4  2002/11/26 23:01:16  fmk
00073  *   removing unused variables
00074  *
00075  *   Revision 1.3  2002/07/05 04:43:53  danda
00076  *   merged in updates from SF project.  bring php repository up to date with xmlrpc-epi version 0.51
00077  *
00078  *   Revision 1.9  2002/07/03 20:54:30  danda
00079  *   root element should not have a parent. patch from anon SF user
00080  *
00081  *   Revision 1.8  2002/05/23 17:46:51  danda
00082  *   patch from mukund - fix non utf-8 encoding conversions
00083  *
00084  *   Revision 1.7  2002/02/13 20:58:50  danda
00085  *   patch to make source more windows friendly, contributed by Jeff Lawson
00086  *
00087  *   Revision 1.6  2002/01/08 01:06:55  danda
00088  *   enable <?xml version="1.0"?> format for parsers that are very picky.
00089  *
00090  *   Revision 1.5  2001/09/29 21:58:05  danda
00091  *   adding cvs log to history section
00092  *
00093  *   10/15/2000 -- danda -- adding robodoc documentation
00094  * TODO
00095  *   Nicer external API. Get rid of macros.  Make opaque types, etc.
00096  * PORTABILITY
00097  *   Coded on RedHat Linux 6.2.  Builds on Solaris x86.  Should build on just
00098  *   about anything with minor mods.
00099  * NOTES
00100  *   This code incorporates ideas from expat-ensor from http://xml.ensor.org.
00101  *  
00102  *   It was coded primarily to act as a go-between for expat and xmlrpc. To this
00103  *   end, it stores xml elements, their sub-elements, and their attributes in an
00104  *   in-memory tree.  When expat is done parsing, the tree can be walked, thus
00105  *   retrieving the values.  The code can also be used to build a tree via API then
00106  *   write out the tree to a buffer, thus "serializing" the xml.
00107  *
00108  *   It turns out this is useful for other purposes, such as parsing config files.
00109  *   YMMV.
00110  *
00111  *   Some Features:
00112  *     - output option for xml escaping data.  Choices include no escaping, entity escaping,
00113  *       or CDATA sections.
00114  *     - output option for character encoding.  Defaults to (none) utf-8.
00115  *     - output option for verbosity/readability.  ultra-compact, newlines, pretty/level indented. 
00116  *
00117  * BUGS
00118  *   there must be some.
00119  ******/
00120  
00121 #include "ext/xml/expat_compat.h"
00122 #ifdef _WIN32
00123 #include "xmlrpc_win32.h"
00124 #endif
00125 #include <stdlib.h>
00126 #include <string.h>
00127 #include <ctype.h>
00128 
00129 #include "xml_element.h"
00130 #include "queue.h"
00131 #include "encodings.h"
00132 
00133 #define my_free(thing)  if(thing) {free(thing); thing = NULL;}
00134 
00135 #define XML_DECL_START                 "<?xml"
00136 #define XML_DECL_START_LEN             sizeof(XML_DECL_START) - 1
00137 #define XML_DECL_VERSION               "version=\"1.0\""
00138 #define XML_DECL_VERSION_LEN           sizeof(XML_DECL_VERSION) - 1
00139 #define XML_DECL_ENCODING_ATTR         "encoding"
00140 #define XML_DECL_ENCODING_ATTR_LEN     sizeof(XML_DECL_ENCODING_ATTR) - 1
00141 #define XML_DECL_ENCODING_DEFAULT      "utf-8"
00142 #define XML_DECL_ENCODING_DEFAULT_LEN  sizeof(XML_DECL_ENCODING_DEFAULT) - 1
00143 #define XML_DECL_END                   "?>"
00144 #define XML_DECL_END_LEN               sizeof(XML_DECL_END) - 1
00145 #define START_TOKEN_BEGIN              "<"
00146 #define START_TOKEN_BEGIN_LEN          sizeof(START_TOKEN_BEGIN) - 1
00147 #define START_TOKEN_END                ">"
00148 #define START_TOKEN_END_LEN            sizeof(START_TOKEN_END) - 1
00149 #define EMPTY_START_TOKEN_END          "/>"
00150 #define EMPTY_START_TOKEN_END_LEN      sizeof(EMPTY_START_TOKEN_END) - 1
00151 #define END_TOKEN_BEGIN                "</"
00152 #define END_TOKEN_BEGIN_LEN            sizeof(END_TOKEN_BEGIN) - 1
00153 #define END_TOKEN_END                  ">"
00154 #define END_TOKEN_END_LEN              sizeof(END_TOKEN_END) - 1
00155 #define ATTR_DELIMITER                 "\""
00156 #define ATTR_DELIMITER_LEN             sizeof(ATTR_DELIMITER) - 1
00157 #define CDATA_BEGIN                    "<![CDATA["
00158 #define CDATA_BEGIN_LEN                sizeof(CDATA_BEGIN) - 1
00159 #define CDATA_END                      "]]>"
00160 #define CDATA_END_LEN                  sizeof(CDATA_END) - 1
00161 #define EQUALS                         "="
00162 #define EQUALS_LEN                     sizeof(EQUALS) - 1
00163 #define WHITESPACE                     " "
00164 #define WHITESPACE_LEN                 sizeof(WHITESPACE) - 1
00165 #define NEWLINE                        "\n"
00166 #define NEWLINE_LEN                    sizeof(NEWLINE) - 1
00167 #define MAX_VAL_BUF                    144
00168 #define SCALAR_STR                     "SCALAR"
00169 #define SCALAR_STR_LEN                 sizeof(SCALAR_STR) - 1
00170 #define VECTOR_STR                     "VECTOR"
00171 #define VECTOR_STR_LEN                 sizeof(VECTOR_STR) - 1
00172 #define RESPONSE_STR                   "RESPONSE"
00173 #define RESPONSE_STR_LEN               sizeof(RESPONSE_STR) - 1
00174 
00175 
00176 /*-----------------------------
00177 - Begin xml_element Functions -
00178 -----------------------------*/
00179 
00180 /****f* xml_element/xml_elem_free_non_recurse
00181  * NAME
00182  *   xml_elem_free_non_recurse
00183  * SYNOPSIS
00184  *   void xml_elem_free_non_recurse(xml_element* root)
00185  * FUNCTION
00186  *   free a single xml element.  child elements will not be freed.
00187  * INPUTS
00188  *   root - the element to free
00189  * RESULT
00190  *   void
00191  * NOTES
00192  * SEE ALSO
00193  *   xml_elem_free ()
00194  *   xml_elem_new ()
00195  * SOURCE
00196  */
00197 void xml_elem_free_non_recurse(xml_element* root) {
00198    if(root) {
00199       xml_element_attr* attrs = Q_Head(&root->attrs);
00200       while(attrs) {
00201          my_free(attrs->key);
00202          my_free(attrs->val);
00203          my_free(attrs);
00204          attrs = Q_Next(&root->attrs);
00205       }
00206 
00207       Q_Destroy(&root->children);
00208       Q_Destroy(&root->attrs);
00209       if(root->name) {
00210           free((char *)root->name);
00211           root->name = NULL;
00212       }
00213       simplestring_free(&root->text);
00214       my_free(root);
00215    }
00216 }
00217 /******/
00218 
00219 /****f* xml_element/xml_elem_free
00220  * NAME
00221  *   xml_elem_free
00222  * SYNOPSIS
00223  *   void xml_elem_free(xml_element* root)
00224  * FUNCTION
00225  *   free an xml element and all of its child elements
00226  * INPUTS
00227  *   root - the root of an xml tree you would like to free
00228  * RESULT
00229  *   void
00230  * NOTES
00231  * SEE ALSO
00232  *   xml_elem_free_non_recurse ()
00233  *   xml_elem_new ()
00234  * SOURCE
00235  */
00236 void xml_elem_free(xml_element* root) {
00237    if(root) {
00238       xml_element* kids = Q_Head(&root->children);
00239       while(kids) {
00240          xml_elem_free(kids);
00241          kids = Q_Next(&root->children);
00242       }
00243       xml_elem_free_non_recurse(root);
00244    }
00245 }
00246 /******/
00247 
00248 /****f* xml_element/xml_elem_new
00249  * NAME
00250  *   xml_elem_new
00251  * SYNOPSIS
00252  *   xml_element* xml_elem_new()
00253  * FUNCTION
00254  *   allocates and initializes a new xml_element
00255  * INPUTS
00256  *   none
00257  * RESULT
00258  *   xml_element* or NULL.  NULL indicates an out-of-memory condition.
00259  * NOTES
00260  * SEE ALSO
00261  *   xml_elem_free ()
00262  *   xml_elem_free_non_recurse ()
00263  * SOURCE
00264  */
00265 xml_element* xml_elem_new() {
00266    xml_element* elem = calloc(1, sizeof(xml_element));
00267    if(elem) {
00268       Q_Init(&elem->children);
00269       Q_Init(&elem->attrs);
00270       simplestring_init(&elem->text);
00271 
00272       /* init empty string in case we don't find any char data */
00273       simplestring_addn(&elem->text, "", 0);
00274    }
00275    return elem;
00276 }
00277 /******/
00278 
00279 static int xml_elem_writefunc(int (*fptr)(void *data, const char *text, int size), const char *text, void *data, int len)
00280 {
00281    return fptr && text ? fptr(data, text, len ? len : strlen(text)) : 0;
00282 }
00283 
00284 
00285 
00286 static int create_xml_escape(char *pString, unsigned char c)
00287 { 
00288   int counter = 0;
00289 
00290   pString[counter++] = '&';
00291   pString[counter++] = '#';
00292   if(c >= 100) {
00293     pString[counter++] = c / 100 + '0';
00294     c = c % 100;
00295   }
00296   pString[counter++] = c / 10 + '0';
00297   c = c % 10;
00298 
00299   pString[counter++] = c + '0';
00300   pString[counter++] = ';';
00301   return counter; 
00302 }
00303 
00304 #define non_ascii(c) (c > 127)
00305 #define non_print(c) (!isprint(c))
00306 #define markup(c) (c == '&' || c == '\"' || c == '>' || c == '<')
00307 #define entity_length(c) ( (c >= 100) ? 3 : ((c >= 10) ? 2 : 1) ) + 3; /* "&#" + c + ";" */
00308 
00309 /*
00310  * xml_elem_entity_escape
00311  *
00312  * Purpose:
00313  *   escape reserved xml chars and non utf-8 chars as xml entities
00314  * Comments:
00315  *   The return value may be a new string, or null if no
00316  *     conversion was performed. In the latter case, *newlen will
00317  *     be 0.
00318  * Flags (to escape)
00319  *  xml_elem_no_escaping             = 0x000,
00320  *  xml_elem_entity_escaping         = 0x002,   // escape xml special chars as entities
00321  *  xml_elem_non_ascii_escaping      = 0x008,   // escape chars above 127
00322  *  xml_elem_cdata_escaping          = 0x010,   // wrap in cdata
00323  */
00324 static char* xml_elem_entity_escape(const char* buf, int old_len, int *newlen, XML_ELEM_ESCAPING flags) {
00325   char *pRetval = 0;
00326   int iNewBufLen=0;
00327 
00328 #define should_escape(c, flag) ( ((flag & xml_elem_markup_escaping) && markup(c)) || \
00329                                  ((flag & xml_elem_non_ascii_escaping) && non_ascii(c)) || \
00330                                  ((flag & xml_elem_non_print_escaping) && non_print(c)) )
00331 
00332   if(buf && *buf) {
00333     const unsigned char *bufcopy;
00334     char *NewBuffer;
00335     int ToBeXmlEscaped=0;
00336     int iLength;
00337     bufcopy = buf;
00338     iLength= old_len ? old_len : strlen(buf);
00339     while(*bufcopy) {
00340       if( should_escape(*bufcopy, flags) ) {
00341        /* the length will increase by length of xml escape - the character length */
00342        iLength += entity_length(*bufcopy);
00343        ToBeXmlEscaped=1;
00344       }
00345       bufcopy++;
00346     }
00347 
00348     if(ToBeXmlEscaped) {
00349 
00350       NewBuffer= malloc(iLength+1);
00351       if(NewBuffer) {
00352        bufcopy=buf;
00353        while(*bufcopy) {
00354          if(should_escape(*bufcopy, flags)) {
00355            iNewBufLen += create_xml_escape(NewBuffer+iNewBufLen,*bufcopy);
00356          }
00357          else {
00358            NewBuffer[iNewBufLen++]=*bufcopy;
00359          }
00360          bufcopy++;
00361        }
00362        NewBuffer[iNewBufLen] = 0;
00363        pRetval = NewBuffer;
00364       }
00365     }
00366   }
00367 
00368   if(newlen) {
00369      *newlen = iNewBufLen;
00370   }
00371 
00372   return pRetval;
00373 }
00374 
00375 
00376 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)
00377 {
00378    int i;
00379    static STRUCT_XML_ELEM_OUTPUT_OPTIONS default_opts = {xml_elem_pretty, xml_elem_markup_escaping | xml_elem_non_print_escaping, XML_DECL_ENCODING_DEFAULT};
00380    static char whitespace[] = "                                                                                               "
00381                               "                                                                                               "
00382                               "                                                                                               ";
00383    depth++;
00384 
00385    if(!el) {
00386       /* fprintf(stderr, "Nothing to write\n"); */
00387       return;
00388    }
00389    if(!options) {
00390       options = &default_opts;
00391    }
00392 
00393    /* print xml declaration if at root level */
00394    if(depth == 1) {
00395       xml_elem_writefunc(fptr, XML_DECL_START, data, XML_DECL_START_LEN);
00396       xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
00397       xml_elem_writefunc(fptr, XML_DECL_VERSION, data, XML_DECL_VERSION_LEN);
00398       if(options->encoding && *options->encoding) {
00399           xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
00400           xml_elem_writefunc(fptr, XML_DECL_ENCODING_ATTR, data, XML_DECL_ENCODING_ATTR_LEN);
00401           xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN);
00402           xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
00403           xml_elem_writefunc(fptr, options->encoding, data, 0);
00404           xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
00405       }
00406       xml_elem_writefunc(fptr, XML_DECL_END, data, XML_DECL_END_LEN);
00407       if(options->verbosity != xml_elem_no_white_space) {
00408          xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN);
00409       }
00410    }
00411 
00412    if(options->verbosity == xml_elem_pretty && depth > 2) {
00413          xml_elem_writefunc(fptr, whitespace, data, depth - 2);
00414    }
00415    /* begin element */
00416    xml_elem_writefunc(fptr,START_TOKEN_BEGIN, data, START_TOKEN_BEGIN_LEN);
00417    if(el->name) {
00418       xml_elem_writefunc(fptr, el->name, data, 0);
00419 
00420       /* write attrs, if any */
00421       if(Q_Size(&el->attrs)) {
00422          xml_element_attr* iter = Q_Head(&el->attrs);
00423          while( iter ) {
00424             xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN);
00425             xml_elem_writefunc(fptr, iter->key, data, 0);
00426             xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN);
00427             xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
00428             xml_elem_writefunc(fptr, iter->val, data, 0);
00429             xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN);
00430 
00431             iter = Q_Next(&el->attrs);
00432          }
00433       }
00434    }
00435    else {
00436       xml_elem_writefunc(fptr, "None", data, 0);
00437    }
00438    /* if no text and no children, use abbreviated form, eg: <foo/> */
00439    if(!el->text.len && !Q_Size(&el->children)) {
00440        xml_elem_writefunc(fptr, EMPTY_START_TOKEN_END, data, EMPTY_START_TOKEN_END_LEN);
00441    }
00442    /* otherwise, print element contents */
00443    else {
00444        xml_elem_writefunc(fptr, START_TOKEN_END, data, START_TOKEN_END_LEN);
00445 
00446        /* print text, if any */
00447        if(el->text.len) {
00448           char* escaped_str = el->text.str;
00449           int buflen = el->text.len;
00450 
00451           if(options->escaping && options->escaping != xml_elem_cdata_escaping) {
00452              escaped_str = xml_elem_entity_escape(el->text.str, buflen, &buflen, options->escaping );
00453              if(!escaped_str) {
00454                 escaped_str = el->text.str;
00455              }
00456           }
00457 
00458           if(options->escaping & xml_elem_cdata_escaping) {
00459              xml_elem_writefunc(fptr, CDATA_BEGIN, data, CDATA_BEGIN_LEN);
00460           }
00461 
00462           xml_elem_writefunc(fptr, escaped_str, data, buflen);
00463 
00464           if(escaped_str != el->text.str) {
00465              my_free(escaped_str);
00466           }
00467 
00468           if(options->escaping & xml_elem_cdata_escaping) {
00469              xml_elem_writefunc(fptr, CDATA_END, data, CDATA_END_LEN);
00470           }
00471        }
00472        /* no text, so print child elems */
00473        else {
00474           xml_element *kids = Q_Head(&el->children);
00475           i = 0;
00476           while( kids ) {
00477              if(i++ == 0) {
00478                 if(options->verbosity != xml_elem_no_white_space) {
00479                    xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN);
00480                 }
00481              }
00482              xml_element_serialize(kids, fptr, data, options, depth);
00483              kids = Q_Next(&el->children);
00484           }
00485           if(i) {
00486              if(options->verbosity == xml_elem_pretty && depth > 2) {
00487                    xml_elem_writefunc(fptr, whitespace, data, depth - 2);
00488              }
00489           }
00490        }
00491 
00492        xml_elem_writefunc(fptr, END_TOKEN_BEGIN, data, END_TOKEN_BEGIN_LEN);
00493        xml_elem_writefunc(fptr,el->name ? el->name : "None", data, 0);
00494        xml_elem_writefunc(fptr, END_TOKEN_END, data, END_TOKEN_END_LEN);
00495    }
00496    if(options->verbosity != xml_elem_no_white_space) {
00497       xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN);
00498    }
00499 }
00500 
00501 /* print buf to file */
00502 static int file_out_fptr(void *f, const char *text, int size)
00503 {
00504    fputs(text, (FILE *)f);
00505    return 0;
00506 }
00507 
00508 /* print buf to simplestring */
00509 static int simplestring_out_fptr(void *f, const char *text, int size)
00510 {
00511    simplestring* buf = (simplestring*)f;
00512    if(buf) {
00513       simplestring_addn(buf, text, size);
00514    }
00515    return 0;
00516 }
00517 
00518 /****f* xml_element/xml_elem_serialize_to_string
00519  * NAME
00520  *   xml_elem_serialize_to_string
00521  * SYNOPSIS
00522  *   void xml_element_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len)
00523  * FUNCTION
00524  *   writes element tree as XML into a newly allocated buffer
00525  * INPUTS
00526  *   el      - root element of tree
00527  *   options - options determining how output is written.  see XML_ELEM_OUTPUT_OPTIONS
00528  *   buf_len - length of returned buffer, if not null.
00529  * RESULT
00530  *   char* or NULL. Must be free'd by caller.
00531  * NOTES
00532  * SEE ALSO
00533  *   xml_elem_serialize_to_stream ()
00534  *   xml_elem_parse_buf ()
00535  * SOURCE
00536  */
00537 char* xml_elem_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len)
00538 {
00539    simplestring buf;
00540    simplestring_init(&buf);
00541 
00542    xml_element_serialize(el, simplestring_out_fptr, (void *)&buf, options, 0);
00543 
00544    if(buf_len) {
00545       *buf_len = buf.len;
00546    }
00547 
00548    return buf.str;
00549 }
00550 /******/
00551 
00552 /****f* xml_element/xml_elem_serialize_to_stream
00553  * NAME
00554  *   xml_elem_serialize_to_stream
00555  * SYNOPSIS
00556  *   void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options)
00557  * FUNCTION
00558  *   writes element tree as XML into a stream (typically an opened file)
00559  * INPUTS
00560  *   el      - root element of tree
00561  *   output  - stream handle
00562  *   options - options determining how output is written.  see XML_ELEM_OUTPUT_OPTIONS
00563  * RESULT
00564  *   void
00565  * NOTES
00566  * SEE ALSO
00567  *   xml_elem_serialize_to_string ()
00568  *   xml_elem_parse_buf ()
00569  * SOURCE
00570  */
00571 void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options)
00572 {
00573    xml_element_serialize(el, file_out_fptr, (void *)output, options, 0);
00574 }
00575 /******/
00576 
00577 /*--------------------------*
00578 * End xml_element Functions *
00579 *--------------------------*/
00580 
00581 
00582 /*----------------------
00583 * Begin Expat Handlers *
00584 *---------------------*/
00585 
00586 typedef struct _xml_elem_data {
00587    xml_element*           root;
00588    xml_element*           current;
00589    XML_ELEM_INPUT_OPTIONS input_options;
00590    int                    needs_enc_conversion;
00591 } xml_elem_data;
00592 
00593 
00594 /* expat start of element handler */
00595 static void _xmlrpc_startElement(void *userData, const char *name, const char **attrs)
00596 {
00597    xml_element *c;
00598    xml_elem_data* mydata = (xml_elem_data*)userData;
00599    const char** p = attrs;
00600 
00601    if(mydata) {
00602       c = mydata->current;
00603 
00604       mydata->current = xml_elem_new();
00605       mydata->current->name = (char*)strdup(name);
00606       mydata->current->parent = c;
00607 
00608       /* init attrs */
00609       while(p && *p) {
00610          xml_element_attr* attr = malloc(sizeof(xml_element_attr));
00611          if(attr) {
00612             attr->key = strdup(*p);
00613             attr->val = strdup(*(p+1));
00614             Q_PushTail(&mydata->current->attrs, attr);
00615 
00616             p += 2;
00617          }
00618       }
00619    }
00620 }
00621 
00622 /* expat end of element handler */
00623 static void _xmlrpc_endElement(void *userData, const char *name)
00624 {
00625    xml_elem_data* mydata = (xml_elem_data*)userData;
00626 
00627    if(mydata && mydata->current && mydata->current->parent) {
00628       Q_PushTail(&mydata->current->parent->children, mydata->current);
00629 
00630       mydata->current = mydata->current->parent;
00631    }
00632 }
00633 
00634 /* expat char data handler */
00635 static void _xmlrpc_charHandler(void *userData,
00636                         const char *s,
00637                         int len)
00638 {
00639    xml_elem_data* mydata = (xml_elem_data*)userData;
00640    if(mydata && mydata->current) {
00641 
00642       /* Check if we need to decode utf-8 parser output to another encoding */
00643       if(mydata->needs_enc_conversion && mydata->input_options->encoding) {
00644          int new_len = 0;
00645          char* add_text = utf8_decode(s, len, &new_len, mydata->input_options->encoding);
00646          if(add_text) {
00647             len = new_len;
00648             simplestring_addn(&mydata->current->text, add_text, len);
00649             free(add_text);
00650             return;
00651          }
00652       }
00653       simplestring_addn(&mydata->current->text, s, len);
00654    }
00655 }
00656 /******/
00657 
00658 /*-------------------*
00659 * End Expat Handlers *
00660 *-------------------*/
00661 
00662 /*-------------------*
00663 * xml_elem_parse_buf *
00664 *-------------------*/
00665 
00666 /****f* xml_element/xml_elem_parse_buf
00667  * NAME
00668  *   xml_elem_parse_buf
00669  * SYNOPSIS
00670  *   xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error)
00671  * FUNCTION
00672  *   parse a buffer containing XML into an xml_element in-memory tree
00673  * INPUTS
00674  *   in_buf   - buffer containing XML document
00675  *   len      - length of buffer
00676  *   options  - input options. optional
00677  *   error    - error result data. optional. check if result is null.
00678  * RESULT
00679  *   void
00680  * NOTES
00681  *   The returned data must be free'd by caller
00682  * SEE ALSO
00683  *   xml_elem_serialize_to_string ()
00684  *   xml_elem_free ()
00685  * SOURCE
00686  */
00687 xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error)
00688 {
00689    xml_element* xReturn = NULL;
00690    char buf[100] = "";
00691    static STRUCT_XML_ELEM_INPUT_OPTIONS default_opts = {encoding_utf_8};
00692 
00693    if(!options) {
00694       options = &default_opts;
00695    }
00696 
00697    if(in_buf) {
00698       XML_Parser parser;
00699       xml_elem_data mydata = {0};
00700 
00701       parser = XML_ParserCreate(NULL);
00702 
00703       mydata.root = xml_elem_new();
00704       mydata.current = mydata.root;
00705       mydata.input_options = options;
00706       mydata.needs_enc_conversion = options->encoding && strcmp(options->encoding, encoding_utf_8);
00707 
00708       XML_SetElementHandler(parser, (XML_StartElementHandler)_xmlrpc_startElement, (XML_EndElementHandler)_xmlrpc_endElement);
00709       XML_SetCharacterDataHandler(parser, (XML_CharacterDataHandler)_xmlrpc_charHandler);
00710 
00711       /* pass the xml_elem_data struct along */
00712       XML_SetUserData(parser, (void*)&mydata);
00713 
00714       if(!len) {
00715          len = strlen(in_buf);
00716       }
00717 
00718       /* parse the XML */
00719       if(XML_Parse(parser, in_buf, len, 1) == 0) {
00720          enum XML_Error err_code = XML_GetErrorCode(parser);
00721          int line_num = XML_GetCurrentLineNumber(parser);
00722          int col_num = XML_GetCurrentColumnNumber(parser);
00723          long byte_idx = XML_GetCurrentByteIndex(parser);
00724 /*         int byte_total = XML_GetCurrentByteCount(parser); */
00725          const char * error_str = XML_ErrorString(err_code);
00726          if(byte_idx >= 0) {
00727              snprintf(buf, 
00728                       sizeof(buf),
00729                       "\n\tdata beginning %ld before byte index: %s\n",
00730                       byte_idx > 10  ? 10 : byte_idx,
00731                       in_buf + (byte_idx > 10 ? byte_idx - 10 : byte_idx));
00732          }
00733 /*
00734          fprintf(stderr, "expat reports error code %i\n"
00735                 "\tdescription: %s\n"
00736                 "\tline: %i\n"
00737                 "\tcolumn: %i\n"
00738                 "\tbyte index: %ld\n"
00739                 "\ttotal bytes: %i\n%s ",
00740                 err_code, error_str, line_num, 
00741                 col_num, byte_idx, byte_total, buf);
00742 */
00743 
00744           /* error condition */
00745           if(error) {
00746               error->parser_code = (long)err_code;
00747               error->line = line_num;
00748               error->column = col_num;
00749               error->byte_index = byte_idx;
00750               error->parser_error = error_str;
00751           }
00752       }
00753       else {
00754          xReturn = (xml_element*)Q_Head(&mydata.root->children);
00755          xReturn->parent = NULL;
00756       }
00757 
00758       XML_ParserFree(parser);
00759 
00760 
00761       xml_elem_free_non_recurse(mydata.root);
00762    }
00763 
00764    return xReturn;
00765 }
00766 
00767 /******/