00001 /* SVN FILE INFO 00002 * $Revision: 174 $ : Last Committed Revision 00003 * $Date: 2008年06月24日 10:50:29 -0700 (2008年6月24日) $ : Last Committed Date */ 00004 /* 00005 * "$Id: mxml-file.c,v 1.1 2007年05月23日 20:43:27 david_ko Exp $" 00006 * 00007 * File loading code for Mini-XML, a small XML-like file parsing library. 00008 * 00009 * Copyright 2003-2005 by Michael Sweet. 00010 * 00011 * This program is free software; you can redistribute it and/or 00012 * modify it under the terms of the GNU Library General Public 00013 * License as published by the Free Software Foundation; either 00014 * version 2, or (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU General Public License for more details. 00020 * 00021 * Contents: 00022 * 00023 * mxmlLoadFd() - Load a file descriptor into an XML node tree. 00024 * mxmlLoadFile() - Load a file into an XML node tree. 00025 * mxmlLoadString() - Load a string into an XML node tree. 00026 * mxmlSaveAllocString() - Save an XML node tree to an allocated string. 00027 * mxmlSaveFd() - Save an XML tree to a file descriptor. 00028 * mxmlSaveFile() - Save an XML tree to a file. 00029 * mxmlSaveString() - Save an XML node tree to a string. 00030 * mxmlSetCustomHandlers() - Set the handling functions for custom data. 00031 * mxmlSetErrorCallback() - Set the error message callback. 00032 * mxml_add_char() - Add a character to a buffer, expanding as needed. 00033 * mxml_fd_getc() - Read a character from a file descriptor. 00034 * mxml_fd_putc() - Write a character to a file descriptor. 00035 * mxml_fd_read() - Read a buffer of data from a file descriptor. 00036 * mxml_fd_write() - Write a buffer of data to a file descriptor. 00037 * mxml_file_getc() - Get a character from a file. 00038 * mxml_file_putc() - Write a character to a file. 00039 * mxml_get_entity() - Get the character corresponding to an entity... 00040 * mxml_load_data() - Load data into an XML node tree. 00041 * mxml_parse_element() - Parse an element for any attributes... 00042 * mxml_string_getc() - Get a character from a string. 00043 * mxml_string_putc() - Write a character to a string. 00044 * mxml_write_name() - Write a name string. 00045 * mxml_write_node() - Save an XML node to a file. 00046 * mxml_write_string() - Write a string, escaping & and < as needed. 00047 * mxml_write_ws() - Do whitespace callback... 00048 */ 00049 00050 /* 00051 * Include necessary headers... 00052 */ 00053 00054 #include "config.h" 00055 #include "mxml.h" 00056 #ifdef WIN32 00057 # include <io.h> 00058 #else 00059 # include <unistd.h> 00060 #endif /* WIN32 */ 00061 00062 00063 /* 00064 * Character encoding... 00065 */ 00066 00067 #define ENCODE_UTF8 0 /* UTF-8 */ 00068 #define ENCODE_UTF16BE 1 /* UTF-16 Big-Endian */ 00069 #define ENCODE_UTF16LE 2 /* UTF-16 Little-Endian */ 00070 00071 00072 /* 00073 * Macro to test for a bad XML character... 00074 */ 00075 00076 #define mxml_bad_char(ch) ((ch) < ' ' && (ch) != '\n' && (ch) != '\r' && (ch) != '\t') 00077 00078 00079 /* 00080 * Structures... 00081 */ 00082 00083 typedef struct mxml_fdbuf_s /**** File descriptor buffer (@private) ****/ 00084 { 00085 int fd; /* File descriptor */ 00086 unsigned char *current, /* Current position in buffer */ 00087 *end, /* End of buffer */ 00088 buffer[8192]; /* Character buffer */ 00089 } mxml_fdbuf_t; 00090 00091 00092 /* 00093 * Global error handler... 00094 */ 00095 00096 extern void (*mxml_error_cb)(const char *); 00097 00098 00099 /* 00100 * Custom data handlers... 00101 */ 00102 00103 static mxml_custom_load_cb_t mxml_custom_load_cb = NULL; 00104 static mxml_custom_save_cb_t mxml_custom_save_cb = NULL; 00105 00106 00107 /* 00108 * Local functions... 00109 */ 00110 00111 static int mxml_add_char(int ch, char **ptr, char **buffer, 00112 int *bufsize); 00113 static int mxml_fd_getc(void *p, int *encoding); 00114 static int mxml_fd_putc(int ch, void *p); 00115 static int mxml_fd_read(mxml_fdbuf_t *buf); 00116 static int mxml_fd_write(mxml_fdbuf_t *buf); 00117 static int mxml_file_getc(void *p, int *encoding); 00118 static int mxml_file_putc(int ch, void *p); 00119 static int mxml_get_entity(mxml_node_t *parent, void *p, 00120 int *encoding, 00121 int (*getc_cb)(void *, int *)); 00122 static mxml_node_t *mxml_load_data(mxml_node_t *top, void *p, 00123 mxml_type_t (*cb)(mxml_node_t *), 00124 int (*getc_cb)(void *, int *)); 00125 static int mxml_parse_element(mxml_node_t *node, void *p, 00126 int *encoding, 00127 int (*getc_cb)(void *, int *)); 00128 static int mxml_string_getc(void *p, int *encoding); 00129 static int mxml_string_putc(int ch, void *p); 00130 static int mxml_write_name(const char *s, void *p, 00131 int (*putc_cb)(int, void *)); 00132 static int mxml_write_node(mxml_node_t *node, void *p, 00133 const char *(*cb)(mxml_node_t *, int), 00134 int col, 00135 int (*putc_cb)(int, void *)); 00136 static int mxml_write_string(const char *s, void *p, 00137 int (*putc_cb)(int, void *)); 00138 static int mxml_write_ws(mxml_node_t *node, void *p, 00139 const char *(*cb)(mxml_node_t *, int), int ws, 00140 int col, int (*putc_cb)(int, void *)); 00141 00142 00143 /* 00144 * 'mxmlLoadFd()' - Load a file descriptor into an XML node tree. 00145 * 00146 * The nodes in the specified file are added to the specified top node. 00147 * If no top node is provided, the XML file MUST be well-formed with a 00148 * single parent node like <?xml> for the entire file. The callback 00149 * function returns the value type that should be used for child nodes. 00150 * If MXML_NO_CALLBACK is specified then all child nodes will be either 00151 * MXML_ELEMENT or MXML_TEXT nodes. 00152 * 00153 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, 00154 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading 00155 * child nodes of the specified type. 00156 */ 00157 00158 mxml_node_t * /* O - First node or NULL if the file could not be read. */ 00159 mxmlLoadFd(mxml_node_t *top, /* I - Top node */ 00160 int fd, /* I - File descriptor to read from */ 00161 mxml_type_t (*cb)(mxml_node_t *node)) 00162 /* I - Callback function or MXML_NO_CALLBACK */ 00163 { 00164 mxml_fdbuf_t buf; /* File descriptor buffer */ 00165 00166 00167 /* 00168 * Initialize the file descriptor buffer... 00169 */ 00170 00171 buf.fd = fd; 00172 buf.current = buf.buffer; 00173 buf.end = buf.buffer; 00174 00175 /* 00176 * Read the XML data... 00177 */ 00178 00179 return (mxml_load_data(top, &buf, cb, mxml_fd_getc)); 00180 } 00181 00182 00183 /* 00184 * 'mxmlLoadFile()' - Load a file into an XML node tree. 00185 * 00186 * The nodes in the specified file are added to the specified top node. 00187 * If no top node is provided, the XML file MUST be well-formed with a 00188 * single parent node like <?xml> for the entire file. The callback 00189 * function returns the value type that should be used for child nodes. 00190 * If MXML_NO_CALLBACK is specified then all child nodes will be either 00191 * MXML_ELEMENT or MXML_TEXT nodes. 00192 * 00193 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, 00194 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading 00195 * child nodes of the specified type. 00196 */ 00197 00198 mxml_node_t * /* O - First node or NULL if the file could not be read. */ 00199 mxmlLoadFile(mxml_node_t *top, /* I - Top node */ 00200 FILE *fp, /* I - File to read from */ 00201 mxml_type_t (*cb)(mxml_node_t *node)) 00202 /* I - Callback function or MXML_NO_CALLBACK */ 00203 { 00204 /* 00205 * Read the XML data... 00206 */ 00207 00208 return (mxml_load_data(top, fp, cb, mxml_file_getc)); 00209 } 00210 00211 00212 /* 00213 * 'mxmlLoadString()' - Load a string into an XML node tree. 00214 * 00215 * The nodes in the specified string are added to the specified top node. 00216 * If no top node is provided, the XML string MUST be well-formed with a 00217 * single parent node like <?xml> for the entire string. The callback 00218 * function returns the value type that should be used for child nodes. 00219 * If MXML_NO_CALLBACK is specified then all child nodes will be either 00220 * MXML_ELEMENT or MXML_TEXT nodes. 00221 * 00222 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, 00223 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading 00224 * child nodes of the specified type. 00225 */ 00226 00227 mxml_node_t * /* O - First node or NULL if the string has errors. */ 00228 mxmlLoadString(mxml_node_t *top, /* I - Top node */ 00229 const char *s, /* I - String to load */ 00230 mxml_type_t (*cb)(mxml_node_t *node)) 00231 /* I - Callback function or MXML_NO_CALLBACK */ 00232 { 00233 /* 00234 * Read the XML data... 00235 */ 00236 00237 return (mxml_load_data(top, &s, cb, mxml_string_getc)); 00238 } 00239 00240 00241 /* 00242 * 'mxmlSaveAllocString()' - Save an XML node tree to an allocated string. 00243 * 00244 * This function returns a pointer to a string containing the textual 00245 * representation of the XML node tree. The string should be freed 00246 * using the free() function when you are done with it. NULL is returned 00247 * if the node would produce an empty string or if the string cannot be 00248 * allocated. 00249 * 00250 * The callback argument specifies a function that returns a whitespace 00251 * string or NULL before and after each element. If MXML_NO_CALLBACK 00252 * is specified, whitespace will only be added before MXML_TEXT nodes 00253 * with leading whitespace and before attribute names inside opening 00254 * element tags. 00255 */ 00256 00257 char * /* O - Allocated string or NULL */ 00258 mxmlSaveAllocString(mxml_node_t *node, /* I - Node to write */ 00259 const char *(*cb)(mxml_node_t *node, int ws)) 00260 /* I - Whitespace callback or MXML_NO_CALLBACK */ 00261 { 00262 int bytes; /* Required bytes */ 00263 char buffer[8192]; /* Temporary buffer */ 00264 char *s; /* Allocated string */ 00265 00266 00267 /* 00268 * Write the node to the temporary buffer... 00269 */ 00270 00271 bytes = mxmlSaveString(node, buffer, sizeof(buffer), cb); 00272 00273 if (bytes <= 0) 00274 return (NULL); 00275 00276 if (bytes < (int)(sizeof(buffer) - 1)) 00277 { 00278 /* 00279 * Node fit inside the buffer, so just duplicate that string and 00280 * return... 00281 */ 00282 00283 return (strdup(buffer)); 00284 } 00285 00286 /* 00287 * Allocate a buffer of the required size and save the node to the 00288 * new buffer... 00289 */ 00290 00291 if ((s = malloc(bytes + 1)) == NULL) 00292 return (NULL); 00293 00294 mxmlSaveString(node, s, bytes + 1, cb); 00295 00296 /* 00297 * Return the allocated string... 00298 */ 00299 00300 return (s); 00301 } 00302 00303 00304 /* 00305 * 'mxmlSaveFd()' - Save an XML tree to a file descriptor. 00306 * 00307 * The callback argument specifies a function that returns a whitespace 00308 * string or NULL before and after each element. If MXML_NO_CALLBACK 00309 * is specified, whitespace will only be added before MXML_TEXT nodes 00310 * with leading whitespace and before attribute names inside opening 00311 * element tags. 00312 */ 00313 00314 int /* O - 0 on success, -1 on error. */ 00315 mxmlSaveFd(mxml_node_t *node, /* I - Node to write */ 00316 int fd, /* I - File descriptor to write to */ 00317 const char *(*cb)(mxml_node_t *node, int ws)) 00318 /* I - Whitespace callback or MXML_NO_CALLBACK */ 00319 { 00320 int col; /* Final column */ 00321 mxml_fdbuf_t buf; /* File descriptor buffer */ 00322 00323 00324 /* 00325 * Initialize the file descriptor buffer... 00326 */ 00327 00328 buf.fd = fd; 00329 buf.current = buf.buffer; 00330 buf.end = buf.buffer + sizeof(buf.buffer) - 4; 00331 00332 /* 00333 * Write the node... 00334 */ 00335 00336 if ((col = mxml_write_node(node, &buf, cb, 0, mxml_fd_putc)) < 0) 00337 return (-1); 00338 00339 if (col > 0) 00340 if (mxml_fd_putc('\n', &buf) < 0) 00341 return (-1); 00342 00343 /* 00344 * Flush and return... 00345 */ 00346 00347 return (mxml_fd_write(&buf)); 00348 } 00349 00350 00351 /* 00352 * 'mxmlSaveFile()' - Save an XML tree to a file. 00353 * 00354 * The callback argument specifies a function that returns a whitespace 00355 * string or NULL before and after each element. If MXML_NO_CALLBACK 00356 * is specified, whitespace will only be added before MXML_TEXT nodes 00357 * with leading whitespace and before attribute names inside opening 00358 * element tags. 00359 */ 00360 00361 int /* O - 0 on success, -1 on error. */ 00362 mxmlSaveFile(mxml_node_t *node, /* I - Node to write */ 00363 FILE *fp, /* I - File to write to */ 00364 const char *(*cb)(mxml_node_t *node, int ws)) 00365 /* I - Whitespace callback or MXML_NO_CALLBACK */ 00366 { 00367 int col; /* Final column */ 00368 00369 00370 /* 00371 * Write the node... 00372 */ 00373 00374 if ((col = mxml_write_node(node, fp, cb, 0, mxml_file_putc)) < 0) 00375 return (-1); 00376 00377 if (col > 0) 00378 if (putc('\n', fp) < 0) 00379 return (-1); 00380 00381 /* 00382 * Return 0 (success)... 00383 */ 00384 00385 return (0); 00386 } 00387 00388 00389 /* 00390 * 'mxmlSaveString()' - Save an XML node tree to a string. 00391 * 00392 * This function returns the total number of bytes that would be 00393 * required for the string but only copies (bufsize - 1) characters 00394 * into the specified buffer. 00395 * 00396 * The callback argument specifies a function that returns a whitespace 00397 * string or NULL before and after each element. If MXML_NO_CALLBACK 00398 * is specified, whitespace will only be added before MXML_TEXT nodes 00399 * with leading whitespace and before attribute names inside opening 00400 * element tags. 00401 */ 00402 00403 int /* O - Size of string */ 00404 mxmlSaveString(mxml_node_t *node, /* I - Node to write */ 00405 char *buffer, /* I - String buffer */ 00406 int bufsize, /* I - Size of string buffer */ 00407 const char *(*cb)(mxml_node_t *node, int ws)) 00408 /* I - Whitespace callback or MXML_NO_CALLBACK */ 00409 { 00410 int col; /* Final column */ 00411 char *ptr[2]; /* Pointers for putc_cb */ 00412 00413 00414 /* 00415 * Write the node... 00416 */ 00417 00418 ptr[0] = buffer; 00419 ptr[1] = buffer + bufsize; 00420 00421 if ((col = mxml_write_node(node, ptr, cb, 0, mxml_string_putc)) < 0) 00422 return (-1); 00423 00424 if (col > 0) 00425 mxml_string_putc('\n', ptr); 00426 00427 /* 00428 * Nul-terminate the buffer... 00429 */ 00430 00431 if (ptr[0] >= ptr[1]) 00432 buffer[bufsize - 1] = '0円'; 00433 else 00434 ptr[0][0] = '0円'; 00435 00436 /* 00437 * Return the number of characters... 00438 */ 00439 00440 return (ptr[0] - buffer); 00441 } 00442 00443 00444 /* 00445 * 'mxmlSetCustomHandlers()' - Set the handling functions for custom data. 00446 * 00447 * The load function accepts a node pointer and a data string and must 00448 * return 0 on success and non-zero on error. 00449 * 00450 * The save function accepts a node pointer and must return a malloc'd 00451 * string on success and NULL on error. 00452 * 00453 */ 00454 00455 void 00456 mxmlSetCustomHandlers(mxml_custom_load_cb_t load, 00457 /* I - Load function */ 00458 mxml_custom_save_cb_t save) 00459 /* I - Save function */ 00460 { 00461 mxml_custom_load_cb = load; 00462 mxml_custom_save_cb = save; 00463 } 00464 00465 00466 /* 00467 * 'mxmlSetErrorCallback()' - Set the error message callback. 00468 */ 00469 00470 void 00471 mxmlSetErrorCallback(void (*cb)(const char *)) 00472 /* I - Error callback function */ 00473 { 00474 mxml_error_cb = cb; 00475 } 00476 00477 00478 /* 00479 * 'mxml_add_char()' - Add a character to a buffer, expanding as needed. 00480 */ 00481 00482 static int /* O - 0 on success, -1 on error */ 00483 mxml_add_char(int ch, /* I - Character to add */ 00484 char **bufptr, /* IO - Current position in buffer */ 00485 char **buffer, /* IO - Current buffer */ 00486 int *bufsize) /* IO - Current buffer size */ 00487 { 00488 char *newbuffer; /* New buffer value */ 00489 00490 00491 if (*bufptr >= (*buffer + *bufsize - 4)) 00492 { 00493 /* 00494 * Increase the size of the buffer... 00495 */ 00496 00497 if (*bufsize < 1024) 00498 (*bufsize) *= 2; 00499 else 00500 (*bufsize) += 1024; 00501 00502 if ((newbuffer = realloc(*buffer, *bufsize)) == NULL) 00503 { 00504 free(*buffer); 00505 00506 mxml_error("Unable to expand string buffer to %d bytes!", *bufsize); 00507 00508 return (-1); 00509 } 00510 00511 *bufptr = newbuffer + (*bufptr - *buffer); 00512 *buffer = newbuffer; 00513 } 00514 00515 if (ch < 0x80) 00516 { 00517 /* 00518 * Single byte ASCII... 00519 */ 00520 00521 *(*bufptr)++ = ch; 00522 } 00523 else if (ch < 0x800) 00524 { 00525 /* 00526 * Two-byte UTF-8... 00527 */ 00528 00529 *(*bufptr)++ = 0xc0 | (ch >> 6); 00530 *(*bufptr)++ = 0x80 | (ch & 0x3f); 00531 } 00532 else if (ch < 0x10000) 00533 { 00534 /* 00535 * Three-byte UTF-8... 00536 */ 00537 00538 *(*bufptr)++ = 0xe0 | (ch >> 12); 00539 *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f); 00540 *(*bufptr)++ = 0x80 | (ch & 0x3f); 00541 } 00542 else 00543 { 00544 /* 00545 * Four-byte UTF-8... 00546 */ 00547 00548 *(*bufptr)++ = 0xf0 | (ch >> 18); 00549 *(*bufptr)++ = 0x80 | ((ch >> 12) & 0x3f); 00550 *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f); 00551 *(*bufptr)++ = 0x80 | (ch & 0x3f); 00552 } 00553 00554 return (0); 00555 } 00556 00557 00558 /* 00559 * 'mxml_fd_getc()' - Read a character from a file descriptor. 00560 */ 00561 00562 static int /* O - Character or EOF */ 00563 mxml_fd_getc(void *p, /* I - File descriptor buffer */ 00564 int *encoding) /* IO - Encoding */ 00565 { 00566 mxml_fdbuf_t *buf; /* File descriptor buffer */ 00567 int ch, /* Current character */ 00568 temp; /* Temporary character */ 00569 00570 00571 /* 00572 * Grab the next character in the buffer... 00573 */ 00574 00575 buf = (mxml_fdbuf_t *)p; 00576 00577 if (buf->current >= buf->end) 00578 if (mxml_fd_read(buf) < 0) 00579 return (EOF); 00580 00581 ch = *(buf->current)++; 00582 00583 switch (*encoding) 00584 { 00585 case ENCODE_UTF8 : 00586 /* 00587 * Got a UTF-8 character; convert UTF-8 to Unicode and return... 00588 */ 00589 00590 if (!(ch & 0x80)) 00591 { 00592 #if DEBUG > 1 00593 printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); 00594 #endif /* DEBUG > 1 */ 00595 00596 if (mxml_bad_char(ch)) 00597 { 00598 mxml_error("Bad control character 0x%02x not allowed by XML standard!", 00599 ch); 00600 return (EOF); 00601 } 00602 00603 return (ch); 00604 } 00605 else if (ch == 0xfe) 00606 { 00607 /* 00608 * UTF-16 big-endian BOM? 00609 */ 00610 00611 if (buf->current >= buf->end) 00612 if (mxml_fd_read(buf) < 0) 00613 return (EOF); 00614 00615 ch = *(buf->current)++; 00616 00617 if (ch != 0xff) 00618 return (EOF); 00619 00620 *encoding = ENCODE_UTF16BE; 00621 00622 return (mxml_fd_getc(p, encoding)); 00623 } 00624 else if (ch == 0xff) 00625 { 00626 /* 00627 * UTF-16 little-endian BOM? 00628 */ 00629 00630 if (buf->current >= buf->end) 00631 if (mxml_fd_read(buf) < 0) 00632 return (EOF); 00633 00634 ch = *(buf->current)++; 00635 00636 if (ch != 0xfe) 00637 return (EOF); 00638 00639 *encoding = ENCODE_UTF16LE; 00640 00641 return (mxml_fd_getc(p, encoding)); 00642 } 00643 else if ((ch & 0xe0) == 0xc0) 00644 { 00645 /* 00646 * Two-byte value... 00647 */ 00648 00649 if (buf->current >= buf->end) 00650 if (mxml_fd_read(buf) < 0) 00651 return (EOF); 00652 00653 temp = *(buf->current)++; 00654 00655 if ((temp & 0xc0) != 0x80) 00656 return (EOF); 00657 00658 ch = ((ch & 0x1f) << 6) | (temp & 0x3f); 00659 00660 if (ch < 0x80) 00661 return (EOF); 00662 } 00663 else if ((ch & 0xf0) == 0xe0) 00664 { 00665 /* 00666 * Three-byte value... 00667 */ 00668 00669 if (buf->current >= buf->end) 00670 if (mxml_fd_read(buf) < 0) 00671 return (EOF); 00672 00673 temp = *(buf->current)++; 00674 00675 if ((temp & 0xc0) != 0x80) 00676 return (EOF); 00677 00678 ch = ((ch & 0x0f) << 6) | (temp & 0x3f); 00679 00680 if (buf->current >= buf->end) 00681 if (mxml_fd_read(buf) < 0) 00682 return (EOF); 00683 00684 temp = *(buf->current)++; 00685 00686 if ((temp & 0xc0) != 0x80) 00687 return (EOF); 00688 00689 ch = (ch << 6) | (temp & 0x3f); 00690 00691 if (ch < 0x800) 00692 return (EOF); 00693 } 00694 else if ((ch & 0xf8) == 0xf0) 00695 { 00696 /* 00697 * Four-byte value... 00698 */ 00699 00700 if (buf->current >= buf->end) 00701 if (mxml_fd_read(buf) < 0) 00702 return (EOF); 00703 00704 temp = *(buf->current)++; 00705 00706 if ((temp & 0xc0) != 0x80) 00707 return (EOF); 00708 00709 ch = ((ch & 0x07) << 6) | (temp & 0x3f); 00710 00711 if (buf->current >= buf->end) 00712 if (mxml_fd_read(buf) < 0) 00713 return (EOF); 00714 00715 temp = *(buf->current)++; 00716 00717 if ((temp & 0xc0) != 0x80) 00718 return (EOF); 00719 00720 ch = (ch << 6) | (temp & 0x3f); 00721 00722 if (buf->current >= buf->end) 00723 if (mxml_fd_read(buf) < 0) 00724 return (EOF); 00725 00726 temp = *(buf->current)++; 00727 00728 if ((temp & 0xc0) != 0x80) 00729 return (EOF); 00730 00731 ch = (ch << 6) | (temp & 0x3f); 00732 00733 if (ch < 0x10000) 00734 return (EOF); 00735 } 00736 else 00737 return (EOF); 00738 break; 00739 00740 case ENCODE_UTF16BE : 00741 /* 00742 * Read UTF-16 big-endian char... 00743 */ 00744 00745 if (buf->current >= buf->end) 00746 if (mxml_fd_read(buf) < 0) 00747 return (EOF); 00748 00749 temp = *(buf->current)++; 00750 00751 ch = (ch << 8) | temp; 00752 00753 if (mxml_bad_char(ch)) 00754 { 00755 mxml_error("Bad control character 0x%02x not allowed by XML standard!", 00756 ch); 00757 return (EOF); 00758 } 00759 else if (ch >= 0xd800 && ch <= 0xdbff) 00760 { 00761 /* 00762 * Multi-word UTF-16 char... 00763 */ 00764 00765 int lch; 00766 00767 if (buf->current >= buf->end) 00768 if (mxml_fd_read(buf) < 0) 00769 return (EOF); 00770 00771 lch = *(buf->current)++; 00772 00773 if (buf->current >= buf->end) 00774 if (mxml_fd_read(buf) < 0) 00775 return (EOF); 00776 00777 temp = *(buf->current)++; 00778 00779 lch = (lch << 8) | temp; 00780 00781 if (lch < 0xdc00 || lch >= 0xdfff) 00782 return (EOF); 00783 00784 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; 00785 } 00786 break; 00787 00788 case ENCODE_UTF16LE : 00789 /* 00790 * Read UTF-16 little-endian char... 00791 */ 00792 00793 if (buf->current >= buf->end) 00794 if (mxml_fd_read(buf) < 0) 00795 return (EOF); 00796 00797 temp = *(buf->current)++; 00798 00799 ch |= (temp << 8); 00800 00801 if (mxml_bad_char(ch)) 00802 { 00803 mxml_error("Bad control character 0x%02x not allowed by XML standard!", 00804 ch); 00805 return (EOF); 00806 } 00807 else if (ch >= 0xd800 && ch <= 0xdbff) 00808 { 00809 /* 00810 * Multi-word UTF-16 char... 00811 */ 00812 00813 int lch; 00814 00815 if (buf->current >= buf->end) 00816 if (mxml_fd_read(buf) < 0) 00817 return (EOF); 00818 00819 lch = *(buf->current)++; 00820 00821 if (buf->current >= buf->end) 00822 if (mxml_fd_read(buf) < 0) 00823 return (EOF); 00824 00825 temp = *(buf->current)++; 00826 00827 lch |= (temp << 8); 00828 00829 if (lch < 0xdc00 || lch >= 0xdfff) 00830 return (EOF); 00831 00832 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; 00833 } 00834 break; 00835 } 00836 00837 #if DEBUG > 1 00838 printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); 00839 #endif /* DEBUG > 1 */ 00840 00841 return (ch); 00842 } 00843 00844 00845 /* 00846 * 'mxml_fd_putc()' - Write a character to a file descriptor. 00847 */ 00848 00849 static int /* O - 0 on success, -1 on error */ 00850 mxml_fd_putc(int ch, /* I - Character */ 00851 void *p) /* I - File descriptor buffer */ 00852 { 00853 mxml_fdbuf_t *buf; /* File descriptor buffer */ 00854 00855 00856 /* 00857 * Flush the write buffer as needed - note above that "end" still leaves 00858 * 4 characters at the end so that we can avoid a lot of extra tests... 00859 */ 00860 00861 buf = (mxml_fdbuf_t *)p; 00862 00863 if (buf->current >= buf->end) 00864 if (mxml_fd_write(buf) < 0) 00865 return (-1); 00866 00867 if (ch < 0x80) 00868 { 00869 /* 00870 * Write ASCII character directly... 00871 */ 00872 00873 *(buf->current)++ = ch; 00874 } 00875 else if (ch < 0x800) 00876 { 00877 /* 00878 * Two-byte UTF-8 character... 00879 */ 00880 00881 *(buf->current)++ = 0xc0 | (ch >> 6); 00882 *(buf->current)++ = 0x80 | (ch & 0x3f); 00883 } 00884 else if (ch < 0x10000) 00885 { 00886 /* 00887 * Three-byte UTF-8 character... 00888 */ 00889 00890 *(buf->current)++ = 0xe0 | (ch >> 12); 00891 *(buf->current)++ = 0x80 | ((ch >> 6) & 0x3f); 00892 *(buf->current)++ = 0x80 | (ch & 0x3f); 00893 } 00894 else 00895 { 00896 /* 00897 * Four-byte UTF-8 character... 00898 */ 00899 00900 *(buf->current)++ = 0xf0 | (ch >> 18); 00901 *(buf->current)++ = 0x80 | ((ch >> 12) & 0x3f); 00902 *(buf->current)++ = 0x80 | ((ch >> 6) & 0x3f); 00903 *(buf->current)++ = 0x80 | (ch & 0x3f); 00904 } 00905 00906 /* 00907 * Return successfully... 00908 */ 00909 00910 return (0); 00911 } 00912 00913 00914 /* 00915 * 'mxml_fd_read()' - Read a buffer of data from a file descriptor. 00916 */ 00917 00918 static int /* O - 0 on success, -1 on error */ 00919 mxml_fd_read(mxml_fdbuf_t *buf) /* I - File descriptor buffer */ 00920 { 00921 int bytes; /* Bytes read... */ 00922 00923 00924 /* 00925 * Range check input... 00926 */ 00927 00928 if (!buf) 00929 return (-1); 00930 00931 /* 00932 * Read from the file descriptor... 00933 */ 00934 00935 while ((bytes = read(buf->fd, buf->buffer, sizeof(buf->buffer))) < 0) 00936 if (errno != EAGAIN && errno != EINTR) 00937 return (-1); 00938 00939 if (bytes == 0) 00940 return (-1); 00941 00942 /* 00943 * Update the pointers and return success... 00944 */ 00945 00946 buf->current = buf->buffer; 00947 buf->end = buf->buffer + bytes; 00948 00949 return (0); 00950 } 00951 00952 00953 /* 00954 * 'mxml_fd_write()' - Write a buffer of data to a file descriptor. 00955 */ 00956 00957 static int /* O - 0 on success, -1 on error */ 00958 mxml_fd_write(mxml_fdbuf_t *buf) /* I - File descriptor buffer */ 00959 { 00960 int bytes; /* Bytes written */ 00961 unsigned char *ptr; /* Pointer into buffer */ 00962 00963 00964 /* 00965 * Range check... 00966 */ 00967 00968 if (!buf) 00969 return (-1); 00970 00971 /* 00972 * Return 0 if there is nothing to write... 00973 */ 00974 00975 if (buf->current == buf->buffer) 00976 return (0); 00977 00978 /* 00979 * Loop until we have written everything... 00980 */ 00981 00982 for (ptr = buf->buffer; ptr < buf->current; ptr += bytes) 00983 if ((bytes = write(buf->fd, ptr, buf->current - ptr)) < 0) 00984 return (-1); 00985 00986 /* 00987 * All done, reset pointers and return success... 00988 */ 00989 00990 buf->current = buf->buffer; 00991 00992 return (0); 00993 } 00994 00995 00996 /* 00997 * 'mxml_file_getc()' - Get a character from a file. 00998 */ 00999 01000 static int /* O - Character or EOF */ 01001 mxml_file_getc(void *p, /* I - Pointer to file */ 01002 int *encoding) /* IO - Encoding */ 01003 { 01004 int ch, /* Character from file */ 01005 temp; /* Temporary character */ 01006 FILE *fp; /* Pointer to file */ 01007 01008 01009 /* 01010 * Read a character from the file and see if it is EOF or ASCII... 01011 */ 01012 01013 fp = (FILE *)p; 01014 ch = getc(fp); 01015 01016 if (ch == EOF) 01017 return (EOF); 01018 01019 switch (*encoding) 01020 { 01021 case ENCODE_UTF8 : 01022 /* 01023 * Got a UTF-8 character; convert UTF-8 to Unicode and return... 01024 */ 01025 01026 if (!(ch & 0x80)) 01027 { 01028 if (mxml_bad_char(ch)) 01029 { 01030 mxml_error("Bad control character 0x%02x not allowed by XML standard!", 01031 ch); 01032 return (EOF); 01033 } 01034 01035 #if DEBUG > 1 01036 printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); 01037 #endif /* DEBUG > 1 */ 01038 01039 return (ch); 01040 } 01041 else if (ch == 0xfe) 01042 { 01043 /* 01044 * UTF-16 big-endian BOM? 01045 */ 01046 01047 ch = getc(fp); 01048 if (ch != 0xff) 01049 return (EOF); 01050 01051 *encoding = ENCODE_UTF16BE; 01052 01053 return (mxml_file_getc(p, encoding)); 01054 } 01055 else if (ch == 0xff) 01056 { 01057 /* 01058 * UTF-16 little-endian BOM? 01059 */ 01060 01061 ch = getc(fp); 01062 if (ch != 0xfe) 01063 return (EOF); 01064 01065 *encoding = ENCODE_UTF16LE; 01066 01067 return (mxml_file_getc(p, encoding)); 01068 } 01069 else if ((ch & 0xe0) == 0xc0) 01070 { 01071 /* 01072 * Two-byte value... 01073 */ 01074 01075 if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) 01076 return (EOF); 01077 01078 ch = ((ch & 0x1f) << 6) | (temp & 0x3f); 01079 01080 if (ch < 0x80) 01081 return (EOF); 01082 } 01083 else if ((ch & 0xf0) == 0xe0) 01084 { 01085 /* 01086 * Three-byte value... 01087 */ 01088 01089 if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) 01090 return (EOF); 01091 01092 ch = ((ch & 0x0f) << 6) | (temp & 0x3f); 01093 01094 if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) 01095 return (EOF); 01096 01097 ch = (ch << 6) | (temp & 0x3f); 01098 01099 if (ch < 0x800) 01100 return (EOF); 01101 } 01102 else if ((ch & 0xf8) == 0xf0) 01103 { 01104 /* 01105 * Four-byte value... 01106 */ 01107 01108 if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) 01109 return (EOF); 01110 01111 ch = ((ch & 0x07) << 6) | (temp & 0x3f); 01112 01113 if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) 01114 return (EOF); 01115 01116 ch = (ch << 6) | (temp & 0x3f); 01117 01118 if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) 01119 return (EOF); 01120 01121 ch = (ch << 6) | (temp & 0x3f); 01122 01123 if (ch < 0x10000) 01124 return (EOF); 01125 } 01126 else 01127 return (EOF); 01128 break; 01129 01130 case ENCODE_UTF16BE : 01131 /* 01132 * Read UTF-16 big-endian char... 01133 */ 01134 01135 ch = (ch << 8) | getc(fp); 01136 01137 if (mxml_bad_char(ch)) 01138 { 01139 mxml_error("Bad control character 0x%02x not allowed by XML standard!", 01140 ch); 01141 return (EOF); 01142 } 01143 else if (ch >= 0xd800 && ch <= 0xdbff) 01144 { 01145 /* 01146 * Multi-word UTF-16 char... 01147 */ 01148 01149 int lch = (getc(fp) << 8) | getc(fp); 01150 01151 if (lch < 0xdc00 || lch >= 0xdfff) 01152 return (EOF); 01153 01154 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; 01155 } 01156 break; 01157 01158 case ENCODE_UTF16LE : 01159 /* 01160 * Read UTF-16 little-endian char... 01161 */ 01162 01163 ch |= (getc(fp) << 8); 01164 01165 if (mxml_bad_char(ch)) 01166 { 01167 mxml_error("Bad control character 0x%02x not allowed by XML standard!", 01168 ch); 01169 return (EOF); 01170 } 01171 else if (ch >= 0xd800 && ch <= 0xdbff) 01172 { 01173 /* 01174 * Multi-word UTF-16 char... 01175 */ 01176 01177 int lch = getc(fp) | (getc(fp) << 8); 01178 01179 if (lch < 0xdc00 || lch >= 0xdfff) 01180 return (EOF); 01181 01182 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; 01183 } 01184 break; 01185 } 01186 01187 #if DEBUG > 1 01188 printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); 01189 #endif /* DEBUG > 1 */ 01190 01191 return (ch); 01192 } 01193 01194 01195 /* 01196 * 'mxml_file_putc()' - Write a character to a file. 01197 */ 01198 01199 static int /* O - 0 on success, -1 on failure */ 01200 mxml_file_putc(int ch, /* I - Character to write */ 01201 void *p) /* I - Pointer to file */ 01202 { 01203 char buffer[4], /* Buffer for character */ 01204 *bufptr; /* Pointer into buffer */ 01205 int buflen; /* Number of bytes to write */ 01206 01207 01208 if (ch < 0x80) 01209 return (putc(ch, (FILE *)p) == EOF ? -1 : 0); 01210 01211 bufptr = buffer; 01212 01213 if (ch < 0x800) 01214 { 01215 /* 01216 * Two-byte UTF-8 character... 01217 */ 01218 01219 *bufptr++ = 0xc0 | (ch >> 6); 01220 *bufptr++ = 0x80 | (ch & 0x3f); 01221 } 01222 else if (ch < 0x10000) 01223 { 01224 /* 01225 * Three-byte UTF-8 character... 01226 */ 01227 01228 *bufptr++ = 0xe0 | (ch >> 12); 01229 *bufptr++ = 0x80 | ((ch >> 6) & 0x3f); 01230 *bufptr++ = 0x80 | (ch & 0x3f); 01231 } 01232 else 01233 { 01234 /* 01235 * Four-byte UTF-8 character... 01236 */ 01237 01238 *bufptr++ = 0xf0 | (ch >> 18); 01239 *bufptr++ = 0x80 | ((ch >> 12) & 0x3f); 01240 *bufptr++ = 0x80 | ((ch >> 6) & 0x3f); 01241 *bufptr++ = 0x80 | (ch & 0x3f); 01242 } 01243 01244 buflen = bufptr - buffer; 01245 01246 return (fwrite(buffer, 1, buflen, (FILE *)p) < buflen ? -1 : 0); 01247 } 01248 01249 01250 /* 01251 * 'mxml_get_entity()' - Get the character corresponding to an entity... 01252 */ 01253 01254 static int /* O - Character value or EOF on error */ 01255 mxml_get_entity(mxml_node_t *parent, /* I - Parent node */ 01256 void *p, /* I - Pointer to source */ 01257 int *encoding, /* IO - Character encoding */ 01258 int (*getc_cb)(void *, int *)) 01259 /* I - Get character function */ 01260 { 01261 int ch; /* Current character */ 01262 char entity[64], /* Entity string */ 01263 *entptr; /* Pointer into entity */ 01264 01265 01266 entptr = entity; 01267 01268 while ((ch = (*getc_cb)(p, encoding)) != EOF) 01269 if (ch > 126 || (!isalnum(ch) && ch != '#')) 01270 break; 01271 else if (entptr < (entity + sizeof(entity) - 1)) 01272 *entptr++ = ch; 01273 else 01274 { 01275 mxml_error("Entity name too long under parent <%s>!", 01276 parent ? parent->value.element.name : "null"); 01277 break; 01278 } 01279 01280 *entptr = '0円'; 01281 01282 if (ch != ';') 01283 { 01284 mxml_error("Character entity \"%s\" not terminated under parent <%s>!", 01285 entity, parent ? parent->value.element.name : "null"); 01286 return (EOF); 01287 } 01288 01289 if (entity[0] == '#') 01290 { 01291 if (entity[1] == 'x') 01292 ch = strtol(entity + 2, NULL, 16); 01293 else 01294 ch = strtol(entity + 1, NULL, 10); 01295 } 01296 else if ((ch = mxmlEntityGetValue(entity)) < 0) 01297 mxml_error("Entity name \"%s;\" not supported under parent <%s>!", 01298 entity, parent ? parent->value.element.name : "null"); 01299 01300 if (mxml_bad_char(ch)) 01301 { 01302 mxml_error("Bad control character 0x%02x under parent <%s> not allowed by XML standard!", 01303 ch, parent ? parent->value.element.name : "null"); 01304 return (EOF); 01305 } 01306 01307 return (ch); 01308 } 01309 01310 01311 /* 01312 * 'mxml_load_data()' - Load data into an XML node tree. 01313 */ 01314 01315 static mxml_node_t * /* O - First node or NULL if the file could not be read. */ 01316 mxml_load_data(mxml_node_t *top, /* I - Top node */ 01317 void *p, /* I - Pointer to data */ 01318 mxml_type_t (*cb)(mxml_node_t *), 01319 /* I - Callback function or MXML_NO_CALLBACK */ 01320 int (*getc_cb)(void *, int *)) 01321 /* I - Read function */ 01322 { 01323 mxml_node_t *node, /* Current node */ 01324 *first, /* First node added */ 01325 *parent; /* Current parent node */ 01326 int ch, /* Character from file */ 01327 whitespace; /* Non-zero if whitespace seen */ 01328 char *buffer, /* String buffer */ 01329 *bufptr; /* Pointer into buffer */ 01330 int bufsize; /* Size of buffer */ 01331 mxml_type_t type; /* Current node type */ 01332 int encoding; /* Character encoding */ 01333 static const char * const types[] = /* Type strings... */ 01334 { 01335 "MXML_ELEMENT", /* XML element with attributes */ 01336 "MXML_INTEGER", /* Integer value */ 01337 "MXML_OPAQUE", /* Opaque string */ 01338 "MXML_REAL", /* Real value */ 01339 "MXML_TEXT", /* Text fragment */ 01340 "MXML_CUSTOM" /* Custom data */ 01341 }; 01342 01343 01344 /* 01345 * Read elements and other nodes from the file... 01346 */ 01347 01348 if ((buffer = malloc(64)) == NULL) 01349 { 01350 mxml_error("Unable to allocate string buffer!"); 01351 return (NULL); 01352 } 01353 01354 bufsize = 64; 01355 bufptr = buffer; 01356 parent = top; 01357 first = NULL; 01358 whitespace = 0; 01359 encoding = ENCODE_UTF8; 01360 01361 if (cb && parent) 01362 type = (*cb)(parent); 01363 else 01364 type = MXML_TEXT; 01365 01366 while ((ch = (*getc_cb)(p, &encoding)) != EOF) 01367 { 01368 if ((ch == '<' || 01369 (isspace(ch) && type != MXML_OPAQUE && type != MXML_CUSTOM)) && 01370 bufptr > buffer) 01371 { 01372 /* 01373 * Add a new value node... 01374 */ 01375 01376 *bufptr = '0円'; 01377 01378 switch (type) 01379 { 01380 case MXML_INTEGER : 01381 node = mxmlNewInteger(parent, strtol(buffer, &bufptr, 0)); 01382 break; 01383 01384 case MXML_OPAQUE : 01385 node = mxmlNewOpaque(parent, buffer); 01386 break; 01387 01388 case MXML_REAL : 01389 node = mxmlNewReal(parent, strtod(buffer, &bufptr)); 01390 break; 01391 01392 case MXML_TEXT : 01393 node = mxmlNewText(parent, whitespace, buffer); 01394 break; 01395 01396 case MXML_CUSTOM : 01397 if (mxml_custom_load_cb) 01398 { 01399 /* 01400 * Use the callback to fill in the custom data... 01401 */ 01402 01403 node = mxmlNewCustom(parent, NULL, NULL); 01404 01405 if ((*mxml_custom_load_cb)(node, buffer)) 01406 { 01407 mxml_error("Bad custom value '%s' in parent <%s>!", 01408 buffer, parent ? parent->value.element.name : "null"); 01409 mxmlDelete(node); 01410 node = NULL; 01411 } 01412 break; 01413 } 01414 01415 default : /* Should never happen... */ 01416 node = NULL; 01417 break; 01418 } 01419 01420 if (*bufptr) 01421 { 01422 /* 01423 * Bad integer/real number value... 01424 */ 01425 01426 mxml_error("Bad %s value '%s' in parent <%s>!", 01427 type == MXML_INTEGER ? "integer" : "real", buffer, 01428 parent ? parent->value.element.name : "null"); 01429 break; 01430 } 01431 01432 bufptr = buffer; 01433 whitespace = isspace(ch) && type == MXML_TEXT; 01434 01435 if (!node) 01436 { 01437 /* 01438 * Print error and return... 01439 */ 01440 01441 mxml_error("Unable to add value node of type %s to parent <%s>!", 01442 types[type], parent ? parent->value.element.name : "null"); 01443 goto error; 01444 } 01445 01446 if (!first) 01447 first = node; 01448 } 01449 else if (isspace(ch) && type == MXML_TEXT) 01450 whitespace = 1; 01451 01452 /* 01453 * Add lone whitespace node if we have an element and existing 01454 * whitespace... 01455 */ 01456 01457 if (ch == '<' && whitespace && type == MXML_TEXT) 01458 { 01459 mxmlNewText(parent, whitespace, ""); 01460 whitespace = 0; 01461 } 01462 01463 if (ch == '<') 01464 { 01465 /* 01466 * Start of open/close tag... 01467 */ 01468 01469 bufptr = buffer; 01470 01471 while ((ch = (*getc_cb)(p, &encoding)) != EOF) 01472 if (isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer)) 01473 break; 01474 else if (ch == '&') 01475 { 01476 if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF) 01477 goto error; 01478 01479 if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) 01480 goto error; 01481 } 01482 else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) 01483 goto error; 01484 else if (((bufptr - buffer) == 1 && buffer[0] == '?') || 01485 ((bufptr - buffer) == 3 && !strncmp(buffer, "!--", 3)) || 01486 ((bufptr - buffer) == 8 && !strncmp(buffer, "![CDATA[", 8))) 01487 break; 01488 01489 *bufptr = '0円'; 01490 01491 if (!strcmp(buffer, "!--")) 01492 { 01493 /* 01494 * Gather rest of comment... 01495 */ 01496 01497 while ((ch = (*getc_cb)(p, &encoding)) != EOF) 01498 { 01499 if (ch == '>' && bufptr > (buffer + 4) && 01500 bufptr[-3] != '-' && bufptr[-2] == '-' && bufptr[-1] == '-') 01501 break; 01502 else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) 01503 goto error; 01504 } 01505 01506 /* 01507 * Error out if we didn't get the whole comment... 01508 */ 01509 01510 if (ch != '>') 01511 { 01512 /* 01513 * Print error and return... 01514 */ 01515 01516 mxml_error("Early EOF in comment node!"); 01517 goto error; 01518 } 01519 01520 01521 /* 01522 * Otherwise add this as an element under the current parent... 01523 */ 01524 01525 *bufptr = '0円'; 01526 01527 if (!mxmlNewElement(parent, buffer)) 01528 { 01529 /* 01530 * Just print error for now... 01531 */ 01532 01533 mxml_error("Unable to add comment node to parent <%s>!", 01534 parent ? parent->value.element.name : "null"); 01535 break; 01536 } 01537 } 01538 else if (!strcmp(buffer, "![CDATA[")) 01539 { 01540 /* 01541 * Gather CDATA section... 01542 */ 01543 01544 while ((ch = (*getc_cb)(p, &encoding)) != EOF) 01545 { 01546 if (ch == '>' && !strncmp(bufptr - 2, "]]", 2)) 01547 break; 01548 else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) 01549 goto error; 01550 } 01551 01552 /* 01553 * Error out if we didn't get the whole comment... 01554 */ 01555 01556 if (ch != '>') 01557 { 01558 /* 01559 * Print error and return... 01560 */ 01561 01562 mxml_error("Early EOF in CDATA node!"); 01563 goto error; 01564 } 01565 01566 01567 /* 01568 * Otherwise add this as an element under the current parent... 01569 */ 01570 01571 *bufptr = '0円'; 01572 01573 if (!mxmlNewElement(parent, buffer)) 01574 { 01575 /* 01576 * Print error and return... 01577 */ 01578 01579 mxml_error("Unable to add CDATA node to parent <%s>!", 01580 parent ? parent->value.element.name : "null"); 01581 goto error; 01582 } 01583 } 01584 else if (buffer[0] == '?') 01585 { 01586 /* 01587 * Gather rest of processing instruction... 01588 */ 01589 01590 while ((ch = (*getc_cb)(p, &encoding)) != EOF) 01591 { 01592 if (ch == '>' && bufptr > buffer && bufptr[-1] == '?') 01593 break; 01594 else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) 01595 goto error; 01596 } 01597 01598 /* 01599 * Error out if we didn't get the whole processing instruction... 01600 */ 01601 01602 if (ch != '>') 01603 { 01604 /* 01605 * Print error and return... 01606 */ 01607 01608 mxml_error("Early EOF in processing instruction node!"); 01609 goto error; 01610 } 01611 01612 01613 /* 01614 * Otherwise add this as an element under the current parent... 01615 */ 01616 01617 *bufptr = '0円'; 01618 01619 if (!(parent = mxmlNewElement(parent, buffer))) 01620 { 01621 /* 01622 * Print error and return... 01623 */ 01624 01625 mxml_error("Unable to add processing instruction node to parent <%s>!", 01626 parent ? parent->value.element.name : "null"); 01627 goto error; 01628 } 01629 01630 if (cb) 01631 type = (*cb)(parent); 01632 } 01633 else if (buffer[0] == '!') 01634 { 01635 /* 01636 * Gather rest of declaration... 01637 */ 01638 01639 do 01640 { 01641 if (ch == '>') 01642 break; 01643 else 01644 { 01645 if (ch == '&') 01646 if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF) 01647 goto error; 01648 01649 if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) 01650 goto error; 01651 } 01652 } 01653 while ((ch = (*getc_cb)(p, &encoding)) != EOF); 01654 01655 /* 01656 * Error out if we didn't get the whole declaration... 01657 */ 01658 01659 if (ch != '>') 01660 { 01661 /* 01662 * Print error and return... 01663 */ 01664 01665 mxml_error("Early EOF in declaration node!"); 01666 goto error; 01667 } 01668 01669 /* 01670 * Otherwise add this as an element under the current parent... 01671 */ 01672 01673 *bufptr = '0円'; 01674 01675 node = mxmlNewElement(parent, buffer); 01676 if (!node) 01677 { 01678 /* 01679 * Print error and return... 01680 */ 01681 01682 mxml_error("Unable to add declaration node to parent <%s>!", 01683 parent ? parent->value.element.name : "null"); 01684 goto error; 01685 } 01686 01687 /* 01688 * Descend into this node, setting the value type as needed... 01689 */ 01690 01691 parent = node; 01692 01693 if (cb) 01694 type = (*cb)(parent); 01695 } 01696 else if (buffer[0] == '/') 01697 { 01698 /* 01699 * Handle close tag... 01700 */ 01701 01702 if (!parent || strcmp(buffer + 1, parent->value.element.name)) 01703 { 01704 /* 01705 * Close tag doesn't match tree; print an error for now... 01706 */ 01707 01708 mxml_error("Mismatched close tag <%s> under parent <%s>!", 01709 buffer, parent->value.element.name); 01710 goto error; 01711 } 01712 01713 /* 01714 * Keep reading until we see >... 01715 */ 01716 01717 while (ch != '>' && ch != EOF) 01718 ch = (*getc_cb)(p, &encoding); 01719 01720 /* 01721 * Ascend into the parent and set the value type as needed... 01722 */ 01723 01724 parent = parent->parent; 01725 01726 if (cb && parent) 01727 type = (*cb)(parent); 01728 } 01729 else 01730 { 01731 /* 01732 * Handle open tag... 01733 */ 01734 01735 node = mxmlNewElement(parent, buffer); 01736 01737 if (!node) 01738 { 01739 /* 01740 * Just print error for now... 01741 */ 01742 01743 mxml_error("Unable to add element node to parent <%s>!", 01744 parent ? parent->value.element.name : "null"); 01745 goto error; 01746 } 01747 01748 if (isspace(ch)) 01749 ch = mxml_parse_element(node, p, &encoding, getc_cb); 01750 else if (ch == '/') 01751 { 01752 if ((ch = (*getc_cb)(p, &encoding)) != '>') 01753 { 01754 mxml_error("Expected > but got '%c' instead for element <%s/>!", 01755 ch, buffer); 01756 goto error; 01757 } 01758 01759 ch = '/'; 01760 } 01761 01762 if (ch == EOF) 01763 break; 01764 01765 if (ch != '/') 01766 { 01767 /* 01768 * Descend into this node, setting the value type as needed... 01769 */ 01770 01771 parent = node; 01772 01773 if (cb && parent) 01774 type = (*cb)(parent); 01775 } 01776 } 01777 01778 bufptr = buffer; 01779 } 01780 else if (ch == '&') 01781 { 01782 /* 01783 * Add character entity to current buffer... 01784 */ 01785 01786 if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF) 01787 goto error; 01788 01789 if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) 01790 goto error; 01791 } 01792 else if (type == MXML_OPAQUE || type == MXML_CUSTOM || !isspace(ch)) 01793 { 01794 /* 01795 * Add character to current buffer... 01796 */ 01797 01798 if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) 01799 goto error; 01800 } 01801 } 01802 01803 /* 01804 * Free the string buffer - we don't need it anymore... 01805 */ 01806 01807 free(buffer); 01808 01809 /* 01810 * Find the top element and return it... 01811 */ 01812 01813 if (parent) 01814 { 01815 while (parent->parent != top && parent->parent) 01816 parent = parent->parent; 01817 } 01818 01819 return (parent); 01820 01821 /* 01822 * Common error return... 01823 */ 01824 01825 error: 01826 01827 mxmlDelete(first); 01828 01829 free(buffer); 01830 01831 return (NULL); 01832 } 01833 01834 01835 /* 01836 * 'mxml_parse_element()' - Parse an element for any attributes... 01837 */ 01838 01839 static int /* O - Terminating character */ 01840 mxml_parse_element(mxml_node_t *node, /* I - Element node */ 01841 void *p, /* I - Data to read from */ 01842 int *encoding, 01843 /* IO - Encoding */ 01844 int (*getc_cb)(void *, int *)) 01845 /* I - Data callback */ 01846 { 01847 int ch, /* Current character in file */ 01848 quote; /* Quoting character */ 01849 char *name, /* Attribute name */ 01850 *value, /* Attribute value */ 01851 *ptr; /* Pointer into name/value */ 01852 int namesize, /* Size of name string */ 01853 valsize; /* Size of value string */ 01854 01855 01856 01857 01858 /* 01859 * Initialize the name and value buffers... 01860 */ 01861 01862 if ((name = malloc(64)) == NULL) 01863 { 01864 mxml_error("Unable to allocate memory for name!"); 01865 return (EOF); 01866 } 01867 01868 namesize = 64; 01869 01870 if ((value = malloc(64)) == NULL) 01871 { 01872 free(name); 01873 mxml_error("Unable to allocate memory for value!"); 01874 return (EOF); 01875 } 01876 01877 valsize = 64; 01878 01879 /* 01880 * Loop until we hit a >, /, ?, or EOF... 01881 */ 01882 01883 while ((ch = (*getc_cb)(p, encoding)) != EOF) 01884 { 01885 #if DEBUG > 1 01886 fprintf(stderr, "parse_element: ch='%c'\n", ch); 01887 #endif /* DEBUG > 1 */ 01888 01889 /* 01890 * Skip leading whitespace... 01891 */ 01892 01893 if (isspace(ch)) 01894 continue; 01895 01896 /* 01897 * Stop at /, ?, or >... 01898 */ 01899 01900 if (ch == '/' || ch == '?') 01901 { 01902 /* 01903 * Grab the > character and print an error if it isn't there... 01904 */ 01905 01906 quote = (*getc_cb)(p, encoding); 01907 01908 if (quote != '>') 01909 { 01910 mxml_error("Expected '>' after '%c' for element %s, but got '%c'!", 01911 ch, node->value.element.name, quote); 01912 ch = EOF; 01913 } 01914 01915 break; 01916 } 01917 else if (ch == '>') 01918 break; 01919 01920 /* 01921 * Read the attribute name... 01922 */ 01923 01924 name[0] = ch; 01925 ptr = name + 1; 01926 01927 if (ch == '\"' || ch == '\'') 01928 { 01929 /* 01930 * Name is in quotes, so get a quoted string... 01931 */ 01932 01933 quote = ch; 01934 01935 while ((ch = (*getc_cb)(p, encoding)) != EOF) 01936 { 01937 if (ch == '&') 01938 if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) 01939 goto error; 01940 01941 if (mxml_add_char(ch, &ptr, &name, &namesize)) 01942 goto error; 01943 01944 if (ch == quote) 01945 break; 01946 } 01947 } 01948 else 01949 { 01950 /* 01951 * Grab an normal, non-quoted name... 01952 */ 01953 01954 while ((ch = (*getc_cb)(p, encoding)) != EOF) 01955 if (isspace(ch) || ch == '=' || ch == '/' || ch == '>' || ch == '?') 01956 break; 01957 else 01958 { 01959 if (ch == '&') 01960 if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) 01961 goto error; 01962 01963 if (mxml_add_char(ch, &ptr, &name, &namesize)) 01964 goto error; 01965 } 01966 } 01967 01968 *ptr = '0円'; 01969 01970 if (mxmlElementGetAttr(node, name)) 01971 goto error; 01972 01973 if (ch == '=') 01974 { 01975 /* 01976 * Read the attribute value... 01977 */ 01978 01979 if ((ch = (*getc_cb)(p, encoding)) == EOF) 01980 { 01981 mxml_error("Missing value for attribute '%s' in element %s!", 01982 name, node->value.element.name); 01983 return (EOF); 01984 } 01985 01986 if (ch == '\'' || ch == '\"') 01987 { 01988 /* 01989 * Read quoted value... 01990 */ 01991 01992 quote = ch; 01993 ptr = value; 01994 01995 while ((ch = (*getc_cb)(p, encoding)) != EOF) 01996 if (ch == quote) 01997 break; 01998 else 01999 { 02000 if (ch == '&') 02001 if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) 02002 goto error; 02003 02004 if (mxml_add_char(ch, &ptr, &value, &valsize)) 02005 goto error; 02006 } 02007 02008 *ptr = '0円'; 02009 } 02010 else 02011 { 02012 /* 02013 * Read unquoted value... 02014 */ 02015 02016 value[0] = ch; 02017 ptr = value + 1; 02018 02019 while ((ch = (*getc_cb)(p, encoding)) != EOF) 02020 if (isspace(ch) || ch == '=' || ch == '/' || ch == '>') 02021 break; 02022 else 02023 { 02024 if (ch == '&') 02025 if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) 02026 goto error; 02027 02028 if (mxml_add_char(ch, &ptr, &value, &valsize)) 02029 goto error; 02030 } 02031 02032 *ptr = '0円'; 02033 } 02034 02035 /* 02036 * Set the attribute with the given string value... 02037 */ 02038 02039 mxmlElementSetAttr(node, name, value); 02040 } 02041 else 02042 { 02043 /* 02044 * Set the attribute with a NULL value... 02045 */ 02046 02047 mxmlElementSetAttr(node, name, NULL); 02048 } 02049 02050 /* 02051 * Check the end character... 02052 */ 02053 02054 if (ch == '/' || ch == '?') 02055 { 02056 /* 02057 * Grab the > character and print an error if it isn't there... 02058 */ 02059 02060 quote = (*getc_cb)(p, encoding); 02061 02062 if (quote != '>') 02063 { 02064 mxml_error("Expected '>' after '%c' for element %s, but got '%c'!", 02065 ch, node->value.element.name, quote); 02066 ch = EOF; 02067 } 02068 02069 break; 02070 } 02071 else if (ch == '>') 02072 break; 02073 } 02074 02075 /* 02076 * Free the name and value buffers and return... 02077 */ 02078 02079 free(name); 02080 free(value); 02081 02082 return (ch); 02083 02084 /* 02085 * Common error return point... 02086 */ 02087 02088 error: 02089 02090 free(name); 02091 free(value); 02092 02093 return (EOF); 02094 } 02095 02096 02097 /* 02098 * 'mxml_string_getc()' - Get a character from a string. 02099 */ 02100 02101 static int /* O - Character or EOF */ 02102 mxml_string_getc(void *p, /* I - Pointer to file */ 02103 int *encoding) /* IO - Encoding */ 02104 { 02105 int ch; /* Character */ 02106 const char **s; /* Pointer to string pointer */ 02107 02108 02109 s = (const char **)p; 02110 02111 if ((ch = (*s)[0] & 255) != 0 || *encoding == ENCODE_UTF16LE) 02112 { 02113 /* 02114 * Got character; convert UTF-8 to integer and return... 02115 */ 02116 02117 (*s)++; 02118 02119 switch (*encoding) 02120 { 02121 case ENCODE_UTF8 : 02122 if (!(ch & 0x80)) 02123 { 02124 #if DEBUG > 1 02125 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); 02126 #endif /* DEBUG > 1 */ 02127 02128 if (mxml_bad_char(ch)) 02129 { 02130 mxml_error("Bad control character 0x%02x not allowed by XML standard!", 02131 ch); 02132 return (EOF); 02133 } 02134 02135 return (ch); 02136 } 02137 else if (ch == 0xfe) 02138 { 02139 /* 02140 * UTF-16 big-endian BOM? 02141 */ 02142 02143 if (((*s)[0] & 255) != 0xff) 02144 return (EOF); 02145 02146 *encoding = ENCODE_UTF16BE; 02147 (*s)++; 02148 02149 return (mxml_string_getc(p, encoding)); 02150 } 02151 else if (ch == 0xff) 02152 { 02153 /* 02154 * UTF-16 little-endian BOM? 02155 */ 02156 02157 if (((*s)[0] & 255) != 0xfe) 02158 return (EOF); 02159 02160 *encoding = ENCODE_UTF16LE; 02161 (*s)++; 02162 02163 return (mxml_string_getc(p, encoding)); 02164 } 02165 else if ((ch & 0xe0) == 0xc0) 02166 { 02167 /* 02168 * Two-byte value... 02169 */ 02170 02171 if (((*s)[0] & 0xc0) != 0x80) 02172 return (EOF); 02173 02174 ch = ((ch & 0x1f) << 6) | ((*s)[0] & 0x3f); 02175 02176 (*s)++; 02177 02178 if (ch < 0x80) 02179 return (EOF); 02180 02181 #if DEBUG > 1 02182 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); 02183 #endif /* DEBUG > 1 */ 02184 02185 return (ch); 02186 } 02187 else if ((ch & 0xf0) == 0xe0) 02188 { 02189 /* 02190 * Three-byte value... 02191 */ 02192 02193 if (((*s)[0] & 0xc0) != 0x80 || 02194 ((*s)[1] & 0xc0) != 0x80) 02195 return (EOF); 02196 02197 ch = ((((ch & 0x0f) << 6) | ((*s)[0] & 0x3f)) << 6) | ((*s)[1] & 0x3f); 02198 02199 (*s) += 2; 02200 02201 if (ch < 0x800) 02202 return (EOF); 02203 02204 #if DEBUG > 1 02205 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); 02206 #endif /* DEBUG > 1 */ 02207 02208 return (ch); 02209 } 02210 else if ((ch & 0xf8) == 0xf0) 02211 { 02212 /* 02213 * Four-byte value... 02214 */ 02215 02216 if (((*s)[0] & 0xc0) != 0x80 || 02217 ((*s)[1] & 0xc0) != 0x80 || 02218 ((*s)[2] & 0xc0) != 0x80) 02219 return (EOF); 02220 02221 ch = ((((((ch & 0x07) << 6) | ((*s)[0] & 0x3f)) << 6) | 02222 ((*s)[1] & 0x3f)) << 6) | ((*s)[2] & 0x3f); 02223 02224 (*s) += 3; 02225 02226 if (ch < 0x10000) 02227 return (EOF); 02228 02229 #if DEBUG > 1 02230 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); 02231 #endif /* DEBUG > 1 */ 02232 02233 return (ch); 02234 } 02235 else 02236 return (EOF); 02237 02238 case ENCODE_UTF16BE : 02239 /* 02240 * Read UTF-16 big-endian char... 02241 */ 02242 02243 ch = (ch << 8) | ((*s)[0] & 255); 02244 (*s) ++; 02245 02246 if (mxml_bad_char(ch)) 02247 { 02248 mxml_error("Bad control character 0x%02x not allowed by XML standard!", 02249 ch); 02250 return (EOF); 02251 } 02252 else if (ch >= 0xd800 && ch <= 0xdbff) 02253 { 02254 /* 02255 * Multi-word UTF-16 char... 02256 */ 02257 02258 int lch; /* Lower word */ 02259 02260 02261 if (!(*s)[0]) 02262 return (EOF); 02263 02264 lch = (((*s)[0] & 255) << 8) | ((*s)[1] & 255); 02265 (*s) += 2; 02266 02267 if (lch < 0xdc00 || lch >= 0xdfff) 02268 return (EOF); 02269 02270 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; 02271 } 02272 02273 #if DEBUG > 1 02274 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); 02275 #endif /* DEBUG > 1 */ 02276 02277 return (ch); 02278 02279 case ENCODE_UTF16LE : 02280 /* 02281 * Read UTF-16 little-endian char... 02282 */ 02283 02284 ch = ch | (((*s)[0] & 255) << 8); 02285 02286 if (!ch) 02287 { 02288 (*s) --; 02289 return (EOF); 02290 } 02291 02292 (*s) ++; 02293 02294 if (mxml_bad_char(ch)) 02295 { 02296 mxml_error("Bad control character 0x%02x not allowed by XML standard!", 02297 ch); 02298 return (EOF); 02299 } 02300 else if (ch >= 0xd800 && ch <= 0xdbff) 02301 { 02302 /* 02303 * Multi-word UTF-16 char... 02304 */ 02305 02306 int lch; /* Lower word */ 02307 02308 02309 if (!(*s)[1]) 02310 return (EOF); 02311 02312 lch = (((*s)[1] & 255) << 8) | ((*s)[0] & 255); 02313 (*s) += 2; 02314 02315 if (lch < 0xdc00 || lch >= 0xdfff) 02316 return (EOF); 02317 02318 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; 02319 } 02320 02321 #if DEBUG > 1 02322 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); 02323 #endif /* DEBUG > 1 */ 02324 02325 return (ch); 02326 } 02327 } 02328 02329 return (EOF); 02330 } 02331 02332 02333 /* 02334 * 'mxml_string_putc()' - Write a character to a string. 02335 */ 02336 02337 static int /* O - 0 on success, -1 on failure */ 02338 mxml_string_putc(int ch, /* I - Character to write */ 02339 void *p) /* I - Pointer to string pointers */ 02340 { 02341 char **pp; /* Pointer to string pointers */ 02342 02343 02344 pp = (char **)p; 02345 02346 if (ch < 0x80) 02347 { 02348 /* 02349 * Plain ASCII doesn't need special encoding... 02350 */ 02351 02352 if (pp[0] < pp[1]) 02353 pp[0][0] = ch; 02354 02355 pp[0] ++; 02356 } 02357 else if (ch < 0x800) 02358 { 02359 /* 02360 * Two-byte UTF-8 character... 02361 */ 02362 02363 if ((pp[0] + 1) < pp[1]) 02364 { 02365 pp[0][0] = 0xc0 | (ch >> 6); 02366 pp[0][1] = 0x80 | (ch & 0x3f); 02367 } 02368 02369 pp[0] += 2; 02370 } 02371 else if (ch < 0x10000) 02372 { 02373 /* 02374 * Three-byte UTF-8 character... 02375 */ 02376 02377 if ((pp[0] + 2) < pp[1]) 02378 { 02379 pp[0][0] = 0xe0 | (ch >> 12); 02380 pp[0][1] = 0x80 | ((ch >> 6) & 0x3f); 02381 pp[0][2] = 0x80 | (ch & 0x3f); 02382 } 02383 02384 pp[0] += 3; 02385 } 02386 else 02387 { 02388 /* 02389 * Four-byte UTF-8 character... 02390 */ 02391 02392 if ((pp[0] + 2) < pp[1]) 02393 { 02394 pp[0][0] = 0xf0 | (ch >> 18); 02395 pp[0][1] = 0x80 | ((ch >> 12) & 0x3f); 02396 pp[0][2] = 0x80 | ((ch >> 6) & 0x3f); 02397 pp[0][3] = 0x80 | (ch & 0x3f); 02398 } 02399 02400 pp[0] += 4; 02401 } 02402 02403 return (0); 02404 } 02405 02406 02407 /* 02408 * 'mxml_write_name()' - Write a name string. 02409 */ 02410 02411 static int /* O - 0 on success, -1 on failure */ 02412 mxml_write_name(const char *s, /* I - Name to write */ 02413 void *p, /* I - Write pointer */ 02414 int (*putc_cb)(int, void *)) 02415 /* I - Write callback */ 02416 { 02417 char quote; /* Quote character */ 02418 const char *name; /* Entity name */ 02419 02420 02421 if (*s == '\"' || *s == '\'') 02422 { 02423 /* 02424 * Write a quoted name string... 02425 */ 02426 02427 if ((*putc_cb)(*s, p) < 0) 02428 return (-1); 02429 02430 quote = *s++; 02431 02432 while (*s && *s != quote) 02433 { 02434 if ((name = mxmlEntityGetName(*s)) != NULL) 02435 { 02436 if ((*putc_cb)('&', p) < 0) 02437 return (-1); 02438 02439 while (*name) 02440 { 02441 if ((*putc_cb)(*name, p) < 0) 02442 return (-1); 02443 02444 name ++; 02445 } 02446 02447 if ((*putc_cb)(';', p) < 0) 02448 return (-1); 02449 } 02450 else if ((*putc_cb)(*s, p) < 0) 02451 return (-1); 02452 02453 s ++; 02454 } 02455 02456 /* 02457 * Write the end quote... 02458 */ 02459 02460 if ((*putc_cb)(quote, p) < 0) 02461 return (-1); 02462 } 02463 else 02464 { 02465 /* 02466 * Write a non-quoted name string... 02467 */ 02468 02469 while (*s) 02470 { 02471 if ((*putc_cb)(*s, p) < 0) 02472 return (-1); 02473 02474 s ++; 02475 } 02476 } 02477 02478 return (0); 02479 } 02480 02481 02482 /* 02483 * 'mxml_write_node()' - Save an XML node to a file. 02484 */ 02485 02486 static int /* O - Column or -1 on error */ 02487 mxml_write_node(mxml_node_t *node, /* I - Node to write */ 02488 void *p, /* I - File to write to */ 02489 const char *(*cb)(mxml_node_t *, int), 02490 /* I - Whitespace callback */ 02491 int col, /* I - Current column */ 02492 int (*putc_cb)(int, void *)) 02493 { 02494 int i, /* Looping var */ 02495 width; /* Width of attr + value */ 02496 mxml_attr_t *attr; /* Current attribute */ 02497 char s[255]; /* Temporary string */ 02498 02499 02500 while (node != NULL) 02501 { 02502 /* 02503 * Print the node value... 02504 */ 02505 02506 switch (node->type) 02507 { 02508 case MXML_ELEMENT : 02509 col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb); 02510 02511 if ((*putc_cb)('<', p) < 0) 02512 return (-1); 02513 if (node->value.element.name[0] == '?' || 02514 !strncmp(node->value.element.name, "!--", 3) || 02515 !strncmp(node->value.element.name, "![CDATA[", 8)) 02516 { 02517 /* 02518 * Comments, CDATA, and processing instructions do not 02519 * use character entities. 02520 */ 02521 02522 const char *ptr; /* Pointer into name */ 02523 02524 02525 for (ptr = node->value.element.name; *ptr; ptr ++) 02526 if ((*putc_cb)(*ptr, p) < 0) 02527 return (-1); 02528 02529 /* 02530 * Prefer a newline for whitespace after ?xml... 02531 */ 02532 02533 if (!strncmp(node->value.element.name, "?xml", 4)) 02534 col = MXML_WRAP; 02535 } 02536 else if (mxml_write_name(node->value.element.name, p, putc_cb) < 0) 02537 return (-1); 02538 02539 col += strlen(node->value.element.name) + 1; 02540 02541 for (i = node->value.element.num_attrs, attr = node->value.element.attrs; 02542 i > 0; 02543 i --, attr ++) 02544 { 02545 width = strlen(attr->name); 02546 02547 if (attr->value) 02548 width += strlen(attr->value) + 3; 02549 02550 if ((col + width) > MXML_WRAP) 02551 { 02552 if ((*putc_cb)('\n', p) < 0) 02553 return (-1); 02554 02555 col = 0; 02556 } 02557 else 02558 { 02559 if ((*putc_cb)(' ', p) < 0) 02560 return (-1); 02561 02562 col ++; 02563 } 02564 02565 if (mxml_write_name(attr->name, p, putc_cb) < 0) 02566 return (-1); 02567 02568 if (attr->value) 02569 { 02570 if ((*putc_cb)('=', p) < 0) 02571 return (-1); 02572 if ((*putc_cb)('\"', p) < 0) 02573 return (-1); 02574 if (mxml_write_string(attr->value, p, putc_cb) < 0) 02575 return (-1); 02576 if ((*putc_cb)('\"', p) < 0) 02577 return (-1); 02578 } 02579 02580 col += width; 02581 } 02582 02583 if (node->child) 02584 { 02585 /* 02586 * Write children... 02587 */ 02588 02589 if ((*putc_cb)('>', p) < 0) 02590 return (-1); 02591 else 02592 col ++; 02593 02594 col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); 02595 02596 if ((col = mxml_write_node(node->child, p, cb, col, putc_cb)) < 0) 02597 return (-1); 02598 02599 /* 02600 * The ? and ! elements are special-cases and have no end tags... 02601 */ 02602 02603 if (node->value.element.name[0] != '!' && 02604 node->value.element.name[0] != '?') 02605 { 02606 col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb); 02607 02608 if ((*putc_cb)('<', p) < 0) 02609 return (-1); 02610 if ((*putc_cb)('/', p) < 0) 02611 return (-1); 02612 if (mxml_write_string(node->value.element.name, p, putc_cb) < 0) 02613 return (-1); 02614 if ((*putc_cb)('>', p) < 0) 02615 return (-1); 02616 02617 col += strlen(node->value.element.name) + 3; 02618 02619 col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_CLOSE, col, putc_cb); 02620 } 02621 } 02622 else if (node->value.element.name[0] == '!' || 02623 node->value.element.name[0] == '?') 02624 { 02625 /* 02626 * The ? and ! elements are special-cases... 02627 */ 02628 02629 if ((*putc_cb)('>', p) < 0) 02630 return (-1); 02631 else 02632 col ++; 02633 02634 col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); 02635 } 02636 else 02637 { 02638 if ((*putc_cb)(' ', p) < 0) 02639 return (-1); 02640 if ((*putc_cb)('/', p) < 0) 02641 return (-1); 02642 if ((*putc_cb)('>', p) < 0) 02643 return (-1); 02644 02645 col += 3; 02646 02647 col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); 02648 } 02649 break; 02650 02651 case MXML_INTEGER : 02652 if (node->prev) 02653 { 02654 if (col > MXML_WRAP) 02655 { 02656 if ((*putc_cb)('\n', p) < 0) 02657 return (-1); 02658 02659 col = 0; 02660 } 02661 else if ((*putc_cb)(' ', p) < 0) 02662 return (-1); 02663 else 02664 col ++; 02665 } 02666 02667 sprintf(s, "%d", node->value.integer); 02668 if (mxml_write_string(s, p, putc_cb) < 0) 02669 return (-1); 02670 02671 col += strlen(s); 02672 break; 02673 02674 case MXML_OPAQUE : 02675 if (mxml_write_string(node->value.opaque, p, putc_cb) < 0) 02676 return (-1); 02677 02678 col += strlen(node->value.opaque); 02679 break; 02680 02681 case MXML_REAL : 02682 if (node->prev) 02683 { 02684 if (col > MXML_WRAP) 02685 { 02686 if ((*putc_cb)('\n', p) < 0) 02687 return (-1); 02688 02689 col = 0; 02690 } 02691 else if ((*putc_cb)(' ', p) < 0) 02692 return (-1); 02693 else 02694 col ++; 02695 } 02696 02697 sprintf(s, "%f", node->value.real); 02698 if (mxml_write_string(s, p, putc_cb) < 0) 02699 return (-1); 02700 02701 col += strlen(s); 02702 break; 02703 02704 case MXML_TEXT : 02705 if (node->value.text.whitespace && col > 0) 02706 { 02707 if (col > MXML_WRAP) 02708 { 02709 if ((*putc_cb)('\n', p) < 0) 02710 return (-1); 02711 02712 col = 0; 02713 } 02714 else if ((*putc_cb)(' ', p) < 0) 02715 return (-1); 02716 else 02717 col ++; 02718 } 02719 02720 if (mxml_write_string(node->value.text.string, p, putc_cb) < 0) 02721 return (-1); 02722 02723 col += strlen(node->value.text.string); 02724 break; 02725 02726 case MXML_CUSTOM : 02727 if (mxml_custom_save_cb) 02728 { 02729 char *data; /* Custom data string */ 02730 const char *newline; /* Last newline in string */ 02731 02732 02733 if ((data = (*mxml_custom_save_cb)(node)) == NULL) 02734 return (-1); 02735 02736 if (mxml_write_string(data, p, putc_cb) < 0) 02737 return (-1); 02738 02739 if ((newline = strrchr(data, '\n')) == NULL) 02740 col += strlen(data); 02741 else 02742 col = strlen(newline); 02743 02744 free(data); 02745 break; 02746 } 02747 02748 default : /* Should never happen */ 02749 return (-1); 02750 } 02751 02752 /* 02753 * Next node... 02754 */ 02755 02756 node = node->next; 02757 } 02758 02759 return (col); 02760 } 02761 02762 02763 /* 02764 * 'mxml_write_string()' - Write a string, escaping & and < as needed. 02765 */ 02766 02767 static int /* O - 0 on success, -1 on failure */ 02768 mxml_write_string(const char *s, /* I - String to write */ 02769 void *p, /* I - Write pointer */ 02770 int (*putc_cb)(int, void *)) 02771 /* I - Write callback */ 02772 { 02773 const char *name; /* Entity name, if any */ 02774 02775 02776 while (*s) 02777 { 02778 if ((name = mxmlEntityGetName(*s)) != NULL) 02779 { 02780 if ((*putc_cb)('&', p) < 0) 02781 return (-1); 02782 02783 while (*name) 02784 { 02785 if ((*putc_cb)(*name, p) < 0) 02786 return (-1); 02787 name ++; 02788 } 02789 02790 if ((*putc_cb)(';', p) < 0) 02791 return (-1); 02792 } 02793 else if ((*putc_cb)(*s, p) < 0) 02794 return (-1); 02795 02796 s ++; 02797 } 02798 02799 return (0); 02800 } 02801 02802 02803 /* 02804 * 'mxml_write_ws()' - Do whitespace callback... 02805 */ 02806 02807 static int /* O - New column */ 02808 mxml_write_ws(mxml_node_t *node, /* I - Current node */ 02809 void *p, /* I - Write pointer */ 02810 const char *(*cb)(mxml_node_t *, int), 02811 /* I - Callback function */ 02812 int ws, /* I - Where value */ 02813 int col, /* I - Current column */ 02814 int (*putc_cb)(int, void *)) 02815 /* I - Write callback */ 02816 { 02817 const char *s; /* Whitespace string */ 02818 02819 02820 if (cb && (s = (*cb)(node, ws)) != NULL) 02821 { 02822 while (*s) 02823 { 02824 if ((*putc_cb)(*s, p) < 0) 02825 return (-1); 02826 else if (*s == '\n') 02827 col = 0; 02828 else if (*s == '\t') 02829 { 02830 col += MXML_TAB; 02831 col = col - (col % MXML_TAB); 02832 } 02833 else 02834 col ++; 02835 02836 s ++; 02837 } 02838 } 02839 02840 return (col); 02841 } 02842 02843 02844 /* 02845 * End of "$Id: mxml-file.c,v 1.1 2007年05月23日 20:43:27 david_ko Exp $". 02846 */