root / logic / trunk / src / mxml / mxml-file.c @ 89
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 |
*/
|