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 34static const char rcsid[] = "#(@) $Id$"; 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 */ 197void 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 */ 236void 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 */ 265xml_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 279static 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 286static 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 */ 324static 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 376static 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 */ 502static 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 */ 509static 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 */ 537char* 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 */ 571void 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 586typedef 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 */ 595static 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 */ 623static 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 */ 635static 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 */ 687xml_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/******/ 768