don't blow up if a jitterbuffer is not in use
[asterisk/asterisk.git] / mxml / mxml-file.c
1 /*
2  * "$Id$"
3  *
4  * File loading code for Mini-XML, a small XML-like file parsing library.
5  *
6  * Copyright 2003-2005 by Michael Sweet.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * Contents:
19  *
20  *   mxmlLoadFd()            - Load a file descriptor into an XML node tree.
21  *   mxmlLoadFile()          - Load a file into an XML node tree.
22  *   mxmlLoadString()        - Load a string into an XML node tree.
23  *   mxmlSaveAllocString()   - Save an XML node tree to an allocated string.
24  *   mxmlSaveFd()            - Save an XML tree to a file descriptor.
25  *   mxmlSaveFile()          - Save an XML tree to a file.
26  *   mxmlSaveString()        - Save an XML node tree to a string.
27  *   mxmlSetCustomHandlers() - Set the handling functions for custom data.
28  *   mxmlSetErrorCallback()  - Set the error message callback.
29  *   mxml_add_char()         - Add a character to a buffer, expanding as needed.
30  *   mxml_fd_getc()          - Read a character from a file descriptor.
31  *   mxml_fd_putc()          - Write a character to a file descriptor.
32  *   mxml_fd_read()          - Read a buffer of data from a file descriptor.
33  *   mxml_fd_write()         - Write a buffer of data to a file descriptor.
34  *   mxml_file_getc()        - Get a character from a file.
35  *   mxml_file_putc()        - Write a character to a file.
36  *   mxml_get_entity()       - Get the character corresponding to an entity...
37  *   mxml_load_data()        - Load data into an XML node tree.
38  *   mxml_parse_element()    - Parse an element for any attributes...
39  *   mxml_string_getc()      - Get a character from a string.
40  *   mxml_string_putc()      - Write a character to a string.
41  *   mxml_write_name()       - Write a name string.
42  *   mxml_write_node()       - Save an XML node to a file.
43  *   mxml_write_string()     - Write a string, escaping & and < as needed.
44  *   mxml_write_ws()         - Do whitespace callback...
45  */
46
47 /*
48  * Include necessary headers...
49  */
50
51 #include "config.h"
52 #include "mxml.h"
53 #ifdef WIN32
54 #  include <io.h>
55 #else
56 #  include <unistd.h>
57 #endif /* WIN32 */
58
59
60 /*
61  * Character encoding...
62  */
63
64 #define ENCODE_UTF8     0               /* UTF-8 */
65 #define ENCODE_UTF16BE  1               /* UTF-16 Big-Endian */
66 #define ENCODE_UTF16LE  2               /* UTF-16 Little-Endian */
67
68
69 /*
70  * Macro to test for a bad XML character...
71  */
72
73 #define mxml_bad_char(ch) ((ch) < ' ' && (ch) != '\n' && (ch) != '\r' && (ch) != '\t')
74
75
76 /*
77  * Structures...
78  */
79
80 typedef struct mxml_fdbuf_s             /**** File descriptor buffer (@private) ****/
81 {
82   int           fd;                     /* File descriptor */
83   unsigned char *current,               /* Current position in buffer */
84                 *end,                   /* End of buffer */
85                 buffer[8192];           /* Character buffer */
86 } mxml_fdbuf_t;
87
88
89 /*
90  * Global error handler...
91  */
92
93 extern void     (*mxml_error_cb)(const char *);
94
95
96 /*
97  * Custom data handlers...
98  */
99
100 static mxml_custom_load_cb_t    mxml_custom_load_cb = NULL;
101 static mxml_custom_save_cb_t    mxml_custom_save_cb = NULL;
102
103
104 /*
105  * Local functions...
106  */
107
108 static int              mxml_add_char(int ch, char **ptr, char **buffer,
109                                       int *bufsize);
110 static int              mxml_fd_getc(void *p, int *encoding);
111 static int              mxml_fd_putc(int ch, void *p);
112 static int              mxml_fd_read(mxml_fdbuf_t *buf);
113 static int              mxml_fd_write(mxml_fdbuf_t *buf);
114 static int              mxml_file_getc(void *p, int *encoding);
115 static int              mxml_file_putc(int ch, void *p);
116 static int              mxml_get_entity(mxml_node_t *parent, void *p,
117                                         int *encoding,
118                                         int (*getc_cb)(void *, int *));
119 static mxml_node_t      *mxml_load_data(mxml_node_t *top, void *p,
120                                         mxml_type_t (*cb)(mxml_node_t *),
121                                         int (*getc_cb)(void *, int *));
122 static int              mxml_parse_element(mxml_node_t *node, void *p,
123                                            int *encoding,
124                                            int (*getc_cb)(void *, int *));
125 static int              mxml_string_getc(void *p, int *encoding);
126 static int              mxml_string_putc(int ch, void *p);
127 static int              mxml_write_name(const char *s, void *p,
128                                         int (*putc_cb)(int, void *));
129 static int              mxml_write_node(mxml_node_t *node, void *p,
130                                         const char *(*cb)(mxml_node_t *, int),
131                                         int col,
132                                         int (*putc_cb)(int, void *));
133 static int              mxml_write_string(const char *s, void *p,
134                                           int (*putc_cb)(int, void *));
135 static int              mxml_write_ws(mxml_node_t *node, void *p, 
136                                       const char *(*cb)(mxml_node_t *, int), int ws,
137                                       int col, int (*putc_cb)(int, void *));
138
139
140 /*
141  * 'mxmlLoadFd()' - Load a file descriptor into an XML node tree.
142  *
143  * The nodes in the specified file are added to the specified top node.
144  * If no top node is provided, the XML file MUST be well-formed with a
145  * single parent node like <?xml> for the entire file. The callback
146  * function returns the value type that should be used for child nodes.
147  * If MXML_NO_CALLBACK is specified then all child nodes will be either
148  * MXML_ELEMENT or MXML_TEXT nodes.
149  *
150  * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
151  * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
152  * child nodes of the specified type.
153  */
154
155 mxml_node_t *                           /* O - First node or NULL if the file could not be read. */
156 mxmlLoadFd(mxml_node_t *top,            /* I - Top node */
157            int         fd,              /* I - File descriptor to read from */
158            mxml_type_t (*cb)(mxml_node_t *node))
159                                         /* I - Callback function or MXML_NO_CALLBACK */
160 {
161   mxml_fdbuf_t  buf;                    /* File descriptor buffer */
162
163
164  /*
165   * Initialize the file descriptor buffer...
166   */
167
168   buf.fd      = fd;
169   buf.current = buf.buffer;
170   buf.end     = buf.buffer;
171
172  /*
173   * Read the XML data...
174   */
175
176   return (mxml_load_data(top, &buf, cb, mxml_fd_getc));
177 }
178
179
180 /*
181  * 'mxmlLoadFile()' - Load a file into an XML node tree.
182  *
183  * The nodes in the specified file are added to the specified top node.
184  * If no top node is provided, the XML file MUST be well-formed with a
185  * single parent node like <?xml> for the entire file. The callback
186  * function returns the value type that should be used for child nodes.
187  * If MXML_NO_CALLBACK is specified then all child nodes will be either
188  * MXML_ELEMENT or MXML_TEXT nodes.
189  *
190  * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
191  * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
192  * child nodes of the specified type.
193  */
194
195 mxml_node_t *                           /* O - First node or NULL if the file could not be read. */
196 mxmlLoadFile(mxml_node_t *top,          /* I - Top node */
197              FILE        *fp,           /* I - File to read from */
198              mxml_type_t (*cb)(mxml_node_t *node))
199                                         /* I - Callback function or MXML_NO_CALLBACK */
200 {
201  /*
202   * Read the XML data...
203   */
204
205   return (mxml_load_data(top, fp, cb, mxml_file_getc));
206 }
207
208
209 /*
210  * 'mxmlLoadString()' - Load a string into an XML node tree.
211  *
212  * The nodes in the specified string are added to the specified top node.
213  * If no top node is provided, the XML string MUST be well-formed with a
214  * single parent node like <?xml> for the entire string. The callback
215  * function returns the value type that should be used for child nodes.
216  * If MXML_NO_CALLBACK is specified then all child nodes will be either
217  * MXML_ELEMENT or MXML_TEXT nodes.
218  *
219  * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
220  * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
221  * child nodes of the specified type.
222  */
223
224 mxml_node_t *                           /* O - First node or NULL if the string has errors. */
225 mxmlLoadString(mxml_node_t *top,        /* I - Top node */
226                const char  *s,          /* I - String to load */
227                mxml_type_t (*cb)(mxml_node_t *node))
228                                         /* I - Callback function or MXML_NO_CALLBACK */
229 {
230  /*
231   * Read the XML data...
232   */
233
234   return (mxml_load_data(top, &s, cb, mxml_string_getc));
235 }
236
237
238 /*
239  * 'mxmlSaveAllocString()' - Save an XML node tree to an allocated string.
240  *
241  * This function returns a pointer to a string containing the textual
242  * representation of the XML node tree.  The string should be freed
243  * using the free() function when you are done with it.  NULL is returned
244  * if the node would produce an empty string or if the string cannot be
245  * allocated.
246  *
247  * The callback argument specifies a function that returns a whitespace
248  * string or NULL before and after each element. If MXML_NO_CALLBACK
249  * is specified, whitespace will only be added before MXML_TEXT nodes
250  * with leading whitespace and before attribute names inside opening
251  * element tags.
252  */
253
254 char *                                  /* O - Allocated string or NULL */
255 mxmlSaveAllocString(mxml_node_t *node,  /* I - Node to write */
256                     const char  *(*cb)(mxml_node_t *node, int ws))
257                                         /* I - Whitespace callback or MXML_NO_CALLBACK */
258 {
259   int   bytes;                          /* Required bytes */
260   char  buffer[8192];                   /* Temporary buffer */
261   char  *s;                             /* Allocated string */
262
263
264  /*
265   * Write the node to the temporary buffer...
266   */
267
268   bytes = mxmlSaveString(node, buffer, sizeof(buffer), cb);
269
270   if (bytes <= 0)
271     return (NULL);
272
273   if (bytes < (int)(sizeof(buffer) - 1))
274   {
275    /*
276     * Node fit inside the buffer, so just duplicate that string and
277     * return...
278     */
279
280     return (strdup(buffer));
281   }
282
283  /*
284   * Allocate a buffer of the required size and save the node to the
285   * new buffer...
286   */
287
288   if ((s = malloc(bytes + 1)) == NULL)
289     return (NULL);
290
291   mxmlSaveString(node, s, bytes + 1, cb);
292
293  /*
294   * Return the allocated string...
295   */
296
297   return (s);
298 }
299
300
301 /*
302  * 'mxmlSaveFd()' - Save an XML tree to a file descriptor.
303  *
304  * The callback argument specifies a function that returns a whitespace
305  * string or NULL before and after each element. If MXML_NO_CALLBACK
306  * is specified, whitespace will only be added before MXML_TEXT nodes
307  * with leading whitespace and before attribute names inside opening
308  * element tags.
309  */
310
311 int                                     /* O - 0 on success, -1 on error. */
312 mxmlSaveFd(mxml_node_t *node,           /* I - Node to write */
313            int         fd,              /* I - File descriptor to write to */
314            const char  *(*cb)(mxml_node_t *node, int ws))
315                                         /* I - Whitespace callback or MXML_NO_CALLBACK */
316 {
317   int           col;                    /* Final column */
318   mxml_fdbuf_t  buf;                    /* File descriptor buffer */
319
320
321  /*
322   * Initialize the file descriptor buffer...
323   */
324
325   buf.fd      = fd;
326   buf.current = buf.buffer;
327   buf.end     = buf.buffer + sizeof(buf.buffer) - 4;
328
329  /*
330   * Write the node...
331   */
332
333   if ((col = mxml_write_node(node, &buf, cb, 0, mxml_fd_putc)) < 0)
334     return (-1);
335
336   if (col > 0)
337     if (mxml_fd_putc('\n', &buf) < 0)
338       return (-1);
339
340  /*
341   * Flush and return...
342   */
343
344   return (mxml_fd_write(&buf));
345 }
346
347
348 /*
349  * 'mxmlSaveFile()' - Save an XML tree to a file.
350  *
351  * The callback argument specifies a function that returns a whitespace
352  * string or NULL before and after each element. If MXML_NO_CALLBACK
353  * is specified, whitespace will only be added before MXML_TEXT nodes
354  * with leading whitespace and before attribute names inside opening
355  * element tags.
356  */
357
358 int                                     /* O - 0 on success, -1 on error. */
359 mxmlSaveFile(mxml_node_t *node,         /* I - Node to write */
360              FILE        *fp,           /* I - File to write to */
361              const char  *(*cb)(mxml_node_t *node, int ws))
362                                         /* I - Whitespace callback or MXML_NO_CALLBACK */
363 {
364   int   col;                            /* Final column */
365
366
367  /*
368   * Write the node...
369   */
370
371   if ((col = mxml_write_node(node, fp, cb, 0, mxml_file_putc)) < 0)
372     return (-1);
373
374   if (col > 0)
375     if (putc('\n', fp) < 0)
376       return (-1);
377
378  /*
379   * Return 0 (success)...
380   */
381
382   return (0);
383 }
384
385
386 /*
387  * 'mxmlSaveString()' - Save an XML node tree to a string.
388  *
389  * This function returns the total number of bytes that would be
390  * required for the string but only copies (bufsize - 1) characters
391  * into the specified buffer.
392  *
393  * The callback argument specifies a function that returns a whitespace
394  * string or NULL before and after each element. If MXML_NO_CALLBACK
395  * is specified, whitespace will only be added before MXML_TEXT nodes
396  * with leading whitespace and before attribute names inside opening
397  * element tags.
398  */
399
400 int                                     /* O - Size of string */
401 mxmlSaveString(mxml_node_t *node,       /* I - Node to write */
402                char        *buffer,     /* I - String buffer */
403                int         bufsize,     /* I - Size of string buffer */
404                const char  *(*cb)(mxml_node_t *node, int ws))
405                                         /* I - Whitespace callback or MXML_NO_CALLBACK */
406 {
407   int   col;                            /* Final column */
408   char  *ptr[2];                        /* Pointers for putc_cb */
409
410
411  /*
412   * Write the node...
413   */
414
415   ptr[0] = buffer;
416   ptr[1] = buffer + bufsize;
417
418   if ((col = mxml_write_node(node, ptr, cb, 0, mxml_string_putc)) < 0)
419     return (-1);
420
421   if (col > 0)
422     mxml_string_putc('\n', ptr);
423
424  /*
425   * Nul-terminate the buffer...
426   */
427
428   if (ptr[0] >= ptr[1])
429     buffer[bufsize - 1] = '\0';
430   else
431     ptr[0][0] = '\0';
432
433  /*
434   * Return the number of characters...
435   */
436
437   return (ptr[0] - buffer);
438 }
439
440
441 /*
442  * 'mxmlSetCustomHandlers()' - Set the handling functions for custom data.
443  *
444  * The load function accepts a node pointer and a data string and must
445  * return 0 on success and non-zero on error.
446  *
447  * The save function accepts a node pointer and must return a malloc'd
448  * string on success and NULL on error.
449  * 
450  */
451
452 void
453 mxmlSetCustomHandlers(mxml_custom_load_cb_t load,
454                                         /* I - Load function */
455                       mxml_custom_save_cb_t save)
456                                         /* I - Save function */
457 {
458   mxml_custom_load_cb = load;
459   mxml_custom_save_cb = save;
460 }
461
462
463 /*
464  * 'mxmlSetErrorCallback()' - Set the error message callback.
465  */
466
467 void
468 mxmlSetErrorCallback(void (*cb)(const char *))
469                                         /* I - Error callback function */
470 {
471   mxml_error_cb = cb;
472 }
473
474
475 /*
476  * 'mxml_add_char()' - Add a character to a buffer, expanding as needed.
477  */
478
479 static int                              /* O  - 0 on success, -1 on error */
480 mxml_add_char(int  ch,                  /* I  - Character to add */
481               char **bufptr,            /* IO - Current position in buffer */
482               char **buffer,            /* IO - Current buffer */
483               int  *bufsize)            /* IO - Current buffer size */
484 {
485   char  *newbuffer;                     /* New buffer value */
486
487
488   if (*bufptr >= (*buffer + *bufsize - 4))
489   {
490    /*
491     * Increase the size of the buffer...
492     */
493
494     if (*bufsize < 1024)
495       (*bufsize) *= 2;
496     else
497       (*bufsize) += 1024;
498
499     if ((newbuffer = realloc(*buffer, *bufsize)) == NULL)
500     {
501       free(*buffer);
502
503       mxml_error("Unable to expand string buffer to %d bytes!", *bufsize);
504
505       return (-1);
506     }
507
508     *bufptr = newbuffer + (*bufptr - *buffer);
509     *buffer = newbuffer;
510   }
511
512   if (ch < 0x80)
513   {
514    /*
515     * Single byte ASCII...
516     */
517
518     *(*bufptr)++ = ch;
519   }
520   else if (ch < 0x800)
521   {
522    /*
523     * Two-byte UTF-8...
524     */
525
526     *(*bufptr)++ = 0xc0 | (ch >> 6);
527     *(*bufptr)++ = 0x80 | (ch & 0x3f);
528   }
529   else if (ch < 0x10000)
530   {
531    /*
532     * Three-byte UTF-8...
533     */
534
535     *(*bufptr)++ = 0xe0 | (ch >> 12);
536     *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f);
537     *(*bufptr)++ = 0x80 | (ch & 0x3f);
538   }
539   else
540   {
541    /*
542     * Four-byte UTF-8...
543     */
544
545     *(*bufptr)++ = 0xf0 | (ch >> 18);
546     *(*bufptr)++ = 0x80 | ((ch >> 12) & 0x3f);
547     *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f);
548     *(*bufptr)++ = 0x80 | (ch & 0x3f);
549   }
550
551   return (0);
552 }
553
554
555 /*
556  * 'mxml_fd_getc()' - Read a character from a file descriptor.
557  */
558
559 static int                              /* O  - Character or EOF */
560 mxml_fd_getc(void *p,                   /* I  - File descriptor buffer */
561              int  *encoding)            /* IO - Encoding */
562 {
563   mxml_fdbuf_t  *buf;                   /* File descriptor buffer */
564   int           ch,                     /* Current character */
565                 temp;                   /* Temporary character */
566
567
568  /*
569   * Grab the next character in the buffer...
570   */
571
572   buf = (mxml_fdbuf_t *)p;
573
574   if (buf->current >= buf->end)
575     if (mxml_fd_read(buf) < 0)
576       return (EOF);
577
578   ch = *(buf->current)++;
579
580   switch (*encoding)
581   {
582     case ENCODE_UTF8 :
583        /*
584         * Got a UTF-8 character; convert UTF-8 to Unicode and return...
585         */
586
587         if (!(ch & 0x80))
588         {
589 #if DEBUG > 1
590           printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
591 #endif /* DEBUG > 1 */
592
593           if (mxml_bad_char(ch))
594           {
595             mxml_error("Bad control character 0x%02x not allowed by XML standard!",
596                        ch);
597             return (EOF);
598           }
599
600           return (ch);
601         }
602         else if (ch == 0xfe)
603         {
604          /*
605           * UTF-16 big-endian BOM?
606           */
607
608           if (buf->current >= buf->end)
609             if (mxml_fd_read(buf) < 0)
610               return (EOF);
611
612           ch = *(buf->current)++;
613           
614           if (ch != 0xff)
615             return (EOF);
616
617           *encoding = ENCODE_UTF16BE;
618
619           return (mxml_fd_getc(p, encoding));
620         }
621         else if (ch == 0xff)
622         {
623          /*
624           * UTF-16 little-endian BOM?
625           */
626
627           if (buf->current >= buf->end)
628             if (mxml_fd_read(buf) < 0)
629               return (EOF);
630
631           ch = *(buf->current)++;
632           
633           if (ch != 0xfe)
634             return (EOF);
635
636           *encoding = ENCODE_UTF16LE;
637
638           return (mxml_fd_getc(p, encoding));
639         }
640         else if ((ch & 0xe0) == 0xc0)
641         {
642          /*
643           * Two-byte value...
644           */
645
646           if (buf->current >= buf->end)
647             if (mxml_fd_read(buf) < 0)
648               return (EOF);
649
650           temp = *(buf->current)++;
651
652           if ((temp & 0xc0) != 0x80)
653             return (EOF);
654
655           ch = ((ch & 0x1f) << 6) | (temp & 0x3f);
656
657           if (ch < 0x80)
658             return (EOF);
659         }
660         else if ((ch & 0xf0) == 0xe0)
661         {
662          /*
663           * Three-byte value...
664           */
665
666           if (buf->current >= buf->end)
667             if (mxml_fd_read(buf) < 0)
668               return (EOF);
669
670           temp = *(buf->current)++;
671
672           if ((temp & 0xc0) != 0x80)
673             return (EOF);
674
675           ch = ((ch & 0x0f) << 6) | (temp & 0x3f);
676
677           if (buf->current >= buf->end)
678             if (mxml_fd_read(buf) < 0)
679               return (EOF);
680
681           temp = *(buf->current)++;
682
683           if ((temp & 0xc0) != 0x80)
684             return (EOF);
685
686           ch = (ch << 6) | (temp & 0x3f);
687
688           if (ch < 0x800)
689             return (EOF);
690         }
691         else if ((ch & 0xf8) == 0xf0)
692         {
693          /*
694           * Four-byte value...
695           */
696
697           if (buf->current >= buf->end)
698             if (mxml_fd_read(buf) < 0)
699               return (EOF);
700
701           temp = *(buf->current)++;
702
703           if ((temp & 0xc0) != 0x80)
704             return (EOF);
705
706           ch = ((ch & 0x07) << 6) | (temp & 0x3f);
707
708           if (buf->current >= buf->end)
709             if (mxml_fd_read(buf) < 0)
710               return (EOF);
711
712           temp = *(buf->current)++;
713
714           if ((temp & 0xc0) != 0x80)
715             return (EOF);
716
717           ch = (ch << 6) | (temp & 0x3f);
718
719           if (buf->current >= buf->end)
720             if (mxml_fd_read(buf) < 0)
721               return (EOF);
722
723           temp = *(buf->current)++;
724
725           if ((temp & 0xc0) != 0x80)
726             return (EOF);
727
728           ch = (ch << 6) | (temp & 0x3f);
729
730           if (ch < 0x10000)
731             return (EOF);
732         }
733         else
734           return (EOF);
735         break;
736
737     case ENCODE_UTF16BE :
738        /*
739         * Read UTF-16 big-endian char...
740         */
741
742         if (buf->current >= buf->end)
743           if (mxml_fd_read(buf) < 0)
744             return (EOF);
745
746         temp = *(buf->current)++;
747
748         ch = (ch << 8) | temp;
749
750         if (mxml_bad_char(ch))
751         {
752           mxml_error("Bad control character 0x%02x not allowed by XML standard!",
753                      ch);
754           return (EOF);
755         }
756         else if (ch >= 0xd800 && ch <= 0xdbff)
757         {
758          /*
759           * Multi-word UTF-16 char...
760           */
761
762           int lch;
763
764           if (buf->current >= buf->end)
765             if (mxml_fd_read(buf) < 0)
766               return (EOF);
767
768           lch = *(buf->current)++;
769
770           if (buf->current >= buf->end)
771             if (mxml_fd_read(buf) < 0)
772               return (EOF);
773
774           temp = *(buf->current)++;
775
776           lch = (lch << 8) | temp;
777
778           if (lch < 0xdc00 || lch >= 0xdfff)
779             return (EOF);
780
781           ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
782         }
783         break;
784
785     case ENCODE_UTF16LE :
786        /*
787         * Read UTF-16 little-endian char...
788         */
789
790         if (buf->current >= buf->end)
791           if (mxml_fd_read(buf) < 0)
792             return (EOF);
793
794         temp = *(buf->current)++;
795
796         ch |= (temp << 8);
797
798         if (mxml_bad_char(ch))
799         {
800           mxml_error("Bad control character 0x%02x not allowed by XML standard!",
801                      ch);
802           return (EOF);
803         }
804         else if (ch >= 0xd800 && ch <= 0xdbff)
805         {
806          /*
807           * Multi-word UTF-16 char...
808           */
809
810           int lch;
811
812           if (buf->current >= buf->end)
813             if (mxml_fd_read(buf) < 0)
814               return (EOF);
815
816           lch = *(buf->current)++;
817
818           if (buf->current >= buf->end)
819             if (mxml_fd_read(buf) < 0)
820               return (EOF);
821
822           temp = *(buf->current)++;
823
824           lch |= (temp << 8);
825
826           if (lch < 0xdc00 || lch >= 0xdfff)
827             return (EOF);
828
829           ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
830         }
831         break;
832   }
833
834 #if DEBUG > 1
835   printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
836 #endif /* DEBUG > 1 */
837
838   return (ch);
839 }
840
841
842 /*
843  * 'mxml_fd_putc()' - Write a character to a file descriptor.
844  */
845
846 static int                              /* O - 0 on success, -1 on error */
847 mxml_fd_putc(int  ch,                   /* I - Character */
848              void *p)                   /* I - File descriptor buffer */
849 {
850   mxml_fdbuf_t  *buf;                   /* File descriptor buffer */
851
852
853  /*
854   * Flush the write buffer as needed - note above that "end" still leaves
855   * 4 characters at the end so that we can avoid a lot of extra tests...
856   */
857
858   buf = (mxml_fdbuf_t *)p;
859
860   if (buf->current >= buf->end)
861     if (mxml_fd_write(buf) < 0)
862       return (-1);
863
864   if (ch < 0x80)
865   {
866    /*
867     * Write ASCII character directly...
868     */
869
870     *(buf->current)++ = ch;
871   }
872   else if (ch < 0x800)
873   {
874    /*
875     * Two-byte UTF-8 character...
876     */
877
878     *(buf->current)++ = 0xc0 | (ch >> 6);
879     *(buf->current)++ = 0x80 | (ch & 0x3f);
880   }
881   else if (ch < 0x10000)
882   {
883    /*
884     * Three-byte UTF-8 character...
885     */
886
887     *(buf->current)++ = 0xe0 | (ch >> 12);
888     *(buf->current)++ = 0x80 | ((ch >> 6) & 0x3f);
889     *(buf->current)++ = 0x80 | (ch & 0x3f);
890   }
891   else
892   {
893    /*
894     * Four-byte UTF-8 character...
895     */
896
897     *(buf->current)++ = 0xf0 | (ch >> 18);
898     *(buf->current)++ = 0x80 | ((ch >> 12) & 0x3f);
899     *(buf->current)++ = 0x80 | ((ch >> 6) & 0x3f);
900     *(buf->current)++ = 0x80 | (ch & 0x3f);
901   }
902
903  /*
904   * Return successfully...
905   */
906
907   return (0);
908 }
909
910
911 /*
912  * 'mxml_fd_read()' - Read a buffer of data from a file descriptor.
913  */
914
915 static int                              /* O - 0 on success, -1 on error */
916 mxml_fd_read(mxml_fdbuf_t *buf)         /* I - File descriptor buffer */
917 {
918   int   bytes;                          /* Bytes read... */
919
920
921  /*
922   * Range check input...
923   */
924
925   if (!buf)
926     return (-1);
927
928  /*
929   * Read from the file descriptor...
930   */
931
932   while ((bytes = read(buf->fd, buf->buffer, sizeof(buf->buffer))) < 0)
933     if (errno != EAGAIN && errno != EINTR)
934       return (-1);
935
936   if (bytes == 0)
937     return (-1);
938
939  /*
940   * Update the pointers and return success...
941   */
942
943   buf->current = buf->buffer;
944   buf->end     = buf->buffer + bytes;
945
946   return (0);
947 }
948
949
950 /*
951  * 'mxml_fd_write()' - Write a buffer of data to a file descriptor.
952  */
953
954 static int                              /* O - 0 on success, -1 on error */
955 mxml_fd_write(mxml_fdbuf_t *buf)        /* I - File descriptor buffer */
956 {
957   int           bytes;                  /* Bytes written */
958   unsigned char *ptr;                   /* Pointer into buffer */
959
960
961  /*
962   * Range check...
963   */
964
965   if (!buf)
966     return (-1);
967
968  /*
969   * Return 0 if there is nothing to write...
970   */
971
972   if (buf->current == buf->buffer)
973     return (0);
974
975  /*
976   * Loop until we have written everything...
977   */
978
979   for (ptr = buf->buffer; ptr < buf->current; ptr += bytes)
980     if ((bytes = write(buf->fd, ptr, buf->current - ptr)) < 0)
981       return (-1);
982
983  /*
984   * All done, reset pointers and return success...
985   */
986
987   buf->current = buf->buffer;
988
989   return (0);
990 }
991
992
993 /*
994  * 'mxml_file_getc()' - Get a character from a file.
995  */
996
997 static int                              /* O  - Character or EOF */
998 mxml_file_getc(void *p,                 /* I  - Pointer to file */
999                int  *encoding)          /* IO - Encoding */
1000 {
1001   int   ch,                             /* Character from file */
1002         temp;                           /* Temporary character */
1003   FILE  *fp;                            /* Pointer to file */
1004
1005
1006  /*
1007   * Read a character from the file and see if it is EOF or ASCII...
1008   */
1009
1010   fp = (FILE *)p;
1011   ch = getc(fp);
1012
1013   if (ch == EOF)
1014     return (EOF);
1015
1016   switch (*encoding)
1017   {
1018     case ENCODE_UTF8 :
1019        /*
1020         * Got a UTF-8 character; convert UTF-8 to Unicode and return...
1021         */
1022
1023         if (!(ch & 0x80))
1024         {
1025           if (mxml_bad_char(ch))
1026           {
1027             mxml_error("Bad control character 0x%02x not allowed by XML standard!",
1028                        ch);
1029             return (EOF);
1030           }
1031
1032 #if DEBUG > 1
1033           printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
1034 #endif /* DEBUG > 1 */
1035
1036           return (ch);
1037         }
1038         else if (ch == 0xfe)
1039         {
1040          /*
1041           * UTF-16 big-endian BOM?
1042           */
1043
1044           ch = getc(fp);
1045           if (ch != 0xff)
1046             return (EOF);
1047
1048           *encoding = ENCODE_UTF16BE;
1049
1050           return (mxml_file_getc(p, encoding));
1051         }
1052         else if (ch == 0xff)
1053         {
1054          /*
1055           * UTF-16 little-endian BOM?
1056           */
1057
1058           ch = getc(fp);
1059           if (ch != 0xfe)
1060             return (EOF);
1061
1062           *encoding = ENCODE_UTF16LE;
1063
1064           return (mxml_file_getc(p, encoding));
1065         }
1066         else if ((ch & 0xe0) == 0xc0)
1067         {
1068          /*
1069           * Two-byte value...
1070           */
1071
1072           if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1073             return (EOF);
1074
1075           ch = ((ch & 0x1f) << 6) | (temp & 0x3f);
1076
1077           if (ch < 0x80)
1078             return (EOF);
1079         }
1080         else if ((ch & 0xf0) == 0xe0)
1081         {
1082          /*
1083           * Three-byte value...
1084           */
1085
1086           if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1087             return (EOF);
1088
1089           ch = ((ch & 0x0f) << 6) | (temp & 0x3f);
1090
1091           if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1092             return (EOF);
1093
1094           ch = (ch << 6) | (temp & 0x3f);
1095
1096           if (ch < 0x800)
1097             return (EOF);
1098         }
1099         else if ((ch & 0xf8) == 0xf0)
1100         {
1101          /*
1102           * Four-byte value...
1103           */
1104
1105           if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1106             return (EOF);
1107
1108           ch = ((ch & 0x07) << 6) | (temp & 0x3f);
1109
1110           if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1111             return (EOF);
1112
1113           ch = (ch << 6) | (temp & 0x3f);
1114
1115           if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1116             return (EOF);
1117
1118           ch = (ch << 6) | (temp & 0x3f);
1119
1120           if (ch < 0x10000)
1121             return (EOF);
1122         }
1123         else
1124           return (EOF);
1125         break;
1126
1127     case ENCODE_UTF16BE :
1128        /*
1129         * Read UTF-16 big-endian char...
1130         */
1131
1132         ch = (ch << 8) | getc(fp);
1133
1134         if (mxml_bad_char(ch))
1135         {
1136           mxml_error("Bad control character 0x%02x not allowed by XML standard!",
1137                      ch);
1138           return (EOF);
1139         }
1140         else if (ch >= 0xd800 && ch <= 0xdbff)
1141         {
1142          /*
1143           * Multi-word UTF-16 char...
1144           */
1145
1146           int lch = (getc(fp) << 8) | getc(fp);
1147
1148           if (lch < 0xdc00 || lch >= 0xdfff)
1149             return (EOF);
1150
1151           ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
1152         }
1153         break;
1154
1155     case ENCODE_UTF16LE :
1156        /*
1157         * Read UTF-16 little-endian char...
1158         */
1159
1160         ch |= (getc(fp) << 8);
1161
1162         if (mxml_bad_char(ch))
1163         {
1164           mxml_error("Bad control character 0x%02x not allowed by XML standard!",
1165                      ch);
1166           return (EOF);
1167         }
1168         else if (ch >= 0xd800 && ch <= 0xdbff)
1169         {
1170          /*
1171           * Multi-word UTF-16 char...
1172           */
1173
1174           int lch = getc(fp) | (getc(fp) << 8);
1175
1176           if (lch < 0xdc00 || lch >= 0xdfff)
1177             return (EOF);
1178
1179           ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
1180         }
1181         break;
1182   }
1183
1184 #if DEBUG > 1
1185   printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
1186 #endif /* DEBUG > 1 */
1187
1188   return (ch);
1189 }
1190
1191
1192 /*
1193  * 'mxml_file_putc()' - Write a character to a file.
1194  */
1195
1196 static int                              /* O - 0 on success, -1 on failure */
1197 mxml_file_putc(int  ch,                 /* I - Character to write */
1198                void *p)                 /* I - Pointer to file */
1199 {
1200   char  buffer[4],                      /* Buffer for character */
1201         *bufptr;                        /* Pointer into buffer */
1202   int   buflen;                         /* Number of bytes to write */
1203
1204
1205   if (ch < 0x80)
1206     return (putc(ch, (FILE *)p) == EOF ? -1 : 0);
1207
1208   bufptr = buffer;
1209
1210   if (ch < 0x800)
1211   {
1212    /*
1213     * Two-byte UTF-8 character...
1214     */
1215
1216     *bufptr++ = 0xc0 | (ch >> 6);
1217     *bufptr++ = 0x80 | (ch & 0x3f);
1218   }
1219   else if (ch < 0x10000)
1220   {
1221    /*
1222     * Three-byte UTF-8 character...
1223     */
1224
1225     *bufptr++ = 0xe0 | (ch >> 12);
1226     *bufptr++ = 0x80 | ((ch >> 6) & 0x3f);
1227     *bufptr++ = 0x80 | (ch & 0x3f);
1228   }
1229   else
1230   {
1231    /*
1232     * Four-byte UTF-8 character...
1233     */
1234
1235     *bufptr++ = 0xf0 | (ch >> 18);
1236     *bufptr++ = 0x80 | ((ch >> 12) & 0x3f);
1237     *bufptr++ = 0x80 | ((ch >> 6) & 0x3f);
1238     *bufptr++ = 0x80 | (ch & 0x3f);
1239   }
1240
1241   buflen = bufptr - buffer;
1242
1243   return (fwrite(buffer, 1, buflen, (FILE *)p) < buflen ? -1 : 0);
1244 }
1245
1246
1247 /*
1248  * 'mxml_get_entity()' - Get the character corresponding to an entity...
1249  */
1250
1251 static int                              /* O  - Character value or EOF on error */
1252 mxml_get_entity(mxml_node_t *parent,    /* I  - Parent node */
1253                 void        *p,         /* I  - Pointer to source */
1254                 int         *encoding,  /* IO - Character encoding */
1255                 int         (*getc_cb)(void *, int *))
1256                                         /* I  - Get character function */
1257 {
1258   int   ch;                             /* Current character */
1259   char  entity[64],                     /* Entity string */
1260         *entptr;                        /* Pointer into entity */
1261
1262
1263   entptr = entity;
1264
1265   while ((ch = (*getc_cb)(p, encoding)) != EOF)
1266     if (ch > 126 || (!isalnum(ch) && ch != '#'))
1267       break;
1268     else if (entptr < (entity + sizeof(entity) - 1))
1269       *entptr++ = ch;
1270     else
1271     {
1272       mxml_error("Entity name too long under parent <%s>!",
1273                  parent ? parent->value.element.name : "null");
1274       break;
1275     }
1276
1277   *entptr = '\0';
1278
1279   if (ch != ';')
1280   {
1281     mxml_error("Character entity \"%s\" not terminated under parent <%s>!",
1282                entity, parent ? parent->value.element.name : "null");
1283     return (EOF);
1284   }
1285
1286   if (entity[0] == '#')
1287   {
1288     if (entity[1] == 'x')
1289       ch = strtol(entity + 2, NULL, 16);
1290     else
1291       ch = strtol(entity + 1, NULL, 10);
1292   }
1293   else if ((ch = mxmlEntityGetValue(entity)) < 0)
1294     mxml_error("Entity name \"%s;\" not supported under parent <%s>!",
1295                entity, parent ? parent->value.element.name : "null");
1296
1297   if (mxml_bad_char(ch))
1298   {
1299     mxml_error("Bad control character 0x%02x under parent <%s> not allowed by XML standard!",
1300                ch, parent ? parent->value.element.name : "null");
1301     return (EOF);
1302   }
1303
1304   return (ch);
1305 }
1306
1307
1308 /*
1309  * 'mxml_load_data()' - Load data into an XML node tree.
1310  */
1311
1312 static mxml_node_t *                    /* O - First node or NULL if the file could not be read. */
1313 mxml_load_data(mxml_node_t *top,        /* I - Top node */
1314                void        *p,          /* I - Pointer to data */
1315                mxml_type_t (*cb)(mxml_node_t *),
1316                                         /* I - Callback function or MXML_NO_CALLBACK */
1317                int         (*getc_cb)(void *, int *))
1318                                         /* I - Read function */
1319 {
1320   mxml_node_t   *node,                  /* Current node */
1321                 *first,                 /* First node added */
1322                 *parent;                /* Current parent node */
1323   int           ch,                     /* Character from file */
1324                 whitespace;             /* Non-zero if whitespace seen */
1325   char          *buffer,                /* String buffer */
1326                 *bufptr;                /* Pointer into buffer */
1327   int           bufsize;                /* Size of buffer */
1328   mxml_type_t   type;                   /* Current node type */
1329   int           encoding;               /* Character encoding */
1330   static const char * const types[] =   /* Type strings... */
1331                 {
1332                   "MXML_ELEMENT",       /* XML element with attributes */
1333                   "MXML_INTEGER",       /* Integer value */
1334                   "MXML_OPAQUE",        /* Opaque string */
1335                   "MXML_REAL",          /* Real value */
1336                   "MXML_TEXT",          /* Text fragment */
1337                   "MXML_CUSTOM"         /* Custom data */
1338                 };
1339
1340
1341  /*
1342   * Read elements and other nodes from the file...
1343   */
1344
1345   if ((buffer = malloc(64)) == NULL)
1346   {
1347     mxml_error("Unable to allocate string buffer!");
1348     return (NULL);
1349   }
1350
1351   bufsize    = 64;
1352   bufptr     = buffer;
1353   parent     = top;
1354   first      = NULL;
1355   whitespace = 0;
1356   encoding   = ENCODE_UTF8;
1357
1358   if (cb && parent)
1359     type = (*cb)(parent);
1360   else
1361     type = MXML_TEXT;
1362
1363   while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1364   {
1365     if ((ch == '<' ||
1366          (isspace(ch) && type != MXML_OPAQUE && type != MXML_CUSTOM)) &&
1367         bufptr > buffer)
1368     {
1369      /*
1370       * Add a new value node...
1371       */
1372
1373       *bufptr = '\0';
1374
1375       switch (type)
1376       {
1377         case MXML_INTEGER :
1378             node = mxmlNewInteger(parent, strtol(buffer, &bufptr, 0));
1379             break;
1380
1381         case MXML_OPAQUE :
1382             node = mxmlNewOpaque(parent, buffer);
1383             break;
1384
1385         case MXML_REAL :
1386             node = mxmlNewReal(parent, strtod(buffer, &bufptr));
1387             break;
1388
1389         case MXML_TEXT :
1390             node = mxmlNewText(parent, whitespace, buffer);
1391             break;
1392
1393         case MXML_CUSTOM :
1394             if (mxml_custom_load_cb)
1395             {
1396              /*
1397               * Use the callback to fill in the custom data...
1398               */
1399
1400               node = mxmlNewCustom(parent, NULL, NULL);
1401
1402               if ((*mxml_custom_load_cb)(node, buffer))
1403               {
1404                 mxml_error("Bad custom value '%s' in parent <%s>!",
1405                            buffer, parent ? parent->value.element.name : "null");
1406                 mxmlDelete(node);
1407                 node = NULL;
1408               }
1409               break;
1410             }
1411
1412         default : /* Should never happen... */
1413             node = NULL;
1414             break;
1415       }   
1416
1417       if (*bufptr)
1418       {
1419        /*
1420         * Bad integer/real number value...
1421         */
1422
1423         mxml_error("Bad %s value '%s' in parent <%s>!",
1424                    type == MXML_INTEGER ? "integer" : "real", buffer,
1425                    parent ? parent->value.element.name : "null");
1426         break;
1427       }
1428
1429       bufptr     = buffer;
1430       whitespace = isspace(ch) && type == MXML_TEXT;
1431
1432       if (!node)
1433       {
1434        /*
1435         * Print error and return...
1436         */
1437
1438         mxml_error("Unable to add value node of type %s to parent <%s>!",
1439                    types[type], parent ? parent->value.element.name : "null");
1440         goto error;
1441       }
1442
1443       if (!first)
1444         first = node;
1445     }
1446     else if (isspace(ch) && type == MXML_TEXT)
1447       whitespace = 1;
1448
1449    /*
1450     * Add lone whitespace node if we have an element and existing
1451     * whitespace...
1452     */
1453
1454     if (ch == '<' && whitespace && type == MXML_TEXT)
1455     {
1456       mxmlNewText(parent, whitespace, "");
1457       whitespace = 0;
1458     }
1459
1460     if (ch == '<')
1461     {
1462      /*
1463       * Start of open/close tag...
1464       */
1465
1466       bufptr = buffer;
1467
1468       while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1469         if (isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer))
1470           break;
1471         else if (ch == '&')
1472         {
1473           if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
1474             goto error;
1475
1476           if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1477             goto error;
1478         }
1479         else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1480           goto error;
1481         else if (((bufptr - buffer) == 1 && buffer[0] == '?') ||
1482                  ((bufptr - buffer) == 3 && !strncmp(buffer, "!--", 3)) ||
1483                  ((bufptr - buffer) == 8 && !strncmp(buffer, "![CDATA[", 8)))
1484           break;
1485
1486       *bufptr = '\0';
1487
1488       if (!strcmp(buffer, "!--"))
1489       {
1490        /*
1491         * Gather rest of comment...
1492         */
1493
1494         while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1495         {
1496           if (ch == '>' && bufptr > (buffer + 4) &&
1497               bufptr[-3] != '-' && bufptr[-2] == '-' && bufptr[-1] == '-')
1498             break;
1499           else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1500             goto error;
1501         }
1502
1503        /*
1504         * Error out if we didn't get the whole comment...
1505         */
1506
1507         if (ch != '>')
1508         {
1509          /*
1510           * Print error and return...
1511           */
1512
1513           mxml_error("Early EOF in comment node!");
1514           goto error;
1515         }
1516
1517
1518        /*
1519         * Otherwise add this as an element under the current parent...
1520         */
1521
1522         *bufptr = '\0';
1523
1524         if (!mxmlNewElement(parent, buffer))
1525         {
1526          /*
1527           * Just print error for now...
1528           */
1529
1530           mxml_error("Unable to add comment node to parent <%s>!",
1531                      parent ? parent->value.element.name : "null");
1532           break;
1533         }
1534       }
1535       else if (!strcmp(buffer, "![CDATA["))
1536       {
1537        /*
1538         * Gather CDATA section...
1539         */
1540
1541         while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1542         {
1543           if (ch == '>' && !strncmp(bufptr - 2, "]]", 2))
1544             break;
1545           else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1546             goto error;
1547         }
1548
1549        /*
1550         * Error out if we didn't get the whole comment...
1551         */
1552
1553         if (ch != '>')
1554         {
1555          /*
1556           * Print error and return...
1557           */
1558
1559           mxml_error("Early EOF in CDATA node!");
1560           goto error;
1561         }
1562
1563
1564        /*
1565         * Otherwise add this as an element under the current parent...
1566         */
1567
1568         *bufptr = '\0';
1569
1570         if (!mxmlNewElement(parent, buffer))
1571         {
1572          /*
1573           * Print error and return...
1574           */
1575
1576           mxml_error("Unable to add CDATA node to parent <%s>!",
1577                      parent ? parent->value.element.name : "null");
1578           goto error;
1579         }
1580       }
1581       else if (buffer[0] == '?')
1582       {
1583        /*
1584         * Gather rest of processing instruction...
1585         */
1586
1587         while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1588         {
1589           if (ch == '>' && bufptr > buffer && bufptr[-1] == '?')
1590             break;
1591           else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1592             goto error;
1593         }
1594
1595        /*
1596         * Error out if we didn't get the whole processing instruction...
1597         */
1598
1599         if (ch != '>')
1600         {
1601          /*
1602           * Print error and return...
1603           */
1604
1605           mxml_error("Early EOF in processing instruction node!");
1606           goto error;
1607         }
1608
1609
1610        /*
1611         * Otherwise add this as an element under the current parent...
1612         */
1613
1614         *bufptr = '\0';
1615
1616         if (!(parent = mxmlNewElement(parent, buffer)))
1617         {
1618          /*
1619           * Print error and return...
1620           */
1621
1622           mxml_error("Unable to add processing instruction node to parent <%s>!",
1623                      parent ? parent->value.element.name : "null");
1624           goto error;
1625         }
1626
1627         if (cb)
1628           type = (*cb)(parent);
1629       }
1630       else if (buffer[0] == '!')
1631       {
1632        /*
1633         * Gather rest of declaration...
1634         */
1635
1636         do
1637         {
1638           if (ch == '>')
1639             break;
1640           else
1641           {
1642             if (ch == '&')
1643               if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
1644                 goto error;
1645
1646             if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1647               goto error;
1648           }
1649         }
1650         while ((ch = (*getc_cb)(p, &encoding)) != EOF);
1651
1652        /*
1653         * Error out if we didn't get the whole declaration...
1654         */
1655
1656         if (ch != '>')
1657         {
1658          /*
1659           * Print error and return...
1660           */
1661
1662           mxml_error("Early EOF in declaration node!");
1663           goto error;
1664         }
1665
1666        /*
1667         * Otherwise add this as an element under the current parent...
1668         */
1669
1670         *bufptr = '\0';
1671
1672         node = mxmlNewElement(parent, buffer);
1673         if (!node)
1674         {
1675          /*
1676           * Print error and return...
1677           */
1678
1679           mxml_error("Unable to add declaration node to parent <%s>!",
1680                      parent ? parent->value.element.name : "null");
1681           goto error;
1682         }
1683
1684        /*
1685         * Descend into this node, setting the value type as needed...
1686         */
1687
1688         parent = node;
1689
1690         if (cb)
1691           type = (*cb)(parent);
1692       }
1693       else if (buffer[0] == '/')
1694       {
1695        /*
1696         * Handle close tag...
1697         */
1698
1699         if (!parent || strcmp(buffer + 1, parent->value.element.name))
1700         {
1701          /*
1702           * Close tag doesn't match tree; print an error for now...
1703           */
1704
1705           mxml_error("Mismatched close tag <%s> under parent <%s>!",
1706                      buffer, parent->value.element.name);
1707           goto error;
1708         }
1709
1710        /*
1711         * Keep reading until we see >...
1712         */
1713
1714         while (ch != '>' && ch != EOF)
1715           ch = (*getc_cb)(p, &encoding);
1716
1717        /*
1718         * Ascend into the parent and set the value type as needed...
1719         */
1720
1721         parent = parent->parent;
1722
1723         if (cb && parent)
1724           type = (*cb)(parent);
1725       }
1726       else
1727       {
1728        /*
1729         * Handle open tag...
1730         */
1731
1732         node = mxmlNewElement(parent, buffer);
1733
1734         if (!node)
1735         {
1736          /*
1737           * Just print error for now...
1738           */
1739
1740           mxml_error("Unable to add element node to parent <%s>!",
1741                      parent ? parent->value.element.name : "null");
1742           goto error;
1743         }
1744
1745         if (isspace(ch))
1746           ch = mxml_parse_element(node, p, &encoding, getc_cb);
1747         else if (ch == '/')
1748         {
1749           if ((ch = (*getc_cb)(p, &encoding)) != '>')
1750           {
1751             mxml_error("Expected > but got '%c' instead for element <%s/>!",
1752                        ch, buffer);
1753             goto error;
1754           }
1755
1756           ch = '/';
1757         }
1758
1759         if (ch == EOF)
1760           break;
1761
1762         if (ch != '/')
1763         {
1764          /*
1765           * Descend into this node, setting the value type as needed...
1766           */
1767
1768           parent = node;
1769
1770           if (cb && parent)
1771             type = (*cb)(parent);
1772         }
1773       }
1774
1775       bufptr  = buffer;
1776     }
1777     else if (ch == '&')
1778     {
1779      /*
1780       * Add character entity to current buffer...
1781       */
1782
1783       if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
1784         goto error;
1785
1786       if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1787         goto error;
1788     }
1789     else if (type == MXML_OPAQUE || type == MXML_CUSTOM || !isspace(ch))
1790     {
1791      /*
1792       * Add character to current buffer...
1793       */
1794
1795       if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1796         goto error;
1797     }
1798   }
1799
1800  /*
1801   * Free the string buffer - we don't need it anymore...
1802   */
1803
1804   free(buffer);
1805
1806  /*
1807   * Find the top element and return it...
1808   */
1809
1810   if (parent)
1811   {
1812     while (parent->parent != top && parent->parent)
1813       parent = parent->parent;
1814   }
1815
1816   return (parent);
1817
1818  /*
1819   * Common error return...
1820   */
1821
1822 error:
1823
1824   mxmlDelete(first);
1825
1826   free(buffer);
1827
1828   return (NULL);
1829 }
1830
1831
1832 /*
1833  * 'mxml_parse_element()' - Parse an element for any attributes...
1834  */
1835
1836 static int                              /* O  - Terminating character */
1837 mxml_parse_element(mxml_node_t *node,   /* I  - Element node */
1838                    void        *p,      /* I  - Data to read from */
1839                    int         *encoding,
1840                                         /* IO - Encoding */
1841                    int         (*getc_cb)(void *, int *))
1842                                         /* I  - Data callback */
1843 {
1844   int   ch,                             /* Current character in file */
1845         quote;                          /* Quoting character */
1846   char  *name,                          /* Attribute name */
1847         *value,                         /* Attribute value */
1848         *ptr;                           /* Pointer into name/value */
1849   int   namesize,                       /* Size of name string */
1850         valsize;                        /* Size of value string */
1851
1852
1853
1854
1855  /*
1856   * Initialize the name and value buffers...
1857   */
1858
1859   if ((name = malloc(64)) == NULL)
1860   {
1861     mxml_error("Unable to allocate memory for name!");
1862     return (EOF);
1863   }
1864
1865   namesize = 64;
1866
1867   if ((value = malloc(64)) == NULL)
1868   {
1869     free(name);
1870     mxml_error("Unable to allocate memory for value!");
1871     return (EOF);
1872   }
1873
1874   valsize = 64;
1875
1876  /*
1877   * Loop until we hit a >, /, ?, or EOF...
1878   */
1879
1880   while ((ch = (*getc_cb)(p, encoding)) != EOF)
1881   {
1882 #if DEBUG > 1
1883     fprintf(stderr, "parse_element: ch='%c'\n", ch);
1884 #endif /* DEBUG > 1 */
1885
1886    /*
1887     * Skip leading whitespace...
1888     */
1889
1890     if (isspace(ch))
1891       continue;
1892
1893    /*
1894     * Stop at /, ?, or >...
1895     */
1896
1897     if (ch == '/' || ch == '?')
1898     {
1899      /*
1900       * Grab the > character and print an error if it isn't there...
1901       */
1902
1903       quote = (*getc_cb)(p, encoding);
1904
1905       if (quote != '>')
1906       {
1907         mxml_error("Expected '>' after '%c' for element %s, but got '%c'!",
1908                    ch, node->value.element.name, quote);
1909         ch = EOF;
1910       }
1911
1912       break;
1913     }
1914     else if (ch == '>')
1915       break;
1916
1917    /*
1918     * Read the attribute name...
1919     */
1920
1921     name[0] = ch;
1922     ptr     = name + 1;
1923
1924     if (ch == '\"' || ch == '\'')
1925     {
1926      /*
1927       * Name is in quotes, so get a quoted string...
1928       */
1929
1930       quote = ch;
1931
1932       while ((ch = (*getc_cb)(p, encoding)) != EOF)
1933       {
1934         if (ch == '&')
1935           if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
1936             goto error;
1937
1938         if (mxml_add_char(ch, &ptr, &name, &namesize))
1939           goto error;
1940
1941         if (ch == quote)
1942           break;
1943       }
1944     }
1945     else
1946     {
1947      /*
1948       * Grab an normal, non-quoted name...
1949       */
1950
1951       while ((ch = (*getc_cb)(p, encoding)) != EOF)
1952         if (isspace(ch) || ch == '=' || ch == '/' || ch == '>' || ch == '?')
1953           break;
1954         else
1955         {
1956           if (ch == '&')
1957             if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
1958               goto error;
1959
1960           if (mxml_add_char(ch, &ptr, &name, &namesize))
1961             goto error;
1962         }
1963     }
1964
1965     *ptr = '\0';
1966
1967     if (mxmlElementGetAttr(node, name))
1968       goto error;
1969
1970     if (ch == '=')
1971     {
1972      /*
1973       * Read the attribute value...
1974       */
1975
1976       if ((ch = (*getc_cb)(p, encoding)) == EOF)
1977       {
1978         mxml_error("Missing value for attribute '%s' in element %s!",
1979                    name, node->value.element.name);
1980         return (EOF);
1981       }
1982
1983       if (ch == '\'' || ch == '\"')
1984       {
1985        /*
1986         * Read quoted value...
1987         */
1988
1989         quote = ch;
1990         ptr   = value;
1991
1992         while ((ch = (*getc_cb)(p, encoding)) != EOF)
1993           if (ch == quote)
1994             break;
1995           else
1996           {
1997             if (ch == '&')
1998               if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
1999                 goto error;
2000               
2001             if (mxml_add_char(ch, &ptr, &value, &valsize))
2002               goto error;
2003           }
2004
2005         *ptr = '\0';
2006       }
2007       else
2008       {
2009        /*
2010         * Read unquoted value...
2011         */
2012
2013         value[0] = ch;
2014         ptr      = value + 1;
2015
2016         while ((ch = (*getc_cb)(p, encoding)) != EOF)
2017           if (isspace(ch) || ch == '=' || ch == '/' || ch == '>')
2018             break;
2019           else
2020           {
2021             if (ch == '&')
2022               if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
2023                 goto error;
2024               
2025             if (mxml_add_char(ch, &ptr, &value, &valsize))
2026               goto error;
2027           }
2028
2029         *ptr = '\0';
2030       }
2031
2032      /*
2033       * Set the attribute with the given string value...
2034       */
2035
2036       mxmlElementSetAttr(node, name, value);
2037     }
2038     else
2039     {
2040      /*
2041       * Set the attribute with a NULL value...
2042       */
2043
2044       mxmlElementSetAttr(node, name, NULL);
2045     }
2046
2047    /*
2048     * Check the end character...
2049     */
2050
2051     if (ch == '/' || ch == '?')
2052     {
2053      /*
2054       * Grab the > character and print an error if it isn't there...
2055       */
2056
2057       quote = (*getc_cb)(p, encoding);
2058
2059       if (quote != '>')
2060       {
2061         mxml_error("Expected '>' after '%c' for element %s, but got '%c'!",
2062                    ch, node->value.element.name, quote);
2063         ch = EOF;
2064       }
2065
2066       break;
2067     }
2068     else if (ch == '>')
2069       break;
2070   }
2071
2072  /*
2073   * Free the name and value buffers and return...
2074   */
2075
2076   free(name);
2077   free(value);
2078
2079   return (ch);
2080
2081  /*
2082   * Common error return point...
2083   */
2084
2085 error:
2086
2087   free(name);
2088   free(value);
2089
2090   return (EOF);
2091 }
2092
2093
2094 /*
2095  * 'mxml_string_getc()' - Get a character from a string.
2096  */
2097
2098 static int                              /* O  - Character or EOF */
2099 mxml_string_getc(void *p,               /* I  - Pointer to file */
2100                  int  *encoding)        /* IO - Encoding */
2101 {
2102   int           ch;                     /* Character */
2103   const char    **s;                    /* Pointer to string pointer */
2104
2105
2106   s = (const char **)p;
2107
2108   if ((ch = (*s)[0] & 255) != 0 || *encoding == ENCODE_UTF16LE)
2109   {
2110    /*
2111     * Got character; convert UTF-8 to integer and return...
2112     */
2113
2114     (*s)++;
2115
2116     switch (*encoding)
2117     {
2118       case ENCODE_UTF8 :
2119           if (!(ch & 0x80))
2120           {
2121 #if DEBUG > 1
2122             printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2123 #endif /* DEBUG > 1 */
2124
2125             if (mxml_bad_char(ch))
2126             {
2127               mxml_error("Bad control character 0x%02x not allowed by XML standard!",
2128                          ch);
2129               return (EOF);
2130             }
2131
2132             return (ch);
2133           }
2134           else if (ch == 0xfe)
2135           {
2136            /*
2137             * UTF-16 big-endian BOM?
2138             */
2139
2140             if (((*s)[0] & 255) != 0xff)
2141               return (EOF);
2142
2143             *encoding = ENCODE_UTF16BE;
2144             (*s)++;
2145
2146             return (mxml_string_getc(p, encoding));
2147           }
2148           else if (ch == 0xff)
2149           {
2150            /*
2151             * UTF-16 little-endian BOM?
2152             */
2153
2154             if (((*s)[0] & 255) != 0xfe)
2155               return (EOF);
2156
2157             *encoding = ENCODE_UTF16LE;
2158             (*s)++;
2159
2160             return (mxml_string_getc(p, encoding));
2161           }
2162           else if ((ch & 0xe0) == 0xc0)
2163           {
2164            /*
2165             * Two-byte value...
2166             */
2167
2168             if (((*s)[0] & 0xc0) != 0x80)
2169               return (EOF);
2170
2171             ch = ((ch & 0x1f) << 6) | ((*s)[0] & 0x3f);
2172
2173             (*s)++;
2174
2175             if (ch < 0x80)
2176               return (EOF);
2177
2178 #if DEBUG > 1
2179             printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2180 #endif /* DEBUG > 1 */
2181
2182             return (ch);
2183           }
2184           else if ((ch & 0xf0) == 0xe0)
2185           {
2186            /*
2187             * Three-byte value...
2188             */
2189
2190             if (((*s)[0] & 0xc0) != 0x80 ||
2191                 ((*s)[1] & 0xc0) != 0x80)
2192               return (EOF);
2193
2194             ch = ((((ch & 0x0f) << 6) | ((*s)[0] & 0x3f)) << 6) | ((*s)[1] & 0x3f);
2195
2196             (*s) += 2;
2197
2198             if (ch < 0x800)
2199               return (EOF);
2200
2201 #if DEBUG > 1
2202             printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2203 #endif /* DEBUG > 1 */
2204
2205             return (ch);
2206           }
2207           else if ((ch & 0xf8) == 0xf0)
2208           {
2209            /*
2210             * Four-byte value...
2211             */
2212
2213             if (((*s)[0] & 0xc0) != 0x80 ||
2214                 ((*s)[1] & 0xc0) != 0x80 ||
2215                 ((*s)[2] & 0xc0) != 0x80)
2216               return (EOF);
2217
2218             ch = ((((((ch & 0x07) << 6) | ((*s)[0] & 0x3f)) << 6) |
2219                    ((*s)[1] & 0x3f)) << 6) | ((*s)[2] & 0x3f);
2220
2221             (*s) += 3;
2222
2223             if (ch < 0x10000)
2224               return (EOF);
2225
2226 #if DEBUG > 1
2227             printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2228 #endif /* DEBUG > 1 */
2229
2230             return (ch);
2231           }
2232           else
2233             return (EOF);
2234
2235       case ENCODE_UTF16BE :
2236          /*
2237           * Read UTF-16 big-endian char...
2238           */
2239
2240           ch = (ch << 8) | ((*s)[0] & 255);
2241           (*s) ++;
2242
2243           if (mxml_bad_char(ch))
2244           {
2245             mxml_error("Bad control character 0x%02x not allowed by XML standard!",
2246                        ch);
2247             return (EOF);
2248           }
2249           else if (ch >= 0xd800 && ch <= 0xdbff)
2250           {
2251            /*
2252             * Multi-word UTF-16 char...
2253             */
2254
2255             int lch;                    /* Lower word */
2256
2257
2258             if (!(*s)[0])
2259               return (EOF);
2260
2261             lch = (((*s)[0] & 255) << 8) | ((*s)[1] & 255);
2262             (*s) += 2;
2263
2264             if (lch < 0xdc00 || lch >= 0xdfff)
2265               return (EOF);
2266
2267             ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
2268           }
2269
2270 #if DEBUG > 1
2271           printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2272 #endif /* DEBUG > 1 */
2273
2274           return (ch);
2275
2276       case ENCODE_UTF16LE :
2277          /*
2278           * Read UTF-16 little-endian char...
2279           */
2280
2281           ch = ch | (((*s)[0] & 255) << 8);
2282
2283           if (!ch)
2284           {
2285             (*s) --;
2286             return (EOF);
2287           }
2288
2289           (*s) ++;
2290
2291           if (mxml_bad_char(ch))
2292           {
2293             mxml_error("Bad control character 0x%02x not allowed by XML standard!",
2294                        ch);
2295             return (EOF);
2296           }
2297           else if (ch >= 0xd800 && ch <= 0xdbff)
2298           {
2299            /*
2300             * Multi-word UTF-16 char...
2301             */
2302
2303             int lch;                    /* Lower word */
2304
2305
2306             if (!(*s)[1])
2307               return (EOF);
2308
2309             lch = (((*s)[1] & 255) << 8) | ((*s)[0] & 255);
2310             (*s) += 2;
2311
2312             if (lch < 0xdc00 || lch >= 0xdfff)
2313               return (EOF);
2314
2315             ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
2316           }
2317
2318 #if DEBUG > 1
2319           printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2320 #endif /* DEBUG > 1 */
2321
2322           return (ch);
2323     }
2324   }
2325
2326   return (EOF);
2327 }
2328
2329
2330 /*
2331  * 'mxml_string_putc()' - Write a character to a string.
2332  */
2333
2334 static int                              /* O - 0 on success, -1 on failure */
2335 mxml_string_putc(int  ch,               /* I - Character to write */
2336                  void *p)               /* I - Pointer to string pointers */
2337 {
2338   char  **pp;                           /* Pointer to string pointers */
2339
2340
2341   pp = (char **)p;
2342
2343   if (ch < 0x80)
2344   {
2345    /*
2346     * Plain ASCII doesn't need special encoding...
2347     */
2348
2349     if (pp[0] < pp[1])
2350       pp[0][0] = ch;
2351
2352     pp[0] ++;
2353   }
2354   else if (ch < 0x800)
2355   {
2356    /*
2357     * Two-byte UTF-8 character...
2358     */
2359
2360     if ((pp[0] + 1) < pp[1])
2361     {
2362       pp[0][0] = 0xc0 | (ch >> 6);
2363       pp[0][1] = 0x80 | (ch & 0x3f);
2364     }
2365
2366     pp[0] += 2;
2367   }
2368   else if (ch < 0x10000)
2369   {
2370    /*
2371     * Three-byte UTF-8 character...
2372     */
2373
2374     if ((pp[0] + 2) < pp[1])
2375     {
2376       pp[0][0] = 0xe0 | (ch >> 12);
2377       pp[0][1] = 0x80 | ((ch >> 6) & 0x3f);
2378       pp[0][2] = 0x80 | (ch & 0x3f);
2379     }
2380
2381     pp[0] += 3;
2382   }
2383   else
2384   {
2385    /*
2386     * Four-byte UTF-8 character...
2387     */
2388
2389     if ((pp[0] + 2) < pp[1])
2390     {
2391       pp[0][0] = 0xf0 | (ch >> 18);
2392       pp[0][1] = 0x80 | ((ch >> 12) & 0x3f);
2393       pp[0][2] = 0x80 | ((ch >> 6) & 0x3f);
2394       pp[0][3] = 0x80 | (ch & 0x3f);
2395     }
2396
2397     pp[0] += 4;
2398   }
2399
2400   return (0);
2401 }
2402
2403
2404 /*
2405  * 'mxml_write_name()' - Write a name string.
2406  */
2407
2408 static int                              /* O - 0 on success, -1 on failure */
2409 mxml_write_name(const char *s,          /* I - Name to write */
2410                 void       *p,          /* I - Write pointer */
2411                 int        (*putc_cb)(int, void *))
2412                                         /* I - Write callback */
2413 {
2414   char          quote;                  /* Quote character */
2415   const char    *name;                  /* Entity name */
2416
2417
2418   if (*s == '\"' || *s == '\'')
2419   {
2420    /*
2421     * Write a quoted name string...
2422     */
2423
2424     if ((*putc_cb)(*s, p) < 0)
2425       return (-1);
2426
2427     quote = *s++;
2428
2429     while (*s && *s != quote)
2430     {
2431       if ((name = mxmlEntityGetName(*s)) != NULL)
2432       {
2433         if ((*putc_cb)('&', p) < 0)
2434           return (-1);
2435
2436         while (*name)
2437         {
2438           if ((*putc_cb)(*name, p) < 0)
2439             return (-1);
2440
2441           name ++;
2442         }
2443
2444         if ((*putc_cb)(';', p) < 0)
2445           return (-1);
2446       }
2447       else if ((*putc_cb)(*s, p) < 0)
2448         return (-1);
2449
2450       s ++;
2451     }
2452
2453    /*
2454     * Write the end quote...
2455     */
2456
2457     if ((*putc_cb)(quote, p) < 0)
2458       return (-1);
2459   }
2460   else
2461   {
2462    /*
2463     * Write a non-quoted name string...
2464     */
2465
2466     while (*s)
2467     {
2468       if ((*putc_cb)(*s, p) < 0)
2469         return (-1);
2470
2471       s ++;
2472     }
2473   }
2474
2475   return (0);
2476 }
2477
2478
2479 /*
2480  * 'mxml_write_node()' - Save an XML node to a file.
2481  */
2482
2483 static int                              /* O - Column or -1 on error */
2484 mxml_write_node(mxml_node_t *node,      /* I - Node to write */
2485                 void        *p,         /* I - File to write to */
2486                 const char  *(*cb)(mxml_node_t *, int),
2487                                         /* I - Whitespace callback */
2488                 int         col,        /* I - Current column */
2489                 int         (*putc_cb)(int, void *))
2490 {
2491   int           i,                      /* Looping var */
2492                 width;                  /* Width of attr + value */
2493   mxml_attr_t   *attr;                  /* Current attribute */
2494   char          s[255];                 /* Temporary string */
2495
2496
2497   while (node != NULL)
2498   {
2499    /*
2500     * Print the node value...
2501     */
2502
2503     switch (node->type)
2504     {
2505       case MXML_ELEMENT :
2506           col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb);
2507
2508           if ((*putc_cb)('<', p) < 0)
2509             return (-1);
2510           if (node->value.element.name[0] == '?' ||
2511               !strncmp(node->value.element.name, "!--", 3) ||
2512               !strncmp(node->value.element.name, "![CDATA[", 8))
2513           {
2514            /*
2515             * Comments, CDATA, and processing instructions do not
2516             * use character entities.
2517             */
2518
2519             const char  *ptr;           /* Pointer into name */
2520
2521
2522             for (ptr = node->value.element.name; *ptr; ptr ++)
2523               if ((*putc_cb)(*ptr, p) < 0)
2524                 return (-1);
2525
2526            /*
2527             * Prefer a newline for whitespace after ?xml...
2528             */
2529
2530             if (!strncmp(node->value.element.name, "?xml", 4))
2531               col = MXML_WRAP;
2532           }
2533           else if (mxml_write_name(node->value.element.name, p, putc_cb) < 0)
2534             return (-1);
2535
2536           col += strlen(node->value.element.name) + 1;
2537
2538           for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
2539                i > 0;
2540                i --, attr ++)
2541           {
2542             width = strlen(attr->name);
2543
2544             if (attr->value)
2545               width += strlen(attr->value) + 3;
2546
2547             if ((col + width) > MXML_WRAP)
2548             {
2549               if ((*putc_cb)('\n', p) < 0)
2550                 return (-1);
2551
2552               col = 0;
2553             }
2554             else
2555             {
2556               if ((*putc_cb)(' ', p) < 0)
2557                 return (-1);
2558
2559               col ++;
2560             }
2561
2562             if (mxml_write_name(attr->name, p, putc_cb) < 0)
2563               return (-1);
2564
2565             if (attr->value)
2566             {
2567               if ((*putc_cb)('=', p) < 0)
2568                 return (-1);
2569               if ((*putc_cb)('\"', p) < 0)
2570                 return (-1);
2571               if (mxml_write_string(attr->value, p, putc_cb) < 0)
2572                 return (-1);
2573               if ((*putc_cb)('\"', p) < 0)
2574                 return (-1);
2575             }
2576
2577             col += width;
2578           }
2579
2580           if (node->child)
2581           {
2582            /*
2583             * Write children...
2584             */
2585
2586             if ((*putc_cb)('>', p) < 0)
2587               return (-1);
2588             else
2589               col ++;
2590
2591             col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
2592
2593             if ((col = mxml_write_node(node->child, p, cb, col, putc_cb)) < 0)
2594               return (-1);
2595
2596            /*
2597             * The ? and ! elements are special-cases and have no end tags...
2598             */
2599
2600             if (node->value.element.name[0] != '!' &&
2601                 node->value.element.name[0] != '?')
2602             {
2603               col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb);
2604
2605               if ((*putc_cb)('<', p) < 0)
2606                 return (-1);
2607               if ((*putc_cb)('/', p) < 0)
2608                 return (-1);
2609               if (mxml_write_string(node->value.element.name, p, putc_cb) < 0)
2610                 return (-1);
2611               if ((*putc_cb)('>', p) < 0)
2612                 return (-1);
2613
2614               col += strlen(node->value.element.name) + 3;
2615
2616               col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_CLOSE, col, putc_cb);
2617             }
2618           }
2619           else if (node->value.element.name[0] == '!' ||
2620                    node->value.element.name[0] == '?')
2621           {
2622            /*
2623             * The ? and ! elements are special-cases...
2624             */
2625
2626             if ((*putc_cb)('>', p) < 0)
2627               return (-1);
2628             else
2629               col ++;
2630
2631             col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
2632           }
2633           else
2634           {
2635             if ((*putc_cb)(' ', p) < 0)
2636               return (-1);
2637             if ((*putc_cb)('/', p) < 0)
2638               return (-1);
2639             if ((*putc_cb)('>', p) < 0)
2640               return (-1);
2641
2642             col += 3;
2643
2644             col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
2645           }
2646           break;
2647
2648       case MXML_INTEGER :
2649           if (node->prev)
2650           {
2651             if (col > MXML_WRAP)
2652             {
2653               if ((*putc_cb)('\n', p) < 0)
2654                 return (-1);
2655
2656               col = 0;
2657             }
2658             else if ((*putc_cb)(' ', p) < 0)
2659               return (-1);
2660             else
2661               col ++;
2662           }
2663
2664           sprintf(s, "%d", node->value.integer);
2665           if (mxml_write_string(s, p, putc_cb) < 0)
2666             return (-1);
2667
2668           col += strlen(s);
2669           break;
2670
2671       case MXML_OPAQUE :
2672           if (mxml_write_string(node->value.opaque, p, putc_cb) < 0)
2673             return (-1);
2674
2675           col += strlen(node->value.opaque);
2676           break;
2677
2678       case MXML_REAL :
2679           if (node->prev)
2680           {
2681             if (col > MXML_WRAP)
2682             {
2683               if ((*putc_cb)('\n', p) < 0)
2684                 return (-1);
2685
2686               col = 0;
2687             }
2688             else if ((*putc_cb)(' ', p) < 0)
2689               return (-1);
2690             else
2691               col ++;
2692           }
2693
2694           sprintf(s, "%f", node->value.real);
2695           if (mxml_write_string(s, p, putc_cb) < 0)
2696             return (-1);
2697
2698           col += strlen(s);
2699           break;
2700
2701       case MXML_TEXT :
2702           if (node->value.text.whitespace && col > 0)
2703           {
2704             if (col > MXML_WRAP)
2705             {
2706               if ((*putc_cb)('\n', p) < 0)
2707                 return (-1);
2708
2709               col = 0;
2710             }
2711             else if ((*putc_cb)(' ', p) < 0)
2712               return (-1);
2713             else
2714               col ++;
2715           }
2716
2717           if (mxml_write_string(node->value.text.string, p, putc_cb) < 0)
2718             return (-1);
2719
2720           col += strlen(node->value.text.string);
2721           break;
2722
2723       case MXML_CUSTOM :
2724           if (mxml_custom_save_cb)
2725           {
2726             char        *data;          /* Custom data string */
2727             const char  *newline;       /* Last newline in string */
2728
2729
2730             if ((data = (*mxml_custom_save_cb)(node)) == NULL)
2731               return (-1);
2732
2733             if (mxml_write_string(data, p, putc_cb) < 0)
2734               return (-1);
2735
2736             if ((newline = strrchr(data, '\n')) == NULL)
2737               col += strlen(data);
2738             else
2739               col = strlen(newline);
2740
2741             free(data);
2742             break;
2743           }
2744
2745       default : /* Should never happen */
2746           return (-1);
2747     }
2748
2749    /*
2750     * Next node...
2751     */
2752
2753     node = node->next;
2754   }
2755
2756   return (col);
2757 }
2758
2759
2760 /*
2761  * 'mxml_write_string()' - Write a string, escaping & and < as needed.
2762  */
2763
2764 static int                              /* O - 0 on success, -1 on failure */
2765 mxml_write_string(const char *s,        /* I - String to write */
2766                   void       *p,        /* I - Write pointer */
2767                   int        (*putc_cb)(int, void *))
2768                                         /* I - Write callback */
2769 {
2770   const char    *name;                  /* Entity name, if any */
2771
2772
2773   while (*s)
2774   {
2775     if ((name = mxmlEntityGetName(*s)) != NULL)
2776     {
2777       if ((*putc_cb)('&', p) < 0)
2778         return (-1);
2779
2780       while (*name)
2781       {
2782         if ((*putc_cb)(*name, p) < 0)
2783           return (-1);
2784         name ++;
2785       }
2786
2787       if ((*putc_cb)(';', p) < 0)
2788         return (-1);
2789     }
2790     else if ((*putc_cb)(*s, p) < 0)
2791       return (-1);
2792
2793     s ++;
2794   }
2795
2796   return (0);
2797 }
2798
2799
2800 /*
2801  * 'mxml_write_ws()' - Do whitespace callback...
2802  */
2803
2804 static int                              /* O - New column */
2805 mxml_write_ws(mxml_node_t *node,        /* I - Current node */
2806               void        *p,           /* I - Write pointer */
2807               const char  *(*cb)(mxml_node_t *, int),
2808                                         /* I - Callback function */
2809               int         ws,           /* I - Where value */
2810               int         col,          /* I - Current column */
2811               int         (*putc_cb)(int, void *))
2812                                         /* I - Write callback */
2813 {
2814   const char    *s;                     /* Whitespace string */
2815
2816
2817   if (cb && (s = (*cb)(node, ws)) != NULL)
2818   {
2819     while (*s)
2820     {
2821       if ((*putc_cb)(*s, p) < 0)
2822         return (-1);
2823       else if (*s == '\n')
2824         col = 0;
2825       else if (*s == '\t')
2826       {
2827         col += MXML_TAB;
2828         col = col - (col % MXML_TAB);
2829       }
2830       else
2831         col ++;
2832
2833       s ++;
2834     }
2835   }
2836
2837   return (col);
2838 }
2839
2840
2841 /*
2842  * End of "$Id$".
2843  */