Line data Source code
1 : /********************************************************************\
2 :
3 : Name: mxml.c
4 : Created by: Stefan Ritt
5 : Copyright 2000 + Stefan Ritt
6 :
7 : Contents: Midas XML Library
8 :
9 : This is a simple implementation of XML functions for writing and
10 : reading XML files. For writing an XML file from scratch, following
11 : functions can be used:
12 :
13 : writer = mxml_open_file(file_name);
14 : mxml_start_element(writer, name);
15 : mxml_write_attribute(writer, name, value);
16 : mxml_write_value(writer, value);
17 : mxml_end_element(writer);
18 : ...
19 : mxml_close_file(writer);
20 :
21 : To read an XML file, the function
22 :
23 : tree = mxml_parse_file(file_name, error, sizeof(error));
24 :
25 : is used. It parses the complete XML file and stores it in a
26 : hierarchical tree in memory. Nodes in that tree can be searched
27 : for with
28 :
29 : mxml_find_node(tree, xml_path);
30 :
31 : or
32 :
33 : mxml_find_nodes(tree, xml_path, &nodelist);
34 :
35 : which support a subset of the XPath specification. Another set of
36 : functions is available to retrieve attributes and values from nodes
37 : in the tree and for manipulating nodes, like replacing, adding and
38 : deleting nodes.
39 :
40 :
41 : This file is part of MIDAS XML Library.
42 :
43 : MIDAS XML Library is free software: you can redistribute it and/or modify
44 : it under the terms of the GNU General Public License as published by
45 : the Free Software Foundation, either version 2 of the License, or
46 : (at your option) any later version.
47 :
48 : MIDAS XML Library is distributed in the hope that it will be useful,
49 : but WITHOUT ANY WARRANTY; without even the implied warranty of
50 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
51 : GNU General Public License for more details.
52 :
53 : You should have received a copy of the GNU General Public License
54 : along with MIDAS XML Library. If not, see <http://www.gnu.org/licenses/>.
55 :
56 : \********************************************************************/
57 :
58 : #include <stdio.h>
59 : #include <fcntl.h>
60 : #include <string.h>
61 : #include <assert.h>
62 :
63 : #include <string>
64 :
65 : #ifdef _MSC_VER
66 :
67 : #include <windows.h>
68 : #include <io.h>
69 : #include <time.h>
70 :
71 : #pragma warning( disable: 4996) /* disable "deprecated" warning */
72 :
73 : #else
74 :
75 : #define TRUE 1
76 : #define FALSE 0
77 :
78 : #ifndef O_TEXT
79 : #define O_TEXT 0
80 : #define O_BINARY 0
81 : #endif
82 :
83 : #include <stdlib.h>
84 : #include <unistd.h>
85 : #include <ctype.h>
86 : #include <stdarg.h>
87 : #include <errno.h>
88 : #ifndef OS_VXWORKS
89 : #include <sys/time.h>
90 : #endif
91 : #include <time.h>
92 :
93 : #endif
94 :
95 : #include "mxml.h"
96 :
97 : #define XML_INDENT " "
98 :
99 : #if defined(__GNUC__) && !defined(__MAKECINT__)
100 : # define MXML_GNUC_PRINTF( format_idx, arg_idx ) \
101 : __attribute__((format (printf, format_idx, arg_idx)))
102 : # define MXML_GNUC_SCANF( format_idx, arg_idx ) \
103 : __attribute__((format (scanf, format_idx, arg_idx)))
104 : # define MXML_GNUC_FORMAT( arg_idx ) \
105 : __attribute__((format_arg (arg_idx)))
106 : #else
107 : # define MXML_GNUC_PRINTF( format_idx, arg_idx )
108 : # define MXML_GNUC_SCANF( format_idx, arg_idx )
109 : # define MXML_GNUC_FORMAT( arg_idx )
110 : #endif
111 :
112 : static int mxml_suppress_date_flag = 0; /* suppress writing date at the top of file. */
113 :
114 : /* local prototypes */
115 : static PMXML_NODE read_error(PMXML_NODE root, const char *file_name, int line_number, char *error, int error_size, int *error_line, const char *format, ...) MXML_GNUC_PRINTF(7, 8);
116 : static void mxml_encode(char* buf, int buf_size, const char *src, int src_len, int translate);
117 : static void mxml_decode(char *str);
118 : static int mxml_write_subtree(MXML_WRITER *writer, PMXML_NODE tree, int indent);
119 : static int mxml_write_line(MXML_WRITER *writer, const char *line);
120 : static int mxml_start_element1(MXML_WRITER *writer, const char *name, int indent);
121 : static int mxml_add_resultnode(PMXML_NODE node, const char *xml_path, PMXML_NODE **nodelist, int *found);
122 : static int mxml_find_nodes1(PMXML_NODE tree, const char *xml_path, PMXML_NODE **nodelist, int *found);
123 : static void *mxml_malloc(size_t size);
124 : static void *mxml_realloc(void *p, size_t size);
125 : static void mxml_free(void *p);
126 :
127 : /*
128 : * Copy src to string dst of size siz. At most siz-1 characters
129 : * will be copied. Always NUL terminates (unless size == 0).
130 : * Returns strlen(src); if retval >= siz, truncation occurred.
131 : */
132 :
133 0 : static size_t mxml_strlcpy(char *dst, const char *src, size_t size)
134 : {
135 0 : char *d = dst;
136 0 : const char *s = src;
137 0 : size_t n = size;
138 :
139 : /* Copy as many bytes as will fit */
140 0 : if (n != 0 && --n != 0) {
141 : do {
142 0 : if ((*d++ = *s++) == 0)
143 0 : break;
144 0 : } while (--n != 0);
145 : }
146 :
147 : /* Not enough room in dst, add NUL and traverse rest of src */
148 0 : if (n == 0) {
149 0 : if (size != 0)
150 0 : *d = '\0'; /* NUL-terminate dst */
151 0 : while (*s++);
152 : }
153 :
154 0 : return (s - src - 1); /* count does not include NUL */
155 : }
156 :
157 : /*------------------------------------------------------------------*/
158 :
159 0 : static std::string toString(int i)
160 : {
161 : char buf[100];
162 0 : snprintf(buf, sizeof(buf), "%d", i);
163 0 : return buf;
164 : }
165 :
166 : /*------------------------------------------------------------------*/
167 :
168 0 : static std::string toStrerror(int err)
169 : {
170 : char buf[256];
171 0 : snprintf(buf, sizeof(buf), "errno %d (%s)", err, strerror(err));
172 0 : return buf;
173 : }
174 :
175 : /*------------------------------------------------------------------*/
176 :
177 0 : void *mxml_malloc(size_t size)
178 : {
179 0 : return malloc(size);
180 : }
181 :
182 : /*------------------------------------------------------------------*/
183 :
184 0 : void *mxml_realloc(void *p, size_t size)
185 : {
186 0 : return realloc(p, size);
187 : }
188 :
189 : /*------------------------------------------------------------------*/
190 :
191 0 : void mxml_free(void *p)
192 : {
193 0 : free(p);
194 0 : }
195 :
196 : /*------------------------------------------------------------------*/
197 :
198 0 : int mxml_write_line(MXML_WRITER *writer, const char *line)
199 : {
200 0 : int len = strlen(line);
201 :
202 0 : if (writer->buffer) {
203 0 : if (writer->buffer_len + len >= writer->buffer_size) {
204 0 : writer->buffer_size += len + 10000;
205 0 : writer->buffer = (char *)mxml_realloc(writer->buffer, writer->buffer_size);
206 0 : assert(writer->buffer);
207 : }
208 0 : memcpy(writer->buffer + writer->buffer_len, line, len+1);
209 0 : writer->buffer_len += len;
210 0 : return len;
211 : } else {
212 0 : return (int)write(writer->fh, line, len);
213 : }
214 :
215 : return 0;
216 : }
217 :
218 : /*------------------------------------------------------------------*/
219 :
220 : /**
221 : * open a memory buffer and write XML header
222 : */
223 0 : MXML_WRITER *mxml_open_buffer(void)
224 : {
225 0 : MXML_WRITER *writer = (MXML_WRITER *)mxml_malloc(sizeof(MXML_WRITER));
226 0 : memset(writer, 0, sizeof(MXML_WRITER));
227 0 : writer->translate = 1;
228 :
229 0 : writer->buffer_size = 10000;
230 0 : writer->buffer = (char *)mxml_malloc(writer->buffer_size);
231 0 : assert(writer->buffer != NULL);
232 0 : writer->buffer[0] = 0;
233 0 : writer->buffer_len = 0;
234 :
235 : /* write XML header */
236 0 : mxml_write_line(writer, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
237 :
238 0 : if (mxml_suppress_date_flag == 0) {
239 0 : time_t now = time(NULL);
240 0 : std::string str = ctime(&now);
241 0 : str.resize(24);
242 0 : std::string line = "";
243 0 : line += "<!-- created by MXML on ";
244 0 : line += str;
245 0 : line += " -->\n";
246 0 : mxml_write_line(writer, line.c_str());
247 0 : }
248 :
249 : /* initialize stack */
250 0 : writer->level = 0;
251 0 : writer->element_is_open = 0;
252 :
253 0 : return writer;
254 : }
255 :
256 : /*------------------------------------------------------------------*/
257 :
258 : /**
259 : * suppress writing date at the top of file.
260 : */
261 0 : void mxml_suppress_date(int suppress)
262 : {
263 0 : mxml_suppress_date_flag = suppress;
264 0 : }
265 :
266 : /*------------------------------------------------------------------*/
267 :
268 : /**
269 : * open a file and write XML header
270 : */
271 0 : MXML_WRITER *mxml_open_file(const char *file_name)
272 : {
273 0 : MXML_WRITER* writer = (MXML_WRITER *)mxml_malloc(sizeof(MXML_WRITER));
274 0 : memset(writer, 0, sizeof(MXML_WRITER));
275 0 : writer->translate = 1;
276 :
277 0 : writer->fh = open(file_name, O_RDWR | O_CREAT | O_TRUNC | O_TEXT, 0644);
278 :
279 0 : if (writer->fh == -1) {
280 0 : std::string line = "";
281 0 : line += "Unable to open file \"";
282 0 : line += file_name;
283 0 : line += "\": ";
284 0 : line += toStrerror(errno);
285 0 : fprintf(stderr, "%s\n", line.c_str());
286 0 : mxml_free(writer);
287 0 : return NULL;
288 0 : }
289 :
290 : /* write XML header */
291 0 : mxml_write_line(writer, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
292 0 : if (mxml_suppress_date_flag == 0) {
293 0 : time_t now = time(NULL);
294 0 : std::string str = ctime(&now);
295 0 : str.resize(24);
296 0 : std::string line = "";
297 0 : line += "<!-- created by MXML on ";
298 0 : line += str;
299 0 : line += " -->\n";
300 0 : mxml_write_line(writer, line.c_str());
301 0 : }
302 :
303 : /* initialize stack */
304 0 : writer->level = 0;
305 0 : writer->element_is_open = 0;
306 :
307 0 : return writer;
308 : }
309 :
310 : /*------------------------------------------------------------------*/
311 :
312 : /**
313 : * convert '<' '>' '&' '"' ''' into &xx;
314 : */
315 0 : void mxml_encode(char* buf, int buf_size, const char *src, int src_len, int translate)
316 : {
317 0 : if (buf_size <= 6*src_len) {
318 0 : printf("mxml_encode: buffer size too small\n");
319 0 : exit(0);
320 : }
321 0 : char *pd = buf;
322 : const char* ps;
323 0 : for (ps = src ; *ps ; ps++) {
324 :
325 0 : if (translate) { /* tranlate "<", ">", "&", """, "'" */
326 0 : switch (*ps) {
327 0 : case '<':
328 0 : strcpy(pd, "<");
329 0 : pd += 4;
330 0 : break;
331 0 : case '>':
332 0 : strcpy(pd, ">");
333 0 : pd += 4;
334 0 : break;
335 0 : case '&':
336 0 : strcpy(pd, "&");
337 0 : pd += 5;
338 0 : break;
339 0 : case '\"':
340 0 : strcpy(pd, """);
341 0 : pd += 6;
342 0 : break;
343 0 : case '\'':
344 0 : strcpy(pd, "'");
345 0 : pd += 6;
346 0 : break;
347 0 : default:
348 0 : *pd++ = *ps;
349 : }
350 : } else {
351 0 : switch (*ps) { /* translate only special XML characters "<" and "&" */
352 0 : case '<':
353 0 : strcpy(pd, "<");
354 0 : pd += 4;
355 0 : break;
356 0 : case '&':
357 0 : strcpy(pd, "&");
358 0 : pd += 5;
359 0 : break;
360 0 : default:
361 0 : *pd++ = *ps;
362 : }
363 : }
364 : }
365 0 : *pd = 0;
366 :
367 : //printf("mxml_encode: size %d, in %d, out %d, [%s] -> [%s]\n", buf_size, src_len, (int)strlen(buf), src, buf);
368 0 : }
369 :
370 : /*------------------------------------------------------------------*/
371 :
372 : /**
373 : * reverse of mxml_encode, strip leading or trailing '"'
374 : */
375 0 : void mxml_decode(char *str)
376 : {
377 : char *p;
378 :
379 0 : p = str;
380 0 : while ((p = strchr(p, '&')) != NULL) {
381 0 : if (strncmp(p, "<", 4) == 0) {
382 0 : *(p++) = '<';
383 0 : memmove(p, p+3, strlen(p+3) + 1);
384 : }
385 0 : else if (strncmp(p, ">", 4) == 0) {
386 0 : *(p++) = '>';
387 0 : memmove(p, p+3, strlen(p+3) + 1);
388 : }
389 0 : else if (strncmp(p, "&", 5) == 0) {
390 0 : *(p++) = '&';
391 0 : memmove(p, p+4, strlen(p+4) + 1);
392 : }
393 0 : else if (strncmp(p, """, 6) == 0) {
394 0 : *(p++) = '\"';
395 0 : memmove(p, p+5, strlen(p+5) + 1);
396 : }
397 0 : else if (strncmp(p, "'", 6) == 0) {
398 0 : *(p++) = '\'';
399 0 : memmove(p, p+5, strlen(p+5) + 1);
400 : }
401 : else {
402 0 : p++; // skip unknown entity
403 : }
404 : }
405 : /* if (str[0] == '\"' && str[strlen(str)-1] == '\"') {
406 : memmove(str, str+1, strlen(str+1) + 1);
407 : str[strlen(str)-1] = 0;
408 : }*/
409 0 : }
410 :
411 : /*------------------------------------------------------------------*/
412 :
413 : /**
414 : * set translation of <,>,",',&, on/off in writer
415 : */
416 0 : int mxml_set_translate(MXML_WRITER *writer, int flag)
417 : {
418 : int old_flag;
419 :
420 0 : old_flag = writer->translate;
421 0 : writer->translate = flag;
422 0 : return old_flag;
423 : }
424 : /*------------------------------------------------------------------*/
425 :
426 : /**
427 : * start a new XML element, must be followed by mxml_end_elemnt
428 : */
429 0 : int mxml_start_element1(MXML_WRITER *writer, const char *name, int indent)
430 : {
431 : int i;
432 :
433 0 : if (writer->element_is_open) {
434 0 : mxml_write_line(writer, ">\n");
435 0 : writer->element_is_open = FALSE;
436 : }
437 :
438 0 : std::string line = "";
439 0 : if (indent)
440 0 : for (i=0 ; i<writer->level ; i++)
441 0 : line += XML_INDENT;
442 0 : line += "<";
443 :
444 0 : unsigned len = strlen(name);
445 0 : unsigned name_enc_size = len*6+10;
446 0 : char* name_enc = (char*)mxml_malloc(name_enc_size);
447 0 : mxml_encode(name_enc, name_enc_size, name, strlen(name), writer->translate);
448 0 : line += name_enc;
449 :
450 : /* put element on stack */
451 0 : if (writer->level == 0)
452 0 : writer->stack = (char **)mxml_malloc(sizeof(char *));
453 : else
454 0 : writer->stack = (char **)mxml_realloc(writer->stack, sizeof(char *)*(writer->level+1));
455 :
456 0 : writer->stack[writer->level] = name_enc;
457 0 : writer->level++;
458 0 : writer->element_is_open = TRUE;
459 0 : writer->data_was_written = FALSE;
460 :
461 0 : return mxml_write_line(writer, line.c_str()) == (int)line.length();
462 0 : }
463 :
464 : /*------------------------------------------------------------------*/
465 :
466 0 : int mxml_start_element(MXML_WRITER *writer, const char *name)
467 : {
468 0 : return mxml_start_element1(writer, name, TRUE);
469 : }
470 :
471 : /*------------------------------------------------------------------*/
472 :
473 0 : int mxml_start_element_noindent(MXML_WRITER *writer, const char *name)
474 : {
475 0 : return mxml_start_element1(writer, name, FALSE);
476 : }
477 :
478 : /*------------------------------------------------------------------*/
479 :
480 : /**
481 : * close an open XML element
482 : */
483 0 : int mxml_end_element(MXML_WRITER *writer)
484 : {
485 : int i;
486 :
487 0 : if (writer->level == 0)
488 0 : return 0;
489 :
490 0 : writer->level--;
491 :
492 0 : if (writer->element_is_open) {
493 0 : writer->element_is_open = FALSE;
494 0 : mxml_free(writer->stack[writer->level]);
495 0 : if (writer->level == 0)
496 0 : mxml_free(writer->stack);
497 0 : const char* line = "/>\n";
498 0 : return mxml_write_line(writer, line) == (int)strlen(line);
499 : }
500 :
501 0 : std::string line = "";
502 0 : if (!writer->data_was_written) {
503 0 : for (i=0 ; i<writer->level ; i++)
504 0 : line += XML_INDENT;
505 : }
506 :
507 0 : line += "</";
508 0 : line += writer->stack[writer->level];
509 0 : mxml_free(writer->stack[writer->level]);
510 0 : if (writer->level == 0)
511 0 : mxml_free(writer->stack);
512 0 : line += ">\n";
513 0 : writer->data_was_written = FALSE;
514 :
515 0 : return mxml_write_line(writer, line.c_str()) == (int)line.length();
516 0 : }
517 :
518 : /*------------------------------------------------------------------*/
519 :
520 : /**
521 : * write an attribute to the currently open XML element
522 : */
523 0 : int mxml_write_attribute(MXML_WRITER *writer, const char *name, const char *value)
524 : {
525 : char buf[6*4096+10];
526 :
527 0 : if (!writer->element_is_open)
528 0 : return FALSE;
529 :
530 0 : std::string line = "";
531 0 : line += " ";
532 0 : mxml_encode(buf, sizeof(buf), name, strlen(name), writer->translate);
533 0 : line += buf;
534 0 : line += "=\"";
535 0 : mxml_encode(buf, sizeof(buf), value, strlen(value), writer->translate);
536 0 : line += buf;
537 0 : line += "\"";
538 :
539 0 : return mxml_write_line(writer, line.c_str()) == (int)line.length();
540 0 : }
541 :
542 : /*------------------------------------------------------------------*/
543 :
544 : /**
545 : * write value of an XML element, like <[name]>[value]</[name]>
546 : */
547 0 : int mxml_write_value(MXML_WRITER *writer, const char *data)
548 : {
549 0 : if (!writer->element_is_open)
550 0 : return FALSE;
551 :
552 0 : if (mxml_write_line(writer, ">") != 1)
553 0 : return FALSE;
554 0 : writer->element_is_open = FALSE;
555 0 : writer->data_was_written = TRUE;
556 :
557 0 : unsigned len = strlen(data);
558 0 : unsigned size = 6*len + 1000;
559 0 : char* buf = (char*)mxml_malloc(size);
560 0 : memcpy(buf, data, len+1);
561 0 : mxml_encode(buf, size, data, len, writer->translate);
562 0 : int v = mxml_write_line(writer, buf) == (int)strlen(buf);
563 0 : mxml_free(buf);
564 0 : return v;
565 : }
566 :
567 : /*------------------------------------------------------------------*/
568 :
569 : /**
570 : * write empty line
571 : */
572 0 : int mxml_write_empty_line(MXML_WRITER *writer)
573 : {
574 0 : if (writer->element_is_open) {
575 0 : mxml_write_line(writer, ">\n");
576 0 : writer->element_is_open = FALSE;
577 : }
578 :
579 0 : if (mxml_write_line(writer, "\n") != 1)
580 0 : return FALSE;
581 :
582 0 : return TRUE;
583 : }
584 :
585 : /*------------------------------------------------------------------*/
586 :
587 : /**
588 : * write a comment to an XML file, enclosed in "<!--" and "-->"
589 : */
590 0 : int mxml_write_comment(MXML_WRITER *writer, const char *string)
591 : {
592 : int i;
593 :
594 0 : if (writer->element_is_open) {
595 0 : mxml_write_line(writer, ">\n");
596 0 : writer->element_is_open = FALSE;
597 : }
598 :
599 0 : std::string line = "";
600 0 : for (i=0 ; i<writer->level ; i++)
601 0 : line += XML_INDENT;
602 :
603 0 : line += "<!-- ";
604 0 : line += string;
605 0 : line += " -->\n";
606 0 : if (mxml_write_line(writer, line.c_str()) != (int)line.length())
607 0 : return FALSE;
608 :
609 0 : return TRUE;
610 0 : }
611 :
612 : /*------------------------------------------------------------------*/
613 :
614 : /**
615 : * shortcut to write an element with a value but without attribute
616 : */
617 0 : int mxml_write_element(MXML_WRITER *writer, const char *name, const char *value)
618 : {
619 : int i;
620 :
621 0 : i = mxml_start_element(writer, name);
622 0 : i += mxml_write_value(writer, value);
623 0 : i += mxml_end_element(writer);
624 0 : return i;
625 : }
626 :
627 : /*------------------------------------------------------------------*/
628 :
629 : /**
630 : * close a file opened with mxml_open_writer
631 : */
632 0 : char *mxml_close_buffer(MXML_WRITER *writer)
633 : {
634 : int i;
635 : char *p;
636 :
637 0 : if (writer->element_is_open) {
638 0 : writer->element_is_open = FALSE;
639 0 : if (mxml_write_line(writer, ">\n") != 2)
640 0 : return NULL;
641 : }
642 :
643 : /* close remaining open levels */
644 0 : for (i = 0 ; i<writer->level ; i++)
645 0 : mxml_end_element(writer);
646 :
647 0 : p = writer->buffer;
648 0 : mxml_free(writer);
649 0 : return p;
650 : }
651 :
652 : /*------------------------------------------------------------------*/
653 :
654 : /**
655 : * close a file opened with mxml_open_writer
656 : */
657 0 : int mxml_close_file(MXML_WRITER *writer)
658 : {
659 : int i;
660 :
661 0 : if (writer->element_is_open) {
662 0 : writer->element_is_open = FALSE;
663 0 : if (mxml_write_line(writer, ">\n") != 2)
664 0 : return 0;
665 : }
666 :
667 : /* close remaining open levels */
668 0 : for (i = 0 ; i<writer->level ; i++)
669 0 : mxml_end_element(writer);
670 :
671 0 : close(writer->fh);
672 0 : mxml_free(writer);
673 0 : return 1;
674 : }
675 :
676 : /*------------------------------------------------------------------*/
677 :
678 : /**
679 : * create root node of an XML tree
680 : */
681 0 : PMXML_NODE mxml_create_root_node(void)
682 : {
683 : PMXML_NODE root;
684 :
685 0 : root = (PMXML_NODE)calloc(1, sizeof(MXML_NODE));
686 0 : strcpy(root->name, "root"); // SAFE
687 0 : root->node_type = DOCUMENT_NODE;
688 :
689 0 : return root;
690 : }
691 :
692 : /*------------------------------------------------------------------*/
693 :
694 : /**
695 : * add a subnode (child) to an existing parent node as a specific position
696 : */
697 0 : PMXML_NODE mxml_add_special_node_at(PMXML_NODE parent, int node_type, const char *node_name, const char *value, int idx)
698 : {
699 : PMXML_NODE pnode, pchild;
700 : int i, j;
701 :
702 0 : assert(parent);
703 0 : if (parent->n_children == 0)
704 0 : parent->child = (PMXML_NODE)mxml_malloc(sizeof(MXML_NODE));
705 : else
706 0 : parent->child = (PMXML_NODE)mxml_realloc(parent->child, sizeof(MXML_NODE)*(parent->n_children+1));
707 0 : assert(parent->child);
708 :
709 : /* move following nodes one down */
710 0 : if (idx < parent->n_children)
711 0 : for (i=parent->n_children ; i > idx ; i--)
712 0 : memcpy(&parent->child[i], &parent->child[i-1], sizeof(MXML_NODE));
713 :
714 : /* correct parent pointer for children */
715 0 : for (i=0 ; i<parent->n_children ; i++) {
716 0 : pchild = parent->child+i;
717 0 : for (j=0 ; j<pchild->n_children ; j++)
718 0 : pchild->child[j].parent = pchild;
719 : }
720 :
721 : /* initialize new node */
722 0 : pnode = &parent->child[idx];
723 0 : memset(pnode, 0, sizeof(MXML_NODE));
724 0 : mxml_strlcpy(pnode->name, node_name, sizeof(pnode->name));
725 0 : pnode->node_type = node_type;
726 0 : pnode->parent = parent;
727 :
728 0 : parent->n_children++;
729 :
730 0 : if (value && *value) {
731 0 : int len = strlen(value);
732 0 : pnode->value = (char *)mxml_malloc(len+1);
733 0 : assert(pnode->value);
734 0 : memcpy(pnode->value, value, len+1);
735 : }
736 :
737 0 : return pnode;
738 : }
739 :
740 : /*------------------------------------------------------------------*/
741 :
742 : /**
743 : * add a subnode (child) to an existing parent node at the end
744 : */
745 0 : PMXML_NODE mxml_add_special_node(PMXML_NODE parent, int node_type, const char *node_name, const char *value)
746 : {
747 0 : return mxml_add_special_node_at(parent, node_type, node_name, value, parent->n_children);
748 : }
749 :
750 : /*------------------------------------------------------------------*/
751 :
752 : /**
753 : * write value of an XML element, like <[name]>[value]</[name]>
754 : */
755 0 : PMXML_NODE mxml_add_node(PMXML_NODE parent, const char *node_name, const char *value)
756 : {
757 0 : return mxml_add_special_node_at(parent, ELEMENT_NODE, node_name, value, parent->n_children);
758 : }
759 :
760 : /*------------------------------------------------------------------*/
761 :
762 : /**
763 : * add a subnode (child) to an existing parent node at the end
764 : */
765 0 : PMXML_NODE mxml_add_node_at(PMXML_NODE parent, const char *node_name, const char *value, int idx)
766 : {
767 0 : return mxml_add_special_node_at(parent, ELEMENT_NODE, node_name, value, idx);
768 : }
769 :
770 : /*------------------------------------------------------------------*/
771 :
772 : /**
773 : * add a whole node tree to an existing parent node at a specific position
774 : */
775 0 : int mxml_add_tree_at(PMXML_NODE parent, PMXML_NODE tree, int idx)
776 : {
777 : PMXML_NODE pchild;
778 : int i, j, k;
779 :
780 0 : assert(parent);
781 0 : assert(tree);
782 0 : if (parent->n_children == 0)
783 0 : parent->child = (PMXML_NODE)mxml_malloc(sizeof(MXML_NODE));
784 : else {
785 0 : pchild = parent->child;
786 0 : parent->child = (PMXML_NODE)mxml_realloc(parent->child, sizeof(MXML_NODE)*(parent->n_children+1));
787 :
788 0 : if (parent->child != pchild) {
789 : /* correct parent pointer for children */
790 0 : for (i=0 ; i<parent->n_children ; i++) {
791 0 : pchild = parent->child+i;
792 0 : for (j=0 ; j<pchild->n_children ; j++)
793 0 : pchild->child[j].parent = pchild;
794 : }
795 : }
796 : }
797 0 : assert(parent->child);
798 :
799 0 : if (idx < parent->n_children)
800 0 : for (i=parent->n_children ; i > idx ; i--) {
801 : /* move following nodes one down */
802 0 : memcpy(&parent->child[i], &parent->child[i-1], sizeof(MXML_NODE));
803 :
804 : /* correct parent pointer for children */
805 0 : for (j=0 ; j<parent->n_children ; j++) {
806 0 : pchild = parent->child+j;
807 0 : for (k=0 ; k<pchild->n_children ; k++)
808 0 : pchild->child[k].parent = pchild;
809 : }
810 : }
811 :
812 : /* initialize new node */
813 0 : memcpy(parent->child+idx, tree, sizeof(MXML_NODE));
814 0 : parent->n_children++;
815 0 : parent->child[idx].parent = parent;
816 :
817 : /* correct parent pointer for children */
818 0 : for (i=0 ; i<parent->n_children ; i++) {
819 0 : pchild = parent->child+i;
820 0 : for (j=0 ; j<pchild->n_children ; j++)
821 0 : pchild->child[j].parent = pchild;
822 : }
823 :
824 0 : return TRUE;
825 : }
826 :
827 : /*------------------------------------------------------------------*/
828 :
829 : /**
830 : * add a whole node tree to an existing parent node at the end
831 : */
832 0 : int mxml_add_tree(PMXML_NODE parent, PMXML_NODE tree)
833 : {
834 0 : return mxml_add_tree_at(parent, tree, parent->n_children);
835 : }
836 :
837 : /*------------------------------------------------------------------*/
838 :
839 : /**
840 : * add an attribute to an existing node
841 : */
842 0 : int mxml_add_attribute(PMXML_NODE pnode, const char *attrib_name, const char *attrib_value)
843 : {
844 0 : if (pnode->n_attributes == 0) {
845 0 : pnode->attribute_name = (char*)mxml_malloc(MXML_NAME_LENGTH);
846 0 : pnode->attribute_value = (char**)mxml_malloc(sizeof(char *));
847 : } else {
848 0 : pnode->attribute_name = (char*)mxml_realloc(pnode->attribute_name, MXML_NAME_LENGTH*(pnode->n_attributes+1));
849 0 : pnode->attribute_value = (char**)mxml_realloc(pnode->attribute_value, sizeof(char *)*(pnode->n_attributes+1));
850 : }
851 :
852 0 : mxml_strlcpy(pnode->attribute_name+pnode->n_attributes*MXML_NAME_LENGTH, attrib_name, MXML_NAME_LENGTH);
853 0 : int len = strlen(attrib_value);
854 0 : pnode->attribute_value[pnode->n_attributes] = (char *)mxml_malloc(len+1);
855 0 : assert(pnode->attribute_value[pnode->n_attributes] != NULL);
856 0 : memcpy(pnode->attribute_value[pnode->n_attributes], attrib_value, len+1);
857 0 : pnode->n_attributes++;
858 :
859 0 : return TRUE;
860 : }
861 :
862 : /*------------------------------------------------------------------*/
863 :
864 : /**
865 : * return number of subnodes (children) of a node
866 : */
867 0 : int mxml_get_number_of_children(PMXML_NODE pnode)
868 : {
869 0 : assert(pnode);
870 0 : return pnode->n_children;
871 : }
872 :
873 : /*------------------------------------------------------------------*/
874 :
875 : /**
876 : * return number of subnodes (children) of a node
877 : */
878 0 : PMXML_NODE mxml_subnode(PMXML_NODE pnode, int idx)
879 : {
880 0 : assert(pnode);
881 0 : if (idx < pnode->n_children)
882 0 : return &pnode->child[idx];
883 0 : return NULL;
884 : }
885 :
886 : /*------------------------------------------------------------------*/
887 :
888 :
889 : int mxml_find_nodes1(PMXML_NODE tree, const char *xml_path, PMXML_NODE **nodelist, int *found);
890 :
891 0 : int mxml_add_resultnode(PMXML_NODE node, const char *xml_path, PMXML_NODE **nodelist, int *found)
892 : {
893 : /* if at end of path, add this node */
894 0 : if (*xml_path == 0) {
895 0 : if (*found == 0)
896 0 : *nodelist = (PMXML_NODE *)mxml_malloc(sizeof(PMXML_NODE));
897 : else
898 0 : *nodelist = (PMXML_NODE *)mxml_realloc(*nodelist, sizeof(PMXML_NODE)*(*found + 1));
899 :
900 0 : (*nodelist)[*found] = node;
901 0 : (*found)++;
902 : } else {
903 : /* if not at end of path, branch into subtree */
904 0 : return mxml_find_nodes1(node, xml_path+1, nodelist, found);
905 : }
906 :
907 0 : return 1;
908 : }
909 :
910 : /*------------------------------------------------------------------*/
911 :
912 : /**
913 : Return list of XML nodes with a subset of XPATH specifications.
914 : Following elemets are possible
915 :
916 : /<node>/<node>/..../<node> Find a node in the tree hierarchy
917 : /<node>[idx] Find child #[idx] of node (index starts from 1)
918 : /<node>[idx]/<node> Find subnode of the above
919 : /<node>[<subnode>=<value>] Find a node which has a specific subnode
920 : /<node>[<subnode>=<value>]/<node> Find subnode of the above
921 : /<node>[@<attrib>=<value>]/<node> Find a node which has a specific attribute
922 : */
923 0 : int mxml_find_nodes1(PMXML_NODE tree, const char *xml_path, PMXML_NODE **nodelist, int *found)
924 : {
925 : PMXML_NODE pnode;
926 : const char *p1,*p2;
927 : char *p3, node_name[256], condition[256];
928 : char cond_name[MXML_MAX_CONDITION][256], cond_value[MXML_MAX_CONDITION][256];
929 : int cond_type[MXML_MAX_CONDITION];
930 : int i, j, k, idx, num_cond;
931 : int cond_satisfied,cond_index;
932 : size_t len;
933 :
934 0 : p1 = xml_path;
935 0 : pnode = tree;
936 :
937 : /* skip leading '/' */
938 0 : if (*p1 && *p1 == '/')
939 0 : p1++;
940 :
941 : do {
942 0 : p2 = p1;
943 0 : while (*p2 && *p2 != '/' && *p2 != '[')
944 0 : p2++;
945 0 : len = (size_t)p2 - (size_t)p1;
946 0 : if (len >= sizeof(node_name))
947 0 : return 0;
948 :
949 0 : memcpy(node_name, p1, len);
950 0 : node_name[len] = 0;
951 0 : idx = 0;
952 0 : num_cond = 0;
953 0 : while (*p2 == '[') {
954 0 : cond_name[num_cond][0] = cond_value[num_cond][0] = cond_type[num_cond] = 0;
955 0 : p2++;
956 0 : if (isdigit(*p2)) {
957 : /* evaluate [idx] */
958 0 : idx = atoi(p2);
959 0 : p2 = strchr(p2, ']');
960 0 : if (p2 == NULL)
961 0 : return 0;
962 0 : p2++;
963 : } else {
964 : /* evaluate [<@attrib>/<subnode>=<value>] */
965 0 : while (*p2 && isspace((unsigned char)*p2))
966 0 : p2++;
967 0 : mxml_strlcpy(condition, p2, sizeof(condition));
968 0 : if (strchr(condition, ']'))
969 0 : *strchr(condition, ']') = 0;
970 : else
971 0 : return 0;
972 0 : p2 = strchr(p2, ']')+1;
973 0 : if ((p3 = strchr(condition, '=')) != NULL) {
974 0 : if (condition[0] == '@') {
975 0 : cond_type[num_cond] = 1;
976 0 : mxml_strlcpy(cond_name[num_cond], &condition[1], sizeof(cond_name[num_cond]));
977 : } else {
978 0 : mxml_strlcpy(cond_name[num_cond], condition, sizeof(cond_name[num_cond]));
979 : }
980 :
981 0 : *strchr(cond_name[num_cond], '=') = 0;
982 0 : while (cond_name[num_cond][0] && isspace(cond_name[num_cond][strlen(cond_name[num_cond])-1]))
983 0 : cond_name[num_cond][strlen(cond_name[num_cond])-1] = 0;
984 :
985 0 : p3++;
986 0 : while (*p3 && isspace(*p3))
987 0 : p3++;
988 0 : if (*p3 == '\"') {
989 0 : mxml_strlcpy(cond_value[num_cond], p3+1, sizeof(cond_value[num_cond]));
990 0 : while (cond_value[num_cond][0] && isspace(cond_value[num_cond][strlen(cond_value[num_cond])-1]))
991 0 : cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0;
992 0 : if (cond_value[num_cond][0] && cond_value[num_cond][strlen(cond_value[num_cond])-1] == '\"')
993 0 : cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0;
994 0 : } else if (*p3 == '\'') {
995 0 : mxml_strlcpy(cond_value[num_cond], p3+1, sizeof(cond_value[num_cond]));
996 0 : while (cond_value[num_cond][0] && isspace(cond_value[num_cond][strlen(cond_value[num_cond])-1]))
997 0 : cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0;
998 0 : if (cond_value[num_cond][0] && cond_value[num_cond][strlen(cond_value[num_cond])-1] == '\'')
999 0 : cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0;
1000 : } else {
1001 0 : mxml_strlcpy(cond_value[num_cond], p3, sizeof(cond_value[num_cond]));
1002 0 : while (cond_value[num_cond][0] && isspace(cond_value[num_cond][strlen(cond_value[num_cond])-1]))
1003 0 : cond_value[num_cond][strlen(cond_value[num_cond])-1] = 0;
1004 : }
1005 0 : num_cond++;
1006 : }
1007 : }
1008 : }
1009 :
1010 0 : cond_index = 0;
1011 0 : for (i=j=0 ; i<pnode->n_children ; i++) {
1012 0 : if (num_cond) {
1013 0 : cond_satisfied = 0;
1014 0 : for (k=0;k<num_cond;k++) {
1015 0 : if (cond_type[k]) {
1016 : /* search node with attribute */
1017 0 : if (strcmp(pnode->child[i].name, node_name) == 0)
1018 0 : if (mxml_get_attribute(pnode->child+i, cond_name[k]) &&
1019 0 : strcmp(mxml_get_attribute(pnode->child+i, cond_name[k]), cond_value[k]) == 0)
1020 0 : cond_satisfied++;
1021 : }
1022 : else {
1023 : /* search subnode */
1024 0 : for (j=0 ; j<pnode->child[i].n_children ; j++)
1025 0 : if (strcmp(pnode->child[i].child[j].name, cond_name[k]) == 0)
1026 0 : if (strcmp(pnode->child[i].child[j].value, cond_value[k]) == 0)
1027 0 : cond_satisfied++;
1028 : }
1029 : }
1030 0 : if (cond_satisfied==num_cond) {
1031 0 : cond_index++;
1032 0 : if (idx == 0 || cond_index == idx) {
1033 0 : if (!mxml_add_resultnode(pnode->child+i, p2, nodelist, found))
1034 0 : return 0;
1035 : }
1036 : }
1037 : } else {
1038 0 : if (strcmp(pnode->child[i].name, node_name) == 0)
1039 0 : if (idx == 0 || ++j == idx)
1040 0 : if (!mxml_add_resultnode(pnode->child+i, p2, nodelist, found))
1041 0 : return 0;
1042 : }
1043 : }
1044 :
1045 0 : if (i == pnode->n_children)
1046 0 : return 1;
1047 :
1048 0 : pnode = &pnode->child[i];
1049 0 : p1 = p2;
1050 0 : if (*p1 == '/')
1051 0 : p1++;
1052 :
1053 0 : } while (*p2);
1054 :
1055 0 : return 1;
1056 : }
1057 :
1058 : /*------------------------------------------------------------------*/
1059 :
1060 0 : int mxml_find_nodes(PMXML_NODE tree, const char *xml_path, PMXML_NODE **nodelist)
1061 : {
1062 0 : int status, found = 0;
1063 :
1064 0 : status = mxml_find_nodes1(tree, xml_path, nodelist, &found);
1065 :
1066 0 : if (status == 0)
1067 0 : return -1;
1068 :
1069 0 : return found;
1070 : }
1071 :
1072 : /*------------------------------------------------------------------*/
1073 :
1074 : /**
1075 : * Search for a specific XML node with a subset of XPATH specifications.
1076 : * Return first found node. For syntax see mxml_find_nodes()
1077 : */
1078 0 : PMXML_NODE mxml_find_node(PMXML_NODE tree, const char *xml_path)
1079 : {
1080 : PMXML_NODE *node, pnode;
1081 : int n;
1082 :
1083 0 : n = mxml_find_nodes(tree, xml_path, &node);
1084 0 : if (n > 0) {
1085 0 : pnode = node[0];
1086 0 : mxml_free(node);
1087 : } else
1088 0 : pnode = NULL;
1089 :
1090 0 : return pnode;
1091 : }
1092 :
1093 : /*------------------------------------------------------------------*/
1094 :
1095 0 : PMXML_NODE mxml_get_parent(PMXML_NODE pnode)
1096 : {
1097 0 : assert(pnode);
1098 0 : return pnode->parent;
1099 : }
1100 :
1101 : /*------------------------------------------------------------------*/
1102 :
1103 0 : char *mxml_get_name(PMXML_NODE pnode)
1104 : {
1105 0 : assert(pnode);
1106 0 : return pnode->name;
1107 : }
1108 :
1109 : /*------------------------------------------------------------------*/
1110 :
1111 0 : char *mxml_get_value(PMXML_NODE pnode)
1112 : {
1113 0 : assert(pnode);
1114 0 : return pnode->value;
1115 : }
1116 :
1117 : /*------------------------------------------------------------------*/
1118 :
1119 0 : int mxml_get_line_number_start(PMXML_NODE pnode)
1120 : {
1121 0 : assert(pnode);
1122 0 : return pnode->line_number_start;
1123 : }
1124 :
1125 : /*------------------------------------------------------------------*/
1126 :
1127 0 : int mxml_get_line_number_end(PMXML_NODE pnode)
1128 : {
1129 0 : assert(pnode);
1130 0 : return pnode->line_number_end;
1131 : }
1132 :
1133 : /*------------------------------------------------------------------*/
1134 :
1135 0 : char *mxml_get_attribute(PMXML_NODE pnode, const char *name)
1136 : {
1137 : int i;
1138 :
1139 0 : assert(pnode);
1140 0 : for (i=0 ; i<pnode->n_attributes ; i++)
1141 0 : if (strcmp(pnode->attribute_name+i*MXML_NAME_LENGTH, name) == 0)
1142 0 : return pnode->attribute_value[i];
1143 :
1144 0 : return NULL;
1145 : }
1146 :
1147 : /*------------------------------------------------------------------*/
1148 :
1149 0 : int mxml_replace_node_name(PMXML_NODE pnode, const char *name)
1150 : {
1151 0 : mxml_strlcpy(pnode->name, name, sizeof(pnode->name));
1152 0 : return TRUE;
1153 : }
1154 :
1155 : /*------------------------------------------------------------------*/
1156 :
1157 0 : int mxml_replace_node_value(PMXML_NODE pnode, const char *value)
1158 : {
1159 0 : int len = strlen(value);
1160 0 : if (pnode->value)
1161 0 : pnode->value = (char *)mxml_realloc(pnode->value, len+1);
1162 0 : else if (value)
1163 0 : pnode->value = (char *)mxml_malloc(len+1);
1164 : else
1165 0 : pnode->value = NULL;
1166 :
1167 0 : if (value)
1168 0 : memcpy(pnode->value, value, len+1);
1169 :
1170 0 : return TRUE;
1171 : }
1172 :
1173 : /*------------------------------------------------------------------*/
1174 :
1175 : /**
1176 : replace value os a subnode, like
1177 :
1178 : <parent>
1179 : <child>value</child>
1180 : </parent>
1181 :
1182 : if pnode=parent, and "name"="child", then "value" gets replaced
1183 : */
1184 0 : int mxml_replace_subvalue(PMXML_NODE pnode, const char *name, const char *value)
1185 : {
1186 : int i;
1187 :
1188 0 : for (i=0 ; i<pnode->n_children ; i++)
1189 0 : if (strcmp(pnode->child[i].name, name) == 0)
1190 0 : break;
1191 :
1192 0 : if (i == pnode->n_children)
1193 0 : return FALSE;
1194 :
1195 0 : return mxml_replace_node_value(&pnode->child[i], value);
1196 : }
1197 :
1198 : /*------------------------------------------------------------------*/
1199 :
1200 : /**
1201 : * change the name of an attribute, keep its value
1202 : */
1203 0 : int mxml_replace_attribute_name(PMXML_NODE pnode, const char *old_name, const char *new_name)
1204 : {
1205 : int i;
1206 :
1207 0 : for (i=0 ; i<pnode->n_attributes ; i++)
1208 0 : if (strcmp(pnode->attribute_name+i*MXML_NAME_LENGTH, old_name) == 0)
1209 0 : break;
1210 :
1211 0 : if (i == pnode->n_attributes)
1212 0 : return FALSE;
1213 :
1214 0 : mxml_strlcpy(pnode->attribute_name+i*MXML_NAME_LENGTH, new_name, MXML_NAME_LENGTH);
1215 0 : return TRUE;
1216 : }
1217 :
1218 : /*------------------------------------------------------------------*/
1219 :
1220 : /**
1221 : * change the value of an attribute
1222 : */
1223 0 : int mxml_replace_attribute_value(PMXML_NODE pnode, const char *attrib_name, const char *attrib_value)
1224 : {
1225 : int i;
1226 :
1227 0 : for (i=0 ; i<pnode->n_attributes ; i++)
1228 0 : if (strcmp(pnode->attribute_name+i*MXML_NAME_LENGTH, attrib_name) == 0)
1229 0 : break;
1230 :
1231 0 : if (i == pnode->n_attributes)
1232 0 : return FALSE;
1233 :
1234 0 : int len = strlen(attrib_value);
1235 0 : pnode->attribute_value[i] = (char *)mxml_realloc(pnode->attribute_value[i], len+1);
1236 0 : memcpy(pnode->attribute_value[i], attrib_value, len+1);
1237 0 : return TRUE;
1238 : }
1239 :
1240 : /*------------------------------------------------------------------*/
1241 :
1242 : /**
1243 : * free memory of a node and remove it from the parent's child list
1244 : */
1245 0 : int mxml_delete_node(PMXML_NODE pnode)
1246 : {
1247 : PMXML_NODE parent;
1248 : int i, j;
1249 :
1250 : /* remove node from parent's list */
1251 0 : parent = pnode->parent;
1252 :
1253 0 : if (parent) {
1254 0 : for (i=0 ; i<parent->n_children ; i++)
1255 0 : if (&parent->child[i] == pnode)
1256 0 : break;
1257 :
1258 : /* free allocated node memory recursively */
1259 0 : mxml_free_tree(pnode);
1260 :
1261 0 : if (i < parent->n_children) {
1262 0 : for (j=i ; j<parent->n_children-1 ; j++)
1263 0 : memcpy(&parent->child[j], &parent->child[j+1], sizeof(MXML_NODE));
1264 0 : parent->n_children--;
1265 0 : if (parent->n_children)
1266 0 : parent->child = (PMXML_NODE)mxml_realloc(parent->child, sizeof(MXML_NODE)*(parent->n_children));
1267 : else
1268 0 : mxml_free(parent->child);
1269 : }
1270 : } else
1271 0 : mxml_free_tree(pnode);
1272 :
1273 0 : return TRUE;
1274 : }
1275 :
1276 : /*------------------------------------------------------------------*/
1277 :
1278 0 : int mxml_delete_attribute(PMXML_NODE pnode, const char *attrib_name)
1279 : {
1280 : int i, j;
1281 :
1282 0 : for (i=0 ; i<pnode->n_attributes ; i++)
1283 0 : if (strcmp(pnode->attribute_name+i*MXML_NAME_LENGTH, attrib_name) == 0)
1284 0 : break;
1285 :
1286 0 : if (i == pnode->n_attributes)
1287 0 : return FALSE;
1288 :
1289 0 : mxml_free(pnode->attribute_value[i]);
1290 0 : for (j=i ; j<pnode->n_attributes-1 ; j++) {
1291 0 : mxml_strlcpy(pnode->attribute_name+j*MXML_NAME_LENGTH, pnode->attribute_name+(j+1)*MXML_NAME_LENGTH, MXML_NAME_LENGTH);
1292 0 : pnode->attribute_value[j] = pnode->attribute_value[j+1];
1293 : }
1294 :
1295 0 : if (pnode->n_attributes > 0) {
1296 0 : pnode->attribute_name = (char *)mxml_realloc(pnode->attribute_name, MXML_NAME_LENGTH*(pnode->n_attributes-1));
1297 0 : pnode->attribute_value = (char **)mxml_realloc(pnode->attribute_value, sizeof(char *)*(pnode->n_attributes-1));
1298 : } else {
1299 0 : mxml_free(pnode->attribute_name);
1300 0 : mxml_free(pnode->attribute_value);
1301 : }
1302 :
1303 0 : return TRUE;
1304 : }
1305 :
1306 : /*------------------------------------------------------------------*/
1307 :
1308 : #define HERE root, file_name, line_number, error, error_size, error_line
1309 :
1310 : /**
1311 : * used inside mxml_parse_file for reporting errors
1312 : */
1313 0 : PMXML_NODE read_error(PMXML_NODE root, const char *file_name, int line_number, char *error, int error_size, int *error_line, const char *format, ...)
1314 : {
1315 0 : std::string msg;
1316 0 : if (file_name && file_name[0]) {
1317 0 : msg += "XML read error in file \"";
1318 0 : msg += file_name;
1319 0 : msg += "\", line ";
1320 0 : msg += toString(line_number);
1321 0 : msg += ": ";
1322 : } else {
1323 0 : msg += "XML read error, line ";
1324 0 : msg += toString(line_number);
1325 0 : msg += ": ";
1326 : }
1327 :
1328 : char str[1000];
1329 : va_list argptr;
1330 0 : va_start(argptr, format);
1331 0 : vsnprintf(str, sizeof(str), (char *) format, argptr);
1332 0 : va_end(argptr);
1333 :
1334 0 : msg += str;
1335 :
1336 0 : if (error) {
1337 0 : mxml_strlcpy(error, msg.c_str(), error_size);
1338 : }
1339 0 : if (error_line) {
1340 0 : *error_line = line_number;
1341 : }
1342 :
1343 0 : mxml_free_tree(root);
1344 :
1345 0 : return NULL;
1346 0 : }
1347 :
1348 : /*------------------------------------------------------------------*/
1349 :
1350 : /**
1351 : * Parse a XML buffer and convert it into a tree of MXML_NODE's.
1352 : * Return NULL in case of an error, return error description.
1353 : * Optional file_name is used for error reporting if called from mxml_parse_file()
1354 : */
1355 0 : PMXML_NODE mxml_parse_buffer(const char *buf, char *error, int error_size, int *error_line)
1356 : {
1357 : char node_name[256], attrib_name[256], attrib_value[1000], quote;
1358 : const char *p, *pv;
1359 : int i,j, line_number;
1360 : PMXML_NODE root, ptree, pnew;
1361 : int end_element;
1362 : size_t len;
1363 0 : char *file_name = NULL; /* dummy for 'HERE' */
1364 :
1365 0 : p = buf;
1366 0 : line_number = 1;
1367 :
1368 0 : root = mxml_create_root_node();
1369 0 : ptree = root;
1370 :
1371 : /* parse file contents */
1372 : do {
1373 0 : if (*p == '<') {
1374 :
1375 0 : end_element = FALSE;
1376 :
1377 : /* found new element */
1378 0 : p++;
1379 0 : while (*p && isspace(*p)) {
1380 0 : if (*p == '\n')
1381 0 : line_number++;
1382 0 : p++;
1383 : }
1384 0 : if (!*p)
1385 0 : return read_error(HERE, "Unexpected end of file");
1386 :
1387 0 : if (strncmp(p, "!--", 3) == 0) {
1388 :
1389 : /* found comment */
1390 :
1391 0 : pnew = mxml_add_special_node(ptree, COMMENT_NODE, "Comment", NULL);
1392 0 : pnew->line_number_start = line_number;
1393 0 : pv = p+3;
1394 0 : while (*pv == ' ')
1395 0 : pv++;
1396 :
1397 0 : p += 3;
1398 0 : if (strstr(p, "-->") == NULL)
1399 0 : return read_error(HERE, "Unterminated comment");
1400 :
1401 0 : while (strncmp(p, "-->", 3) != 0) {
1402 0 : if (*p == '\n')
1403 0 : line_number++;
1404 0 : p++;
1405 : }
1406 :
1407 0 : len = (size_t)p - (size_t)pv;
1408 0 : pnew->value = (char *)mxml_malloc(len+1);
1409 0 : memcpy(pnew->value, pv, len);
1410 0 : pnew->value[len] = 0;
1411 0 : pnew->line_number_end = line_number;
1412 0 : mxml_decode(pnew->value);
1413 :
1414 0 : p += 3;
1415 :
1416 0 : } else if (*p == '?') {
1417 :
1418 : /* found ?...? element */
1419 0 : pnew = mxml_add_special_node(ptree, PROCESSING_INSTRUCTION_NODE, "PI", NULL);
1420 0 : pnew->line_number_start = line_number;
1421 0 : pv = p+1;
1422 :
1423 0 : p++;
1424 0 : if (strstr(p, "?>") == NULL)
1425 0 : return read_error(HERE, "Unterminated ?...? element");
1426 :
1427 0 : while (strncmp(p, "?>", 2) != 0) {
1428 0 : if (*p == '\n')
1429 0 : line_number++;
1430 0 : p++;
1431 : }
1432 :
1433 0 : len = (size_t)p - (size_t)pv;
1434 0 : pnew->value = (char *)mxml_malloc(len+1);
1435 0 : memcpy(pnew->value, pv, len);
1436 0 : pnew->value[len] = 0;
1437 0 : pnew->line_number_end = line_number;
1438 0 : mxml_decode(pnew->value);
1439 :
1440 0 : p += 2;
1441 :
1442 0 : } else if (strncmp(p, "!DOCTYPE", 8) == 0 ) {
1443 :
1444 : /* found !DOCTYPE element , skip it */
1445 0 : p += 8;
1446 0 : if (strstr(p, ">") == NULL)
1447 0 : return read_error(HERE, "Unterminated !DOCTYPE element");
1448 :
1449 0 : j = 0;
1450 0 : while (*p && (*p != '>' || j > 0)) {
1451 0 : if (*p == '\n')
1452 0 : line_number++;
1453 0 : else if (*p == '<')
1454 0 : j++;
1455 0 : else if (*p == '>')
1456 0 : j--;
1457 0 : p++;
1458 : }
1459 0 : if (!*p)
1460 0 : return read_error(HERE, "Unexpected end of file");
1461 :
1462 0 : p++;
1463 :
1464 : } else {
1465 :
1466 : /* found normal element */
1467 0 : if (*p == '/') {
1468 0 : end_element = TRUE;
1469 0 : p++;
1470 0 : while (*p && isspace((unsigned char)*p)) {
1471 0 : if (*p == '\n')
1472 0 : line_number++;
1473 0 : p++;
1474 : }
1475 0 : if (!*p)
1476 0 : return read_error(HERE, "Unexpected end of file");
1477 : }
1478 :
1479 : /* extract node name */
1480 0 : i = 0;
1481 0 : node_name[i] = 0;
1482 0 : while (*p && !isspace((unsigned char)*p) && *p != '/' && *p != '>' && *p != '<')
1483 0 : node_name[i++] = *p++;
1484 0 : node_name[i] = 0;
1485 0 : if (!*p)
1486 0 : return read_error(HERE, "Unexpected end of file");
1487 0 : if (*p == '<')
1488 0 : return read_error(HERE, "Unexpected \'<\' inside element \"%s\"", node_name);
1489 :
1490 0 : mxml_decode(node_name);
1491 :
1492 0 : if (end_element) {
1493 :
1494 0 : if (!ptree)
1495 0 : return read_error(HERE, "Found unexpected </%s>", node_name);
1496 :
1497 : /* close previously opened element */
1498 0 : if (strcmp(ptree->name, node_name) != 0)
1499 0 : return read_error(HERE, "Found </%s>, expected </%s>", node_name, ptree->name);
1500 0 : ptree->line_number_end = line_number;
1501 :
1502 : /* go up one level on the tree */
1503 0 : ptree = ptree->parent;
1504 :
1505 : } else {
1506 :
1507 0 : if (ptree == NULL)
1508 0 : return read_error(HERE, "Unexpected second top level node");
1509 :
1510 : /* allocate new element structure in parent tree */
1511 0 : pnew = mxml_add_node(ptree, node_name, NULL);
1512 0 : pnew->line_number_start = line_number;
1513 0 : pnew->line_number_end = line_number;
1514 :
1515 0 : while (*p && isspace((unsigned char)*p)) {
1516 0 : if (*p == '\n')
1517 0 : line_number++;
1518 0 : p++;
1519 : }
1520 0 : if (!*p)
1521 0 : return read_error(HERE, "Unexpected end of file");
1522 :
1523 0 : while (*p != '>' && *p != '/') {
1524 :
1525 : /* found attribute */
1526 0 : pv = p;
1527 0 : while (*pv && !isspace((unsigned char)*pv) && *pv != '=' && *pv != '<' && *pv != '>')
1528 0 : pv++;
1529 0 : if (!*pv)
1530 0 : return read_error(HERE, "Unexpected end of file");
1531 0 : if (*pv == '<' || *pv == '>')
1532 0 : return read_error(HERE, "Unexpected \'%c\' inside element \"%s\"", *pv, node_name);
1533 :
1534 : /* extract attribute name */
1535 0 : len = (size_t)pv - (size_t)p;
1536 0 : if (len > sizeof(attrib_name)-1)
1537 0 : len = sizeof(attrib_name)-1;
1538 0 : memcpy(attrib_name, p, len);
1539 0 : attrib_name[len] = 0;
1540 0 : mxml_decode(attrib_name);
1541 :
1542 0 : p = pv;
1543 0 : while (*p && isspace((unsigned char)*p)) {
1544 0 : if (*p == '\n')
1545 0 : line_number++;
1546 0 : p++;
1547 : }
1548 0 : if (!*p)
1549 0 : return read_error(HERE, "Unexpected end of file");
1550 0 : if (*p != '=')
1551 0 : return read_error(HERE, "Expect \"=\" here");
1552 :
1553 0 : p++;
1554 0 : while (*p && isspace((unsigned char)*p)) {
1555 0 : if (*p == '\n')
1556 0 : line_number++;
1557 0 : p++;
1558 : }
1559 0 : if (!*p)
1560 0 : return read_error(HERE, "Unexpected end of file");
1561 0 : if (*p != '\"' && *p != '\'')
1562 0 : return read_error(HERE, "Expect \" or \' here");
1563 0 : quote = *p;
1564 0 : p++;
1565 :
1566 : /* extract attribute value */
1567 0 : pv = p;
1568 0 : while (*pv && *pv != quote)
1569 0 : pv++;
1570 0 : if (!*pv)
1571 0 : return read_error(HERE, "Unexpected end of file");
1572 :
1573 0 : len = (size_t)pv - (size_t)p;
1574 0 : if (len > sizeof(attrib_value)-1)
1575 0 : len = sizeof(attrib_value)-1;
1576 0 : memcpy(attrib_value, p, len);
1577 0 : attrib_value[len] = 0;
1578 0 : mxml_decode(attrib_value);
1579 :
1580 : /* add attribute to current node */
1581 0 : mxml_add_attribute(pnew, attrib_name, attrib_value);
1582 :
1583 0 : p = pv+1;
1584 0 : while (*p && isspace((unsigned char)*p)) {
1585 0 : if (*p == '\n')
1586 0 : line_number++;
1587 0 : p++;
1588 : }
1589 0 : if (!*p)
1590 0 : return read_error(HERE, "Unexpected end of file");
1591 : }
1592 :
1593 0 : if (*p == '/') {
1594 :
1595 : /* found empty node, like <node/>, just skip closing bracket */
1596 0 : p++;
1597 :
1598 0 : while (*p && isspace((unsigned char)*p)) {
1599 0 : if (*p == '\n')
1600 0 : line_number++;
1601 0 : p++;
1602 : }
1603 0 : if (!*p)
1604 0 : return read_error(HERE, "Unexpected end of file");
1605 0 : if (*p != '>')
1606 0 : return read_error(HERE, "Expected \">\" after \"/\"");
1607 0 : p++;
1608 : }
1609 :
1610 0 : if (*p == '>') {
1611 :
1612 0 : p++;
1613 :
1614 : /* check if we have sub-element or value */
1615 0 : pv = p;
1616 0 : while (*pv && isspace((unsigned char)*pv)) {
1617 0 : if (*pv == '\n')
1618 0 : line_number++;
1619 0 : pv++;
1620 : }
1621 0 : if (!*pv)
1622 0 : return read_error(HERE, "Unexpected end of file");
1623 :
1624 0 : if (*pv == '<' && *(pv+1) != '/') {
1625 :
1626 : /* start new subtree */
1627 0 : ptree = pnew;
1628 0 : p = pv;
1629 :
1630 : } else {
1631 :
1632 : /* extract value */
1633 0 : while (*pv && *pv != '<') {
1634 0 : if (*pv == '\n')
1635 0 : line_number++;
1636 0 : pv++;
1637 : }
1638 0 : if (!*pv)
1639 0 : return read_error(HERE, "Unexpected end of file");
1640 :
1641 0 : len = (size_t)pv - (size_t)p;
1642 0 : pnew->value = (char *)mxml_malloc(len+1);
1643 0 : memcpy(pnew->value, p, len);
1644 0 : pnew->value[len] = 0;
1645 0 : mxml_decode(pnew->value);
1646 0 : p = pv;
1647 :
1648 0 : ptree = pnew;
1649 : }
1650 : }
1651 : }
1652 : }
1653 : }
1654 :
1655 : /* go to next element */
1656 0 : while (*p && *p != '<') {
1657 0 : if (*p == '\n')
1658 0 : line_number++;
1659 0 : p++;
1660 : }
1661 0 : } while (*p);
1662 :
1663 0 : return root;
1664 : }
1665 :
1666 : /*------------------------------------------------------------------*/
1667 :
1668 : /**
1669 : * parse !ENTYTY entries of XML files and replace with references.
1670 : * Return 0 in case of no errors, return error description.
1671 : * Optional file_name is used for error reporting if called from mxml_parse_file()
1672 : */
1673 0 : int mxml_parse_entity(char **buf, const char *file_name, char *error, int error_size, int *error_line)
1674 : {
1675 : char *p;
1676 : char *pv;
1677 : char delimiter;
1678 : int i, j, k, line_number, status;
1679 : char *replacement;
1680 : char entity_name[MXML_MAX_ENTITY][256];
1681 : char entity_reference_name[MXML_MAX_ENTITY][256];
1682 : char *entity_value[MXML_MAX_ENTITY];
1683 : int entity_type[MXML_MAX_ENTITY]; /* internal or external */
1684 : int entity_line_number[MXML_MAX_ENTITY];
1685 : int nentity;
1686 : int fh, length;
1687 : int entity_value_length[MXML_MAX_ENTITY];
1688 : int entity_name_length[MXML_MAX_ENTITY];
1689 :
1690 0 : PMXML_NODE root = mxml_create_root_node(); /* dummy for 'HERE' */
1691 :
1692 0 : for (int ip = 0; ip < MXML_MAX_ENTITY; ip++)
1693 0 : entity_value[ip] = NULL;
1694 :
1695 0 : line_number = 1;
1696 0 : nentity = -1;
1697 0 : status = 0;
1698 :
1699 0 : if (!buf || !(*buf) || !strlen(*buf))
1700 0 : return 0;
1701 :
1702 : char directoryname[FILENAME_MAX];
1703 0 : mxml_strlcpy(directoryname, file_name, FILENAME_MAX);
1704 0 : mxml_dirname(directoryname);
1705 :
1706 : /* copy string to temporary space */
1707 0 : int len = strlen(*buf);
1708 0 : char* buffer = (char *) mxml_malloc(len+1);
1709 0 : if (buffer == NULL) {
1710 0 : read_error(HERE, "Cannot allocate memory.");
1711 0 : status = 1;
1712 0 : goto error;
1713 : }
1714 0 : memcpy(buffer, *buf, len+1);
1715 :
1716 0 : p = strstr(buffer, "!DOCTYPE");
1717 0 : if (p == NULL) { /* no entities */
1718 0 : status = 0;
1719 0 : goto error;
1720 : }
1721 :
1722 0 : pv = strstr(p, "[");
1723 0 : if (pv == NULL) { /* no entities */
1724 0 : status = 1;
1725 0 : goto error;
1726 : }
1727 :
1728 0 : p = pv + 1;
1729 :
1730 : /* search !ENTITY */
1731 : do {
1732 0 : if (*p == ']')
1733 0 : break;
1734 :
1735 0 : if (*p == '<') {
1736 :
1737 : /* found new entity */
1738 0 : p++;
1739 0 : while (*p && isspace((unsigned char)*p)) {
1740 0 : if (*p == '\n')
1741 0 : line_number++;
1742 0 : p++;
1743 : }
1744 0 : if (!*p) {
1745 0 : read_error(HERE, "Unexpected end of file");
1746 0 : status = 1;
1747 0 : goto error;
1748 : }
1749 :
1750 0 : if (strncmp(p, "!--", 3) == 0) {
1751 : /* found comment */
1752 0 : p += 3;
1753 0 : if (strstr(p, "-->") == NULL) {
1754 0 : read_error(HERE, "Unterminated comment");
1755 0 : status = 1;
1756 0 : goto error;
1757 : }
1758 :
1759 0 : while (strncmp(p, "-->", 3) != 0) {
1760 0 : if (*p == '\n')
1761 0 : line_number++;
1762 0 : p++;
1763 : }
1764 0 : p += 3;
1765 : }
1766 :
1767 0 : else if (strncmp(p, "!ENTITY", 7) == 0) {
1768 : /* found entity */
1769 0 : nentity++;
1770 0 : if (nentity >= MXML_MAX_ENTITY) {
1771 0 : read_error(HERE, "Too much entities");
1772 0 : status = 1;
1773 0 : goto error;
1774 : }
1775 :
1776 0 : entity_line_number[nentity] = line_number;
1777 :
1778 0 : pv = p + 7;
1779 0 : while (*pv == ' ')
1780 0 : pv++;
1781 :
1782 : /* extract entity name */
1783 0 : p = pv;
1784 :
1785 0 : while (*p && isspace((unsigned char)*p) && *p != '<' && *p != '>') {
1786 0 : if (*p == '\n')
1787 0 : line_number++;
1788 0 : p++;
1789 : }
1790 0 : if (!*p) {
1791 0 : read_error(HERE, "Unexpected end of file");
1792 0 : status = 1;
1793 0 : goto error;
1794 : }
1795 0 : if (*p == '<' || *p == '>') {
1796 0 : read_error(HERE, "Unexpected \'%c\' inside !ENTITY", *p);
1797 0 : status = 1;
1798 0 : goto error;
1799 : }
1800 :
1801 0 : pv = p;
1802 0 : while (*pv && !isspace((unsigned char)*pv) && *pv != '<' && *pv != '>')
1803 0 : pv++;
1804 :
1805 0 : if (!*pv) {
1806 0 : read_error(HERE, "Unexpected end of file");
1807 0 : status = 1;
1808 0 : goto error;
1809 : }
1810 0 : if (*pv == '<' || *pv == '>') {
1811 0 : read_error(HERE, "Unexpected \'%c\' inside entity \"%s\"", *pv, &entity_name[nentity][1]);
1812 0 : status = 1;
1813 0 : goto error;
1814 : }
1815 :
1816 0 : entity_name[nentity][0] = '&';
1817 0 : i = 1;
1818 0 : entity_name[nentity][i] = 0;
1819 0 : while (*p && !isspace((unsigned char)*p) && *p != '/' && *p != '>' && *p != '<' && i < 253)
1820 0 : entity_name[nentity][i++] = *p++;
1821 0 : entity_name[nentity][i++] = ';';
1822 0 : entity_name[nentity][i] = 0;
1823 :
1824 0 : if (!*p) {
1825 0 : read_error(HERE, "Unexpected end of file");
1826 0 : status = 1;
1827 0 : goto error;
1828 : }
1829 0 : if (*p == '<') {
1830 0 : read_error(HERE, "Unexpected \'<\' inside entity \"%s\"", &entity_name[nentity][1]);
1831 0 : status = 1;
1832 0 : goto error;
1833 : }
1834 :
1835 : /* extract replacement or SYSTEM */
1836 0 : while (*p && isspace((unsigned char)*p)) {
1837 0 : if (*p == '\n')
1838 0 : line_number++;
1839 0 : p++;
1840 : }
1841 0 : if (!*p) {
1842 0 : read_error(HERE, "Unexpected end of file");
1843 0 : status = 1;
1844 0 : goto error;
1845 : }
1846 0 : if (*p == '>') {
1847 0 : read_error(HERE, "Unexpected \'>\' inside entity \"%s\"", &entity_name[nentity][1]);
1848 0 : status = 1;
1849 0 : goto error;
1850 : }
1851 :
1852 : /* check if SYSTEM */
1853 0 : if (strncmp(p, "SYSTEM", 6) == 0) {
1854 0 : entity_type[nentity] = EXTERNAL_ENTITY;
1855 0 : p += 6;
1856 : } else {
1857 0 : entity_type[nentity] = INTERNAL_ENTITY;
1858 : }
1859 :
1860 : /* extract replacement */
1861 0 : while (*p && isspace((unsigned char)*p)) {
1862 0 : if (*p == '\n')
1863 0 : line_number++;
1864 0 : p++;
1865 : }
1866 0 : if (!*p) {
1867 0 : read_error(HERE, "Unexpected end of file");
1868 0 : status = 1;
1869 0 : goto error;
1870 : }
1871 0 : if (*p == '>') {
1872 0 : read_error(HERE, "Unexpected \'>\' inside entity \"%s\"", &entity_name[nentity][1]);
1873 0 : status = 1;
1874 0 : goto error;
1875 : }
1876 :
1877 0 : if (*p != '\"' && *p != '\'') {
1878 0 : read_error(HERE, "Replacement was not found for entity \"%s\"", &entity_name[nentity][1]);
1879 0 : status = 1;
1880 0 : goto error;
1881 : }
1882 0 : delimiter = *p;
1883 0 : p++;
1884 0 : if (!*p) {
1885 0 : read_error(HERE, "Unexpected end of file");
1886 0 : status = 1;
1887 0 : goto error;
1888 : }
1889 0 : pv = p;
1890 0 : while (*pv && *pv != delimiter)
1891 0 : pv++;
1892 :
1893 0 : if (!*pv) {
1894 0 : read_error(HERE, "Unexpected end of file");
1895 0 : status = 1;
1896 0 : goto error;
1897 : }
1898 0 : if (*pv == '<') {
1899 0 : read_error(HERE, "Unexpected \'%c\' inside entity \"%s\"", *pv, &entity_name[nentity][1]);
1900 0 : status = 1;
1901 0 : goto error;
1902 : }
1903 :
1904 0 : len = (int)((size_t) pv - (size_t) p);
1905 0 : replacement = (char *) mxml_malloc(len + 1);
1906 0 : if (replacement == NULL) {
1907 0 : read_error(HERE, "Cannot allocate memory.");
1908 0 : status = 1;
1909 0 : goto error;
1910 : }
1911 :
1912 0 : memcpy(replacement, p, len);
1913 0 : replacement[len] = 0;
1914 0 : mxml_decode(replacement);
1915 :
1916 0 : if (entity_type[nentity] == EXTERNAL_ENTITY) {
1917 0 : mxml_strlcpy(entity_reference_name[nentity], replacement, sizeof(entity_reference_name[nentity]));
1918 : } else {
1919 0 : int rlen = strlen(replacement);
1920 0 : entity_value[nentity] = (char *) mxml_malloc(rlen+1);
1921 0 : if (entity_value[nentity] == NULL) {
1922 0 : read_error(HERE, "Cannot allocate memory.");
1923 0 : status = 1;
1924 0 : goto error;
1925 : }
1926 0 : memcpy(entity_value[nentity], replacement, rlen+1);
1927 : }
1928 0 : mxml_free(replacement);
1929 :
1930 0 : p = pv;
1931 0 : while (*p && isspace((unsigned char)*p)) {
1932 0 : if (*p == '\n')
1933 0 : line_number++;
1934 0 : p++;
1935 : }
1936 0 : if (!*p) {
1937 0 : read_error(HERE, "Unexpected end of file");
1938 0 : status = 1;
1939 0 : goto error;
1940 : }
1941 : }
1942 : }
1943 :
1944 : /* go to next element */
1945 0 : while (*p && *p != '<') {
1946 0 : if (*p == '\n')
1947 0 : line_number++;
1948 0 : p++;
1949 : }
1950 0 : } while (*p);
1951 0 : nentity++;
1952 :
1953 : /* read external file */
1954 0 : for (i = 0; i < nentity; i++) {
1955 0 : if (entity_type[i] == EXTERNAL_ENTITY) {
1956 0 : std::string filename;
1957 0 : if ( entity_reference_name[i][0] == DIR_SEPARATOR ) { /* absolute path */
1958 0 : filename = entity_reference_name[i];
1959 : } else { /* relative path */
1960 0 : filename += directoryname;
1961 0 : filename += DIR_SEPARATOR;
1962 0 : filename += entity_reference_name[i];
1963 : }
1964 0 : fh = open(filename.c_str(), O_RDONLY | O_TEXT, 0644);
1965 :
1966 0 : if (fh == -1) {
1967 0 : line_number = entity_line_number[i];
1968 0 : read_error(HERE, "%s is missing", entity_reference_name[i]);
1969 0 : status = 1;
1970 0 : goto error;
1971 : } else {
1972 0 : length = (int)lseek(fh, 0, SEEK_END);
1973 0 : lseek(fh, 0, SEEK_SET);
1974 0 : if (length == 0) {
1975 0 : entity_value[i] = (char *) mxml_malloc(1);
1976 0 : if (entity_value[i] == NULL) {
1977 0 : read_error(HERE, "Cannot allocate memory.");
1978 0 : close(fh);
1979 0 : status = 1;
1980 0 : goto error;
1981 : }
1982 0 : entity_value[i][0] = 0;
1983 : } else {
1984 0 : entity_value[i] = (char *) mxml_malloc(length);
1985 0 : if (entity_value[i] == NULL) {
1986 0 : read_error(HERE, "Cannot allocate memory.");
1987 0 : close(fh);
1988 0 : status = 1;
1989 0 : goto error;
1990 : }
1991 :
1992 : /* read complete file at once */
1993 0 : length = (int)read(fh, entity_value[i], length);
1994 0 : entity_value[i][length - 1] = 0;
1995 0 : close(fh);
1996 :
1997 : /* recursive parse */
1998 0 : if (mxml_parse_entity(&entity_value[i], filename.c_str(), error, error_size, error_line) != 0) {
1999 0 : status = 1;
2000 0 : goto error;
2001 : }
2002 : }
2003 : }
2004 0 : }
2005 : }
2006 :
2007 : /* count length of output string */
2008 0 : length = (int)strlen(buffer);
2009 0 : for (i = 0; i < nentity; i++) {
2010 0 : p = buffer;
2011 0 : entity_value_length[i] = (int)strlen(entity_value[i]);
2012 0 : entity_name_length[i] = (int)strlen(entity_name[i]);
2013 : while (1) {
2014 0 : pv = strstr(p, entity_name[i]);
2015 0 : if (pv) {
2016 0 : length += entity_value_length[i] - entity_name_length[i];
2017 0 : p = pv + 1;
2018 : } else {
2019 0 : break;
2020 : }
2021 : }
2022 : }
2023 :
2024 : /* re-allocate memory */
2025 0 : *buf = (char *) mxml_realloc(*buf, length + 1);
2026 0 : if (*buf == NULL) {
2027 0 : read_error(HERE, "Cannot allocate memory.");
2028 0 : status = 1;
2029 0 : goto error;
2030 : }
2031 :
2032 : /* replace entities */
2033 0 : p = buffer;
2034 0 : pv = *buf;
2035 : do {
2036 0 : if (*p == '&') {
2037 : /* found entity */
2038 0 : for (j = 0; j < nentity; j++) {
2039 0 : if (strncmp(p, entity_name[j], entity_name_length[j]) == 0) {
2040 0 : for (k = 0; k < (int) entity_value_length[j]; k++)
2041 0 : *pv++ = entity_value[j][k];
2042 0 : p += entity_name_length[j];
2043 0 : break;
2044 : }
2045 : }
2046 : }
2047 0 : *pv++ = *p++;
2048 0 : } while (*p);
2049 0 : *pv = 0;
2050 :
2051 0 : mxml_free_tree(root);
2052 :
2053 0 : error:
2054 :
2055 0 : if (buffer != NULL)
2056 0 : mxml_free(buffer);
2057 0 : for (int ip = 0; ip < MXML_MAX_ENTITY; ip++)
2058 0 : if (entity_value[ip] != NULL)
2059 0 : mxml_free(entity_value[ip]);
2060 :
2061 0 : return status;
2062 : }
2063 :
2064 : /*------------------------------------------------------------------*/
2065 :
2066 : /**
2067 : * parse a XML file and convert it into a tree of MXML_NODE's.
2068 : * Return NULL in case of an error, return error description
2069 : */
2070 0 : PMXML_NODE mxml_parse_file(const char *file_name, char *error, int error_size, int *error_line)
2071 : {
2072 0 : if (error)
2073 0 : error[0] = 0;
2074 :
2075 0 : int fh = open(file_name, O_RDONLY | O_TEXT, 0644);
2076 :
2077 0 : if (fh == -1) {
2078 0 : std::string msg = "";
2079 0 : msg += "Cannot open file \"";
2080 0 : msg += file_name;
2081 0 : msg += "\": ";
2082 0 : msg += toStrerror(errno);
2083 0 : mxml_strlcpy(error, msg.c_str(), error_size);
2084 0 : return NULL;
2085 0 : }
2086 :
2087 0 : off_t length = lseek(fh, 0, SEEK_END);
2088 0 : lseek(fh, 0, SEEK_SET);
2089 :
2090 0 : char* buf = (char *)mxml_malloc(length+1);
2091 0 : if (buf == NULL) {
2092 0 : close(fh);
2093 0 : std::string msg = "";
2094 0 : msg += "Cannot allocate buffer size ";
2095 0 : msg += toString(length);
2096 0 : msg += " for file \"";
2097 0 : msg += file_name;
2098 0 : msg += "\": ";
2099 0 : msg += toStrerror(errno);
2100 0 : mxml_strlcpy(error, msg.c_str(), error_size);
2101 0 : return NULL;
2102 0 : }
2103 :
2104 : /* read complete file at once */
2105 0 : int rd = read(fh, buf, length);
2106 0 : if (rd != length) {
2107 0 : std::string msg = "";
2108 0 : msg += "Cannot read file \"";
2109 0 : msg += file_name;
2110 0 : msg += "\", read of ";
2111 0 : msg += toString(length);
2112 0 : msg += " returned ";
2113 0 : msg += toString(rd);
2114 0 : msg += ": ";
2115 0 : msg += toStrerror(errno);
2116 0 : mxml_strlcpy(error, msg.c_str(), error_size);
2117 0 : mxml_free(buf);
2118 0 : return NULL;
2119 0 : }
2120 :
2121 0 : buf[length] = 0;
2122 0 : close(fh);
2123 :
2124 0 : if (mxml_parse_entity(&buf, file_name, error, error_size, error_line) != 0) {
2125 0 : mxml_free(buf);
2126 0 : return NULL;
2127 : }
2128 :
2129 0 : PMXML_NODE root = mxml_parse_buffer(buf, error, error_size, error_line);
2130 :
2131 0 : mxml_free(buf);
2132 :
2133 0 : return root;
2134 : }
2135 :
2136 : /*------------------------------------------------------------------*/
2137 :
2138 : /**
2139 : * write complete subtree recursively into file opened with mxml_open_document()
2140 : */
2141 0 : int mxml_write_subtree(MXML_WRITER *writer, PMXML_NODE tree, int indent)
2142 : {
2143 : int i;
2144 :
2145 0 : mxml_start_element1(writer, tree->name, indent);
2146 0 : for (i=0 ; i<tree->n_attributes ; i++)
2147 0 : if (!mxml_write_attribute(writer, tree->attribute_name+i*MXML_NAME_LENGTH, tree->attribute_value[i]))
2148 0 : return FALSE;
2149 :
2150 0 : if (tree->value)
2151 0 : if (!mxml_write_value(writer, tree->value))
2152 0 : return FALSE;
2153 :
2154 0 : for (i=0 ; i<tree->n_children ; i++)
2155 0 : if (!mxml_write_subtree(writer, &tree->child[i], (tree->value == NULL) || i > 0))
2156 0 : return FALSE;
2157 :
2158 0 : return mxml_end_element(writer);
2159 : }
2160 :
2161 : /*------------------------------------------------------------------*/
2162 :
2163 : /**
2164 : * write a complete XML tree to a file
2165 : */
2166 0 : int mxml_write_tree(const char *file_name, PMXML_NODE tree)
2167 : {
2168 : MXML_WRITER *writer;
2169 : int i;
2170 :
2171 0 : assert(tree);
2172 0 : writer = mxml_open_file(file_name);
2173 0 : if (!writer)
2174 0 : return FALSE;
2175 :
2176 0 : for (i=0 ; i<tree->n_children ; i++)
2177 0 : if (tree->child[i].node_type == ELEMENT_NODE) /* skip PI and comments */
2178 0 : if (!mxml_write_subtree(writer, &tree->child[i], TRUE))
2179 0 : return FALSE;
2180 :
2181 0 : if (!mxml_close_file(writer))
2182 0 : return FALSE;
2183 :
2184 0 : return TRUE;
2185 : }
2186 :
2187 : /*------------------------------------------------------------------*/
2188 :
2189 : /**
2190 : * write a complete XML tree to a buffer
2191 : */
2192 0 : int mxml_print_tree(char *buffer, int *buffer_size, PMXML_NODE tree)
2193 : {
2194 : int len;
2195 : char *p;
2196 : MXML_WRITER *writer;
2197 :
2198 : /* open file */
2199 0 : writer = mxml_open_buffer();
2200 0 : if (writer == NULL)
2201 0 : return FALSE;
2202 :
2203 0 : for (int i=0 ; i<tree->n_children ; i++)
2204 0 : if (tree->child[i].node_type == ELEMENT_NODE) /* skip PI and comments */
2205 0 : if (!mxml_write_subtree(writer, &tree->child[i], TRUE))
2206 0 : return FALSE;
2207 :
2208 0 : p = mxml_close_buffer(writer);
2209 0 : len = strlen(p) + 1;
2210 0 : if (len > *buffer_size) {
2211 0 : free(p);
2212 0 : *buffer_size = 0;
2213 0 : return FALSE;
2214 : }
2215 :
2216 0 : mxml_strlcpy(buffer, p, *buffer_size);
2217 0 : free(p);
2218 0 : *buffer_size = len;
2219 0 : return TRUE;
2220 : }
2221 :
2222 : /*------------------------------------------------------------------*/
2223 :
2224 0 : PMXML_NODE mxml_clone_tree(PMXML_NODE tree)
2225 : {
2226 : PMXML_NODE clone;
2227 : int i;
2228 :
2229 0 : clone = (PMXML_NODE)calloc(1, sizeof(MXML_NODE));
2230 :
2231 : /* copy name, node_type, n_attributes and n_children */
2232 0 : memcpy(clone, tree, sizeof(MXML_NODE));
2233 :
2234 0 : clone->value = NULL;
2235 0 : mxml_replace_node_value(clone, tree->value);
2236 :
2237 0 : clone->attribute_name = NULL;
2238 0 : clone->attribute_value = NULL;
2239 0 : for (i=0 ; i<tree->n_attributes ; i++)
2240 0 : mxml_add_attribute(clone, tree->attribute_name+i*MXML_NAME_LENGTH, tree->attribute_value[i]);
2241 :
2242 0 : clone->child = NULL;
2243 0 : clone->n_children = 0;
2244 0 : for (i=0 ; i<tree->n_children ; i++)
2245 0 : mxml_add_tree(clone, mxml_clone_tree(mxml_subnode(tree, i)));
2246 :
2247 0 : return clone;
2248 : }
2249 :
2250 : /*------------------------------------------------------------------*/
2251 :
2252 : /**
2253 : * print XML tree for debugging
2254 : */
2255 0 : void mxml_debug_tree(PMXML_NODE tree, int level)
2256 : {
2257 : int i, j;
2258 :
2259 0 : for (i=0 ; i<level ; i++)
2260 0 : printf(" ");
2261 0 : printf("Name: %s\n", tree->name);
2262 0 : for (i=0 ; i<level ; i++)
2263 0 : printf(" ");
2264 0 : printf("Valu: %s\n", tree->value);
2265 0 : for (i=0 ; i<level ; i++)
2266 0 : printf(" ");
2267 0 : printf("Type: %d\n", tree->node_type);
2268 0 : for (i=0 ; i<level ; i++)
2269 0 : printf(" ");
2270 0 : printf("Lin1: %d\n", tree->line_number_start);
2271 0 : for (i=0 ; i<level ; i++)
2272 0 : printf(" ");
2273 0 : printf("Lin2: %d\n", tree->line_number_end);
2274 :
2275 0 : for (j=0 ; j<tree->n_attributes ; j++) {
2276 0 : for (i=0 ; i<level ; i++)
2277 0 : printf(" ");
2278 0 : printf("%s: %s\n", tree->attribute_name+j*MXML_NAME_LENGTH,
2279 0 : tree->attribute_value[j]);
2280 : }
2281 :
2282 0 : for (i=0 ; i<level ; i++)
2283 0 : printf(" ");
2284 0 : printf("Addr: %08zX\n", (size_t)tree);
2285 0 : for (i=0 ; i<level ; i++)
2286 0 : printf(" ");
2287 0 : printf("Prnt: %08zX\n", (size_t)tree->parent);
2288 0 : for (i=0 ; i<level ; i++)
2289 0 : printf(" ");
2290 0 : printf("NCld: %d\n", tree->n_children);
2291 :
2292 0 : for (i=0 ; i<tree->n_children ; i++)
2293 0 : mxml_debug_tree(tree->child+i, level+1);
2294 :
2295 0 : if (level == 0)
2296 0 : printf("\n");
2297 0 : }
2298 :
2299 : /*------------------------------------------------------------------*/
2300 :
2301 : /**
2302 : * free memory of XML tree, must be called after any
2303 : * mxml_create_root_node() or mxml_parse_file()
2304 : */
2305 0 : void mxml_free_tree(PMXML_NODE tree)
2306 : {
2307 : int i;
2308 :
2309 : /* first free children recursively */
2310 0 : for (i=0 ; i<tree->n_children ; i++)
2311 0 : mxml_free_tree(&tree->child[i]);
2312 0 : if (tree->n_children)
2313 0 : mxml_free(tree->child);
2314 :
2315 : /* now free dynamic data */
2316 0 : for (i=0 ; i<tree->n_attributes ; i++)
2317 0 : mxml_free(tree->attribute_value[i]);
2318 :
2319 0 : if (tree->n_attributes) {
2320 0 : mxml_free(tree->attribute_name);
2321 0 : mxml_free(tree->attribute_value);
2322 : }
2323 :
2324 0 : if (tree->value)
2325 0 : mxml_free(tree->value);
2326 :
2327 : /* if we are the root node, free it */
2328 0 : if (tree->parent == NULL)
2329 0 : mxml_free(tree);
2330 0 : }
2331 :
2332 : /*------------------------------------------------------------------*/
2333 :
2334 : /*
2335 : void mxml_test()
2336 : {
2337 : char err[256];
2338 : PMXML_NODE tree, tree2, node;
2339 :
2340 : tree = mxml_parse_file("c:\\tmp\\test.xml", err, sizeof(err));
2341 : tree2 = mxml_clone_tree(tree);
2342 :
2343 : printf("Orig:\n");
2344 : mxml_debug_tree(tree, 0);
2345 :
2346 : printf("\nClone:\n");
2347 : mxml_debug_tree(tree2, 0);
2348 :
2349 : printf("\nCombined:\n");
2350 : node = mxml_find_node(tree2, "cddb");
2351 : mxml_add_tree(tree, node);
2352 : mxml_debug_tree(tree, 0);
2353 :
2354 : mxml_free_tree(tree);
2355 : }
2356 : */
2357 :
2358 : /*------------------------------------------------------------------*/
2359 : /**
2360 : mxml_basename deletes any prefix ending with the last slash '/' character
2361 : present in path. mxml_dirname deletes the filename portion, beginning with
2362 : the last slash '/' character to the end of path. Followings are examples
2363 : from these functions
2364 :
2365 : path dirname basename
2366 : "/" "/" ""
2367 : "." "." "."
2368 : "" "" ""
2369 : "/test.txt" "/" "test.txt"
2370 : "path/to/test.txt" "path/to" "test.txt"
2371 : "test.txt "." "test.txt"
2372 :
2373 : Under Windows, '\\' and ':' are recognized ad separator too.
2374 : */
2375 :
2376 0 : void mxml_basename(char *path)
2377 : {
2378 : char str[FILENAME_MAX];
2379 : char *p;
2380 : char *name;
2381 :
2382 0 : if (path) {
2383 0 : strcpy(str, path);
2384 0 : p = str;
2385 0 : name = str;
2386 : while (1) {
2387 0 : if (*p == 0)
2388 0 : break;
2389 0 : if (*p == '/'
2390 : #ifdef _MSC_VER
2391 : || *p == ':' || *p == '\\'
2392 : #endif
2393 : )
2394 0 : name = p + 1;
2395 0 : p++;
2396 : }
2397 0 : strcpy(path, name);
2398 : }
2399 :
2400 0 : return;
2401 : }
2402 :
2403 0 : void mxml_dirname(char *path)
2404 : {
2405 : char *p;
2406 : #ifdef _MSC_VER
2407 : char *pv;
2408 : #endif
2409 :
2410 0 : if (!path || strlen(path) == 0)
2411 0 : return;
2412 :
2413 0 : p = strrchr(path, '/');
2414 : #ifdef _MSC_VER
2415 : pv = strrchr(path, ':');
2416 : if (pv > p)
2417 : p = pv;
2418 : pv = strrchr(path, '\\');
2419 : if (pv > p)
2420 : p = pv;
2421 : #endif
2422 :
2423 0 : if (p == 0) /* current directory */
2424 0 : strcpy(path, ".");
2425 0 : else if (p == path) /* root directory */
2426 0 : snprintf(path, FILENAME_MAX, "%c", *p);
2427 : else
2428 0 : *p = 0;
2429 :
2430 0 : return;
2431 : }
2432 :
2433 : /*------------------------------------------------------------------*/
2434 :
2435 : /**
2436 : * Retieve node at a certain line number
2437 : */
2438 0 : PMXML_NODE mxml_get_node_at_line(PMXML_NODE tree, int line_number)
2439 : {
2440 : int i;
2441 : PMXML_NODE pn;
2442 :
2443 0 : if (tree->line_number_start == line_number)
2444 0 : return tree;
2445 :
2446 0 : for (i=0 ; i<tree->n_children ; i++) {
2447 0 : pn = mxml_get_node_at_line(&tree->child[i], line_number);
2448 0 : if (pn)
2449 0 : return pn;
2450 : }
2451 :
2452 0 : return NULL;
2453 : }
2454 :
2455 : /* emacs
2456 : * Local Variables:
2457 : * tab-width: 8
2458 : * c-basic-offset: 3
2459 : * indent-tabs-mode: nil
2460 : * End:
2461 : */
|