Annotation of embedaddon/php/ext/xmlrpc/libxmlrpc/xml_element.c, revision 1.1.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>