Statistics
| Revision:

root / logic / trunk / src / mxml / mxml-file.c @ 49

History | View | Annotate | Download (65.1 KB)

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
#ifndef WIN32
59
#  include <unistd.h>
60
#endif /* !WIN32 */
61
#include "mxml-private.h"
62

    
63

    
64
/*
65
 * Character encoding...
66
 */
67

    
68
#define ENCODE_UTF8        0                /* UTF-8 */
69
#define ENCODE_UTF16BE        1                /* UTF-16 Big-Endian */
70
#define ENCODE_UTF16LE        2                /* UTF-16 Little-Endian */
71

    
72

    
73
/*
74
 * Macro to test for a bad XML character...
75
 */
76

    
77
#define mxml_bad_char(ch) ((ch) < ' ' && (ch) != '\n' && (ch) != '\r' && (ch) != '\t')
78

    
79

    
80
/*
81
 * Types and structures...
82
 */
83

    
84
typedef int (*_mxml_getc_cb_t)(void *, int *);
85
typedef int (*_mxml_putc_cb_t)(int, void *);
86

    
87
typedef struct _mxml_fdbuf_s                /**** File descriptor buffer ****/
88
{
89
  int                fd;                        /* File descriptor */
90
  unsigned char        *current,                /* Current position in buffer */
91
                *end,                        /* End of buffer */
92
                buffer[8192];                /* Character buffer */
93
} _mxml_fdbuf_t;
94

    
95

    
96
/*
97
 * Local functions...
98
 */
99

    
100
static int                mxml_add_char(int ch, char **ptr, char **buffer,
101
                                      int *bufsize);
102
static int                mxml_fd_getc(void *p, int *encoding);
103
static int                mxml_fd_putc(int ch, void *p);
104
static int                mxml_fd_read(_mxml_fdbuf_t *buf);
105
static int                mxml_fd_write(_mxml_fdbuf_t *buf);
106
static int                mxml_file_getc(void *p, int *encoding);
107
static int                mxml_file_putc(int ch, void *p);
108
static int                mxml_get_entity(mxml_node_t *parent, void *p,
109
                                        int *encoding,
110
                                        _mxml_getc_cb_t getc_cb);
111
static inline int        mxml_isspace(int ch)
112
                        {
113
                          return (ch == ' ' || ch == '\t' || ch == '\r' ||
114
                                  ch == '\n');
115
                        }
116
static mxml_node_t        *mxml_load_data(mxml_node_t *top, void *p,
117
                                        mxml_load_cb_t cb,
118
                                        _mxml_getc_cb_t getc_cb,
119
                                        mxml_sax_cb_t sax_cb, void *sax_data);
120
static int                mxml_parse_element(mxml_node_t *node, void *p,
121
                                           int *encoding,
122
                                           _mxml_getc_cb_t getc_cb);
123
static int                mxml_string_getc(void *p, int *encoding);
124
static int                mxml_string_putc(int ch, void *p);
125
static int                mxml_write_name(const char *s, void *p,
126
                                        _mxml_putc_cb_t putc_cb);
127
static int                mxml_write_node(mxml_node_t *node, void *p,
128
                                        mxml_save_cb_t cb, int col,
129
                                        _mxml_putc_cb_t putc_cb,
130
                                        _mxml_global_t *global);
131
static int                mxml_write_string(const char *s, void *p,
132
                                          _mxml_putc_cb_t putc_cb);
133
static int                mxml_write_ws(mxml_node_t *node, void *p, 
134
                                      mxml_save_cb_t cb, int ws,
135
                                      int col, _mxml_putc_cb_t putc_cb);
136

    
137

    
138
/*
139
 * 'mxmlLoadFd()' - Load a file descriptor into an XML node tree.
140
 *
141
 * The nodes in the specified file are added to the specified top node.
142
 * If no top node is provided, the XML file MUST be well-formed with a
143
 * single parent node like <?xml> for the entire file. The callback
144
 * function returns the value type that should be used for child nodes.
145
 * If MXML_NO_CALLBACK is specified then all child nodes will be either
146
 * MXML_ELEMENT or MXML_TEXT nodes.
147
 *
148
 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
149
 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
150
 * child nodes of the specified type.
151
 */
152

    
153
mxml_node_t *                                /* O - First node or NULL if the file could not be read. */
154
mxmlLoadFd(mxml_node_t    *top,                /* I - Top node */
155
           int            fd,                /* I - File descriptor to read from */
156
           mxml_load_cb_t cb)                /* I - Callback function or MXML_NO_CALLBACK */
157
{
158
  _mxml_fdbuf_t        buf;                        /* File descriptor buffer */
159

    
160

    
161
 /*
162
  * Initialize the file descriptor buffer...
163
  */
164

    
165
  buf.fd      = fd;
166
  buf.current = buf.buffer;
167
  buf.end     = buf.buffer;
168

    
169
 /*
170
  * Read the XML data...
171
  */
172

    
173
  return (mxml_load_data(top, &buf, cb, mxml_fd_getc, MXML_NO_CALLBACK, NULL));
174
}
175

    
176

    
177
/*
178
 * 'mxmlLoadFile()' - Load a file into an XML node tree.
179
 *
180
 * The nodes in the specified file are added to the specified top node.
181
 * If no top node is provided, the XML file MUST be well-formed with a
182
 * single parent node like <?xml> for the entire file. The callback
183
 * function returns the value type that should be used for child nodes.
184
 * If MXML_NO_CALLBACK is specified then all child nodes will be either
185
 * MXML_ELEMENT or MXML_TEXT nodes.
186
 *
187
 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
188
 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
189
 * child nodes of the specified type.
190
 */
191

    
192
mxml_node_t *                                /* O - First node or NULL if the file could not be read. */
193
mxmlLoadFile(mxml_node_t    *top,        /* I - Top node */
194
             FILE           *fp,        /* I - File to read from */
195
             mxml_load_cb_t cb)                /* I - Callback function or MXML_NO_CALLBACK */
196
{
197
 /*
198
  * Read the XML data...
199
  */
200

    
201
  return (mxml_load_data(top, fp, cb, mxml_file_getc, MXML_NO_CALLBACK, NULL));
202
}
203

    
204

    
205
/*
206
 * 'mxmlLoadString()' - Load a string into an XML node tree.
207
 *
208
 * The nodes in the specified string are added to the specified top node.
209
 * If no top node is provided, the XML string MUST be well-formed with a
210
 * single parent node like <?xml> for the entire string. The callback
211
 * function returns the value type that should be used for child nodes.
212
 * If MXML_NO_CALLBACK is specified then all child nodes will be either
213
 * MXML_ELEMENT or MXML_TEXT nodes.
214
 *
215
 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
216
 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
217
 * child nodes of the specified type.
218
 */
219

    
220
mxml_node_t *                                /* O - First node or NULL if the string has errors. */
221
mxmlLoadString(mxml_node_t    *top,        /* I - Top node */
222
               const char     *s,        /* I - String to load */
223
               mxml_load_cb_t cb)        /* I - Callback function or MXML_NO_CALLBACK */
224
{
225
 /*
226
  * Read the XML data...
227
  */
228

    
229
  return (mxml_load_data(top, (void *)&s, cb, mxml_string_getc, MXML_NO_CALLBACK,
230
                         NULL));
231
}
232

    
233

    
234
/*
235
 * 'mxmlSaveAllocString()' - Save an XML node tree to an allocated string.
236
 *
237
 * This function returns a pointer to a string containing the textual
238
 * representation of the XML node tree.  The string should be freed
239
 * using the free() function when you are done with it.  NULL is returned
240
 * if the node would produce an empty string or if the string cannot be
241
 * allocated.
242
 *
243
 * The callback argument specifies a function that returns a whitespace
244
 * string or NULL before and after each element. If MXML_NO_CALLBACK
245
 * is specified, whitespace will only be added before MXML_TEXT nodes
246
 * with leading whitespace and before attribute names inside opening
247
 * element tags.
248
 */
249

    
250
char *                                        /* O - Allocated string or NULL */
251
mxmlSaveAllocString(
252
    mxml_node_t    *node,                /* I - Node to write */
253
    mxml_save_cb_t cb)                        /* I - Whitespace callback or MXML_NO_CALLBACK */
254
{
255
  int        bytes;                                /* Required bytes */
256
  char        buffer[8192];                        /* Temporary buffer */
257
  char        *s;                                /* Allocated string */
258

    
259

    
260
 /*
261
  * Write the node to the temporary buffer...
262
  */
263

    
264
  bytes = mxmlSaveString(node, buffer, sizeof(buffer), cb);
265

    
266
  if (bytes <= 0)
267
    return (NULL);
268

    
269
  if (bytes < (int)(sizeof(buffer) - 1))
270
  {
271
   /*
272
    * Node fit inside the buffer, so just duplicate that string and
273
    * return...
274
    */
275

    
276
    return (strdup(buffer));
277
  }
278

    
279
 /*
280
  * Allocate a buffer of the required size and save the node to the
281
  * new buffer...
282
  */
283

    
284
  if ((s = malloc(bytes + 1)) == NULL)
285
    return (NULL);
286

    
287
  mxmlSaveString(node, s, bytes + 1, cb);
288

    
289
 /*
290
  * Return the allocated string...
291
  */
292

    
293
  return (s);
294
}
295

    
296

    
297
/*
298
 * 'mxmlSaveFd()' - Save an XML tree to a file descriptor.
299
 *
300
 * The callback argument specifies a function that returns a whitespace
301
 * string or NULL before and after each element. If MXML_NO_CALLBACK
302
 * is specified, whitespace will only be added before MXML_TEXT nodes
303
 * with leading whitespace and before attribute names inside opening
304
 * element tags.
305
 */
306

    
307
int                                        /* O - 0 on success, -1 on error. */
308
mxmlSaveFd(mxml_node_t    *node,        /* I - Node to write */
309
           int            fd,                /* I - File descriptor to write to */
310
           mxml_save_cb_t cb)                /* I - Whitespace callback or MXML_NO_CALLBACK */
311
{
312
  int                col;                        /* Final column */
313
  _mxml_fdbuf_t        buf;                        /* File descriptor buffer */
314
  _mxml_global_t *global = _mxml_global();
315
                                        /* Global data */
316

    
317

    
318
 /*
319
  * Initialize the file descriptor buffer...
320
  */
321

    
322
  buf.fd      = fd;
323
  buf.current = buf.buffer;
324
  buf.end     = buf.buffer + sizeof(buf.buffer);
325

    
326
 /*
327
  * Write the node...
328
  */
329

    
330
  if ((col = mxml_write_node(node, &buf, cb, 0, mxml_fd_putc, global)) < 0)
331
    return (-1);
332

    
333
  if (col > 0)
334
    if (mxml_fd_putc('\n', &buf) < 0)
335
      return (-1);
336

    
337
 /*
338
  * Flush and return...
339
  */
340

    
341
  return (mxml_fd_write(&buf));
342
}
343

    
344

    
345
/*
346
 * 'mxmlSaveFile()' - Save an XML tree to a file.
347
 *
348
 * The callback argument specifies a function that returns a whitespace
349
 * string or NULL before and after each element. If MXML_NO_CALLBACK
350
 * is specified, whitespace will only be added before MXML_TEXT nodes
351
 * with leading whitespace and before attribute names inside opening
352
 * element tags.
353
 */
