menuselect: Add menuselect to Asterisk trunk (Patch 1)
[asterisk/asterisk.git] / menuselect / 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         free(value);
1981         return (EOF);
1982       }
1983
1984       if (ch == '\'' || ch == '\"')
1985       {
1986        /*
1987         * Read quoted value...
1988         */
1989
1990         quote = ch;
1991         ptr   = value;
1992
1993         while ((ch = (*getc_cb)(p, encoding)) != EOF)
1994           if (ch == quote)
1995             break;
1996           else
1997           {
1998             if (ch == '&')
1999               if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
2000                 goto error;
2001               
2002             if (mxml_add_char(ch, &ptr, &value, &valsize))
2003               goto error;
2004           }
2005
2006         *ptr = '\0';
2007       }
2008       else
2009       {
2010        /*
2011         * Read unquoted value...
2012         */
2013
2014         value[0] = ch;
2015         ptr      = value + 1;
2016
2017         while ((ch = (*getc_cb)(p, encoding)) != EOF)
2018           if (isspace(ch) || ch == '=' || ch == '/' || ch == '>')
2019             break;
2020           else
2021           {
2022             if (ch == '&')
2023               if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
2024                 goto error;
2025               
2026             if (mxml_add_char(ch, &ptr, &value, &valsize))
2027               goto error;
2028           }
2029
2030         *ptr = '\0';
2031       }
2032
2033      /*
2034       * Set the attribute with the given string value...
2035       */
2036
2037       mxmlElementSetAttr(node, name, value);
2038     }
2039     else
2040     {
2041      /*
2042       * Set the attribute with a NULL value...
2043       */
2044
2045       mxmlElementSetAttr(node, name, NULL);
2046     }
2047
2048    /*
2049     * Check the end character...
2050     */
2051
2052     if (ch == '/' || ch == '?')
2053     {
2054      /*
2055       * Grab the > character and print an error if it isn't there...
2056       */
2057
2058       quote = (*getc_cb)(p, encoding);
2059
2060       if (quote != '>')
2061       {
2062         mxml_error("Expected '>' after '%c' for element %s, but got '%c'!",
2063                    ch, node->value.element.name, quote);
2064         ch = EOF;
2065       }
2066
2067       break;
2068     }
2069     else if (ch == '>')
2070       break;
2071   }
2072
2073  /*
2074   * Free the name and value buffers and return...
2075   */
2076
2077   free(name);
2078   free(value);
2079
2080   return (ch);
2081
2082  /*
2083   * Common error return point...
2084   */
2085
2086 error:
2087
2088   free(name);
2089   free(value);
2090
2091   return (EOF);
2092 }
2093
2094
2095 /*
2096  * 'mxml_string_getc()' - Get a character from a string.
2097  */
2098
2099 static int                              /* O  - Character or EOF */
2100 mxml_string_getc(void *p,               /* I  - Pointer to file */
2101                  int  *encoding)        /* IO - Encoding */
2102 {
2103   int           ch;                     /* Character */
2104   const char    **s;                    /* Pointer to string pointer */
2105
2106
2107   s = (const char **)p;
2108
2109   if ((ch = (*s)[0] & 255) != 0 || *encoding == ENCODE_UTF16LE)
2110   {
2111    /*
2112     * Got character; convert UTF-8 to integer and return...
2113     */
2114
2115     (*s)++;
2116
2117     switch (*encoding)
2118     {
2119       case ENCODE_UTF8 :
2120           if (!(ch & 0x80))
2121           {
2122 #if DEBUG > 1
2123             printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2124 #endif /* DEBUG > 1 */
2125
2126             if (mxml_bad_char(ch))
2127             {
2128               mxml_error("Bad control character 0x%02x not allowed by XML standard!",
2129                          ch);
2130               return (EOF);
2131             }
2132
2133             return (ch);
2134           }
2135           else if (ch == 0xfe)
2136           {
2137            /*
2138             * UTF-16 big-endian BOM?
2139             */
2140
2141             if (((*s)[0] & 255) != 0xff)
2142               return (EOF);
2143
2144             *encoding = ENCODE_UTF16BE;
2145             (*s)++;
2146
2147             return (mxml_string_getc(p, encoding));
2148           }
2149           else if (ch == 0xff)
2150           {
2151            /*
2152             * UTF-16 little-endian BOM?
2153             */
2154
2155             if (((*s)[0] & 255) != 0xfe)
2156               return (EOF);
2157
2158             *encoding = ENCODE_UTF16LE;
2159             (*s)++;
2160
2161             return (mxml_string_getc(p, encoding));
2162           }
2163           else if ((ch & 0xe0) == 0xc0)
2164           {
2165            /*
2166             * Two-byte value...
2167             */
2168
2169             if (((*s)[0] & 0xc0) != 0x80)
2170               return (EOF);
2171
2172             ch = ((ch & 0x1f) << 6) | ((*s)[0] & 0x3f);
2173
2174             (*s)++;
2175
2176             if (ch < 0x80)
2177               return (EOF);
2178
2179 #if DEBUG > 1
2180             printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2181 #endif /* DEBUG > 1 */
2182
2183             return (ch);
2184           }
2185           else if ((ch & 0xf0) == 0xe0)
2186           {
2187            /*
2188             * Three-byte value...
2189             */
2190
2191             if (((*s)[0] & 0xc0) != 0x80 ||
2192                 ((*s)[1] & 0xc0) != 0x80)
2193               return (EOF);
2194
2195             ch = ((((ch & 0x0f) << 6) | ((*s)[0] & 0x3f)) << 6) | ((*s)[1] & 0x3f);
2196
2197             (*s) += 2;
2198
2199             if (ch < 0x800)
2200               return (EOF);
2201
2202 #if DEBUG > 1
2203             printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2204 #endif /* DEBUG > 1 */
2205
2206             return (ch);
2207           }
2208           else if ((ch & 0xf8) == 0xf0)
2209           {
2210            /*
2211             * Four-byte value...
2212             */
2213
2214             if (((*s)[0] & 0xc0) != 0x80 ||
2215                 ((*s)[1] & 0xc0) != 0x80 ||
2216                 ((*s)[2] & 0xc0) != 0x80)
2217               return (EOF);
2218
2219             ch = ((((((ch & 0x07) << 6) | ((*s)[0] & 0x3f)) << 6) |
2220                    ((*s)[1] & 0x3f)) << 6) | ((*s)[2] & 0x3f);
2221
2222             (*s) += 3;
2223
2224             if (ch < 0x10000)
2225               return (EOF);
2226
2227 #if DEBUG > 1
2228             printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2229 #endif /* DEBUG > 1 */
2230
2231             return (ch);
2232           }
2233           else
2234             return (EOF);
2235
2236       case ENCODE_UTF16BE :
2237          /*
2238           * Read UTF-16 big-endian char...
2239           */
2240
2241           ch = (ch << 8) | ((*s)[0] & 255);
2242           (*s) ++;
2243
2244           if (mxml_bad_char(ch))
2245           {
2246             mxml_error("Bad control character 0x%02x not allowed by XML standard!",
2247                        ch);
2248             return (EOF);
2249           }
2250           else if (ch >= 0xd800 && ch <= 0xdbff)
2251           {
2252            /*
2253             * Multi-word UTF-16 char...
2254             */
2255
2256             int lch;                    /* Lower word */
2257
2258
2259             if (!(*s)[0])
2260               return (EOF);
2261
2262             lch = (((*s)[0] & 255) << 8) | ((*s)[1] & 255);
2263             (*s) += 2;
2264
2265             if (lch < 0xdc00 || lch >= 0xdfff)
2266               return (EOF);
2267
2268             ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
2269           }
2270
2271 #if DEBUG > 1
2272           printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2273 #endif /* DEBUG > 1 */
2274
2275           return (ch);
2276
2277       case ENCODE_UTF16LE :
2278          /*
2279           * Read UTF-16 little-endian char...
2280           */
2281
2282           ch = ch | (((*s)[0] & 255) << 8);
2283
2284           if (!ch)
2285           {
2286             (*s) --;
2287             return (EOF);
2288           }
2289
2290           (*s) ++;
2291
2292           if (mxml_bad_char(ch))
2293           {
2294             mxml_error("Bad control character 0x%02x not allowed by XML standard!",
2295                        ch);
2296             return (EOF);
2297           }
2298           else if (ch >= 0xd800 && ch <= 0xdbff)
2299           {
2300            /*
2301             * Multi-word UTF-16 char...
2302             */
2303
2304             int lch;                    /* Lower word */
2305
2306
2307             if (!(*s)[1])
2308               return (EOF);
2309
2310             lch = (((*s)[1] & 255) << 8) | ((*s)[0] & 255);
2311             (*s) += 2;
2312
2313             if (lch < 0xdc00 || lch >= 0xdfff)
2314               return (EOF);
2315
2316             ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
2317           }
2318
2319 #if DEBUG > 1
2320           printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2321 #endif /* DEBUG > 1 */
2322
2323           return (ch);
2324     }
2325   }
2326
2327   return (EOF);
2328 }
2329
2330
2331 /*
2332  * 'mxml_string_putc()' - Write a character to a string.
2333  */
2334
2335 static int                              /* O - 0 on success, -1 on failure */
2336 mxml_string_putc(int  ch,               /* I - Character to write */
2337                  void *p)               /* I - Pointer to string pointers */
2338 {
2339   char  **pp;                           /* Pointer to string pointers */
2340
2341
2342   pp = (char **)p;
2343
2344   if (ch < 0x80)
2345   {
2346    /*
2347     * Plain ASCII doesn't need special encoding...
2348     */
2349
2350     if (pp[0] < pp[1])
2351       pp[0][0] = ch;
2352
2353     pp[0] ++;
2354   }
2355   else if (ch < 0x800)
2356   {
2357    /*
2358     * Two-byte UTF-8 character...
2359     */
2360
2361     if ((pp[0] + 1) < pp[1])
2362     {
2363       pp[0][0] = 0xc0 | (ch >> 6);
2364       pp[0][1] = 0x80 | (ch & 0x3f);
2365     }
2366
2367     pp[0] += 2;
2368   }
2369   else if (ch < 0x10000)
2370   {
2371    /*
2372     * Three-byte UTF-8 character...
2373     */
2374
2375     if ((pp[0] + 2) < pp[1])
2376     {
2377       pp[0][0] = 0xe0 | (ch >> 12);
2378       pp[0][1] = 0x80 | ((ch >> 6) & 0x3f);
2379       pp[0][2] = 0x80 | (ch & 0x3f);
2380     }
2381
2382     pp[0] += 3;
2383   }
2384   else
2385   {
2386    /*
2387     * Four-byte UTF-8 character...
2388     */
2389
2390     if ((pp[0] + 2) < pp[1])
2391     {
2392       pp[0][0] = 0xf0 | (ch >> 18);
2393       pp[0][1] = 0x80 | ((ch >> 12) & 0x3f);
2394       pp[0][2] = 0x80 | ((ch >> 6) & 0x3f);
2395       pp[0][3] = 0x80 | (ch & 0x3f);
2396     }
2397
2398     pp[0] += 4;
2399   }
2400
2401   return (0);
2402 }
2403
2404
2405 /*
2406  * 'mxml_write_name()' - Write a name string.
2407  */
2408
2409 static int                              /* O - 0 on success, -1 on failure */
2410 mxml_write_name(const char *s,          /* I - Name to write */
2411                 void       *p,          /* I - Write pointer */
2412                 int        (*putc_cb)(int, void *))
2413                                         /* I - Write callback */
2414 {
2415   char          quote;                  /* Quote character */
2416   const char    *name;                  /* Entity name */
2417
2418
2419   if (*s == '\"' || *s == '\'')
2420   {
2421    /*
2422     * Write a quoted name string...
2423     */
2424
2425     if ((*putc_cb)(*s, p) < 0)
2426       return (-1);
2427
2428     quote = *s++;
2429
2430     while (*s && *s != quote)
2431     {
2432       if ((name = mxmlEntityGetName(*s)) != NULL)
2433       {
2434         if ((*putc_cb)('&', p) < 0)
2435           return (-1);
2436
2437         while (*name)
2438         {
2439           if ((*putc_cb)(*name, p) < 0)
2440             return (-1);
2441
2442           name ++;
2443         }
2444
2445         if ((*putc_cb)(';', p) < 0)
2446           return (-1);
2447       }
2448       else if ((*putc_cb)(*s, p) < 0)
2449         return (-1);
2450
2451       s ++;
2452     }
2453
2454    /*
2455     * Write the end quote...
2456     */
2457
2458     if ((*putc_cb)(quote, p) < 0)
2459       return (-1);
2460   }
2461   else
2462   {
2463    /*
2464     * Write a non-quoted name string...
2465     */
2466
2467     while (*s)
2468     {
2469       if ((*putc_cb)(*s, p) < 0)
2470         return (-1);
2471
2472       s ++;
2473     }
2474   }
2475
2476   return (0);
2477 }
2478
2479
2480 /*
2481  * 'mxml_write_node()' - Save an XML node to a file.
2482  */
2483
2484 static int                              /* O - Column or -1 on error */
2485 mxml_write_node(mxml_node_t *node,      /* I - Node to write */
2486                 void        *p,         /* I - File to write to */
2487                 const char  *(*cb)(mxml_node_t *, int),
2488                                         /* I - Whitespace callback */
2489                 int         col,        /* I - Current column */
2490                 int         (*putc_cb)(int, void *))
2491 {
2492   int           i,                      /* Looping var */
2493                 width;                  /* Width of attr + value */
2494   mxml_attr_t   *attr;                  /* Current attribute */
2495   char          s[255];                 /* Temporary string */
2496
2497
2498   while (node != NULL)
2499   {
2500    /*
2501     * Print the node value...
2502     */
2503
2504     switch (node->type)
2505     {
2506       case MXML_ELEMENT :
2507           col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb);
2508
2509           if ((*putc_cb)('<', p) < 0)
2510             return (-1);
2511           if (node->value.element.name[0] == '?' ||
2512               !strncmp(node->value.element.name, "!--", 3) ||
2513               !strncmp(node->value.element.name, "![CDATA[", 8))
2514           {
2515            /*
2516             * Comments, CDATA, and processing instructions do not
2517             * use character entities.
2518             */
2519
2520             const char  *ptr;           /* Pointer into name */
2521
2522
2523             for (ptr = node->value.element.name; *ptr; ptr ++)
2524               if ((*putc_cb)(*ptr, p) < 0)
2525                 return (-1);
2526
2527            /*
2528             * Prefer a newline for whitespace after ?xml...
2529             */
2530
2531             if (!strncmp(node->value.element.name, "?xml", 4))
2532               col = MXML_WRAP;
2533           }
2534           else if (mxml_write_name(node->value.element.name, p, putc_cb) < 0)
2535             return (-1);
2536
2537           col += strlen(node->value.element.name) + 1;
2538
2539           for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
2540                i > 0;
2541                i --, attr ++)
2542           {
2543             width = strlen(attr->name);
2544
2545             if (attr->value)
2546               width += strlen(attr->value) + 3;
2547
2548             if ((col + width) > MXML_WRAP)
2549             {
2550               if ((*putc_cb)('\n', p) < 0)
2551                 return (-1);
2552
2553               col = 0;
2554             }
2555             else
2556             {
2557               if ((*putc_cb)(' ', p) < 0)
2558                 return (-1);
2559
2560               col ++;
2561             }
2562
2563             if (mxml_write_name(attr->name, p, putc_cb) < 0)
2564               return (-1);
2565
2566             if (attr->value)
2567             {
2568               if ((*putc_cb)('=', p) < 0)
2569                 return (-1);
2570               if ((*putc_cb)('\"', p) < 0)
2571                 return (-1);
2572               if (mxml_write_string(attr->value, p, putc_cb) < 0)
2573                 return (-1);
2574               if ((*putc_cb)('\"', p) < 0)
2575                 return (-1);
2576             }
2577
2578             col += width;
2579           }
2580
2581           if (node->child)
2582           {
2583            /*
2584             * Write children...
2585             */
2586
2587             if ((*putc_cb)('>', p) < 0)
2588               return (-1);
2589             else
2590               col ++;
2591
2592             col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
2593
2594             if ((col = mxml_write_node(node->child, p, cb, col, putc_cb)) < 0)
2595               return (-1);
2596
2597            /*
2598             * The ? and ! elements are special-cases and have no end tags...
2599             */
2600
2601             if (node->value.element.name[0] != '!' &&
2602                 node->value.element.name[0] != '?')
2603             {
2604               col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb);
2605
2606               if ((*putc_cb)('<', p) < 0)
2607                 return (-1);
2608               if ((*putc_cb)('/', p) < 0)
2609                 return (-1);
2610               if (mxml_write_string(node->value.element.name, p, putc_cb) < 0)
2611                 return (-1);
2612               if ((*putc_cb)('>', p) < 0)
2613                 return (-1);
2614
2615               col += strlen(node->value.element.name) + 3;
2616
2617               col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_CLOSE, col, putc_cb);
2618             }
2619           }
2620           else if (node->value.element.name[0] == '!' ||
2621                    node->value.element.name[0] == '?')
2622           {
2623            /*
2624             * The ? and ! elements are special-cases...
2625             */
2626
2627             if ((*putc_cb)('>', p) < 0)
2628               return (-1);
2629             else
2630               col ++;
2631
2632             col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
2633           }
2634           else
2635           {
2636             if ((*putc_cb)(' ', p) < 0)
2637               return (-1);
2638             if ((*putc_cb)('/', p) < 0)
2639               return (-1);
2640             if ((*putc_cb)('>', p) < 0)
2641               return (-1);
2642
2643             col += 3;
2644
2645             col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
2646           }
2647           break;
2648
2649       case MXML_INTEGER :
2650           if (node->prev)
2651           {
2652             if (col > MXML_WRAP)
2653             {
2654               if ((*putc_cb)('\n', p) < 0)
2655                 return (-1);
2656
2657               col = 0;
2658             }
2659             else if ((*putc_cb)(' ', p) < 0)
2660               return (-1);
2661             else
2662               col ++;
2663           }
2664
2665           sprintf(s, "%d", node->value.integer);
2666           if (mxml_write_string(s, p, putc_cb) < 0)
2667             return (-1);
2668
2669           col += strlen(s);
2670           break;
2671
2672       case MXML_OPAQUE :
2673           if (mxml_write_string(node->value.opaque, p, putc_cb) < 0)
2674             return (-1);
2675
2676           col += strlen(node->value.opaque);
2677           break;
2678
2679       case MXML_REAL :
2680           if (node->prev)
2681           {
2682             if (col > MXML_WRAP)
2683             {
2684               if ((*putc_cb)('\n', p) < 0)
2685                 return (-1);
2686
2687               col = 0;
2688             }
2689             else if ((*putc_cb)(' ', p) < 0)
2690               return (-1);
2691             else
2692               col ++;
2693           }
2694
2695           sprintf(s, "%f", node->value.real);
2696           if (mxml_write_string(s, p, putc_cb) < 0)
2697             return (-1);
2698
2699           col += strlen(s);
2700           break;
2701
2702       case MXML_TEXT :
2703           if (node->value.text.whitespace && col > 0)
2704           {
2705             if (col > MXML_WRAP)
2706             {
2707               if ((*putc_cb)('\n', p) < 0)
2708                 return (-1);
2709
2710               col = 0;
2711             }
2712             else if ((*putc_cb)(' ', p) < 0)
2713               return (-1);
2714             else
2715               col ++;
2716           }
2717
2718           if (mxml_write_string(node->value.text.string, p, putc_cb) < 0)
2719             return (-1);
2720
2721           col += strlen(node->value.text.string);
2722           break;
2723
2724       case MXML_CUSTOM :
2725           if (mxml_custom_save_cb)
2726           {
2727             char        *data;          /* Custom data string */
2728             const char  *newline;       /* Last newline in string */
2729
2730
2731             if ((data = (*mxml_custom_save_cb)(node)) == NULL)
2732               return (-1);
2733
2734             if (mxml_write_string(data, p, putc_cb) < 0)
2735               return (-1);
2736
2737             if ((newline = strrchr(data, '\n')) == NULL)
2738               col += strlen(data);
2739             else
2740               col = strlen(newline);
2741
2742             free(data);
2743             break;
2744           }
2745
2746       default : /* Should never happen */
2747           return (-1);
2748     }
2749
2750    /*
2751     * Next node...
2752     */
2753
2754     node = node->next;
2755   }
2756
2757   return (col);
2758 }
2759
2760
2761 /*
2762  * 'mxml_write_string()' - Write a string, escaping & and < as needed.
2763  */
2764
2765 static int                              /* O - 0 on success, -1 on failure */
2766 mxml_write_string(const char *s,        /* I - String to write */
2767                   void       *p,        /* I - Write pointer */
2768                   int        (*putc_cb)(int, void *))
2769                                         /* I - Write callback */
2770 {
2771   const char    *name;                  /* Entity name, if any */
2772
2773
2774   while (*s)
2775   {
2776     if ((name = mxmlEntityGetName(*s)) != NULL)
2777     {
2778       if ((*putc_cb)('&', p) < 0)
2779         return (-1);
2780
2781       while (*name)
2782       {
2783         if ((*putc_cb)(*name, p) < 0)
2784           return (-1);
2785         name ++;
2786       }
2787
2788       if ((*putc_cb)(';', p) < 0)
2789         return (-1);
2790     }
2791     else if ((*putc_cb)(*s, p) < 0)
2792       return (-1);
2793
2794     s ++;
2795   }
2796
2797   return (0);
2798 }
2799
2800
2801 /*
2802  * 'mxml_write_ws()' - Do whitespace callback...
2803  */
2804
2805 static int                              /* O - New column */
2806 mxml_write_ws(mxml_node_t *node,        /* I - Current node */
2807               void        *p,           /* I - Write pointer */
2808               const char  *(*cb)(mxml_node_t *, int),
2809                                         /* I - Callback function */
2810               int         ws,           /* I - Where value */
2811               int         col,          /* I - Current column */
2812               int         (*putc_cb)(int, void *))
2813                                         /* I - Write callback */
2814 {
2815   const char    *s;                     /* Whitespace string */
2816
2817
2818   if (cb && (s = (*cb)(node, ws)) != NULL)
2819   {
2820     while (*s)
2821     {
2822       if ((*putc_cb)(*s, p) < 0)
2823         return (-1);
2824       else if (*s == '\n')
2825         col = 0;
2826       else if (*s == '\t')
2827       {
2828         col += MXML_TAB;
2829         col = col - (col % MXML_TAB);
2830       }
2831       else
2832         col ++;
2833
2834       s ++;
2835     }
2836   }
2837
2838   return (col);
2839 }
2840
2841
2842 /*
2843  * End of "$Id$".
2844  */