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