354

    
355
int                                        /* O - 0 on success, -1 on error. */
356
mxmlSaveFile(mxml_node_t    *node,        /* I - Node to write */
357
             FILE           *fp,        /* I - File to write to */
358
             mxml_save_cb_t cb)                /* I - Whitespace callback or MXML_NO_CALLBACK */
359
{
360
  int        col;                                /* Final column */
361
  _mxml_global_t *global = _mxml_global();
362
                                        /* Global data */
363

    
364

    
365
 /*
366
  * Write the node...
367
  */
368

    
369
  if ((col = mxml_write_node(node, fp, cb, 0, mxml_file_putc, global)) < 0)
370
    return (-1);
371

    
372
  if (col > 0)
373
    if (putc('\n', fp) < 0)
374
      return (-1);
375

    
376
 /*
377
  * Return 0 (success)...
378
  */
379

    
380
  return (0);
381
}
382

    
383

    
384
/*
385
 * 'mxmlSaveString()' - Save an XML node tree to a string.
386
 *
387
 * This function returns the total number of bytes that would be
388
 * required for the string but only copies (bufsize - 1) characters
389
 * into the specified buffer.
390
 *
391
 * The callback argument specifies a function that returns a whitespace
392
 * string or NULL before and after each element. If MXML_NO_CALLBACK
393
 * is specified, whitespace will only be added before MXML_TEXT nodes
394
 * with leading whitespace and before attribute names inside opening
395
 * element tags.
396
 */
397

    
398
int                                        /* O - Size of string */
399
mxmlSaveString(mxml_node_t    *node,        /* I - Node to write */
400
               char           *buffer,        /* I - String buffer */
401
               int            bufsize,        /* I - Size of string buffer */
402
               mxml_save_cb_t cb)        /* I - Whitespace callback or MXML_NO_CALLBACK */
403
{
404
  int        col;                                /* Final column */
405
  char        *ptr[2];                        /* Pointers for putc_cb */
406
  _mxml_global_t *global = _mxml_global();
407
                                        /* Global data */
408

    
409

    
410
 /*
411
  * Write the node...
412
  */
413

    
414
  ptr[0] = buffer;
415
  ptr[1] = buffer + bufsize;
416

    
417
  if ((col = mxml_write_node(node, ptr, cb, 0, mxml_string_putc, global)) < 0)
418
    return (-1);
419

    
420
  if (col > 0)
421
    mxml_string_putc('\n', ptr);
422

    
423
 /*
424
  * Nul-terminate the buffer...
425
  */
426

    
427
  if (ptr[0] >= ptr[1])
428
    buffer[bufsize - 1] = '\0';
429
  else
430
    ptr[0][0] = '\0';
431

    
432
 /*
433
  * Return the number of characters...
434
  */
435

    
436
  return (ptr[0] - buffer);
437
}
438

    
439

    
440
/*
441
 * 'mxmlSAXLoadFd()' - Load a file descriptor into an XML node tree
442
 *                     using a SAX callback.
443
 *
444
 * The nodes in the specified file are added to the specified top node.
445
 * If no top node is provided, the XML file MUST be well-formed with a
446
 * single parent node like <?xml> for the entire file. The callback
447
 * function returns the value type that should be used for child nodes.
448
 * If MXML_NO_CALLBACK is specified then all child nodes will be either
449
 * MXML_ELEMENT or MXML_TEXT nodes.
450
 *
451
 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
452
 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
453
 * child nodes of the specified type.
454
 *
455
 * The SAX callback must call mxmlRetain() for any nodes that need to
456
 * be kept for later use. Otherwise, nodes are deleted when the parent
457
 * node is closed or after each data, comment, CDATA, or directive node.
458
 *
459
 * @since Mini-XML 2.3@
460
 */
461

    
462
mxml_node_t *                                /* O - First node or NULL if the file could not be read. */
463
mxmlSAXLoadFd(mxml_node_t    *top,        /* I - Top node */
464
              int            fd,        /* I - File descriptor to read from */
465
              mxml_load_cb_t cb,        /* I - Callback function or MXML_NO_CALLBACK */
466
              mxml_sax_cb_t  sax_cb,        /* I - SAX callback or MXML_NO_CALLBACK */
467
              void           *sax_data)        /* I - SAX user data */
468
{
469
  _mxml_fdbuf_t        buf;                        /* File descriptor buffer */
470

    
471

    
472
 /*
473
  * Initialize the file descriptor buffer...
474
  */
475

    
476
  buf.fd      = fd;
477
  buf.current = buf.buffer;
478
  buf.end     = buf.buffer;
479

    
480
 /*
481
  * Read the XML data...
482
  */
483

    
484
  return (mxml_load_data(top, &buf, cb, mxml_fd_getc, sax_cb, sax_data));
485
}
486

    
487

    
488
/*
489
 * 'mxmlSAXLoadFile()' - Load a file into an XML node tree
490
 *                       using a SAX callback.
491
 *
492
 * The nodes in the specified file are added to the specified top node.
493
 * If no top node is provided, the XML file MUST be well-formed with a
494
 * single parent node like <?xml> for the entire file. The callback
495
 * function returns the value type that should be used for child nodes.
496
 * If MXML_NO_CALLBACK is specified then all child nodes will be either
497
 * MXML_ELEMENT or MXML_TEXT nodes.
498
 *
499
 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
500
 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
501
 * child nodes of the specified type.
502
 *
503
 * The SAX callback must call mxmlRetain() for any nodes that need to
504
 * be kept for later use. Otherwise, nodes are deleted when the parent
505
 * node is closed or after each data, comment, CDATA, or directive node.
506
 *
507
 * @since Mini-XML 2.3@
508
 */
509

    
510
mxml_node_t *                                /* O - First node or NULL if the file could not be read. */
511
mxmlSAXLoadFile(
512
    mxml_node_t    *top,                /* I - Top node */
513
    FILE           *fp,                        /* I - File to read from */
514
    mxml_load_cb_t cb,                        /* I - Callback function or MXML_NO_CALLBACK */
515
    mxml_sax_cb_t  sax_cb,                /* I - SAX callback or MXML_NO_CALLBACK */
516
    void           *sax_data)                /* I - SAX user data */
517
{
518
 /*
519
  * Read the XML data...
520
  */
521

    
522
  return (mxml_load_data(top, fp, cb, mxml_file_getc, sax_cb, sax_data));
523
}
524

    
525

    
526
/*
527
 * 'mxmlSAXLoadString()' - Load a string into an XML node tree
528
 *                         using a SAX callback.
529
 *
530
 * The nodes in the specified string are added to the specified top node.
531
 * If no top node is provided, the XML string MUST be well-formed with a
532
 * single parent node like <?xml> for the entire string. The callback
533
 * function returns the value type that should be used for child nodes.
534
 * If MXML_NO_CALLBACK is specified then all child nodes will be either
535
 * MXML_ELEMENT or MXML_TEXT nodes.
536
 *
537
 * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK,
538
 * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading
539
 * child nodes of the specified type.
540
 *
541
 * The SAX callback must call mxmlRetain() for any nodes that need to
542
 * be kept for later use. Otherwise, nodes are deleted when the parent
543
 * node is closed or after each data, comment, CDATA, or directive node.
544
 *
545
 * @since Mini-XML 2.3@
546
 */
547

    
548
mxml_node_t *                                /* O - First node or NULL if the string has errors. */
549
mxmlSAXLoadString(
550
    mxml_node_t    *top,                /* I - Top node */
551
    const char     *s,                        /* I - String to load */
552
    mxml_load_cb_t cb,                        /* I - Callback function or MXML_NO_CALLBACK */
553
    mxml_sax_cb_t  sax_cb,                /* I - SAX callback or MXML_NO_CALLBACK */
554
    void           *sax_data)                /* I - SAX user data */
555
{
556
 /*
557
  * Read the XML data...
558
  */
559

    
560
  return (mxml_load_data(top, (void *)&s, cb, mxml_string_getc, sax_cb, sax_data));
561
}
562

    
563

    
564
/*
565
 * 'mxmlSetCustomHandlers()' - Set the handling functions for custom data.
566
 *
567
 * The load function accepts a node pointer and a data string and must
568
 * return 0 on success and non-zero on error.
569
 *
570
 * The save function accepts a node pointer and must return a malloc'd
571
 * string on success and NULL on error.
572
 * 
573
 */
574

    
575
void
576
mxmlSetCustomHandlers(
577
    mxml_custom_load_cb_t load,                /* I - Load function */
578
    mxml_custom_save_cb_t save)                /* I - Save function */
579
{
580
  _mxml_global_t *global = _mxml_global();
581
                                        /* Global data */
582

    
583

    
584
  global->custom_load_cb = load;
585
  global->custom_save_cb = save;
586
}
587

    
588

    
589
/*
590
 * 'mxmlSetErrorCallback()' - Set the error message callback.
591
 */
592

    
593
void
594
mxmlSetErrorCallback(mxml_error_cb_t cb)/* I - Error callback function */
595
{
596
  _mxml_global_t *global = _mxml_global();
597
                                        /* Global data */
598

    
599

    
600
  global->error_cb = cb;
601
}
602

    
603

    
604
/*
605
 * 'mxmlSetWrapMargin()' - Set the the wrap margin when saving XML data.
606
 *
607
 * Wrapping is disabled when "column" is 0.
608
 *
609
 * @since Mini-XML 2.3@
610
 */
611

    
612
void
613
mxmlSetWrapMargin(int column)                /* I - Column for wrapping, 0 to disable wrapping */
614
{
615
  _mxml_global_t *global = _mxml_global();
616
                                        /* Global data */
617

    
618

    
619
  global->wrap = column;
620
}
621

    
622

    
623
/*
624
 * 'mxml_add_char()' - Add a character to a buffer, expanding as needed.
625
 */
626

    
627
static int                                /* O  - 0 on success, -1 on error */
628
mxml_add_char(int  ch,                        /* I  - Character to add */
629
              char **bufptr,                /* IO - Current position in buffer */
630
              char **buffer,                /* IO - Current buffer */
631
              int  *bufsize)                /* IO - Current buffer size */
632
{
633
  char        *newbuffer;                        /* New buffer value */
634

    
635

    
636
  if (*bufptr >= (*buffer + *bufsize - 4))
637
  {
638
   /*
639
    * Increase the size of the buffer...
640
    */
641

    
642
    if (*bufsize < 1024)
643
      (*bufsize) *= 2;
644
    else
645
      (*bufsize) += 1024;
646

    
647
    if ((newbuffer = realloc(*buffer, *bufsize)) == NULL)
648
    {
649
      free(*buffer);
650

    
651
      mxml_error("Unable to expand string buffer to %d bytes!", *bufsize);
652

    
653
      return (-1);
654
    }
655

    
656
    *bufptr = newbuffer + (*bufptr - *buffer);
657
    *buffer = newbuffer;
658
  }
659

    
660
  if (ch < 0x80)
661
  {
662
   /*
663
    * Single byte ASCII...
664
    */
665

    
666
    *(*bufptr)++ = ch;
667
  }
668
  else if (ch < 0x800)
669
  {
670
   /*
671
    * Two-byte UTF-8...
672
    */
673

    
674
    *(*bufptr)++ = 0xc0 | (ch >> 6);
675
    *(*bufptr)++ = 0x80 | (ch & 0x3f);
676
  }
677
  else if (ch < 0x10000)
678
  {
679
   /*
680
    * Three-byte UTF-8...
681
    */
682

    
683
    *(*bufptr)++ = 0xe0 | (ch >> 12);
684
    *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f);
685
    *(*bufptr)++ = 0x80 | (ch & 0x3f);
686
  }
