Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

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