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