687
  else
688
  {
689
   /*
690
    * Four-byte UTF-8...
691
    */
692

    
693
    *(*bufptr)++ = 0xf0 | (ch >> 18);
694
    *(*bufptr)++ = 0x80 | ((ch >> 12) & 0x3f);
695
    *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f);
696
    *(*bufptr)++ = 0x80 | (ch & 0x3f);
697
  }
698

    
699
  return (0);
700
}
701

    
702

    
703
/*
704
 * 'mxml_fd_getc()' - Read a character from a file descriptor.
705
 */
706

    
707
static int                                /* O  - Character or EOF */
708
mxml_fd_getc(void *p,                        /* I  - File descriptor buffer */
709
             int  *encoding)                /* IO - Encoding */
710
{
711
  _mxml_fdbuf_t        *buf;                        /* File descriptor buffer */
712
  int                ch,                        /* Current character */
713
                temp;                        /* Temporary character */
714

    
715

    
716
 /*
717
  * Grab the next character in the buffer...
718
  */
719

    
720
  buf = (_mxml_fdbuf_t *)p;
721

    
722
  if (buf->current >= buf->end)
723
    if (mxml_fd_read(buf) < 0)
724
      return (EOF);
725

    
726
  ch = *(buf->current)++;
727

    
728
  switch (*encoding)
729
  {
730
    case ENCODE_UTF8 :
731
       /*
732
        * Got a UTF-8 character; convert UTF-8 to Unicode and return...
733
        */
734

    
735
        if (!(ch & 0x80))
736
        {
737
#if DEBUG > 1
738
          printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
739
#endif /* DEBUG > 1 */
740

    
741
          if (mxml_bad_char(ch))
742
          {
743
            mxml_error("Bad control character 0x%02x not allowed by XML standard!",
744
                       ch);
745
            return (EOF);
746
          }
747

    
748
          return (ch);
749
        }
750
        else if (ch == 0xfe)
751
        {
752
         /*
753
          * UTF-16 big-endian BOM?
754
          */
755

    
756
          if (buf->current >= buf->end)
757
            if (mxml_fd_read(buf) < 0)
758
              return (EOF);
759

    
760
          ch = *(buf->current)++;
761
          
762
          if (ch != 0xff)
763
            return (EOF);
764

    
765
          *encoding = ENCODE_UTF16BE;
766

    
767
          return (mxml_fd_getc(p, encoding));
768
        }
769
        else if (ch == 0xff)
770
        {
771
         /*
772
          * UTF-16 little-endian BOM?
773
          */
774

    
775
          if (buf->current >= buf->end)
776
            if (mxml_fd_read(buf) < 0)
777
              return (EOF);
778

    
779
          ch = *(buf->current)++;
780
          
781
          if (ch != 0xfe)
782
            return (EOF);
783

    
784
          *encoding = ENCODE_UTF16LE;
785

    
786
          return (mxml_fd_getc(p, encoding));
787
        }
788
        else if ((ch & 0xe0) == 0xc0)
789
        {
790
         /*
791
          * Two-byte value...
792
          */
793

    
794
          if (buf->current >= buf->end)
795
            if (mxml_fd_read(buf) < 0)
796
              return (EOF);
797

    
798
          temp = *(buf->current)++;
799

    
800
          if ((temp & 0xc0) != 0x80)
801
            return (EOF);
802

    
803
          ch = ((ch & 0x1f) << 6) | (temp & 0x3f);
804

    
805
          if (ch < 0x80)
806
          {
807
            mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
808
            return (EOF);
809
          }
810
        }
811
        else if ((ch & 0xf0) == 0xe0)
812
        {
813
         /*
814
          * Three-byte value...
815
          */
816

    
817
          if (buf->current >= buf->end)
818
            if (mxml_fd_read(buf) < 0)
819
              return (EOF);
820

    
821
          temp = *(buf->current)++;
822

    
823
          if ((temp & 0xc0) != 0x80)
824
            return (EOF);
825

    
826
          ch = ((ch & 0x0f) << 6) | (temp & 0x3f);
827

    
828
          if (buf->current >= buf->end)
829
            if (mxml_fd_read(buf) < 0)
830
              return (EOF);
831

    
832
          temp = *(buf->current)++;
833

    
834
          if ((temp & 0xc0) != 0x80)
835
            return (EOF);
836

    
837
          ch = (ch << 6) | (temp & 0x3f);
838

    
839
          if (ch < 0x800)
840
          {
841
            mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
842
            return (EOF);
843
          }
844

    
845
         /*
846
          * Ignore (strip) Byte Order Mark (BOM)...
847
          */
848

    
849
          if (ch == 0xfeff)
850
            return (mxml_fd_getc(p, encoding));
851
        }
852
        else if ((ch & 0xf8) == 0xf0)
853
        {
854
         /*
855
          * Four-byte value...
856
          */
857

    
858
          if (buf->current >= buf->end)
859
            if (mxml_fd_read(buf) < 0)
860
              return (EOF);
861

    
862
          temp = *(buf->current)++;
863

    
864
          if ((temp & 0xc0) != 0x80)
865
            return (EOF);
866

    
867
          ch = ((ch & 0x07) << 6) | (temp & 0x3f);
868

    
869
          if (buf->current >= buf->end)
870
            if (mxml_fd_read(buf) < 0)
871
              return (EOF);
872

    
873
          temp = *(buf->current)++;
874

    
875
          if ((temp & 0xc0) != 0x80)
876
            return (EOF);
877

    
878
          ch = (ch << 6) | (temp & 0x3f);
879

    
880
          if (buf->current >= buf->end)
881
            if (mxml_fd_read(buf) < 0)
882
              return (EOF);
883

    
884
          temp = *(buf->current)++;
885

    
886
          if ((temp & 0xc0) != 0x80)
887
            return (EOF);
888

    
889
          ch = (ch << 6) | (temp & 0x3f);
890

    
891
          if (ch < 0x10000)
892
          {
893
            mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
894
            return (EOF);
895
          }
896
        }
897
        else
898
          return (EOF);
899
        break;
900

    
901
    case ENCODE_UTF16BE :
902
       /*
903
        * Read UTF-16 big-endian char...
904
        */
905

    
906
        if (buf->current >= buf->end)
907
          if (mxml_fd_read(buf) < 0)
908
            return (EOF);
909

    
910
        temp = *(buf->current)++;
911

    
912
        ch = (ch << 8) | temp;
913

    
914
        if (mxml_bad_char(ch))
915
        {
916
          mxml_error("Bad control character 0x%02x not allowed by XML standard!",
917
                     ch);
918
          return (EOF);
919
        }
920
        else if (ch >= 0xd800 && ch <= 0xdbff)
921
        {
922
         /*
923
          * Multi-word UTF-16 char...
924
          */
925

    
926
          int lch;
927

    
928
          if (buf->current >= buf->end)
929
            if (mxml_fd_read(buf) < 0)
930
              return (EOF);
931

    
932
          lch = *(buf->current)++;
933

    
934
          if (buf->current >= buf->end)
935
            if (mxml_fd_read(buf) < 0)
936
              return (EOF);
937

    
938
          temp = *(buf->current)++;
939

    
940
          lch = (lch << 8) | temp;
941

    
942
          if (lch < 0xdc00 || lch >= 0xdfff)
943
            return (EOF);
944

    
945
          ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
946
        }
947
        break;
948

    
949
    case ENCODE_UTF16LE :
950
       /*
951
        * Read UTF-16 little-endian char...
952
        */
953

    
954
        if (buf->current >= buf->end)
955
          if (mxml_fd_read(buf) < 0)
956
            return (EOF);
957

    
958
        temp = *(buf->current)++;
959

    
960
        ch |= (temp << 8);
961

    
962
        if (mxml_bad_char(ch))
963
        {
964
          mxml_error("Bad control character 0x%02x not allowed by XML standard!",
965
                     ch);
966
          return (EOF);
967
        }
968
        else if (ch >= 0xd800 && ch <= 0xdbff)
969
        {
970
         /*
971
          * Multi-word UTF-16 char...
972
          */
973

    
974
          int lch;
975

    
976
          if (buf->current >= buf->end)
977
            if (mxml_fd_read(buf) < 0)
978
              return (EOF);
979

    
980
          lch = *(buf->current)++;
981

    
982
          if (buf->current >= buf->end)
983
            if (mxml_fd_read(buf) < 0)
984
              return (EOF);
985

    
986
          temp = *(buf->current)++;
987

    
988
          lch |= (temp << 8);
989

    
990
          if (lch < 0xdc00 || lch >= 0xdfff)
991
            return (EOF);
992

    
993
          ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
994
        }
995
        break;
996
  }
997

    
998
#if DEBUG > 1
999
  printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
1000
#endif /* DEBUG > 1 */
1001

    
1002
  return (ch);
1003
}
1004

    
1005

    
1006
/*
1007
 * 'mxml_fd_putc()' - Write a character to a file descriptor.
1008
 */
1009

    
1010
static int                                /* O - 0 on success, -1 on error */
1011
mxml_fd_putc(int  ch,                        /* I - Character */
1012
             void *p)                        /* I - File descriptor buffer */
1013
{
1014
  _mxml_fdbuf_t        *buf;                        /* File descriptor buffer */
1015

    
1016

    
1017
 /*
1018
  * Flush the write buffer as needed...
1019
  */
1020

    
1021
  buf = (_mxml_fdbuf_t *)p;
1022

    
1023
  if (buf->current >= buf->end)
1024
    if (mxml_fd_write(buf) < 0)
1025
      return (-1);
1026

    
1027
  *(buf->current)++ = ch;
1028

    
1029
 /*
1030
  * Return successfully...
1031
  */
1032

    
1033
  return (0);
1034
}
1035

    
1036

    
1037
/*
1038
 * 'mxml_fd_read()' - Read a buffer of data from a file descriptor.
1039
 */
1040

    
1041
static int                                /* O - 0 on success, -1 on error */
1042
mxml_fd_read(_mxml_fdbuf_t *buf)                /* I - File descriptor buffer */
1043
{
1044
  int        bytes;                                /* Bytes read... */
1045

    
1046

    
1047
 /*
1048
  * Range check input...
1049
  */
1050

    
1051
  if (!buf)
1052
    return (-1);
1053

    
1054
 /*
1055
  * Read from the file descriptor...
1056
  */
1057

    
1058
  while ((bytes = read(buf->fd, buf->buffer, sizeof(buf->buffer))) < 0)
1059
#ifdef EINTR
1060
    if (errno != EAGAIN && errno != EINTR)
1061
#else
1062
    if (errno != EAGAIN)
1063
#endif /* EINTR */
1064
      return (-1);
1065

    
1066
  if (bytes == 0)
1067
    return (-1);
1068

    
1069
 /*
1070
  * Update the pointers and return success...
1071
  */
1072

    
1073
  buf->current = buf->buffer;
1074
  buf->end     = buf->buffer + bytes;
1075

    
1076
  return (0);
1077
}
1078

    
1079

    
1080
/*
1081
 * 'mxml_fd_write()' - Write a buffer of data to a file descriptor.
1082
 */
