1 /*
2 * "$Id: mxml-file.c 438 2011-03-24 05:47:51Z mike $"
3 *
4 * File loading code for Mini-XML, a small XML-like file parsing library.
5 *
6 * Copyright 2003-2011 by Michael R Sweet.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Michael R Sweet and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "COPYING"
11 * which should have been included with this file. If this file is
12 * missing or damaged, see the license at:
13 *
14 * http://www.minixml.org/
15 *
16 * Contents:
17 *
18 * mxmlLoadFd() - Load a file descriptor into an XML node tree.
19 * mxmlLoadFile() - Load a file into an XML node tree.
20 * mxmlLoadString() - Load a string into an XML node tree.
21 * mxmlSaveAllocString() - Save an XML tree to an allocated string.
22 * mxmlSaveFd() - Save an XML tree to a file descriptor.
23 * mxmlSaveFile() - Save an XML tree to a file.
24 * mxmlSaveString() - Save an XML node tree to a string.
25 * mxmlSAXLoadFd() - Load a file descriptor into an XML node tree
26 * using a SAX callback.
27 * mxmlSAXLoadFile() - Load a file into an XML node tree
28 * using a SAX callback.
29 * mxmlSAXLoadString() - Load a string into an XML node tree
30 * using a SAX callback.
31 * mxmlSetCustomHandlers() - Set the handling functions for custom data.
32 * mxmlSetErrorCallback() - Set the error message callback.
33 * mxmlSetWrapMargin() - Set the wrap margin when saving XML data.
34 * mxml_add_char() - Add a character to a buffer, expanding as needed.
35 * mxml_fd_getc() - Read a character from a file descriptor.
36 * mxml_fd_putc() - Write a character to a file descriptor.
37 * mxml_fd_read() - Read a buffer of data from a file descriptor.
38 * mxml_fd_write() - Write a buffer of data to a file descriptor.
39 * mxml_file_getc() - Get a character from a file.
40 * mxml_file_putc() - Write a character to a file.
41 * mxml_get_entity() - Get the character corresponding to an entity...
42 * mxml_load_data() - Load data into an XML node tree.
43 * mxml_parse_element() - Parse an element for any attributes...
44 * mxml_string_getc() - Get a character from a string.
45 * mxml_string_putc() - Write a character to a string.
46 * mxml_write_name() - Write a name string.
47 * mxml_write_node() - Save an XML node to a file.
48 * mxml_write_string() - Write a string, escaping & and < as needed.
49 * mxml_write_ws() - Do whitespace callback...
50 */
52 /*
53 * Include necessary headers...
54 */
56 #ifndef WIN32
57 # include <unistd.h>
58 #endif /* !WIN32 */
59 #include "mxml-private.h"
62 /*
63 * Character encoding...
64 */
66 #define ENCODE_UTF8 0 /* UTF-8 */
67 #define ENCODE_UTF16BE 1 /* UTF-16 Big-Endian */
68 #define ENCODE_UTF16LE 2 /* UTF-16 Little-Endian */
71 /*
72 * Macro to test for a bad XML character...
73 */
75 #define mxml_bad_char(ch) ((ch) < ' ' && (ch) != '\n' && (ch) != '\r' && (ch) != '\t')
78 /*
79 * Types and structures...
80 */
82 typedef int (*_mxml_getc_cb_t)(void *, int *);
83 typedef int (*_mxml_putc_cb_t)(int, void *);
85 typedef struct _mxml_fdbuf_s /**** File descriptor buffer ****/
86 {
87 int fd; /* File descriptor */
88 unsigned char *current, /* Current position in buffer */
89 *end, /* End of buffer */
90 buffer[8192]; /* Character buffer */
91 } _mxml_fdbuf_t;
94 /*
95 * Local functions...
96 */
98 static int mxml_add_char(int ch, char **ptr, char **buffer,
99 int *bufsize);
100 static int mxml_fd_getc(void *p, int *encoding);
101 static int mxml_fd_putc(int ch, void *p);
102 static int mxml_fd_read(_mxml_fdbuf_t *buf);
103 static int mxml_fd_write(_mxml_fdbuf_t *buf);
104 static int mxml_file_getc(void *p, int *encoding);
105 static int mxml_file_putc(int ch, void *p);
106 static int mxml_get_entity(mxml_node_t *parent, void *p,
107 int *encoding,
108 _mxml_getc_cb_t getc_cb);
109 static inline int mxml_isspace(int ch)
110 {
111 return (ch == ' ' || ch == '\t' || ch == '\r' ||
112 ch == '\n');
113 }
114 static mxml_node_t *mxml_load_data(mxml_node_t *top, void *p,
115 mxml_load_cb_t cb,
116 _mxml_getc_cb_t getc_cb,
117 mxml_sax_cb_t sax_cb, void *sax_data);
118 static int mxml_parse_element(mxml_node_t *node, void *p,
119 int *encoding,
120 _mxml_getc_cb_t getc_cb);
121 static int mxml_string_getc(void *p, int *encoding);
122 static int mxml_string_putc(int ch, void *p);
123 static int mxml_write_name(const char *s, void *p,
124 _mxml_putc_cb_t putc_cb);
125 static int mxml_write_node(mxml_node_t *node, void *p,
126 mxml_save_cb_t cb, int col,
127 _mxml_putc_cb_t putc_cb,
128 _mxml_global_t *global);
129 static int mxml_write_string(const char *s, void *p,
130 _mxml_putc_cb_t putc_cb);
131 static int mxml_write_ws(mxml_node_t *node, void *p,
132 mxml_save_cb_t cb, int ws,
133 int col, _mxml_putc_cb_t putc_cb);
136 /*
137 * 'mxmlLoadFd()' - Load a file descriptor into an XML node tree.
138 *
139 * The nodes in the specified file are added to the specified top node.
140 * If no top node is provided, the XML file MUST be well-formed with a
141 * single parent node like <?xml> for the entire file. The callback
142 * function returns the value type that should be used for child nodes.
143 * If MXML_NO_CALLBACK is specified then all child nodes will be either
144 * MXML_ELEMENT or MXML_TEXT nodes.
145 *
146 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
147 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
148 * child nodes of the specified type.
149 */
151 mxml_node_t * /* O - First node or NULL if the file could not be read. */
152 mxmlLoadFd(mxml_node_t *top, /* I - Top node */
153 int fd, /* I - File descriptor to read from */
154 mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */
155 {
156 _mxml_fdbuf_t buf; /* File descriptor buffer */
159 /*
160 * Initialize the file descriptor buffer...
161 */
163 buf.fd = fd;
164 buf.current = buf.buffer;
165 buf.end = buf.buffer;
167 /*
168 * Read the XML data...
169 */
171 return (mxml_load_data(top, &buf, cb, mxml_fd_getc, MXML_NO_CALLBACK, NULL));
172 }
175 /*
176 * 'mxmlLoadFile()' - Load a file into an XML node tree.
177 *
178 * The nodes in the specified file are added to the specified top node.
179 * If no top node is provided, the XML file MUST be well-formed with a
180 * single parent node like <?xml> for the entire file. The callback
181 * function returns the value type that should be used for child nodes.
182 * If MXML_NO_CALLBACK is specified then all child nodes will be either
183 * MXML_ELEMENT or MXML_TEXT nodes.
184 *
185 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
186 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
187 * child nodes of the specified type.
188 */
190 mxml_node_t * /* O - First node or NULL if the file could not be read. */
191 mxmlLoadFile(mxml_node_t *top, /* I - Top node */
192 FILE *fp, /* I - File to read from */
193 mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */
194 {
195 /*
196 * Read the XML data...
197 */
199 return (mxml_load_data(top, fp, cb, mxml_file_getc, MXML_NO_CALLBACK, NULL));
200 }
203 /*
204 * 'mxmlLoadString()' - Load a string into an XML node tree.
205 *
206 * The nodes in the specified string are added to the specified top node.
207 * If no top node is provided, the XML string MUST be well-formed with a
208 * single parent node like <?xml> for the entire string. The callback
209 * function returns the value type that should be used for child nodes.
210 * If MXML_NO_CALLBACK is specified then all child nodes will be either
211 * MXML_ELEMENT or MXML_TEXT nodes.
212 *
213 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
214 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
215 * child nodes of the specified type.
216 */
218 mxml_node_t * /* O - First node or NULL if the string has errors. */
219 mxmlLoadString(mxml_node_t *top, /* I - Top node */
220 const char *s, /* I - String to load */
221 mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */
222 {
223 /*
224 * Read the XML data...
225 */
227 return (mxml_load_data(top, (void *)&s, cb, mxml_string_getc, MXML_NO_CALLBACK,
228 NULL));
229 }
232 /*
233 * 'mxmlSaveAllocString()' - Save an XML tree to an allocated string.
234 *
235 * This function returns a pointer to a string containing the textual
236 * representation of the XML node tree. The string should be freed
237 * using the free() function when you are done with it. NULL is returned
238 * if the node would produce an empty string or if the string cannot be
239 * allocated.
240 *
241 * The callback argument specifies a function that returns a whitespace
242 * string or NULL before and after each element. If MXML_NO_CALLBACK
243 * is specified, whitespace will only be added before MXML_TEXT nodes
244 * with leading whitespace and before attribute names inside opening
245 * element tags.
246 */
248 char * /* O - Allocated string or NULL */
249 mxmlSaveAllocString(
250 mxml_node_t *node, /* I - Node to write */
251 mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */
252 {
253 int bytes; /* Required bytes */
254 char buffer[8192]; /* Temporary buffer */
255 char *s; /* Allocated string */
258 /*
259 * Write the node to the temporary buffer...
260 */
262 bytes = mxmlSaveString(node, buffer, sizeof(buffer), cb);
264 if (bytes <= 0)
265 return (NULL);
267 if (bytes < (int)(sizeof(buffer) - 1))
268 {
269 /*
270 * Node fit inside the buffer, so just duplicate that string and
271 * return...
272 */
274 return (strdup(buffer));
275 }
277 /*
278 * Allocate a buffer of the required size and save the node to the
279 * new buffer...
280 */
282 if ((s = malloc(bytes + 1)) == NULL)
283 return (NULL);
285 mxmlSaveString(node, s, bytes + 1, cb);
287 /*
288 * Return the allocated string...
289 */
291 return (s);
292 }
295 /*
296 * 'mxmlSaveFd()' - Save an XML tree to a file descriptor.
297 *
298 * The callback argument specifies a function that returns a whitespace
299 * string or NULL before and after each element. If MXML_NO_CALLBACK
300 * is specified, whitespace will only be added before MXML_TEXT nodes
301 * with leading whitespace and before attribute names inside opening
302 * element tags.
303 */
305 int /* O - 0 on success, -1 on error. */
306 mxmlSaveFd(mxml_node_t *node, /* I - Node to write */
307 int fd, /* I - File descriptor to write to */
308 mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */
309 {
310 int col; /* Final column */
311 _mxml_fdbuf_t buf; /* File descriptor buffer */
312 _mxml_global_t *global = _mxml_global();
313 /* Global data */
316 /*
317 * Initialize the file descriptor buffer...
318 */
320 buf.fd = fd;
321 buf.current = buf.buffer;
322 buf.end = buf.buffer + sizeof(buf.buffer);
324 /*
325 * Write the node...
326 */
328 if ((col = mxml_write_node(node, &buf, cb, 0, mxml_fd_putc, global)) < 0)
329 return (-1);
331 if (col > 0)
332 if (mxml_fd_putc('\n', &buf) < 0)
333 return (-1);
335 /*
336 * Flush and return...
337 */
339 return (mxml_fd_write(&buf));
340 }
343 /*
344 * 'mxmlSaveFile()' - Save an XML tree to a file.
345 *
346 * The callback argument specifies a function that returns a whitespace
347 * string or NULL before and after each element. If MXML_NO_CALLBACK
348 * is specified, whitespace will only be added before MXML_TEXT nodes
349 * with leading whitespace and before attribute names inside opening
350 * element tags.
351 */
353 int /* O - 0 on success, -1 on error. */
354 mxmlSaveFile(mxml_node_t *node, /* I - Node to write */
355 FILE *fp, /* I - File to write to */
356 mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */
357 {
358 int col; /* Final column */
359 _mxml_global_t *global = _mxml_global();
360 /* Global data */
363 /*
364 * Write the node...
365 */
367 if ((col = mxml_write_node(node, fp, cb, 0, mxml_file_putc, global)) < 0)
368 return (-1);
370 if (col > 0)
371 if (putc('\n', fp) < 0)
372 return (-1);
374 /*
375 * Return 0 (success)...
376 */
378 return (0);
379 }
382 /*
383 * 'mxmlSaveString()' - Save an XML node tree to a string.
384 *
385 * This function returns the total number of bytes that would be
386 * required for the string but only copies (bufsize - 1) characters
387 * into the specified buffer.
388 *
389 * The callback argument specifies a function that returns a whitespace
390 * string or NULL before and after each element. If MXML_NO_CALLBACK
391 * is specified, whitespace will only be added before MXML_TEXT nodes
392 * with leading whitespace and before attribute names inside opening
393 * element tags.
394 */
396 int /* O - Size of string */
397 mxmlSaveString(mxml_node_t *node, /* I - Node to write */
398 char *buffer, /* I - String buffer */
399 int bufsize, /* I - Size of string buffer */
400 mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */
401 {
402 int col; /* Final column */
403 char *ptr[2]; /* Pointers for putc_cb */
404 _mxml_global_t *global = _mxml_global();
405 /* Global data */
408 /*
409 * Write the node...
410 */
412 ptr[0] = buffer;
413 ptr[1] = buffer + bufsize;
415 if ((col = mxml_write_node(node, ptr, cb, 0, mxml_string_putc, global)) < 0)
416 return (-1);
418 if (col > 0)
419 mxml_string_putc('\n', ptr);
421 /*
422 * Nul-terminate the buffer...
423 */
425 if (ptr[0] >= ptr[1])
426 buffer[bufsize - 1] = '\0';
427 else
428 ptr[0][0] = '\0';
430 /*
431 * Return the number of characters...
432 */
434 return (ptr[0] - buffer);
435 }
438 /*
439 * 'mxmlSAXLoadFd()' - Load a file descriptor into an XML node tree
440 * using a SAX callback.
441 *
442 * The nodes in the specified file are added to the specified top node.
443 * If no top node is provided, the XML file MUST be well-formed with a
444 * single parent node like <?xml> for the entire file. The callback
445 * function returns the value type that should be used for child nodes.
446 * If MXML_NO_CALLBACK is specified then all child nodes will be either
447 * MXML_ELEMENT or MXML_TEXT nodes.
448 *
449 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
450 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
451 * child nodes of the specified type.
452 *
453 * The SAX callback must call mxmlRetain() for any nodes that need to
454 * be kept for later use. Otherwise, nodes are deleted when the parent
455 * node is closed or after each data, comment, CDATA, or directive node.
456 *
457 * @since Mini-XML 2.3@
458 */
460 mxml_node_t * /* O - First node or NULL if the file could not be read. */
461 mxmlSAXLoadFd(mxml_node_t *top, /* I - Top node */
462 int fd, /* I - File descriptor to read from */
463 mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */
464 mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */
465 void *sax_data) /* I - SAX user data */
466 {
467 _mxml_fdbuf_t buf; /* File descriptor buffer */
470 /*
471 * Initialize the file descriptor buffer...
472 */
474 buf.fd = fd;
475 buf.current = buf.buffer;
476 buf.end = buf.buffer;
478 /*
479 * Read the XML data...
480 */
482 return (mxml_load_data(top, &buf, cb, mxml_fd_getc, sax_cb, sax_data));
483 }
486 /*
487 * 'mxmlSAXLoadFile()' - Load a file into an XML node tree
488 * using a SAX callback.
489 *
490 * The nodes in the specified file are added to the specified top node.
491 * If no top node is provided, the XML file MUST be well-formed with a
492 * single parent node like <?xml> for the entire file. The callback
493 * function returns the value type that should be used for child nodes.
494 * If MXML_NO_CALLBACK is specified then all child nodes will be either
495 * MXML_ELEMENT or MXML_TEXT nodes.
496 *
497 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
498 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
499 * child nodes of the specified type.
500 *
501 * The SAX callback must call mxmlRetain() for any nodes that need to
502 * be kept for later use. Otherwise, nodes are deleted when the parent
503 * node is closed or after each data, comment, CDATA, or directive node.
504 *
505 * @since Mini-XML 2.3@
506 */
508 mxml_node_t * /* O - First node or NULL if the file could not be read. */
509 mxmlSAXLoadFile(
510 mxml_node_t *top, /* I - Top node */
511 FILE *fp, /* I - File to read from */
512 mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */
513 mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */
514 void *sax_data) /* I - SAX user data */
515 {
516 /*
517 * Read the XML data...
518 */
520 return (mxml_load_data(top, fp, cb, mxml_file_getc, sax_cb, sax_data));
521 }
524 /*
525 * 'mxmlSAXLoadString()' - Load a string into an XML node tree
526 * using a SAX callback.
527 *
528 * The nodes in the specified string are added to the specified top node.
529 * If no top node is provided, the XML string MUST be well-formed with a
530 * single parent node like <?xml> for the entire string. The callback
531 * function returns the value type that should be used for child nodes.
532 * If MXML_NO_CALLBACK is specified then all child nodes will be either
533 * MXML_ELEMENT or MXML_TEXT nodes.
534 *
535 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
536 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
537 * child nodes of the specified type.
538 *
539 * The SAX callback must call mxmlRetain() for any nodes that need to
540 * be kept for later use. Otherwise, nodes are deleted when the parent
541 * node is closed or after each data, comment, CDATA, or directive node.
542 *
543 * @since Mini-XML 2.3@
544 */
546 mxml_node_t * /* O - First node or NULL if the string has errors. */
547 mxmlSAXLoadString(
548 mxml_node_t *top, /* I - Top node */
549 const char *s, /* I - String to load */
550 mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */
551 mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */
552 void *sax_data) /* I - SAX user data */
553 {
554 /*
555 * Read the XML data...
556 */
558 return (mxml_load_data(top, (void *)&s, cb, mxml_string_getc, sax_cb, sax_data));
559 }
562 /*
563 * 'mxmlSetCustomHandlers()' - Set the handling functions for custom data.
564 *
565 * The load function accepts a node pointer and a data string and must
566 * return 0 on success and non-zero on error.
567 *
568 * The save function accepts a node pointer and must return a malloc'd
569 * string on success and NULL on error.
570 *
571 */
573 void
574 mxmlSetCustomHandlers(
575 mxml_custom_load_cb_t load, /* I - Load function */
576 mxml_custom_save_cb_t save) /* I - Save function */
577 {
578 _mxml_global_t *global = _mxml_global();
579 /* Global data */
582 global->custom_load_cb = load;
583 global->custom_save_cb = save;
584 }
587 /*
588 * 'mxmlSetErrorCallback()' - Set the error message callback.
589 */
591 void
592 mxmlSetErrorCallback(mxml_error_cb_t cb)/* I - Error callback function */
593 {
594 _mxml_global_t *global = _mxml_global();
595 /* Global data */
598 global->error_cb = cb;
599 }
602 /*
603 * 'mxmlSetWrapMargin()' - Set the wrap margin when saving XML data.
604 *
605 * Wrapping is disabled when "column" is 0.
606 *
607 * @since Mini-XML 2.3@
608 */
610 void
611 mxmlSetWrapMargin(int column) /* I - Column for wrapping, 0 to disable wrapping */
612 {
613 _mxml_global_t *global = _mxml_global();
614 /* Global data */
617 global->wrap = column;
618 }
621 /*
622 * 'mxml_add_char()' - Add a character to a buffer, expanding as needed.
623 */
625 static int /* O - 0 on success, -1 on error */
626 mxml_add_char(int ch, /* I - Character to add */
627 char **bufptr, /* IO - Current position in buffer */
628 char **buffer, /* IO - Current buffer */
629 int *bufsize) /* IO - Current buffer size */
630 {
631 char *newbuffer; /* New buffer value */
634 if (*bufptr >= (*buffer + *bufsize - 4))
635 {
636 /*
637 * Increase the size of the buffer...
638 */
640 if (*bufsize < 1024)
641 (*bufsize) *= 2;
642 else
643 (*bufsize) += 1024;
645 if ((newbuffer = realloc(*buffer, *bufsize)) == NULL)
646 {
647 free(*buffer);
649 mxml_error("Unable to expand string buffer to %d bytes!", *bufsize);
651 return (-1);
652 }
654 *bufptr = newbuffer + (*bufptr - *buffer);
655 *buffer = newbuffer;
656 }
658 if (ch < 0x80)
659 {
660 /*
661 * Single byte ASCII...
662 */
664 *(*bufptr)++ = ch;
665 }
666 else if (ch < 0x800)
667 {
668 /*
669 * Two-byte UTF-8...
670 */
672 *(*bufptr)++ = 0xc0 | (ch >> 6);
673 *(*bufptr)++ = 0x80 | (ch & 0x3f);
674 }
675 else if (ch < 0x10000)
676 {
677 /*
678 * Three-byte UTF-8...
679 */
681 *(*bufptr)++ = 0xe0 | (ch >> 12);
682 *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f);
683 *(*bufptr)++ = 0x80 | (ch & 0x3f);
684 }
685 else
686 {
687 /*
688 * Four-byte UTF-8...
689 */
691 *(*bufptr)++ = 0xf0 | (ch >> 18);
692 *(*bufptr)++ = 0x80 | ((ch >> 12) & 0x3f);
693 *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f);
694 *(*bufptr)++ = 0x80 | (ch & 0x3f);
695 }
697 return (0);
698 }
701 /*
702 * 'mxml_fd_getc()' - Read a character from a file descriptor.
703 */
705 static int /* O - Character or EOF */
706 mxml_fd_getc(void *p, /* I - File descriptor buffer */
707 int *encoding) /* IO - Encoding */
708 {
709 _mxml_fdbuf_t *buf; /* File descriptor buffer */
710 int ch, /* Current character */
711 temp; /* Temporary character */
714 /*
715 * Grab the next character in the buffer...
716 */
718 buf = (_mxml_fdbuf_t *)p;
720 if (buf->current >= buf->end)
721 if (mxml_fd_read(buf) < 0)
722 return (EOF);
724 ch = *(buf->current)++;
726 switch (*encoding)
727 {
728 case ENCODE_UTF8 :
729 /*
730 * Got a UTF-8 character; convert UTF-8 to Unicode and return...
731 */
733 if (!(ch & 0x80))
734 {
735 #if DEBUG > 1
736 printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
737 #endif /* DEBUG > 1 */
739 if (mxml_bad_char(ch))
740 {
741 mxml_error("Bad control character 0x%02x not allowed by XML standard!",
742 ch);
743 return (EOF);
744 }
746 return (ch);
747 }
748 else if (ch == 0xfe)
749 {
750 /*
751 * UTF-16 big-endian BOM?
752 */
754 if (buf->current >= buf->end)
755 if (mxml_fd_read(buf) < 0)
756 return (EOF);
758 ch = *(buf->current)++;
760 if (ch != 0xff)
761 return (EOF);
763 *encoding = ENCODE_UTF16BE;
765 return (mxml_fd_getc(p, encoding));
766 }
767 else if (ch == 0xff)
768 {
769 /*
770 * UTF-16 little-endian BOM?
771 */
773 if (buf->current >= buf->end)
774 if (mxml_fd_read(buf) < 0)
775 return (EOF);
777 ch = *(buf->current)++;
779 if (ch != 0xfe)
780 return (EOF);
782 *encoding = ENCODE_UTF16LE;
784 return (mxml_fd_getc(p, encoding));
785 }
786 else if ((ch & 0xe0) == 0xc0)
787 {
788 /*
789 * Two-byte value...
790 */
792 if (buf->current >= buf->end)
793 if (mxml_fd_read(buf) < 0)
794 return (EOF);
796 temp = *(buf->current)++;
798 if ((temp & 0xc0) != 0x80)
799 return (EOF);
801 ch = ((ch & 0x1f) << 6) | (temp & 0x3f);
803 if (ch < 0x80)
804 {
805 mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
806 return (EOF);
807 }
808 }
809 else if ((ch & 0xf0) == 0xe0)
810 {
811 /*
812 * Three-byte value...
813 */
815 if (buf->current >= buf->end)
816 if (mxml_fd_read(buf) < 0)
817 return (EOF);
819 temp = *(buf->current)++;
821 if ((temp & 0xc0) != 0x80)
822 return (EOF);
824 ch = ((ch & 0x0f) << 6) | (temp & 0x3f);
826 if (buf->current >= buf->end)
827 if (mxml_fd_read(buf) < 0)
828 return (EOF);
830 temp = *(buf->current)++;
832 if ((temp & 0xc0) != 0x80)
833 return (EOF);
835 ch = (ch << 6) | (temp & 0x3f);
837 if (ch < 0x800)
838 {
839 mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
840 return (EOF);
841 }
843 /*
844 * Ignore (strip) Byte Order Mark (BOM)...
845 */
847 if (ch == 0xfeff)
848 return (mxml_fd_getc(p, encoding));
849 }
850 else if ((ch & 0xf8) == 0xf0)
851 {
852 /*
853 * Four-byte value...
854 */
856 if (buf->current >= buf->end)
857 if (mxml_fd_read(buf) < 0)
858 return (EOF);
860 temp = *(buf->current)++;
862 if ((temp & 0xc0) != 0x80)
863 return (EOF);
865 ch = ((ch & 0x07) << 6) | (temp & 0x3f);
867 if (buf->current >= buf->end)
868 if (mxml_fd_read(buf) < 0)
869 return (EOF);
871 temp = *(buf->current)++;
873 if ((temp & 0xc0) != 0x80)
874 return (EOF);
876 ch = (ch << 6) | (temp & 0x3f);
878 if (buf->current >= buf->end)
879 if (mxml_fd_read(buf) < 0)
880 return (EOF);
882 temp = *(buf->current)++;
884 if ((temp & 0xc0) != 0x80)
885 return (EOF);
887 ch = (ch << 6) | (temp & 0x3f);
889 if (ch < 0x10000)
890 {
891 mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
892 return (EOF);
893 }
894 }
895 else
896 return (EOF);
897 break;
899 case ENCODE_UTF16BE :
900 /*
901 * Read UTF-16 big-endian char...
902 */
904 if (buf->current >= buf->end)
905 if (mxml_fd_read(buf) < 0)
906 return (EOF);
908 temp = *(buf->current)++;
910 ch = (ch << 8) | temp;
912 if (mxml_bad_char(ch))
913 {
914 mxml_error("Bad control character 0x%02x not allowed by XML standard!",
915 ch);
916 return (EOF);
917 }
918 else if (ch >= 0xd800 && ch <= 0xdbff)
919 {
920 /*
921 * Multi-word UTF-16 char...
922 */
924 int lch;
926 if (buf->current >= buf->end)
927 if (mxml_fd_read(buf) < 0)
928 return (EOF);
930 lch = *(buf->current)++;
932 if (buf->current >= buf->end)
933 if (mxml_fd_read(buf) < 0)
934 return (EOF);
936 temp = *(buf->current)++;
938 lch = (lch << 8) | temp;
940 if (lch < 0xdc00 || lch >= 0xdfff)
941 return (EOF);
943 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
944 }
945 break;
947 case ENCODE_UTF16LE :
948 /*
949 * Read UTF-16 little-endian char...
950 */
952 if (buf->current >= buf->end)
953 if (mxml_fd_read(buf) < 0)
954 return (EOF);
956 temp = *(buf->current)++;
958 ch |= (temp << 8);
960 if (mxml_bad_char(ch))
961 {
962 mxml_error("Bad control character 0x%02x not allowed by XML standard!",
963 ch);
964 return (EOF);
965 }
966 else if (ch >= 0xd800 && ch <= 0xdbff)
967 {
968 /*
969 * Multi-word UTF-16 char...
970 */
972 int lch;
974 if (buf->current >= buf->end)
975 if (mxml_fd_read(buf) < 0)
976 return (EOF);
978 lch = *(buf->current)++;
980 if (buf->current >= buf->end)
981 if (mxml_fd_read(buf) < 0)
982 return (EOF);
984 temp = *(buf->current)++;
986 lch |= (temp << 8);
988 if (lch < 0xdc00 || lch >= 0xdfff)
989 return (EOF);
991 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
992 }
993 break;
994 }
996 #if DEBUG > 1
997 printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
998 #endif /* DEBUG > 1 */
1000 return (ch);
1001 }
1004 /*
1005 * 'mxml_fd_putc()' - Write a character to a file descriptor.
1006 */
1008 static int /* O - 0 on success, -1 on error */
1009 mxml_fd_putc(int ch, /* I - Character */
1010 void *p) /* I - File descriptor buffer */
1011 {
1012 _mxml_fdbuf_t *buf; /* File descriptor buffer */
1015 /*
1016 * Flush the write buffer as needed...
1017 */
1019 buf = (_mxml_fdbuf_t *)p;
1021 if (buf->current >= buf->end)
1022 if (mxml_fd_write(buf) < 0)
1023 return (-1);
1025 *(buf->current)++ = ch;
1027 /*
1028 * Return successfully...
1029 */
1031 return (0);
1032 }
1035 /*
1036 * 'mxml_fd_read()' - Read a buffer of data from a file descriptor.
1037 */
1039 static int /* O - 0 on success, -1 on error */
1040 mxml_fd_read(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */
1041 {
1042 int bytes; /* Bytes read... */
1045 /*
1046 * Range check input...
1047 */
1049 if (!buf)
1050 return (-1);
1052 /*
1053 * Read from the file descriptor...
1054 */
1056 while ((bytes = read(buf->fd, buf->buffer, sizeof(buf->buffer))) < 0)
1057 #ifdef EINTR
1058 if (errno != EAGAIN && errno != EINTR)
1059 #else
1060 if (errno != EAGAIN)
1061 #endif /* EINTR */
1062 return (-1);
1064 if (bytes == 0)
1065 return (-1);
1067 /*
1068 * Update the pointers and return success...
1069 */
1071 buf->current = buf->buffer;
1072 buf->end = buf->buffer + bytes;
1074 return (0);
1075 }
1078 /*
1079 * 'mxml_fd_write()' - Write a buffer of data to a file descriptor.
1080 */
1082 static int /* O - 0 on success, -1 on error */
1083 mxml_fd_write(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */
1084 {
1085 int bytes; /* Bytes written */
1086 unsigned char *ptr; /* Pointer into buffer */
1089 /*
1090 * Range check...
1091 */
1093 if (!buf)
1094 return (-1);
1096 /*
1097 * Return 0 if there is nothing to write...
1098 */
1100 if (buf->current == buf->buffer)
1101 return (0);
1103 /*
1104 * Loop until we have written everything...
1105 */
1107 for (ptr = buf->buffer; ptr < buf->current; ptr += bytes)
1108 if ((bytes = write(buf->fd, ptr, buf->current - ptr)) < 0)
1109 return (-1);
1111 /*
1112 * All done, reset pointers and return success...
1113 */
1115 buf->current = buf->buffer;
1117 return (0);
1118 }
1121 /*
1122 * 'mxml_file_getc()' - Get a character from a file.
1123 */
1125 static int /* O - Character or EOF */
1126 mxml_file_getc(void *p, /* I - Pointer to file */
1127 int *encoding) /* IO - Encoding */
1128 {
1129 int ch, /* Character from file */
1130 temp; /* Temporary character */
1131 FILE *fp; /* Pointer to file */
1134 /*
1135 * Read a character from the file and see if it is EOF or ASCII...
1136 */
1138 fp = (FILE *)p;
1139 ch = getc(fp);
1141 if (ch == EOF)
1142 return (EOF);
1144 switch (*encoding)
1145 {
1146 case ENCODE_UTF8 :
1147 /*
1148 * Got a UTF-8 character; convert UTF-8 to Unicode and return...
1149 */
1151 if (!(ch & 0x80))
1152 {
1153 if (mxml_bad_char(ch))
1154 {
1155 mxml_error("Bad control character 0x%02x not allowed by XML standard!",
1156 ch);
1157 return (EOF);
1158 }
1160 #if DEBUG > 1
1161 printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
1162 #endif /* DEBUG > 1 */
1164 return (ch);
1165 }
1166 else if (ch == 0xfe)
1167 {
1168 /*
1169 * UTF-16 big-endian BOM?
1170 */
1172 ch = getc(fp);
1173 if (ch != 0xff)
1174 return (EOF);
1176 *encoding = ENCODE_UTF16BE;
1178 return (mxml_file_getc(p, encoding));
1179 }
1180 else if (ch == 0xff)
1181 {
1182 /*
1183 * UTF-16 little-endian BOM?
1184 */
1186 ch = getc(fp);
1187 if (ch != 0xfe)
1188 return (EOF);
1190 *encoding = ENCODE_UTF16LE;
1192 return (mxml_file_getc(p, encoding));
1193 }
1194 else if ((ch & 0xe0) == 0xc0)
1195 {
1196 /*
1197 * Two-byte value...
1198 */
1200 if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1201 return (EOF);
1203 ch = ((ch & 0x1f) << 6) | (temp & 0x3f);
1205 if (ch < 0x80)
1206 {
1207 mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
1208 return (EOF);
1209 }
1210 }
1211 else if ((ch & 0xf0) == 0xe0)
1212 {
1213 /*
1214 * Three-byte value...
1215 */
1217 if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1218 return (EOF);
1220 ch = ((ch & 0x0f) << 6) | (temp & 0x3f);
1222 if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1223 return (EOF);
1225 ch = (ch << 6) | (temp & 0x3f);
1227 if (ch < 0x800)
1228 {
1229 mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
1230 return (EOF);
1231 }
1233 /*
1234 * Ignore (strip) Byte Order Mark (BOM)...
1235 */
1237 if (ch == 0xfeff)
1238 return (mxml_file_getc(p, encoding));
1239 }
1240 else if ((ch & 0xf8) == 0xf0)
1241 {
1242 /*
1243 * Four-byte value...
1244 */
1246 if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1247 return (EOF);
1249 ch = ((ch & 0x07) << 6) | (temp & 0x3f);
1251 if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1252 return (EOF);
1254 ch = (ch << 6) | (temp & 0x3f);
1256 if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1257 return (EOF);
1259 ch = (ch << 6) | (temp & 0x3f);
1261 if (ch < 0x10000)
1262 {
1263 mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
1264 return (EOF);
1265 }
1266 }
1267 else
1268 return (EOF);
1269 break;
1271 case ENCODE_UTF16BE :
1272 /*
1273 * Read UTF-16 big-endian char...
1274 */
1276 ch = (ch << 8) | getc(fp);
1278 if (mxml_bad_char(ch))
1279 {
1280 mxml_error("Bad control character 0x%02x not allowed by XML standard!",
1281 ch);
1282 return (EOF);
1283 }
1284 else if (ch >= 0xd800 && ch <= 0xdbff)
1285 {
1286 /*
1287 * Multi-word UTF-16 char...
1288 */
1290 int lch = (getc(fp) << 8) | getc(fp);
1292 if (lch < 0xdc00 || lch >= 0xdfff)
1293 return (EOF);
1295 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
1296 }
1297 break;
1299 case ENCODE_UTF16LE :
1300 /*
1301 * Read UTF-16 little-endian char...
1302 */
1304 ch |= (getc(fp) << 8);
1306 if (mxml_bad_char(ch))
1307 {
1308 mxml_error("Bad control character 0x%02x not allowed by XML standard!",
1309 ch);
1310 return (EOF);
1311 }
1312 else if (ch >= 0xd800 && ch <= 0xdbff)
1313 {
1314 /*
1315 * Multi-word UTF-16 char...
1316 */
1318 int lch = getc(fp) | (getc(fp) << 8);
1320 if (lch < 0xdc00 || lch >= 0xdfff)
1321 return (EOF);
1323 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
1324 }
1325 break;
1326 }
1328 #if DEBUG > 1
1329 printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
1330 #endif /* DEBUG > 1 */
1332 return (ch);
1333 }
1336 /*
1337 * 'mxml_file_putc()' - Write a character to a file.
1338 */
1340 static int /* O - 0 on success, -1 on failure */
1341 mxml_file_putc(int ch, /* I - Character to write */
1342 void *p) /* I - Pointer to file */
1343 {
1344 return (putc(ch, (FILE *)p) == EOF ? -1 : 0);
1345 }
1348 /*
1349 * 'mxml_get_entity()' - Get the character corresponding to an entity...
1350 */
1352 static int /* O - Character value or EOF on error */
1353 mxml_get_entity(mxml_node_t *parent, /* I - Parent node */
1354 void *p, /* I - Pointer to source */
1355 int *encoding, /* IO - Character encoding */
1356 int (*getc_cb)(void *, int *))
1357 /* I - Get character function */
1358 {
1359 int ch; /* Current character */
1360 char entity[64], /* Entity string */
1361 *entptr; /* Pointer into entity */
1364 entptr = entity;
1366 while ((ch = (*getc_cb)(p, encoding)) != EOF)
1367 if (ch > 126 || (!isalnum(ch) && ch != '#'))
1368 break;
1369 else if (entptr < (entity + sizeof(entity) - 1))
1370 *entptr++ = ch;
1371 else
1372 {
1373 mxml_error("Entity name too long under parent <%s>!",
1374 parent ? parent->value.element.name : "null");
1375 break;
1376 }
1378 *entptr = '\0';
1380 if (ch != ';')
1381 {
1382 mxml_error("Character entity \"%s\" not terminated under parent <%s>!",
1383 entity, parent ? parent->value.element.name : "null");
1384 return (EOF);
1385 }
1387 if (entity[0] == '#')
1388 {
1389 if (entity[1] == 'x')
1390 ch = strtol(entity + 2, NULL, 16);
1391 else
1392 ch = strtol(entity + 1, NULL, 10);
1393 }
1394 else if ((ch = mxmlEntityGetValue(entity)) < 0)
1395 mxml_error("Entity name \"%s;\" not supported under parent <%s>!",
1396 entity, parent ? parent->value.element.name : "null");
1398 if (mxml_bad_char(ch))
1399 {
1400 mxml_error("Bad control character 0x%02x under parent <%s> not allowed by XML standard!",
1401 ch, parent ? parent->value.element.name : "null");
1402 return (EOF);
1403 }
1405 return (ch);
1406 }
1409 /*
1410 * 'mxml_load_data()' - Load data into an XML node tree.
1411 */
1413 static mxml_node_t * /* O - First node or NULL if the file could not be read. */
1414 mxml_load_data(
1415 mxml_node_t *top, /* I - Top node */
1416 void *p, /* I - Pointer to data */
1417 mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */
1418 _mxml_getc_cb_t getc_cb, /* I - Read function */
1419 mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */
1420 void *sax_data) /* I - SAX user data */
1421 {
1422 mxml_node_t *node, /* Current node */
1423 *first, /* First node added */
1424 *parent; /* Current parent node */
1425 int ch, /* Character from file */
1426 whitespace; /* Non-zero if whitespace seen */
1427 char *buffer, /* String buffer */
1428 *bufptr; /* Pointer into buffer */
1429 int bufsize; /* Size of buffer */
1430 mxml_type_t type; /* Current node type */
1431 int encoding; /* Character encoding */
1432 _mxml_global_t *global = _mxml_global();
1433 /* Global data */
1434 static const char * const types[] = /* Type strings... */
1435 {
1436 "MXML_ELEMENT", /* XML element with attributes */
1437 "MXML_INTEGER", /* Integer value */
1438 "MXML_OPAQUE", /* Opaque string */
1439 "MXML_REAL", /* Real value */
1440 "MXML_TEXT", /* Text fragment */
1441 "MXML_CUSTOM" /* Custom data */
1442 };
1445 /*
1446 * Read elements and other nodes from the file...
1447 */
1449 if ((buffer = malloc(64)) == NULL)
1450 {
1451 mxml_error("Unable to allocate string buffer!");
1452 return (NULL);
1453 }
1455 bufsize = 64;
1456 bufptr = buffer;
1457 parent = top;
1458 first = NULL;
1459 whitespace = 0;
1460 encoding = ENCODE_UTF8;
1462 if (cb && parent)
1463 type = (*cb)(parent);
1464 else
1465 type = MXML_TEXT;
1467 while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1468 {
1469 if ((ch == '<' ||
1470 (mxml_isspace(ch) && type != MXML_OPAQUE && type != MXML_CUSTOM)) &&
1471 bufptr > buffer)
1472 {
1473 /*
1474 * Add a new value node...
1475 */
1477 *bufptr = '\0';
1479 switch (type)
1480 {
1481 case MXML_INTEGER :
1482 node = mxmlNewInteger(parent, strtol(buffer, &bufptr, 0));
1483 break;
1485 case MXML_OPAQUE :
1486 node = mxmlNewOpaque(parent, buffer);
1487 break;
1489 case MXML_REAL :
1490 node = mxmlNewReal(parent, strtod(buffer, &bufptr));
1491 break;
1493 case MXML_TEXT :
1494 node = mxmlNewText(parent, whitespace, buffer);
1495 break;
1497 case MXML_CUSTOM :
1498 if (global->custom_load_cb)
1499 {
1500 /*
1501 * Use the callback to fill in the custom data...
1502 */
1504 node = mxmlNewCustom(parent, NULL, NULL);
1506 if ((*global->custom_load_cb)(node, buffer))
1507 {
1508 mxml_error("Bad custom value '%s' in parent <%s>!",
1509 buffer, parent ? parent->value.element.name : "null");
1510 mxmlDelete(node);
1511 node = NULL;
1512 }
1513 break;
1514 }
1516 default : /* Ignore... */
1517 node = NULL;
1518 break;
1519 }
1521 if (*bufptr)
1522 {
1523 /*
1524 * Bad integer/real number value...
1525 */
1527 mxml_error("Bad %s value '%s' in parent <%s>!",
1528 type == MXML_INTEGER ? "integer" : "real", buffer,
1529 parent ? parent->value.element.name : "null");
1530 break;
1531 }
1533 bufptr = buffer;
1534 whitespace = mxml_isspace(ch) && type == MXML_TEXT;
1536 if (!node && type != MXML_IGNORE)
1537 {
1538 /*
1539 * Print error and return...
1540 */
1542 mxml_error("Unable to add value node of type %s to parent <%s>!",
1543 types[type], parent ? parent->value.element.name : "null");
1544 goto error;
1545 }
1547 if (sax_cb)
1548 {
1549 (*sax_cb)(node, MXML_SAX_DATA, sax_data);
1551 if (!mxmlRelease(node))
1552 node = NULL;
1553 }
1555 if (!first && node)
1556 first = node;
1557 }
1558 else if (mxml_isspace(ch) && type == MXML_TEXT)
1559 whitespace = 1;
1561 /*
1562 * Add lone whitespace node if we have an element and existing
1563 * whitespace...
1564 */
1566 if (ch == '<' && whitespace && type == MXML_TEXT)
1567 {
1568 if (parent)
1569 {
1570 node = mxmlNewText(parent, whitespace, "");
1572 if (sax_cb)
1573 {
1574 (*sax_cb)(node, MXML_SAX_DATA, sax_data);
1576 if (!mxmlRelease(node))
1577 node = NULL;
1578 }
1580 if (!first && node)
1581 first = node;
1582 }
1584 whitespace = 0;
1585 }
1587 if (ch == '<')
1588 {
1589 /*
1590 * Start of open/close tag...
1591 */
1593 bufptr = buffer;
1595 while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1596 if (mxml_isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer))
1597 break;
1598 else if (ch == '<')
1599 {
1600 mxml_error("Bare < in element!");
1601 goto error;
1602 }
1603 else if (ch == '&')
1604 {
1605 if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
1606 goto error;
1608 if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1609 goto error;
1610 }
1611 else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1612 goto error;
1613 else if (((bufptr - buffer) == 1 && buffer[0] == '?') ||
1614 ((bufptr - buffer) == 3 && !strncmp(buffer, "!--", 3)) ||
1615 ((bufptr - buffer) == 8 && !strncmp(buffer, "![CDATA[", 8)))
1616 break;
1618 *bufptr = '\0';
1620 if (!strcmp(buffer, "!--"))
1621 {
1622 /*
1623 * Gather rest of comment...
1624 */
1626 while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1627 {
1628 if (ch == '>' && bufptr > (buffer + 4) &&
1629 bufptr[-3] != '-' && bufptr[-2] == '-' && bufptr[-1] == '-')
1630 break;
1631 else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1632 goto error;
1633 }
1635 /*
1636 * Error out if we didn't get the whole comment...
1637 */
1639 if (ch != '>')
1640 {
1641 /*
1642 * Print error and return...
1643 */
1645 mxml_error("Early EOF in comment node!");
1646 goto error;
1647 }
1650 /*
1651 * Otherwise add this as an element under the current parent...
1652 */
1654 *bufptr = '\0';
1656 if (!parent && first)
1657 {
1658 /*
1659 * There can only be one root element!
1660 */
1662 mxml_error("<%s> cannot be a second root node after <%s>",
1663 buffer, first->value.element.name);
1664 goto error;
1665 }
1667 if ((node = mxmlNewElement(parent, buffer)) == NULL)
1668 {
1669 /*
1670 * Just print error for now...
1671 */
1673 mxml_error("Unable to add comment node to parent <%s>!",
1674 parent ? parent->value.element.name : "null");
1675 break;
1676 }
1678 if (sax_cb)
1679 {
1680 (*sax_cb)(node, MXML_SAX_COMMENT, sax_data);
1682 if (!mxmlRelease(node))
1683 node = NULL;
1684 }
1686 if (node && !first)
1687 first = node;
1688 }
1689 else if (!strcmp(buffer, "![CDATA["))
1690 {
1691 /*
1692 * Gather CDATA section...
1693 */
1695 while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1696 {
1697 if (ch == '>' && !strncmp(bufptr - 2, "]]", 2))
1698 break;
1699 else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1700 goto error;
1701 }
1703 /*
1704 * Error out if we didn't get the whole comment...
1705 */
1707 if (ch != '>')
1708 {
1709 /*
1710 * Print error and return...
1711 */
1713 mxml_error("Early EOF in CDATA node!");
1714 goto error;
1715 }
1718 /*
1719 * Otherwise add this as an element under the current parent...
1720 */
1722 *bufptr = '\0';
1724 if (!parent && first)
1725 {
1726 /*
1727 * There can only be one root element!
1728 */
1730 mxml_error("<%s> cannot be a second root node after <%s>",
1731 buffer, first->value.element.name);
1732 goto error;
1733 }
1735 if ((node = mxmlNewElement(parent, buffer)) == NULL)
1736 {
1737 /*
1738 * Print error and return...
1739 */
1741 mxml_error("Unable to add CDATA node to parent <%s>!",
1742 parent ? parent->value.element.name : "null");
1743 goto error;
1744 }
1746 if (sax_cb)
1747 {
1748 (*sax_cb)(node, MXML_SAX_CDATA, sax_data);
1750 if (!mxmlRelease(node))
1751 node = NULL;
1752 }
1754 if (node && !first)
1755 first = node;
1756 }
1757 else if (buffer[0] == '?')
1758 {
1759 /*
1760 * Gather rest of processing instruction...
1761 */
1763 while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1764 {
1765 if (ch == '>' && bufptr > buffer && bufptr[-1] == '?')
1766 break;
1767 else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1768 goto error;
1769 }
1771 /*
1772 * Error out if we didn't get the whole processing instruction...
1773 */
1775 if (ch != '>')
1776 {
1777 /*
1778 * Print error and return...
1779 */
1781 mxml_error("Early EOF in processing instruction node!");
1782 goto error;
1783 }
1785 /*
1786 * Otherwise add this as an element under the current parent...
1787 */
1789 *bufptr = '\0';
1791 if (!parent && first)
1792 {
1793 /*
1794 * There can only be one root element!
1795 */
1797 mxml_error("<%s> cannot be a second root node after <%s>",
1798 buffer, first->value.element.name);
1799 goto error;
1800 }
1802 if ((node = mxmlNewElement(parent, buffer)) == NULL)
1803 {
1804 /*
1805 * Print error and return...
1806 */
1808 mxml_error("Unable to add processing instruction node to parent <%s>!",
1809 parent ? parent->value.element.name : "null");
1810 goto error;
1811 }
1813 if (sax_cb)
1814 {
1815 (*sax_cb)(node, MXML_SAX_DIRECTIVE, sax_data);
1817 if (!mxmlRelease(node))
1818 node = NULL;
1819 }
1821 if (node)
1822 {
1823 if (!first)
1824 first = node;
1826 if (!parent)
1827 {
1828 parent = node;
1830 if (cb)
1831 type = (*cb)(parent);
1832 }
1833 }
1834 }
1835 else if (buffer[0] == '!')
1836 {
1837 /*
1838 * Gather rest of declaration...
1839 */
1841 do
1842 {
1843 if (ch == '>')
1844 break;
1845 else
1846 {
1847 if (ch == '&')
1848 if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
1849 goto error;
1851 if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1852 goto error;
1853 }
1854 }
1855 while ((ch = (*getc_cb)(p, &encoding)) != EOF);
1857 /*
1858 * Error out if we didn't get the whole declaration...
1859 */
1861 if (ch != '>')
1862 {
1863 /*
1864 * Print error and return...
1865 */
1867 mxml_error("Early EOF in declaration node!");
1868 goto error;
1869 }
1871 /*
1872 * Otherwise add this as an element under the current parent...
1873 */
1875 *bufptr = '\0';
1877 if (!parent && first)
1878 {
1879 /*
1880 * There can only be one root element!
1881 */
1883 mxml_error("<%s> cannot be a second root node after <%s>",
1884 buffer, first->value.element.name);
1885 goto error;
1886 }
1888 if ((node = mxmlNewElement(parent, buffer)) == NULL)
1889 {
1890 /*
1891 * Print error and return...
1892 */
1894 mxml_error("Unable to add declaration node to parent <%s>!",
1895 parent ? parent->value.element.name : "null");
1896 goto error;
1897 }
1899 if (sax_cb)
1900 {
1901 (*sax_cb)(node, MXML_SAX_DIRECTIVE, sax_data);
1903 if (!mxmlRelease(node))
1904 node = NULL;
1905 }
1907 if (node)
1908 {
1909 if (!first)
1910 first = node;
1912 if (!parent)
1913 {
1914 parent = node;
1916 if (cb)
1917 type = (*cb)(parent);
1918 }
1919 }
1920 }
1921 else if (buffer[0] == '/')
1922 {
1923 /*
1924 * Handle close tag...
1925 */
1927 if (!parent || strcmp(buffer + 1, parent->value.element.name))
1928 {
1929 /*
1930 * Close tag doesn't match tree; print an error for now...
1931 */
1933 mxml_error("Mismatched close tag <%s> under parent <%s>!",
1934 buffer, parent ? parent->value.element.name : "(null)");
1935 goto error;
1936 }
1938 /*
1939 * Keep reading until we see >...
1940 */
1942 while (ch != '>' && ch != EOF)
1943 ch = (*getc_cb)(p, &encoding);
1945 node = parent;
1946 parent = parent->parent;
1948 if (sax_cb)
1949 {
1950 (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data);
1952 if (!mxmlRelease(node) && first == node)
1953 first = NULL;
1954 }
1956 /*
1957 * Ascend into the parent and set the value type as needed...
1958 */
1960 if (cb && parent)
1961 type = (*cb)(parent);
1962 }
1963 else
1964 {
1965 /*
1966 * Handle open tag...
1967 */
1969 if (!parent && first)
1970 {
1971 /*
1972 * There can only be one root element!
1973 */
1975 mxml_error("<%s> cannot be a second root node after <%s>",
1976 buffer, first->value.element.name);
1977 goto error;
1978 }
1980 if ((node = mxmlNewElement(parent, buffer)) == NULL)
1981 {
1982 /*
1983 * Just print error for now...
1984 */
1986 mxml_error("Unable to add element node to parent <%s>!",
1987 parent ? parent->value.element.name : "null");
1988 goto error;
1989 }
1991 if (mxml_isspace(ch))
1992 {
1993 if ((ch = mxml_parse_element(node, p, &encoding, getc_cb)) == EOF)
1994 goto error;
1995 }
1996 else if (ch == '/')
1997 {
1998 if ((ch = (*getc_cb)(p, &encoding)) != '>')
1999 {
2000 mxml_error("Expected > but got '%c' instead for element <%s/>!",
2001 ch, buffer);
2002 mxmlDelete(node);
2003 goto error;
2004 }
2006 ch = '/';
2007 }
2009 if (sax_cb)
2010 (*sax_cb)(node, MXML_SAX_ELEMENT_OPEN, sax_data);
2012 if (!first)
2013 first = node;
2015 if (ch == EOF)
2016 break;
2018 if (ch != '/')
2019 {
2020 /*
2021 * Descend into this node, setting the value type as needed...
2022 */
2024 parent = node;
2026 if (cb && parent)
2027 type = (*cb)(parent);
2028 }
2029 else if (sax_cb)
2030 {
2031 (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data);
2033 if (!mxmlRelease(node) && first == node)
2034 first = NULL;
2035 }
2036 }
2038 bufptr = buffer;
2039 }
2040 else if (ch == '&')
2041 {
2042 /*
2043 * Add character entity to current buffer...
2044 */
2046 if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
2047 goto error;
2049 if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
2050 goto error;
2051 }
2052 else if (type == MXML_OPAQUE || type == MXML_CUSTOM || !mxml_isspace(ch))
2053 {
2054 /*
2055 * Add character to current buffer...
2056 */
2058 if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
2059 goto error;
2060 }
2061 }
2063 /*
2064 * Free the string buffer - we don't need it anymore...
2065 */
2067 free(buffer);
2069 /*
2070 * Find the top element and return it...
2071 */
2073 if (parent)
2074 {
2075 node = parent;
2077 while (parent->parent != top && parent->parent)
2078 parent = parent->parent;
2080 if (node != parent)
2081 {
2082 mxml_error("Missing close tag </%s> under parent <%s>!",
2083 node->value.element.name,
2084 node->parent ? node->parent->value.element.name : "(null)");
2086 mxmlDelete(first);
2088 return (NULL);
2089 }
2090 }
2092 if (parent)
2093 return (parent);
2094 else
2095 return (first);
2097 /*
2098 * Common error return...
2099 */
2101 error:
2103 mxmlDelete(first);
2105 free(buffer);
2107 return (NULL);
2108 }
2111 /*
2112 * 'mxml_parse_element()' - Parse an element for any attributes...
2113 */
2115 static int /* O - Terminating character */
2116 mxml_parse_element(
2117 mxml_node_t *node, /* I - Element node */
2118 void *p, /* I - Data to read from */
2119 int *encoding, /* IO - Encoding */
2120 _mxml_getc_cb_t getc_cb) /* I - Data callback */
2121 {
2122 int ch, /* Current character in file */
2123 quote; /* Quoting character */
2124 char *name, /* Attribute name */
2125 *value, /* Attribute value */
2126 *ptr; /* Pointer into name/value */
2127 int namesize, /* Size of name string */
2128 valsize; /* Size of value string */
2131 /*
2132 * Initialize the name and value buffers...
2133 */
2135 if ((name = malloc(64)) == NULL)
2136 {
2137 mxml_error("Unable to allocate memory for name!");
2138 return (EOF);
2139 }
2141 namesize = 64;
2143 if ((value = malloc(64)) == NULL)
2144 {
2145 free(name);
2146 mxml_error("Unable to allocate memory for value!");
2147 return (EOF);
2148 }
2150 valsize = 64;
2152 /*
2153 * Loop until we hit a >, /, ?, or EOF...
2154 */
2156 while ((ch = (*getc_cb)(p, encoding)) != EOF)
2157 {
2158 #if DEBUG > 1
2159 fprintf(stderr, "parse_element: ch='%c'\n", ch);
2160 #endif /* DEBUG > 1 */
2162 /*
2163 * Skip leading whitespace...
2164 */
2166 if (mxml_isspace(ch))
2167 continue;
2169 /*
2170 * Stop at /, ?, or >...
2171 */
2173 if (ch == '/' || ch == '?')
2174 {
2175 /*
2176 * Grab the > character and print an error if it isn't there...
2177 */
2179 quote = (*getc_cb)(p, encoding);
2181 if (quote != '>')
2182 {
2183 mxml_error("Expected '>' after '%c' for element %s, but got '%c'!",
2184 ch, node->value.element.name, quote);
2185 goto error;
2186 }
2188 break;
2189 }
2190 else if (ch == '<')
2191 {
2192 mxml_error("Bare < in element %s!", node->value.element.name);
2193 goto error;
2194 }
2195 else if (ch == '>')
2196 break;
2198 /*
2199 * Read the attribute name...
2200 */
2202 name[0] = ch;
2203 ptr = name + 1;
2205 if (ch == '\"' || ch == '\'')
2206 {
2207 /*
2208 * Name is in quotes, so get a quoted string...
2209 */
2211 quote = ch;
2213 while ((ch = (*getc_cb)(p, encoding)) != EOF)
2214 {
2215 if (ch == '&')
2216 if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
2217 goto error;
2219 if (mxml_add_char(ch, &ptr, &name, &namesize))
2220 goto error;
2222 if (ch == quote)
2223 break;
2224 }
2225 }
2226 else
2227 {
2228 /*
2229 * Grab an normal, non-quoted name...
2230 */
2232 while ((ch = (*getc_cb)(p, encoding)) != EOF)
2233 if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>' ||
2234 ch == '?')
2235 break;
2236 else
2237 {
2238 if (ch == '&')
2239 if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
2240 goto error;
2242 if (mxml_add_char(ch, &ptr, &name, &namesize))
2243 goto error;
2244 }
2245 }
2247 *ptr = '\0';
2249 if (mxmlElementGetAttr(node, name))
2250 goto error;
2252 while (ch != EOF && mxml_isspace(ch))
2253 ch = (*getc_cb)(p, encoding);
2255 if (ch == '=')
2256 {
2257 /*
2258 * Read the attribute value...
2259 */
2261 while ((ch = (*getc_cb)(p, encoding)) != EOF && mxml_isspace(ch));
2263 if (ch == EOF)
2264 {
2265 mxml_error("Missing value for attribute '%s' in element %s!",
2266 name, node->value.element.name);
2267 goto error;
2268 }
2270 if (ch == '\'' || ch == '\"')
2271 {
2272 /*
2273 * Read quoted value...
2274 */
2276 quote = ch;
2277 ptr = value;
2279 while ((ch = (*getc_cb)(p, encoding)) != EOF)
2280 if (ch == quote)
2281 break;
2282 else
2283 {
2284 if (ch == '&')
2285 if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
2286 goto error;
2288 if (mxml_add_char(ch, &ptr, &value, &valsize))
2289 goto error;
2290 }
2292 *ptr = '\0';
2293 }
2294 else
2295 {
2296 /*
2297 * Read unquoted value...
2298 */
2300 value[0] = ch;
2301 ptr = value + 1;
2303 while ((ch = (*getc_cb)(p, encoding)) != EOF)
2304 if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>')
2305 break;
2306 else
2307 {
2308 if (ch == '&')
2309 if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
2310 goto error;
2312 if (mxml_add_char(ch, &ptr, &value, &valsize))
2313 goto error;
2314 }
2316 *ptr = '\0';
2317 }
2319 /*
2320 * Set the attribute with the given string value...
2321 */
2323 mxmlElementSetAttr(node, name, value);
2324 }
2325 else
2326 {
2327 mxml_error("Missing value for attribute '%s' in element %s!",
2328 name, node->value.element.name);
2329 goto error;
2330 }
2332 /*
2333 * Check the end character...
2334 */
2336 if (ch == '/' || ch == '?')
2337 {
2338 /*
2339 * Grab the > character and print an error if it isn't there...
2340 */
2342 quote = (*getc_cb)(p, encoding);
2344 if (quote != '>')
2345 {
2346 mxml_error("Expected '>' after '%c' for element %s, but got '%c'!",
2347 ch, node->value.element.name, quote);
2348 ch = EOF;
2349 }
2351 break;
2352 }
2353 else if (ch == '>')
2354 break;
2355 }
2357 /*
2358 * Free the name and value buffers and return...
2359 */
2361 free(name);
2362 free(value);
2364 return (ch);
2366 /*
2367 * Common error return point...
2368 */
2370 error:
2372 free(name);
2373 free(value);
2375 return (EOF);
2376 }
2379 /*
2380 * 'mxml_string_getc()' - Get a character from a string.
2381 */
2383 static int /* O - Character or EOF */
2384 mxml_string_getc(void *p, /* I - Pointer to file */
2385 int *encoding) /* IO - Encoding */
2386 {
2387 int ch; /* Character */
2388 const char **s; /* Pointer to string pointer */
2391 s = (const char **)p;
2393 if ((ch = (*s)[0] & 255) != 0 || *encoding == ENCODE_UTF16LE)
2394 {
2395 /*
2396 * Got character; convert UTF-8 to integer and return...
2397 */
2399 (*s)++;
2401 switch (*encoding)
2402 {
2403 case ENCODE_UTF8 :
2404 if (!(ch & 0x80))
2405 {
2406 #if DEBUG > 1
2407 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2408 #endif /* DEBUG > 1 */
2410 if (mxml_bad_char(ch))
2411 {
2412 mxml_error("Bad control character 0x%02x not allowed by XML standard!",
2413 ch);
2414 return (EOF);
2415 }
2417 return (ch);
2418 }
2419 else if (ch == 0xfe)
2420 {
2421 /*
2422 * UTF-16 big-endian BOM?
2423 */
2425 if (((*s)[0] & 255) != 0xff)
2426 return (EOF);
2428 *encoding = ENCODE_UTF16BE;
2429 (*s)++;
2431 return (mxml_string_getc(p, encoding));
2432 }
2433 else if (ch == 0xff)
2434 {
2435 /*
2436 * UTF-16 little-endian BOM?
2437 */
2439 if (((*s)[0] & 255) != 0xfe)
2440 return (EOF);
2442 *encoding = ENCODE_UTF16LE;
2443 (*s)++;
2445 return (mxml_string_getc(p, encoding));
2446 }
2447 else if ((ch & 0xe0) == 0xc0)
2448 {
2449 /*
2450 * Two-byte value...
2451 */
2453 if (((*s)[0] & 0xc0) != 0x80)
2454 return (EOF);
2456 ch = ((ch & 0x1f) << 6) | ((*s)[0] & 0x3f);
2458 (*s)++;
2460 if (ch < 0x80)
2461 {
2462 mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
2463 return (EOF);
2464 }
2466 #if DEBUG > 1
2467 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2468 #endif /* DEBUG > 1 */
2470 return (ch);
2471 }
2472 else if ((ch & 0xf0) == 0xe0)
2473 {
2474 /*
2475 * Three-byte value...
2476 */
2478 if (((*s)[0] & 0xc0) != 0x80 ||
2479 ((*s)[1] & 0xc0) != 0x80)
2480 return (EOF);
2482 ch = ((((ch & 0x0f) << 6) | ((*s)[0] & 0x3f)) << 6) | ((*s)[1] & 0x3f);
2484 (*s) += 2;
2486 if (ch < 0x800)
2487 {
2488 mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
2489 return (EOF);
2490 }
2492 /*
2493 * Ignore (strip) Byte Order Mark (BOM)...
2494 */
2496 if (ch == 0xfeff)
2497 return (mxml_string_getc(p, encoding));
2499 #if DEBUG > 1
2500 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2501 #endif /* DEBUG > 1 */
2503 return (ch);
2504 }
2505 else if ((ch & 0xf8) == 0xf0)
2506 {
2507 /*
2508 * Four-byte value...
2509 */
2511 if (((*s)[0] & 0xc0) != 0x80 ||
2512 ((*s)[1] & 0xc0) != 0x80 ||
2513 ((*s)[2] & 0xc0) != 0x80)
2514 return (EOF);
2516 ch = ((((((ch & 0x07) << 6) | ((*s)[0] & 0x3f)) << 6) |
2517 ((*s)[1] & 0x3f)) << 6) | ((*s)[2] & 0x3f);
2519 (*s) += 3;
2521 if (ch < 0x10000)
2522 {
2523 mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
2524 return (EOF);
2525 }
2527 #if DEBUG > 1
2528 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2529 #endif /* DEBUG > 1 */
2531 return (ch);
2532 }
2533 else
2534 return (EOF);
2536 case ENCODE_UTF16BE :
2537 /*
2538 * Read UTF-16 big-endian char...
2539 */
2541 ch = (ch << 8) | ((*s)[0] & 255);
2542 (*s) ++;
2544 if (mxml_bad_char(ch))
2545 {
2546 mxml_error("Bad control character 0x%02x not allowed by XML standard!",
2547 ch);
2548 return (EOF);
2549 }
2550 else if (ch >= 0xd800 && ch <= 0xdbff)
2551 {
2552 /*
2553 * Multi-word UTF-16 char...
2554 */
2556 int lch; /* Lower word */
2559 if (!(*s)[0])
2560 return (EOF);
2562 lch = (((*s)[0] & 255) << 8) | ((*s)[1] & 255);
2563 (*s) += 2;
2565 if (lch < 0xdc00 || lch >= 0xdfff)
2566 return (EOF);
2568 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
2569 }
2571 #if DEBUG > 1
2572 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2573 #endif /* DEBUG > 1 */
2575 return (ch);
2577 case ENCODE_UTF16LE :
2578 /*
2579 * Read UTF-16 little-endian char...
2580 */
2582 ch = ch | (((*s)[0] & 255) << 8);
2584 if (!ch)
2585 {
2586 (*s) --;
2587 return (EOF);
2588 }
2590 (*s) ++;
2592 if (mxml_bad_char(ch))
2593 {
2594 mxml_error("Bad control character 0x%02x not allowed by XML standard!",
2595 ch);
2596 return (EOF);
2597 }
2598 else if (ch >= 0xd800 && ch <= 0xdbff)
2599 {
2600 /*
2601 * Multi-word UTF-16 char...
2602 */
2604 int lch; /* Lower word */
2607 if (!(*s)[1])
2608 return (EOF);
2610 lch = (((*s)[1] & 255) << 8) | ((*s)[0] & 255);
2611 (*s) += 2;
2613 if (lch < 0xdc00 || lch >= 0xdfff)
2614 return (EOF);
2616 ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
2617 }
2619 #if DEBUG > 1
2620 printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2621 #endif /* DEBUG > 1 */
2623 return (ch);
2624 }
2625 }
2627 return (EOF);
2628 }
2631 /*
2632 * 'mxml_string_putc()' - Write a character to a string.
2633 */
2635 static int /* O - 0 on success, -1 on failure */
2636 mxml_string_putc(int ch, /* I - Character to write */
2637 void *p) /* I - Pointer to string pointers */
2638 {
2639 char **pp; /* Pointer to string pointers */
2642 pp = (char **)p;
2644 if (pp[0] < pp[1])
2645 pp[0][0] = ch;
2647 pp[0] ++;
2649 return (0);
2650 }
2653 /*
2654 * 'mxml_write_name()' - Write a name string.
2655 */
2657 static int /* O - 0 on success, -1 on failure */
2658 mxml_write_name(const char *s, /* I - Name to write */
2659 void *p, /* I - Write pointer */
2660 int (*putc_cb)(int, void *))
2661 /* I - Write callback */
2662 {
2663 char quote; /* Quote character */
2664 const char *name; /* Entity name */
2667 if (*s == '\"' || *s == '\'')
2668 {
2669 /*
2670 * Write a quoted name string...
2671 */
2673 if ((*putc_cb)(*s, p) < 0)
2674 return (-1);
2676 quote = *s++;
2678 while (*s && *s != quote)
2679 {
2680 if ((name = mxmlEntityGetName(*s)) != NULL)
2681 {
2682 if ((*putc_cb)('&', p) < 0)
2683 return (-1);
2685 while (*name)
2686 {
2687 if ((*putc_cb)(*name, p) < 0)
2688 return (-1);
2690 name ++;
2691 }
2693 if ((*putc_cb)(';', p) < 0)
2694 return (-1);
2695 }
2696 else if ((*putc_cb)(*s, p) < 0)
2697 return (-1);
2699 s ++;
2700 }
2702 /*
2703 * Write the end quote...
2704 */
2706 if ((*putc_cb)(quote, p) < 0)
2707 return (-1);
2708 }
2709 else
2710 {
2711 /*
2712 * Write a non-quoted name string...
2713 */
2715 while (*s)
2716 {
2717 if ((*putc_cb)(*s, p) < 0)
2718 return (-1);
2720 s ++;
2721 }
2722 }
2724 return (0);
2725 }
2728 /*
2729 * 'mxml_write_node()' - Save an XML node to a file.
2730 */
2732 static int /* O - Column or -1 on error */
2733 mxml_write_node(mxml_node_t *node, /* I - Node to write */
2734 void *p, /* I - File to write to */
2735 mxml_save_cb_t cb, /* I - Whitespace callback */
2736 int col, /* I - Current column */
2737 _mxml_putc_cb_t putc_cb,/* I - Output callback */
2738 _mxml_global_t *global)/* I - Global data */
2739 {
2740 int i, /* Looping var */
2741 width; /* Width of attr + value */
2742 mxml_attr_t *attr; /* Current attribute */
2743 char s[255]; /* Temporary string */
2746 /*
2747 * Print the node value...
2748 */
2750 switch (node->type)
2751 {
2752 case MXML_ELEMENT :
2753 col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb);
2755 if ((*putc_cb)('<', p) < 0)
2756 return (-1);
2757 if (node->value.element.name[0] == '?' ||
2758 !strncmp(node->value.element.name, "!--", 3) ||
2759 !strncmp(node->value.element.name, "![CDATA[", 8))
2760 {
2761 /*
2762 * Comments, CDATA, and processing instructions do not
2763 * use character entities.
2764 */
2766 const char *ptr; /* Pointer into name */
2769 for (ptr = node->value.element.name; *ptr; ptr ++)
2770 if ((*putc_cb)(*ptr, p) < 0)
2771 return (-1);
2772 }
2773 else if (mxml_write_name(node->value.element.name, p, putc_cb) < 0)
2774 return (-1);
2776 col += strlen(node->value.element.name) + 1;
2778 for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
2779 i > 0;
2780 i --, attr ++)
2781 {
2782 width = strlen(attr->name);
2784 if (attr->value)
2785 width += strlen(attr->value) + 3;
2787 if (global->wrap > 0 && (col + width) > global->wrap)
2788 {
2789 if ((*putc_cb)('\n', p) < 0)
2790 return (-1);
2792 col = 0;
2793 }
2794 else
2795 {
2796 if ((*putc_cb)(' ', p) < 0)
2797 return (-1);
2799 col ++;
2800 }
2802 if (mxml_write_name(attr->name, p, putc_cb) < 0)
2803 return (-1);
2805 if (attr->value)
2806 {
2807 if ((*putc_cb)('=', p) < 0)
2808 return (-1);
2809 if ((*putc_cb)('\"', p) < 0)
2810 return (-1);
2811 if (mxml_write_string(attr->value, p, putc_cb) < 0)
2812 return (-1);
2813 if ((*putc_cb)('\"', p) < 0)
2814 return (-1);
2815 }
2817 col += width;
2818 }
2820 if (node->child)
2821 {
2822 /*
2823 * Write children...
2824 */
2826 mxml_node_t *child; /* Current child */
2829 if ((*putc_cb)('>', p) < 0)
2830 return (-1);
2831 else
2832 col ++;
2834 col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
2836 for (child = node->child; child; child = child->next)
2837 {
2838 if ((col = mxml_write_node(child, p, cb, col, putc_cb, global)) < 0)
2839 return (-1);
2840 }
2842 /*
2843 * The ? and ! elements are special-cases and have no end tags...
2844 */
2846 if (node->value.element.name[0] != '!' &&
2847 node->value.element.name[0] != '?')
2848 {
2849 col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb);
2851 if ((*putc_cb)('<', p) < 0)
2852 return (-1);
2853 if ((*putc_cb)('/', p) < 0)
2854 return (-1);
2855 if (mxml_write_string(node->value.element.name, p, putc_cb) < 0)
2856 return (-1);
2857 if ((*putc_cb)('>', p) < 0)
2858 return (-1);
2860 col += strlen(node->value.element.name) + 3;
2862 col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_CLOSE, col, putc_cb);
2863 }
2864 }
2865 else if (node->value.element.name[0] == '!' ||
2866 node->value.element.name[0] == '?')
2867 {
2868 /*
2869 * The ? and ! elements are special-cases...
2870 */
2872 if ((*putc_cb)('>', p) < 0)
2873 return (-1);
2874 else
2875 col ++;
2877 col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
2878 }
2879 else
2880 {
2881 if ((*putc_cb)(' ', p) < 0)
2882 return (-1);
2883 if ((*putc_cb)('/', p) < 0)
2884 return (-1);
2885 if ((*putc_cb)('>', p) < 0)
2886 return (-1);
2888 col += 3;
2890 col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
2891 }
2892 break;
2894 case MXML_INTEGER :
2895 if (node->prev)
2896 {
2897 if (global->wrap > 0 && col > global->wrap)
2898 {
2899 if ((*putc_cb)('\n', p) < 0)
2900 return (-1);
2902 col = 0;
2903 }
2904 else if ((*putc_cb)(' ', p) < 0)
2905 return (-1);
2906 else
2907 col ++;
2908 }
2910 sprintf(s, "%d", node->value.integer);
2911 if (mxml_write_string(s, p, putc_cb) < 0)
2912 return (-1);
2914 col += strlen(s);
2915 break;
2917 case MXML_OPAQUE :
2918 if (mxml_write_string(node->value.opaque, p, putc_cb) < 0)
2919 return (-1);
2921 col += strlen(node->value.opaque);
2922 break;
2924 case MXML_REAL :
2925 if (node->prev)
2926 {
2927 if (global->wrap > 0 && col > global->wrap)
2928 {
2929 if ((*putc_cb)('\n', p) < 0)
2930 return (-1);
2932 col = 0;
2933 }
2934 else if ((*putc_cb)(' ', p) < 0)
2935 return (-1);
2936 else
2937 col ++;
2938 }
2940 sprintf(s, "%f", node->value.real);
2941 if (mxml_write_string(s, p, putc_cb) < 0)
2942 return (-1);
2944 col += strlen(s);
2945 break;
2947 case MXML_TEXT :
2948 if (node->value.text.whitespace && col > 0)
2949 {
2950 if (global->wrap > 0 && col > global->wrap)
2951 {
2952 if ((*putc_cb)('\n', p) < 0)
2953 return (-1);
2955 col = 0;
2956 }
2957 else if ((*putc_cb)(' ', p) < 0)
2958 return (-1);
2959 else
2960 col ++;
2961 }
2963 if (mxml_write_string(node->value.text.string, p, putc_cb) < 0)
2964 return (-1);
2966 col += strlen(node->value.text.string);
2967 break;
2969 case MXML_CUSTOM :
2970 if (global->custom_save_cb)
2971 {
2972 char *data; /* Custom data string */
2973 const char *newline; /* Last newline in string */
2976 if ((data = (*global->custom_save_cb)(node)) == NULL)
2977 return (-1);
2979 if (mxml_write_string(data, p, putc_cb) < 0)
2980 return (-1);
2982 if ((newline = strrchr(data, '\n')) == NULL)
2983 col += strlen(data);
2984 else
2985 col = strlen(newline);
2987 free(data);
2988 break;
2989 }
2991 default : /* Should never happen */
2992 return (-1);
2993 }
2995 return (col);
2996 }
2999 /*
3000 * 'mxml_write_string()' - Write a string, escaping & and < as needed.
3001 */
3003 static int /* O - 0 on success, -1 on failure */
3004 mxml_write_string(
3005 const char *s, /* I - String to write */
3006 void *p, /* I - Write pointer */
3007 _mxml_putc_cb_t putc_cb) /* I - Write callback */
3008 {
3009 const char *name; /* Entity name, if any */
3012 while (*s)
3013 {
3014 if ((name = mxmlEntityGetName(*s)) != NULL)
3015 {
3016 if ((*putc_cb)('&', p) < 0)
3017 return (-1);
3019 while (*name)
3020 {
3021 if ((*putc_cb)(*name, p) < 0)
3022 return (-1);
3023 name ++;
3024 }
3026 if ((*putc_cb)(';', p) < 0)
3027 return (-1);
3028 }
3029 else if ((*putc_cb)(*s, p) < 0)
3030 return (-1);
3032 s ++;
3033 }
3035 return (0);
3036 }
3039 /*
3040 * 'mxml_write_ws()' - Do whitespace callback...
3041 */
3043 static int /* O - New column */
3044 mxml_write_ws(mxml_node_t *node, /* I - Current node */
3045 void *p, /* I - Write pointer */
3046 mxml_save_cb_t cb, /* I - Callback function */
3047 int ws, /* I - Where value */
3048 int col, /* I - Current column */
3049 _mxml_putc_cb_t putc_cb) /* I - Write callback */
3050 {
3051 const char *s; /* Whitespace string */
3054 if (cb && (s = (*cb)(node, ws)) != NULL)
3055 {
3056 while (*s)
3057 {
3058 if ((*putc_cb)(*s, p) < 0)
3059 return (-1);
3060 else if (*s == '\n')
3061 col = 0;
3062 else if (*s == '\t')
3063 {
3064 col += MXML_TAB;
3065 col = col - (col % MXML_TAB);
3066 }
3067 else
3068 col ++;
3070 s ++;
3071 }
3072 }
3074 return (col);
3075 }
3078 /*
3079 * End of "$Id: mxml-file.c 438 2011-03-24 05:47:51Z mike $".
3080 */