1083

    
1084
static int                                /* O - 0 on success, -1 on error */
1085
mxml_fd_write(_mxml_fdbuf_t *buf)        /* I - File descriptor buffer */
1086
{
1087
  int                bytes;                        /* Bytes written */
1088
  unsigned char        *ptr;                        /* Pointer into buffer */
1089

    
1090

    
1091
 /*
1092
  * Range check...
1093
  */
1094

    
1095
  if (!buf)
1096
    return (-1);
1097

    
1098
 /*
1099
  * Return 0 if there is nothing to write...
1100
  */
1101

    
1102
  if (buf->current == buf->buffer)
1103
    return (0);
1104

    
1105
 /*
1106
  * Loop until we have written everything...
1107
  */
1108

    
1109
  for (ptr = buf->buffer; ptr < buf->current; ptr += bytes)
1110
    if ((bytes = write(buf->fd, ptr, buf->current - ptr)) < 0)
1111
      return (-1);
1112

    
1113
 /*
1114
  * All done, reset pointers and return success...
1115
  */
1116

    
1117
  buf->current = buf->buffer;
1118

    
1119
  return (0);
1120
}
1121

    
1122

    
1123
/*
1124
 * 'mxml_file_getc()' - Get a character from a file.
1125
 */
1126

    
1127
static int                                /* O  - Character or EOF */
1128
mxml_file_getc(void *p,                        /* I  - Pointer to file */
1129
               int  *encoding)                /* IO - Encoding */
1130
{
1131
  int        ch,                                /* Character from file */
1132
        temp;                                /* Temporary character */
1133
  FILE        *fp;                                /* Pointer to file */
1134

    
1135

    
1136
 /*
1137
  * Read a character from the file and see if it is EOF or ASCII...
1138
  */
1139

    
1140
  fp = (FILE *)p;
1141
  ch = getc(fp);
1142

    
1143
  if (ch == EOF)
1144
    return (EOF);
1145

    
1146
  switch (*encoding)
1147
  {
1148
    case ENCODE_UTF8 :
1149
       /*
1150
        * Got a UTF-8 character; convert UTF-8 to Unicode and return...
1151
        */
1152

    
1153
        if (!(ch & 0x80))
1154
        {
1155
          if (mxml_bad_char(ch))
1156
          {
1157
            mxml_error("Bad control character 0x%02x not allowed by XML standard!",
1158
                       ch);
1159
            return (EOF);
1160
          }
1161

    
1162
#if DEBUG > 1
1163
          printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
1164
#endif /* DEBUG > 1 */
1165

    
1166
          return (ch);
1167
        }
1168
        else if (ch == 0xfe)
1169
        {
1170
         /*
1171
          * UTF-16 big-endian BOM?
1172
          */
1173

    
1174
          ch = getc(fp);
1175
          if (ch != 0xff)
1176
            return (EOF);
1177

    
1178
          *encoding = ENCODE_UTF16BE;
1179

    
1180
          return (mxml_file_getc(p, encoding));
1181
        }
1182
        else if (ch == 0xff)
1183
        {
1184
         /*
1185
          * UTF-16 little-endian BOM?
1186
          */
1187

    
1188
          ch = getc(fp);
1189
          if (ch != 0xfe)
1190
            return (EOF);
1191

    
1192
          *encoding = ENCODE_UTF16LE;
1193

    
1194
          return (mxml_file_getc(p, encoding));
1195
        }
1196
        else if ((ch & 0xe0) == 0xc0)
1197
        {
1198
         /*
1199
          * Two-byte value...
1200
          */
1201

    
1202
          if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1203
            return (EOF);
1204

    
1205
          ch = ((ch & 0x1f) << 6) | (temp & 0x3f);
1206

    
1207
          if (ch < 0x80)
1208
          {
1209
            mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
1210
            return (EOF);
1211
          }
1212
        }
1213
        else if ((ch & 0xf0) == 0xe0)
1214
        {
1215
         /*
1216
          * Three-byte value...
1217
          */
1218

    
1219
          if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1220
            return (EOF);
1221

    
1222
          ch = ((ch & 0x0f) << 6) | (temp & 0x3f);
1223

    
1224
          if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1225
            return (EOF);
1226

    
1227
          ch = (ch << 6) | (temp & 0x3f);
1228

    
1229
          if (ch < 0x800)
1230
          {
1231
            mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
1232
            return (EOF);
1233
          }
1234

    
1235
         /*
1236
          * Ignore (strip) Byte Order Mark (BOM)...
1237
          */
1238

    
1239
          if (ch == 0xfeff)
1240
            return (mxml_file_getc(p, encoding));
1241
        }
1242
        else if ((ch & 0xf8) == 0xf0)
1243
        {
1244
         /*
1245
          * Four-byte value...
1246
          */
1247

    
1248
          if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1249
            return (EOF);
1250

    
1251
          ch = ((ch & 0x07) << 6) | (temp & 0x3f);
1252

    
1253
          if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1254
            return (EOF);
1255

    
1256
          ch = (ch << 6) | (temp & 0x3f);
1257

    
1258
          if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80)
1259
            return (EOF);
1260

    
1261
          ch = (ch << 6) | (temp & 0x3f);
1262

    
1263
          if (ch < 0x10000)
1264
          {
1265
            mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
1266
            return (EOF);
1267
          }
1268
        }
1269
        else
1270
          return (EOF);
1271
        break;
1272

    
1273
    case ENCODE_UTF16BE :
1274
       /*
1275
        * Read UTF-16 big-endian char...
1276
        */
1277

    
1278
        ch = (ch << 8) | getc(fp);
1279

    
1280
        if (mxml_bad_char(ch))
1281
        {
1282
          mxml_error("Bad control character 0x%02x not allowed by XML standard!",
1283
                     ch);
1284
          return (EOF);
1285
        }
1286
        else if (ch >= 0xd800 && ch <= 0xdbff)
1287
        {
1288
         /*
1289
          * Multi-word UTF-16 char...
1290
          */
1291

    
1292
          int lch = (getc(fp) << 8) | getc(fp);
1293

    
1294
          if (lch < 0xdc00 || lch >= 0xdfff)
1295
            return (EOF);
1296

    
1297
          ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
1298
        }
1299
        break;
1300

    
1301
    case ENCODE_UTF16LE :
1302
       /*
1303
        * Read UTF-16 little-endian char...
1304
        */
1305

    
1306
        ch |= (getc(fp) << 8);
1307

    
1308
        if (mxml_bad_char(ch))
1309
        {
1310
          mxml_error("Bad control character 0x%02x not allowed by XML standard!",
1311
                     ch);
1312
          return (EOF);
1313
        }
1314
        else if (ch >= 0xd800 && ch <= 0xdbff)
1315
        {
1316
         /*
1317
          * Multi-word UTF-16 char...
1318
          */
1319

    
1320
          int lch = getc(fp) | (getc(fp) << 8);
1321

    
1322
          if (lch < 0xdc00 || lch >= 0xdfff)
1323
            return (EOF);
1324

    
1325
          ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
1326
        }
1327
        break;
1328
  }
1329

    
1330
#if DEBUG > 1
1331
  printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
1332
#endif /* DEBUG > 1 */
1333

    
1334
  return (ch);
1335
}
1336

    
1337

    
1338
/*
1339
 * 'mxml_file_putc()' - Write a character to a file.
1340
 */
1341

    
1342
static int                                /* O - 0 on success, -1 on failure */
1343
mxml_file_putc(int  ch,                        /* I - Character to write */
1344
               void *p)                        /* I - Pointer to file */
1345
{
1346
  return (putc(ch, (FILE *)p) == EOF ? -1 : 0);
1347
}
1348

    
1349

    
1350
/*
1351
 * 'mxml_get_entity()' - Get the character corresponding to an entity...
1352
 */
1353

    
1354
static int                                /* O  - Character value or EOF on error */
1355
mxml_get_entity(mxml_node_t *parent,        /* I  - Parent node */
1356
                void        *p,                /* I  - Pointer to source */
1357
                int         *encoding,        /* IO - Character encoding */
1358
                int         (*getc_cb)(void *, int *))
1359
                                        /* I  - Get character function */
1360
{
1361
  int        ch;                                /* Current character */
1362
  char        entity[64],                        /* Entity string */
1363
        *entptr;                        /* Pointer into entity */
1364

    
1365

    
1366
  entptr = entity;
1367

    
1368
  while ((ch = (*getc_cb)(p, encoding)) != EOF)
1369
    if (ch > 126 || (!isalnum(ch) && ch != '#'))
1370
      break;
1371
    else if (entptr < (entity + sizeof(entity) - 1))
1372
      *entptr++ = ch;
1373
    else
1374
    {
1375
      mxml_error("Entity name too long under parent <%s>!",
1376
                 parent ? parent->value.element.name : "null");
1377
      break;
1378
    }
1379

    
1380
  *entptr = '\0';
1381

    
1382
  if (ch != ';')
1383
  {
1384
    mxml_error("Character entity \"%s\" not terminated under parent <%s>!",
1385
               entity, parent ? parent->value.element.name : "null");
1386
    return (EOF);
1387
  }
1388

    
1389
  if (entity[0] == '#')
1390
  {
1391
    if (entity[1] == 'x')
1392
      ch = strtol(entity + 2, NULL, 16);
1393
    else
1394
      ch = strtol(entity + 1, NULL, 10);
1395
  }
1396
  else if ((ch = mxmlEntityGetValue(entity)) < 0)
1397
    mxml_error("Entity name \"%s;\" not supported under parent <%s>!",
1398
               entity, parent ? parent->value.element.name : "null");
1399

    
1400
  if (mxml_bad_char(ch))
1401
  {
1402
    mxml_error("Bad control character 0x%02x under parent <%s> not allowed by XML standard!",
1403
               ch, parent ? parent->value.element.name : "null");
1404
    return (EOF);
1405
  }
1406

    
1407
  return (ch);
1408
}
1409

    
1410

    
1411
/*
1412
 * 'mxml_load_data()' - Load data into an XML node tree.
1413
 */
1414

    
1415
static mxml_node_t *                        /* O - First node or NULL if the file could not be read. */
1416
mxml_load_data(
1417
    mxml_node_t     *top,                /* I - Top node */
1418
    void            *p,                        /* I - Pointer to data */
1419
    mxml_load_cb_t  cb,                        /* I - Callback function or MXML_NO_CALLBACK */
1420
    _mxml_getc_cb_t getc_cb,                /* I - Read function */
1421
    mxml_sax_cb_t   sax_cb,                /* I - SAX callback or MXML_NO_CALLBACK */
1422
    void            *sax_data)                /* I - SAX user data */
1423
{
1424
  mxml_node_t        *node,                        /* Current node */
1425
                *first,                        /* First node added */
1426
                *parent;                /* Current parent node */
1427
  int                ch,                        /* Character from file */
1428
                whitespace;                /* Non-zero if whitespace seen */
1429
  char                *buffer,                /* String buffer */
1430
                *bufptr;                /* Pointer into buffer */
1431
  int                bufsize;                /* Size of buffer */
1432
  mxml_type_t        type;                        /* Current node type */
1433
  int                encoding;                /* Character encoding */
1434
  _mxml_global_t *global = _mxml_global();
1435
                                        /* Global data */
1436
  static const char * const types[] =        /* Type strings... */
1437
                {
1438
                  "MXML_ELEMENT",        /* XML element with attributes */
1439
                  "MXML_INTEGER",        /* Integer value */
1440
                  "MXML_OPAQUE",        /* Opaque string */
1441
                  "MXML_REAL",                /* Real value */
1442
                  "MXML_TEXT",                /* Text fragment */
1443
                  "MXML_CUSTOM"                /* Custom data */
1444
                };
1445

    
1446

    
1447
 /*
1448
  * Read elements and other nodes from the file...
1449
  */
1450

    
1451
  if ((buffer = malloc(64)) == NULL)
1452
  {
1453
    mxml_error("Unable to allocate string buffer!");
1454
    return (NULL);
1455
  }
1456

    
1457
  bufsize    = 64;
1458
  bufptr     = buffer;
1459
  parent     = top;
1460
  first      = NULL;
1461
  whitespace = 0;
1462
  encoding   = ENCODE_UTF8;
1463

    
1464
  if (cb && parent)
1465
    type = (*cb)(parent);
1466
  else
1467
    type = MXML_TEXT;
1468

    
1469
  while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1470
  {
1471
    if ((ch == '<' ||
1472
         (mxml_isspace(ch) && type != MXML_OPAQUE && type != MXML_CUSTOM)) &&
1473
        bufptr > buffer)
1474
    {
1475
     /*
1476
      * Add a new value node...
1477
      */
1478

    
1479
      *bufptr = '\0';
1480

    
1481
      switch (type)
1482
      {
1483
        case MXML_INTEGER :
1484
            node = mxmlNewInteger(parent, strtol(buffer, &bufptr, 0));
1485
            break;
1486

    
1487
        case MXML_OPAQUE :
1488
            node = mxmlNewOpaque(parent, buffer);
1489
            break;
1490

    
1491
        case MXML_REAL :
1492
            node = mxmlNewReal(parent, strtod(buffer, &bufptr));
1493
            break;
1494

    
1495
        case MXML_TEXT :
1496
            node = mxmlNewText(parent, whitespace, buffer);
1497
            break;
1498

    
1499
        case MXML_CUSTOM :
1500
            if (global->custom_load_cb)
1501
            {
1502
             /*
1503
              * Use the callback to fill in the custom data...
1504
              */
1505

    
1506
              node = mxmlNewCustom(parent, NULL, NULL);
1507

    
1508
              if ((*global->custom_load_cb)(node, buffer))
1509
              {
1510
                mxml_error("Bad custom value '%s' in parent <%s>!",
1511
                           buffer, parent ? parent->value.element.name : "null");
1512
                mxmlDelete(node);
1513
                node = NULL;
1514
              }
1515
              break;
1516
            }
1517

    
1518
        default : /* Ignore... */
1519
            node = NULL;
1520
            break;
1521
      }          
1522

    
1523
      if (*bufptr)
1524
      {
1525
       /*
1526
        * Bad integer/real number value...
1527
        */
1528

    
1529
        mxml_error("Bad %s value '%s' in parent <%s>!",
1530
                   type == MXML_INTEGER ? "integer" : "real", buffer,
1531
                   parent ? parent->value.element.name : "null");
1532
        break;
1533
      }
1534

    
1535
      bufptr     = buffer;
1536
      whitespace = mxml_isspace(ch) && type == MXML_TEXT;
1537

    
1538
      if (!node && type != MXML_IGNORE)
1539
      {
1540
       /*
1541
        * Print error and return...
1542
        */
1543

    
1544
        mxml_error("Unable to add value node of type %s to parent <%s>!",
1545
                   types[type], parent ? parent->value.element.name : "null");
1546
        goto error;
1547
      }
1548

    
1549
      if (sax_cb)
1550
      {
1551
        (*sax_cb)(node, MXML_SAX_DATA, sax_data);
1552

    
1553
        if (!mxmlRelease(node))
1554
          node = NULL;
1555
      }
1556

    
1557
      if (!first && node)
1558
        first = node;
1559
    }
1560
    else if (mxml_isspace(ch) && type == MXML_TEXT)
1561
      whitespace = 1;
1562

    
1563
   /*
1564
    * Add lone whitespace node if we have an element and existing
1565
    * whitespace...
1566
    */
1567

    
1568
    if (ch == '<' && whitespace && type == MXML_TEXT)
1569
    {
1570
      node = mxmlNewText(parent, whitespace, "");
1571

    
1572
      if (sax_cb)
1573
      {
1574
        (*sax_cb)(node, MXML_SAX_DATA, sax_data);
1575

    
1576
        if (!mxmlRelease(node))
1577
          node = NULL;
1578
      }
1579

    
1580
      if (!first && node)
1581
        first = node;
1582

    
1583
      whitespace = 0;
1584
    }
1585

    
1586
    if (ch == '<')
1587
    {
1588
     /*
1589
      * Start of open/close tag...
1590
      */
1591

    
1592
      bufptr = buffer;
1593

    
1594
      while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1595
        if (mxml_isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer))
1596
          break;
1597
        else if (ch == '<')
1598
        {
1599
          mxml_error("Bare < in element!");
1600
          goto error;
1601
        }
1602
        else if (ch == '&')
1603
        {
1604
          if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
1605
            goto error;
1606

    
1607
          if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1608
            goto error;
1609
        }
1610
        else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1611
          goto error;
1612
        else if (((bufptr - buffer) == 1 && buffer[0] == '?') ||
1613
                 ((bufptr - buffer) == 3 && !strncmp(buffer, "!--", 3)) ||
1614
                 ((bufptr - buffer) == 8 && !strncmp(buffer, "![CDATA[", 8)))
1615
          break;
1616

    
1617
      *bufptr = '\0';
1618

    
1619
      if (!strcmp(buffer, "!--"))
1620
      {
1621
       /*
1622
        * Gather rest of comment...
1623
        */
1624

    
1625
        while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1626
        {
1627
          if (ch == '>' && bufptr > (buffer + 4) &&
1628
              bufptr[-3] != '-' && bufptr[-2] == '-' && bufptr[-1] == '-')
1629
            break;
1630
          else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1631
            goto error;
1632
        }
1633

    
1634
       /*
1635
        * Error out if we didn't get the whole comment...
1636
        */
1637

    
1638
        if (ch != '>')
1639
        {
1640
         /*
1641
          * Print error and return...
1642
          */
1643

    
1644
          mxml_error("Early EOF in comment node!");
1645
          goto error;
1646
        }
1647

    
1648

    
1649
       /*
1650
        * Otherwise add this as an element under the current parent...
1651
        */
1652

    
1653
        *bufptr = '\0';
1654

    
1655
        if ((node = mxmlNewElement(parent, buffer)) == NULL)
1656
        {
1657
         /*
1658
          * Just print error for now...
1659
          */
1660

    
1661
          mxml_error("Unable to add comment node to parent <%s>!",
1662
                     parent ? parent->value.element.name : "null");
1663
          break;
1664
        }
1665

    
1666
        if (sax_cb)
1667
        {
1668
          (*sax_cb)(node, MXML_SAX_COMMENT, sax_data);
1669

    
1670
          if (!mxmlRelease(node))
1671
            node = NULL;
1672
        }
1673

    
1674
        if (node && !first)
1675
          first = node;
1676
      }
1677
      else if (!strcmp(buffer, "![CDATA["))
1678
      {
1679
       /*
1680
        * Gather CDATA section...
1681
        */
1682

    
1683
        while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1684
        {
1685
          if (ch == '>' && !strncmp(bufptr - 2, "]]", 2))
1686
            break;
1687
          else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1688
            goto error;
1689
        }
1690

    
1691
       /*
1692
        * Error out if we didn't get the whole comment...
1693
        */
1694

    
1695
        if (ch != '>')
1696
        {
1697
         /*
1698
          * Print error and return...
1699
          */
1700

    
1701
          mxml_error("Early EOF in CDATA node!");
1702
          goto error;
1703
        }
1704

    
1705

    
1706
       /*
1707
        * Otherwise add this as an element under the current parent...
1708
        */
1709

    
1710
        *bufptr = '\0';
1711

    
1712
        if ((node = mxmlNewElement(parent, buffer)) == NULL)
1713
        {
1714
         /*
1715
          * Print error and return...
1716
          */
1717

    
1718
          mxml_error("Unable to add CDATA node to parent <%s>!",
1719
                     parent ? parent->value.element.name : "null");
1720
          goto error;
1721
        }
1722

    
1723
        if (sax_cb)
1724
        {
1725
          (*sax_cb)(node, MXML_SAX_CDATA, sax_data);
1726

    
1727
          if (!mxmlRelease(node))
1728
            node = NULL;
1729
        }
1730

    
1731
        if (node && !first)
1732
          first = node;
1733
      }
1734
      else if (buffer[0] == '?')
1735
      {
1736
       /*
1737
        * Gather rest of processing instruction...
1738
        */
1739

    
1740
        while ((ch = (*getc_cb)(p, &encoding)) != EOF)
1741
        {
1742
          if (ch == '>' && bufptr > buffer && bufptr[-1] == '?')
1743
            break;
1744
          else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1745
            goto error;
1746
        }
1747

    
1748
       /*
1749
        * Error out if we didn't get the whole processing instruction...
1750
        */
1751

    
1752
        if (ch != '>')
1753
        {
1754
         /*
1755
          * Print error and return...
1756
          */
1757

    
1758
          mxml_error("Early EOF in processing instruction node!");
1759
          goto error;
1760
        }
1761

    
1762
       /*
1763
        * Otherwise add this as an element under the current parent...
1764
        */
1765

    
1766
        *bufptr = '\0';
1767

    
1768
        if ((node = mxmlNewElement(parent, buffer)) == NULL)
1769
        {
1770
         /*
1771
          * Print error and return...
1772
          */
1773

    
1774
          mxml_error("Unable to add processing instruction node to parent <%s>!",
1775
                     parent ? parent->value.element.name : "null");
1776
          goto error;
1777
        }
1778

    
1779
        if (sax_cb)
1780
        {
1781
          (*sax_cb)(node, MXML_SAX_DIRECTIVE, sax_data);
1782

    
1783
          if (!mxmlRelease(node))
1784
            node = NULL;
1785
        }
1786

    
1787
        if (node)
1788
        {
1789
          if (!first)
1790
            first = node;
1791

    
1792
          if (!parent)
1793
          {
1794
            parent = node;
1795

    
1796
            if (cb)
1797
              type = (*cb)(parent);
1798
          }
1799
        }
1800
      }
1801
      else if (buffer[0] == '!')
1802
      {
1803
       /*
1804
        * Gather rest of declaration...
1805
        */
1806

    
1807
        do
1808
        {
1809
          if (ch == '>')
1810
            break;
1811
          else
1812
          {
1813
            if (ch == '&')
1814
              if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
1815
                goto error;
1816

    
1817
            if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1818
              goto error;
1819
          }
1820
        }
1821
        while ((ch = (*getc_cb)(p, &encoding)) != EOF);
1822

    
1823
       /*
1824
        * Error out if we didn't get the whole declaration...
1825
        */
1826

    
1827
        if (ch != '>')
1828
        {
1829
         /*
1830
          * Print error and return...
1831
          */
1832

    
1833
          mxml_error("Early EOF in declaration node!");
1834
          goto error;
1835
        }
1836

    
1837
       /*
1838
        * Otherwise add this as an element under the current parent...
1839
        */
1840

    
1841
        *bufptr = '\0';
1842

    
1843
        if ((node = mxmlNewElement(parent, buffer)) == NULL)
1844
        {
1845
         /*
1846
          * Print error and return...
1847
          */
1848

    
1849
          mxml_error("Unable to add declaration node to parent <%s>!",
1850
                     parent ? parent->value.element.name : "null");
1851
          goto error;
1852
        }
1853

    
1854
        if (sax_cb)
1855
        {
1856
          (*sax_cb)(node, MXML_SAX_DIRECTIVE, sax_data);
1857

    
1858
          if (!mxmlRelease(node))
1859
            node = NULL;
1860
        }
1861

    
1862
        if (node)
1863
        {
1864
          if (!first)
1865
            first = node;
1866

    
1867
          if (!parent)
1868
          {
1869
            parent = node;
1870

    
1871
            if (cb)
1872
              type = (*cb)(parent);
1873
          }
1874
        }
1875
      }
1876
      else if (buffer[0] == '/')
1877
      {
1878
       /*
1879
        * Handle close tag...
1880
        */
1881

    
1882
        if (!parent || strcmp(buffer + 1, parent->value.element.name))
1883
        {
1884
         /*
1885
          * Close tag doesn't match tree; print an error for now...
1886
          */
1887

    
1888
          mxml_error("Mismatched close tag <%s> under parent <%s>!",
1889
                     buffer, parent ? parent->value.element.name : "(null)");
1890
          goto error;
1891
        }
1892

    
1893
       /*
1894
        * Keep reading until we see >...
1895
        */
1896

    
1897
        while (ch != '>' && ch != EOF)
1898
          ch = (*getc_cb)(p, &encoding);
1899

    
1900
        node   = parent;
1901
        parent = parent->parent;
1902

    
1903
        if (sax_cb)
1904
        {
1905
          (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data);
1906

    
1907
          mxmlRelease(node);
1908
        }
1909

    
1910
       /*
1911
        * Ascend into the parent and set the value type as needed...
1912
        */
1913

    
1914
        if (cb && parent)
1915
          type = (*cb)(parent);
1916
      }
1917
      else
1918
      {
1919
       /*
1920
        * Handle open tag...
1921
        */
1922

    
1923
        if ((node = mxmlNewElement(parent, buffer)) == NULL)
1924
        {
1925
         /*
1926
          * Just print error for now...
1927
          */
1928

    
1929
          mxml_error("Unable to add element node to parent <%s>!",
1930
                     parent ? parent->value.element.name : "null");
1931
          goto error;
1932
        }
1933

    
1934
        if (mxml_isspace(ch))
1935
        {
1936
          if ((ch = mxml_parse_element(node, p, &encoding, getc_cb)) == EOF)
1937
            goto error;
1938
        }
1939
        else if (ch == '/')
1940
        {
1941
          if ((ch = (*getc_cb)(p, &encoding)) != '>')
1942
          {
1943
            mxml_error("Expected > but got '%c' instead for element <%s/>!",
1944
                       ch, buffer);
1945
            mxmlDelete(node);
1946
            goto error;
1947
          }
1948

    
1949
          ch = '/';
1950
        }
1951

    
1952
        if (sax_cb)
1953
          (*sax_cb)(node, MXML_SAX_ELEMENT_OPEN, sax_data);
1954

    
1955
        if (!first)
1956
          first = node;
1957

    
1958
        if (ch == EOF)
1959
          break;
1960

    
1961
        if (ch != '/')
1962
        {
1963
         /*
1964
          * Descend into this node, setting the value type as needed...
1965
          */
1966

    
1967
          parent = node;
1968

    
1969
          if (cb && parent)
1970
            type = (*cb)(parent);
1971
        }
1972
        else if (sax_cb)
1973
        {
1974
          (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data);
1975

    
1976
          if (!mxmlRelease(node) && first == node)
1977
            first = NULL;
1978
        }
1979
      }
1980

    
1981
      bufptr  = buffer;
1982
    }
1983
    else if (ch == '&')
1984
    {
1985
     /*
1986
      * Add character entity to current buffer...
1987
      */
1988

    
1989
      if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF)
1990
        goto error;
1991

    
1992
      if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
1993
        goto error;
1994
    }
1995
    else if (type == MXML_OPAQUE || type == MXML_CUSTOM || !mxml_isspace(ch))
1996
    {
1997
     /*
1998
      * Add character to current buffer...
1999
      */
2000

    
2001
      if (mxml_add_char(ch, &bufptr, &buffer, &bufsize))
2002
        goto error;
2003
    }
2004
  }
2005

    
2006
 /*
2007
  * Free the string buffer - we don't need it anymore...
2008
  */
2009

    
2010
  free(buffer);
2011

    
2012
 /*
2013
  * Find the top element and return it...
2014
  */
2015

    
2016
  if (parent)
2017
  {
2018
    node = parent;
2019

    
2020
    while (parent->parent != top && parent->parent)
2021
      parent = parent->parent;
2022

    
2023
    if (node != parent)
2024
    {
2025
      mxml_error("Missing close tag </%s> under parent <%s>!",
2026
                 node->value.element.name,
2027
                 node->parent ? node->parent->value.element.name : "(null)");
2028

    
2029
      mxmlDelete(first);
2030

    
2031
      return (NULL);
2032
    }
2033
  }
2034

    
2035
  if (parent)
2036
    return (parent);
2037
  else
2038
    return (first);
2039

    
2040
 /*
2041
  * Common error return...
2042
  */
2043

    
2044
error:
2045

    
2046
  mxmlDelete(first);
2047

    
2048
  free(buffer);
2049

    
2050
  return (NULL);
2051
}
2052

    
2053

    
2054
/*
2055
 * 'mxml_parse_element()' - Parse an element for any attributes...
2056
 */
2057

    
2058
static int                                /* O  - Terminating character */
2059
mxml_parse_element(
2060
    mxml_node_t     *node,                /* I  - Element node */
2061
    void            *p,                        /* I  - Data to read from */
2062
    int             *encoding,                /* IO - Encoding */
2063
    _mxml_getc_cb_t getc_cb)                /* I  - Data callback */
2064
{
2065
  int        ch,                                /* Current character in file */
2066
        quote;                                /* Quoting character */
2067
  char        *name,                                /* Attribute name */
2068
        *value,                                /* Attribute value */
2069
        *ptr;                                /* Pointer into name/value */
2070
  int        namesize,                        /* Size of name string */
2071
        valsize;                        /* Size of value string */
2072

    
2073

    
2074
 /*
2075
  * Initialize the name and value buffers...
2076
  */
2077

    
2078
  if ((name = malloc(64)) == NULL)
2079
  {
2080
    mxml_error("Unable to allocate memory for name!");
2081
    return (EOF);
2082
  }
2083

    
2084
  namesize = 64;
2085

    
2086
  if ((value = malloc(64)) == NULL)
2087
  {
2088
    free(name);
2089
    mxml_error("Unable to allocate memory for value!");
2090
    return (EOF);
2091
  }
2092

    
2093
  valsize = 64;
2094

    
2095
 /*
2096
  * Loop until we hit a >, /, ?, or EOF...
2097
  */
2098

    
2099
  while ((ch = (*getc_cb)(p, encoding)) != EOF)
2100
  {
2101
#if DEBUG > 1
2102
    fprintf(stderr, "parse_element: ch='%c'\n", ch);
2103
#endif /* DEBUG > 1 */
2104

    
2105
   /*
2106
    * Skip leading whitespace...
2107
    */
2108

    
2109
    if (mxml_isspace(ch))
2110
      continue;
2111

    
2112
   /*
2113
    * Stop at /, ?, or >...
2114
    */
2115

    
2116
    if (ch == '/' || ch == '?')
2117
    {
2118
     /*
2119
      * Grab the > character and print an error if it isn't there...
2120
      */
2121

    
2122
      quote = (*getc_cb)(p, encoding);
2123

    
2124
      if (quote != '>')
2125
      {
2126
        mxml_error("Expected '>' after '%c' for element %s, but got '%c'!",
2127
                   ch, node->value.element.name, quote);
2128
        goto error;
2129
      }
2130

    
2131
      break;
2132
    }
2133
    else if (ch == '<')
2134
    {
2135
      mxml_error("Bare < in element %s!", node->value.element.name);
2136
      goto error;
2137
    }
2138
    else if (ch == '>')
2139
      break;
2140

    
2141
   /*
2142
    * Read the attribute name...
2143
    */
2144

    
2145
    name[0] = ch;
2146
    ptr     = name + 1;
2147

    
2148
    if (ch == '\"' || ch == '\'')
2149
    {
2150
     /*
2151
      * Name is in quotes, so get a quoted string...
2152
      */
2153

    
2154
      quote = ch;
2155

    
2156
      while ((ch = (*getc_cb)(p, encoding)) != EOF)
2157
      {
2158
        if (ch == '&')
2159
          if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
2160
            goto error;
2161

    
2162
        if (mxml_add_char(ch, &ptr, &name, &namesize))
2163
          goto error;
2164

    
2165
        if (ch == quote)
2166
          break;
2167
      }
2168
    }
2169
    else
2170
    {
2171
     /*
2172
      * Grab an normal, non-quoted name...
2173
      */
2174

    
2175
      while ((ch = (*getc_cb)(p, encoding)) != EOF)
2176
        if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>' ||
2177
            ch == '?')
2178
          break;
2179
        else
2180
        {
2181
          if (ch == '&')
2182
            if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
2183
              goto error;
2184

    
2185
          if (mxml_add_char(ch, &ptr, &name, &namesize))
2186
            goto error;
2187
        }
2188
    }
2189

    
2190
    *ptr = '\0';
2191

    
2192
    if (mxmlElementGetAttr(node, name))
2193
      goto error;
2194

    
2195
    while (ch != EOF && mxml_isspace(ch))
2196
      ch = (*getc_cb)(p, encoding);
2197

    
2198
    if (ch == '=')
2199
    {
2200
     /*
2201
      * Read the attribute value...
2202
      */
2203

    
2204
      while ((ch = (*getc_cb)(p, encoding)) != EOF && mxml_isspace(ch));
2205

    
2206
      if (ch == EOF)
2207
      {
2208
        mxml_error("Missing value for attribute '%s' in element %s!",
2209
                   name, node->value.element.name);
2210
        goto error;
2211
      }
2212

    
2213
      if (ch == '\'' || ch == '\"')
2214
      {
2215
       /*
2216
        * Read quoted value...
2217
        */
2218

    
2219
        quote = ch;
2220
        ptr   = value;
2221

    
2222
        while ((ch = (*getc_cb)(p, encoding)) != EOF)
2223
          if (ch == quote)
2224
            break;
2225
          else
2226
          {
2227
            if (ch == '&')
2228
              if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
2229
                goto error;
2230
              
2231
            if (mxml_add_char(ch, &ptr, &value, &valsize))
2232
              goto error;
2233
          }
2234

    
2235
        *ptr = '\0';
2236
      }
2237
      else
2238
      {
2239
       /*
2240
        * Read unquoted value...
2241
        */
2242

    
2243
        value[0] = ch;
2244
        ptr      = value + 1;
2245

    
2246
        while ((ch = (*getc_cb)(p, encoding)) != EOF)
2247
          if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>')
2248
            break;
2249
          else
2250
          {
2251
            if (ch == '&')
2252
              if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF)
2253
                goto error;
2254
              
2255
            if (mxml_add_char(ch, &ptr, &value, &valsize))
2256
              goto error;
2257
          }
2258

    
2259
        *ptr = '\0';
2260
      }
2261

    
2262
     /*
2263
      * Set the attribute with the given string value...
2264
      */
2265

    
2266
      mxmlElementSetAttr(node, name, value);
2267
    }
2268
    else
2269
    {
2270
      mxml_error("Missing value for attribute '%s' in element %s!",
2271
                 name, node->value.element.name);
2272
      goto error;
2273
    }
2274

    
2275
   /*
2276
    * Check the end character...
2277
    */
2278

    
2279
    if (ch == '/' || ch == '?')
2280
    {
2281
     /*
2282
      * Grab the > character and print an error if it isn't there...
2283
      */
2284

    
2285
      quote = (*getc_cb)(p, encoding);
2286

    
2287
      if (quote != '>')
2288
      {
2289
        mxml_error("Expected '>' after '%c' for element %s, but got '%c'!",
2290
                   ch, node->value.element.name, quote);
2291
        ch = EOF;
2292
      }
2293

    
2294
      break;
2295
    }
2296
    else if (ch == '>')
2297
      break;
2298
  }
2299

    
2300
 /*
2301
  * Free the name and value buffers and return...
2302
  */
2303

    
2304
  free(name);
2305
  free(value);
2306

    
2307
  return (ch);
2308

    
2309
 /*
2310
  * Common error return point...
2311
  */
2312

    
2313
error:
2314

    
2315
  free(name);
2316
  free(value);
2317

    
2318
  return (EOF);
2319
}
2320

    
2321

    
2322
/*
2323
 * 'mxml_string_getc()' - Get a character from a string.
2324
 */
2325

    
2326
static int                                /* O  - Character or EOF */
2327
mxml_string_getc(void *p,                /* I  - Pointer to file */
2328
                 int  *encoding)        /* IO - Encoding */
2329
{
2330
  int                ch;                        /* Character */
2331
  const char        **s;                        /* Pointer to string pointer */
2332

    
2333

    
2334
  s = (const char **)p;
2335

    
2336
  if ((ch = (*s)[0] & 255) != 0 || *encoding == ENCODE_UTF16LE)
2337
  {
2338
   /*
2339
    * Got character; convert UTF-8 to integer and return...
2340
    */
2341

    
2342
    (*s)++;
2343

    
2344
    switch (*encoding)
2345
    {
2346
      case ENCODE_UTF8 :
2347
          if (!(ch & 0x80))
2348
          {
2349
#if DEBUG > 1
2350
            printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2351
#endif /* DEBUG > 1 */
2352

    
2353
            if (mxml_bad_char(ch))
2354
            {
2355
              mxml_error("Bad control character 0x%02x not allowed by XML standard!",
2356
                         ch);
2357
              return (EOF);
2358
            }
2359

    
2360
            return (ch);
2361
          }
2362
          else if (ch == 0xfe)
2363
          {
2364
           /*
2365
            * UTF-16 big-endian BOM?
2366
            */
2367

    
2368
            if (((*s)[0] & 255) != 0xff)
2369
              return (EOF);
2370

    
2371
            *encoding = ENCODE_UTF16BE;
2372
            (*s)++;
2373

    
2374
            return (mxml_string_getc(p, encoding));
2375
          }
2376
          else if (ch == 0xff)
2377
          {
2378
           /*
2379
            * UTF-16 little-endian BOM?
2380
            */
2381

    
2382
            if (((*s)[0] & 255) != 0xfe)
2383
              return (EOF);
2384

    
2385
            *encoding = ENCODE_UTF16LE;
2386
            (*s)++;
2387

    
2388
            return (mxml_string_getc(p, encoding));
2389
          }
2390
          else if ((ch & 0xe0) == 0xc0)
2391
          {
2392
           /*
2393
            * Two-byte value...
2394
            */
2395

    
2396
            if (((*s)[0] & 0xc0) != 0x80)
2397
              return (EOF);
2398

    
2399
            ch = ((ch & 0x1f) << 6) | ((*s)[0] & 0x3f);
2400

    
2401
            (*s)++;
2402

    
2403
            if (ch < 0x80)
2404
            {
2405
              mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
2406
              return (EOF);
2407
            }
2408

    
2409
#if DEBUG > 1
2410
            printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2411
#endif /* DEBUG > 1 */
2412

    
2413
            return (ch);
2414
          }
2415
          else if ((ch & 0xf0) == 0xe0)
2416
          {
2417
           /*
2418
            * Three-byte value...
2419
            */
2420

    
2421
            if (((*s)[0] & 0xc0) != 0x80 ||
2422
                ((*s)[1] & 0xc0) != 0x80)
2423
              return (EOF);
2424

    
2425
            ch = ((((ch & 0x0f) << 6) | ((*s)[0] & 0x3f)) << 6) | ((*s)[1] & 0x3f);
2426

    
2427
            (*s) += 2;
2428

    
2429
            if (ch < 0x800)
2430
            {
2431
              mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
2432
              return (EOF);
2433
            }
2434

    
2435
           /*
2436
            * Ignore (strip) Byte Order Mark (BOM)...
2437
            */
2438

    
2439
            if (ch == 0xfeff)
2440
              return (mxml_string_getc(p, encoding));
2441

    
2442
#if DEBUG > 1
2443
            printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2444
#endif /* DEBUG > 1 */
2445

    
2446
            return (ch);
2447
          }
2448
          else if ((ch & 0xf8) == 0xf0)
2449
          {
2450
           /*
2451
            * Four-byte value...
2452
            */
2453

    
2454
            if (((*s)[0] & 0xc0) != 0x80 ||
2455
                ((*s)[1] & 0xc0) != 0x80 ||
2456
                ((*s)[2] & 0xc0) != 0x80)
2457
              return (EOF);
2458

    
2459
            ch = ((((((ch & 0x07) << 6) | ((*s)[0] & 0x3f)) << 6) |
2460
                   ((*s)[1] & 0x3f)) << 6) | ((*s)[2] & 0x3f);
2461

    
2462
            (*s) += 3;
2463

    
2464
            if (ch < 0x10000)
2465
            {
2466
              mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch);
2467
              return (EOF);
2468
            }
2469

    
2470
#if DEBUG > 1
2471
            printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2472
#endif /* DEBUG > 1 */
2473

    
2474
            return (ch);
2475
          }
2476
          else
2477
            return (EOF);
2478

    
2479
      case ENCODE_UTF16BE :
2480
         /*
2481
          * Read UTF-16 big-endian char...
2482
          */
2483

    
2484
          ch = (ch << 8) | ((*s)[0] & 255);
2485
          (*s) ++;
2486

    
2487
          if (mxml_bad_char(ch))
2488
          {
2489
            mxml_error("Bad control character 0x%02x not allowed by XML standard!",
2490
                       ch);
2491
            return (EOF);
2492
          }
2493
          else if (ch >= 0xd800 && ch <= 0xdbff)
2494
          {
2495
           /*
2496
            * Multi-word UTF-16 char...
2497
            */
2498

    
2499
            int lch;                        /* Lower word */
2500

    
2501

    
2502
            if (!(*s)[0])
2503
              return (EOF);
2504

    
2505
            lch = (((*s)[0] & 255) << 8) | ((*s)[1] & 255);
2506
            (*s) += 2;
2507

    
2508
            if (lch < 0xdc00 || lch >= 0xdfff)
2509
              return (EOF);
2510

    
2511
            ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
2512
          }
2513

    
2514
#if DEBUG > 1
2515
          printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2516
#endif /* DEBUG > 1 */
2517

    
2518
          return (ch);
2519

    
2520
      case ENCODE_UTF16LE :
2521
         /*
2522
          * Read UTF-16 little-endian char...
2523
          */
2524

    
2525
          ch = ch | (((*s)[0] & 255) << 8);
2526

    
2527
          if (!ch)
2528
          {
2529
            (*s) --;
2530
            return (EOF);
2531
          }
2532

    
2533
          (*s) ++;
2534

    
2535
          if (mxml_bad_char(ch))
2536
          {
2537
            mxml_error("Bad control character 0x%02x not allowed by XML standard!",
2538
                       ch);
2539
            return (EOF);
2540
          }
2541
          else if (ch >= 0xd800 && ch <= 0xdbff)
2542
          {
2543
           /*
2544
            * Multi-word UTF-16 char...
2545
            */
2546

    
2547
            int lch;                        /* Lower word */
2548

    
2549

    
2550
            if (!(*s)[1])
2551
              return (EOF);
2552

    
2553
            lch = (((*s)[1] & 255) << 8) | ((*s)[0] & 255);
2554
            (*s) += 2;
2555

    
2556
            if (lch < 0xdc00 || lch >= 0xdfff)
2557
              return (EOF);
2558

    
2559
            ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
2560
          }
2561

    
2562
#if DEBUG > 1
2563
          printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2564
#endif /* DEBUG > 1 */
2565

    
2566
          return (ch);
2567
    }
2568
  }
2569

    
2570
  return (EOF);
2571
}
2572

    
2573

    
2574
/*
2575
 * 'mxml_string_putc()' - Write a character to a string.
2576
 */
2577

    
2578
static int                                /* O - 0 on success, -1 on failure */
2579
mxml_string_putc(int  ch,                /* I - Character to write */
2580
                 void *p)                /* I - Pointer to string pointers */
2581
{
2582
  char        **pp;                                /* Pointer to string pointers */
2583

    
2584

    
2585
  pp = (char **)p;
2586

    
2587
  if (pp[0] < pp[1])
2588
    pp[0][0] = ch;
2589

    
2590
  pp[0] ++;
2591

    
2592
  return (0);
2593
}
2594

    
2595

    
2596
/*
2597
 * 'mxml_write_name()' - Write a name string.
2598
 */
2599

    
2600
static int                                /* O - 0 on success, -1 on failure */
2601
mxml_write_name(const char *s,                /* I - Name to write */
2602
                void       *p,                /* I - Write pointer */
2603
                int        (*putc_cb)(int, void *))
2604
                                        /* I - Write callback */
2605
{
2606
  char                quote;                        /* Quote character */
2607
  const char        *name;                        /* Entity name */
2608

    
2609

    
2610
  if (*s == '\"' || *s == '\'')
2611
  {
2612
   /*
2613
    * Write a quoted name string...
2614
    */
2615

    
2616
    if ((*putc_cb)(*s, p) < 0)
2617
      return (-1);
2618

    
2619
    quote = *s++;
2620

    
2621
    while (*s && *s != quote)
2622
    {
2623
      if ((name = mxmlEntityGetName(*s)) != NULL)
2624
      {
2625
        if ((*putc_cb)('&', p) < 0)
2626
          return (-1);
2627

    
2628
        while (*name)
2629
        {
2630
          if ((*putc_cb)(*name, p) < 0)
2631
            return (-1);
2632

    
2633
          name ++;
2634
        }
2635

    
2636
        if ((*putc_cb)(';', p) < 0)
2637
          return (-1);
2638
      }
2639
      else if ((*putc_cb)(*s, p) < 0)
2640
        return (-1);
2641

    
2642
      s ++;
2643
    }
2644

    
2645
   /*
2646
    * Write the end quote...
2647
    */
2648

    
2649
    if ((*putc_cb)(quote, p) < 0)
2650
      return (-1);
2651
  }
2652
  else
2653
  {
2654
   /*
2655
    * Write a non-quoted name string...
2656
    */
2657

    
2658
    while (*s)
2659
    {
2660
      if ((*putc_cb)(*s, p) < 0)
2661
        return (-1);
2662

    
2663
      s ++;
2664
    }
2665
  }
2666

    
2667
  return (0);
2668
}
2669

    
2670

    
2671
/*
2672
 * 'mxml_write_node()' - Save an XML node to a file.
2673
 */
2674

    
2675
static int                                /* O - Column or -1 on error */
2676
mxml_write_node(mxml_node_t     *node,        /* I - Node to write */
2677
                void            *p,        /* I - File to write to */
2678
                mxml_save_cb_t  cb,        /* I - Whitespace callback */
2679
                int             col,        /* I - Current column */
2680
                _mxml_putc_cb_t putc_cb,/* I - Output callback */
2681
                _mxml_global_t  *global)/* I - Global data */
2682
{
2683
  int                i,                        /* Looping var */
2684
                width;                        /* Width of attr + value */
2685
  mxml_attr_t        *attr;                        /* Current attribute */
2686
  char                s[255];                        /* Temporary string */
2687

    
2688

    
2689
  while (node != NULL)
2690
  {
2691
   /*
2692
    * Print the node value...
2693
    */
2694

    
2695
    switch (node->type)
2696
    {
2697
      case MXML_ELEMENT :
2698
          col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb);
2699

    
2700
          if ((*putc_cb)('<', p) < 0)
2701
            return (-1);
2702
          if (node->value.element.name[0] == '?' ||
2703
              !strncmp(node->value.element.name, "!--", 3) ||
2704
              !strncmp(node->value.element.name, "![CDATA[", 8))
2705
          {
2706
           /*
2707
            * Comments, CDATA, and processing instructions do not
2708
            * use character entities.
2709
            */
2710

    
2711
            const char        *ptr;                /* Pointer into name */
2712

    
2713

    
2714
            for (ptr = node->value.element.name; *ptr; ptr ++)
2715
              if ((*putc_cb)(*ptr, p) < 0)
2716
                return (-1);
2717
          }
2718
          else if (mxml_write_name(node->value.element.name, p, putc_cb) < 0)
2719
            return (-1);
2720

    
2721
          col += strlen(node->value.element.name) + 1;
2722

    
2723
          for (i = node->value.element.num_attrs, attr = node->value.element.attrs;
2724
               i > 0;
2725
               i --, attr ++)
2726
          {
2727
            width = strlen(attr->name);
2728

    
2729
            if (attr->value)
2730
              width += strlen(attr->value) + 3;
2731

    
2732
            if (global->wrap > 0 && (col + width) > global->wrap)
2733
            {
2734
              if ((*putc_cb)('\n', p) < 0)
2735
                return (-1);
2736

    
2737
              col = 0;
2738
            }
2739
            else
2740
            {
2741
              if ((*putc_cb)(' ', p) < 0)
2742
                return (-1);
2743

    
2744
              col ++;
2745
            }
2746

    
2747
            if (mxml_write_name(attr->name, p, putc_cb) < 0)
2748
              return (-1);
2749

    
2750
            if (attr->value)
2751
            {
2752
              if ((*putc_cb)('=', p) < 0)
2753
                return (-1);
2754
              if ((*putc_cb)('\"', p) < 0)
2755
                return (-1);
2756
              if (mxml_write_string(attr->value, p, putc_cb) < 0)
2757
                return (-1);
2758
              if ((*putc_cb)('\"', p) < 0)
2759
                return (-1);
2760
            }
2761

    
2762
            col += width;
2763
          }
2764

    
2765
          if (node->child)
2766
          {
2767
           /*
2768
            * Write children...
2769
            */
2770

    
2771
            if ((*putc_cb)('>', p) < 0)
2772
              return (-1);
2773
            else
2774
              col ++;
2775

    
2776
            col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
2777

    
2778
            if ((col = mxml_write_node(node->child, p, cb, col, putc_cb,
2779
                                       global)) < 0)
2780
              return (-1);
2781

    
2782
           /*
2783
            * The ? and ! elements are special-cases and have no end tags...
2784
            */
2785

    
2786
            if (node->value.element.name[0] != '!' &&
2787
                node->value.element.name[0] != '?')
2788
            {
2789
              col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb);
2790

    
2791
              if ((*putc_cb)('<', p) < 0)
2792
                return (-1);
2793
              if ((*putc_cb)('/', p) < 0)
2794
                return (-1);
2795
              if (mxml_write_string(node->value.element.name, p, putc_cb) < 0)
2796
                return (-1);
2797
              if ((*putc_cb)('>', p) < 0)
2798
                return (-1);
2799

    
2800
              col += strlen(node->value.element.name) + 3;
2801

    
2802
              col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_CLOSE, col, putc_cb);
2803
            }
2804
          }
2805
          else if (node->value.element.name[0] == '!' ||
2806
                   node->value.element.name[0] == '?')
2807
          {
2808
           /*
2809
            * The ? and ! elements are special-cases...
2810
            */
2811

    
2812
            if ((*putc_cb)('>', p) < 0)
2813
              return (-1);
2814
            else
2815
              col ++;
2816

    
2817
            col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
2818
          }
2819
          else
2820
          {
2821
            if ((*putc_cb)(' ', p) < 0)
2822
              return (-1);
2823
            if ((*putc_cb)('/', p) < 0)
2824
              return (-1);
2825
            if ((*putc_cb)('>', p) < 0)
2826
              return (-1);
2827

    
2828
            col += 3;
2829

    
2830
            col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
2831
          }
2832
          break;
2833

    
2834
      case MXML_INTEGER :
2835
          if (node->prev)
2836
          {
2837
            if (global->wrap > 0 && col > global->wrap)
2838
            {
2839
              if ((*putc_cb)('\n', p) < 0)
2840
                return (-1);
2841

    
2842
              col = 0;
2843
            }
2844
            else if ((*putc_cb)(' ', p) < 0)
2845
              return (-1);
2846
            else
2847
              col ++;
2848
          }
2849

    
2850
          sprintf(s, "%d", node->value.integer);
2851
          if (mxml_write_string(s, p, putc_cb) < 0)
2852
            return (-1);
2853

    
2854
          col += strlen(s);
2855
          break;
2856

    
2857
      case MXML_OPAQUE :
2858
          if (mxml_write_string(node->value.opaque, p, putc_cb) < 0)
2859
            return (-1);
2860

    
2861
          col += strlen(node->value.opaque);
2862
          break;
2863

    
2864
      case MXML_REAL :
2865
          if (node->prev)
2866
          {
2867
            if (global->wrap > 0 && col > global->wrap)
2868
            {
2869
              if ((*putc_cb)('\n', p) < 0)
2870
                return (-1);
2871

    
2872
              col = 0;
2873
            }
2874
            else if ((*putc_cb)(' ', p) < 0)
2875
              return (-1);
2876
            else
2877
              col ++;
2878
          }
2879

    
2880
          sprintf(s, "%f", node->value.real);
2881
          if (mxml_write_string(s, p, putc_cb) < 0)
2882
            return (-1);
2883

    
2884
          col += strlen(s);
2885
          break;
2886

    
2887
      case MXML_TEXT :
2888
          if (node->value.text.whitespace && col > 0)
2889
          {
2890
            if (global->wrap > 0 && col > global->wrap)
2891
            {
2892
              if ((*putc_cb)('\n', p) < 0)
2893
                return (-1);
2894

    
2895
              col = 0;
2896
            }
2897
            else if ((*putc_cb)(' ', p) < 0)
2898
              return (-1);
2899
            else
2900
              col ++;
2901
          }
2902

    
2903
          if (mxml_write_string(node->value.text.string, p, putc_cb) < 0)
2904
            return (-1);
2905

    
2906
          col += strlen(node->value.text.string);
2907
          break;
2908

    
2909
      case MXML_CUSTOM :
2910
          if (global->custom_save_cb)
2911
          {
2912
            char        *data;                /* Custom data string */
2913
            const char        *newline;        /* Last newline in string */
2914

    
2915

    
2916
            if ((data = (*global->custom_save_cb)(node)) == NULL)
2917
              return (-1);
2918

    
2919
            if (mxml_write_string(data, p, putc_cb) < 0)
2920
              return (-1);
2921

    
2922
            if ((newline = strrchr(data, '\n')) == NULL)
2923
              col += strlen(data);
2924
            else
2925
              col = strlen(newline);
2926

    
2927
            free(data);
2928
            break;
2929
          }
2930

    
2931
      default : /* Should never happen */
2932
          return (-1);
2933
    }
2934

    
2935
   /*
2936
    * Next node...
2937
    */
2938

    
2939
    node = node->next;
2940
  }
2941

    
2942
  return (col);
2943
}
2944

    
2945

    
2946
/*
2947
 * 'mxml_write_string()' - Write a string, escaping & and < as needed.
2948
 */
2949

    
2950
static int                                /* O - 0 on success, -1 on failure */
2951
mxml_write_string(
2952
    const char      *s,                        /* I - String to write */
2953
    void            *p,                        /* I - Write pointer */
2954
    _mxml_putc_cb_t putc_cb)                /* I - Write callback */
2955
{
2956
  const char        *name;                        /* Entity name, if any */
2957

    
2958

    
2959
  while (*s)
2960
  {
2961
    if ((name = mxmlEntityGetName(*s)) != NULL)
2962
    {
2963
      if ((*putc_cb)('&', p) < 0)
2964
        return (-1);
2965

    
2966
      while (*name)
2967
      {
2968
        if ((*putc_cb)(*name, p) < 0)
2969
          return (-1);
2970
        name ++;
2971
      }
2972

    
2973
      if ((*putc_cb)(';', p) < 0)
2974
        return (-1);
2975
    }
2976
    else if ((*putc_cb)(*s, p) < 0)
2977
      return (-1);
2978

    
2979
    s ++;
2980
  }
2981

    
2982
  return (0);
2983
}
2984

    
2985

    
2986
/*
2987
 * 'mxml_write_ws()' - Do whitespace callback...
2988
 */
2989

    
2990
static int                                /* O - New column */
2991
mxml_write_ws(mxml_node_t     *node,        /* I - Current node */
2992
              void            *p,        /* I - Write pointer */
2993
              mxml_save_cb_t  cb,        /* I - Callback function */
2994
              int             ws,        /* I - Where value */
2995
              int             col,        /* I - Current column */
2996
              _mxml_putc_cb_t putc_cb)        /* I - Write callback */
2997
{
2998
  const char        *s;                        /* Whitespace string */
2999

    
3000

    
3001
  if (cb && (s = (*cb)(node, ws)) != NULL)
3002
  {
3003
    while (*s)
3004
    {
3005
      if ((*putc_cb)(*s, p) < 0)
3006
        return (-1);
3007
      else if (*s == '\n')
3008
        col = 0;
3009
      else if (*s == '\t')
3010
      {
3011
        col += MXML_TAB;
3012
        col = col - (col % MXML_TAB);
3013
      }
3014
      else
3015
        col ++;
3016

    
3017
      s ++;
3018
    }
3019
  }
3020

    
3021
  return (col);
3022
}
3023

    
3024

    
3025
/*
3026
 * End of "$Id: mxml-file.c 391 2009-05-17 05:20:52Z mike $".
3027
 */