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 availabe 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  sprintf(buf, "%d", i);
163  return buf;
164 }
165 
166 /*------------------------------------------------------------------*/
167 
168 static std::string toStrerror(int err)
169 {
170  char buf[256];
171  sprintf(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  //sprintf(line, "<!-- created by MXML on %s -->\n", str);
244  line += "<!-- created by MXML on ";
245  line += str;
246  line += " -->\n";
247  mxml_write_line(writer, line.c_str());
248  }
249 
250  /* initialize stack */
251  writer->level = 0;
252  writer->element_is_open = 0;
253 
254  return writer;
255 }
256 
257 /*------------------------------------------------------------------*/
258 
259 /**
260  * suppress writing date at the top of file.
261  */
262 void mxml_suppress_date(int suppress)
263 {
264  mxml_suppress_date_flag = suppress;
265 }
266 
267 /*------------------------------------------------------------------*/
268 
269 /**
270  * open a file and write XML header
271  */
272 MXML_WRITER *mxml_open_file(const char *file_name)
273 {
274  MXML_WRITER* writer = (MXML_WRITER *)mxml_malloc(sizeof(MXML_WRITER));
275  memset(writer, 0, sizeof(MXML_WRITER));
276  writer->translate = 1;
277 
278  writer->fh = open(file_name, O_RDWR | O_CREAT | O_TRUNC | O_TEXT, 0644);
279 
280  if (writer->fh == -1) {
281  std::string line = "";
282  //sprintf(line, "Unable to open file \"%s\": ", file_name);
283  line += "Unable to open file \"";
284  line += file_name;
285  line += "\": ";
286  line += toStrerror(errno);
287  fprintf(stderr, "%s\n", line.c_str());
288  mxml_free(writer);
289  return NULL;
290  }
291 
292  /* write XML header */
293  mxml_write_line(writer, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
294  if (mxml_suppress_date_flag == 0) {
295  time_t now = time(NULL);
296  std::string str = ctime(&now);
297  str.resize(24);
298  std::string line = "";
299  //sprintf(line, "<!-- created by MXML on %s -->\n", str);
300  line += "<!-- created by MXML on ";
301  line += str;
302  line += " -->\n";
303  mxml_write_line(writer, line.c_str());
304  }
305 
306  /* initialize stack */
307  writer->level = 0;
308  writer->element_is_open = 0;
309 
310  return writer;
311 }
312 
313 /*------------------------------------------------------------------*/
314 
315 /**
316  * convert '<' '>' '&' '"' ''' into &xx;
317  */
318 void mxml_encode(char* buf, int buf_size, const char *src, int src_len, int translate)
319 {
320  assert(buf_size > 6*src_len); // each input byte is expanded into 6 bytes at most
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  //sprintf(str, "XML read error in file \"%s\", line %d: ", file_name, line_number);
1318  msg += "XML read error in file \"";
1319  msg += file_name;
1320  msg += "\", line ";
1321  msg += toString(line_number);
1322  msg += ": ";
1323  } else {
1324  //sprintf(str, "XML read error, line %d: ", line_number);
1325  msg += "XML read error, line ";
1326  msg += toString(line_number);
1327  msg += ": ";
1328  }
1329 
1330  char str[1000];
1331  va_list argptr;
1332  va_start(argptr, format);
1333  vsnprintf(str, sizeof(str), (char *) format, argptr);
1334  va_end(argptr);
1335 
1336  msg += str;
1337 
1338  if (error) {
1339  mxml_strlcpy(error, msg.c_str(), error_size);
1340  }
1341  if (error_line) {
1342  *error_line = line_number;
1343  }
1344 
1345  mxml_free_tree(root);
1346 
1347  return NULL;
1348 }
1349 
1350 /*------------------------------------------------------------------*/
1351 
1352 /**
1353  * Parse a XML buffer and convert it into a tree of MXML_NODE's.
1354  * Return NULL in case of an error, return error description.
1355  * Optional file_name is used for error reporting if called from mxml_parse_file()
1356  */
1357 PMXML_NODE mxml_parse_buffer(const char *buf, char *error, int error_size, int *error_line)
1358 {
1359  char node_name[256], attrib_name[256], attrib_value[1000], quote;
1360  const char *p, *pv;
1361  int i,j, line_number;
1362  PMXML_NODE root, ptree, pnew;
1363  int end_element;
1364  size_t len;
1365  char *file_name = NULL; /* dummy for 'HERE' */
1366 
1367  p = buf;
1368  line_number = 1;
1369 
1370  root = mxml_create_root_node();
1371  ptree = root;
1372 
1373  /* parse file contents */
1374  do {
1375  if (*p == '<') {
1376 
1377  end_element = FALSE;
1378 
1379  /* found new element */
1380  p++;
1381  while (*p && isspace(*p)) {
1382  if (*p == '\n')
1383  line_number++;
1384  p++;
1385  }
1386  if (!*p)
1387  return read_error(HERE, "Unexpected end of file");
1388 
1389  if (strncmp(p, "!--", 3) == 0) {
1390 
1391  /* found comment */
1392 
1393  pnew = mxml_add_special_node(ptree, COMMENT_NODE, "Comment", NULL);
1394  pnew->line_number_start = line_number;
1395  pv = p+3;
1396  while (*pv == ' ')
1397  pv++;
1398 
1399  p += 3;
1400  if (strstr(p, "-->") == NULL)
1401  return read_error(HERE, "Unterminated comment");
1402 
1403  while (strncmp(p, "-->", 3) != 0) {
1404  if (*p == '\n')
1405  line_number++;
1406  p++;
1407  }
1408 
1409  len = (size_t)p - (size_t)pv;
1410  pnew->value = (char *)mxml_malloc(len+1);
1411  memcpy(pnew->value, pv, len);
1412  pnew->value[len] = 0;
1413  pnew->line_number_end = line_number;
1414  mxml_decode(pnew->value);
1415 
1416  p += 3;
1417 
1418  } else if (*p == '?') {
1419 
1420  /* found ?...? element */
1421  pnew = mxml_add_special_node(ptree, PROCESSING_INSTRUCTION_NODE, "PI", NULL);
1422  pnew->line_number_start = line_number;
1423  pv = p+1;
1424 
1425  p++;
1426  if (strstr(p, "?>") == NULL)
1427  return read_error(HERE, "Unterminated ?...? element");
1428 
1429  while (strncmp(p, "?>", 2) != 0) {
1430  if (*p == '\n')
1431  line_number++;
1432  p++;
1433  }
1434 
1435  len = (size_t)p - (size_t)pv;
1436  pnew->value = (char *)mxml_malloc(len+1);
1437  memcpy(pnew->value, pv, len);
1438  pnew->value[len] = 0;
1439  pnew->line_number_end = line_number;
1440  mxml_decode(pnew->value);
1441 
1442  p += 2;
1443 
1444  } else if (strncmp(p, "!DOCTYPE", 8) == 0 ) {
1445 
1446  /* found !DOCTYPE element , skip it */
1447  p += 8;
1448  if (strstr(p, ">") == NULL)
1449  return read_error(HERE, "Unterminated !DOCTYPE element");
1450 
1451  j = 0;
1452  while (*p && (*p != '>' || j > 0)) {
1453  if (*p == '\n')
1454  line_number++;
1455  else if (*p == '<')
1456  j++;
1457  else if (*p == '>')
1458  j--;
1459  p++;
1460  }
1461  if (!*p)
1462  return read_error(HERE, "Unexpected end of file");
1463 
1464  p++;
1465 
1466  } else {
1467 
1468  /* found normal element */
1469  if (*p == '/') {
1470  end_element = TRUE;
1471  p++;
1472  while (*p && isspace((unsigned char)*p)) {
1473  if (*p == '\n')
1474  line_number++;
1475  p++;
1476  }
1477  if (!*p)
1478  return read_error(HERE, "Unexpected end of file");
1479  }
1480 
1481  /* extract node name */
1482  i = 0;
1483  node_name[i] = 0;
1484  while (*p && !isspace((unsigned char)*p) && *p != '/' && *p != '>' && *p != '<')
1485  node_name[i++] = *p++;
1486  node_name[i] = 0;
1487  if (!*p)
1488  return read_error(HERE, "Unexpected end of file");
1489  if (*p == '<')
1490  return read_error(HERE, "Unexpected \'<\' inside element \"%s\"", node_name);
1491 
1492  mxml_decode(node_name);
1493 
1494  if (end_element) {
1495 
1496  if (!ptree)
1497  return read_error(HERE, "Found unexpected </%s>", node_name);
1498 
1499  /* close previously opened element */
1500  if (strcmp(ptree->name, node_name) != 0)
1501  return read_error(HERE, "Found </%s>, expected </%s>", node_name, ptree->name);
1502  ptree->line_number_end = line_number;
1503 
1504  /* go up one level on the tree */
1505  ptree = ptree->parent;
1506 
1507  } else {
1508 
1509  if (ptree == NULL)
1510  return read_error(HERE, "Unexpected second top level node");
1511 
1512  /* allocate new element structure in parent tree */
1513  pnew = mxml_add_node(ptree, node_name, NULL);
1514  pnew->line_number_start = line_number;
1515  pnew->line_number_end = line_number;
1516 
1517  while (*p && isspace((unsigned char)*p)) {
1518  if (*p == '\n')
1519  line_number++;
1520  p++;
1521  }
1522  if (!*p)
1523  return read_error(HERE, "Unexpected end of file");
1524 
1525  while (*p != '>' && *p != '/') {
1526 
1527  /* found attribute */
1528  pv = p;
1529  while (*pv && !isspace((unsigned char)*pv) && *pv != '=' && *pv != '<' && *pv != '>')
1530  pv++;
1531  if (!*pv)
1532  return read_error(HERE, "Unexpected end of file");
1533  if (*pv == '<' || *pv == '>')
1534  return read_error(HERE, "Unexpected \'%c\' inside element \"%s\"", *pv, node_name);
1535 
1536  /* extract attribute name */
1537  len = (size_t)pv - (size_t)p;
1538  if (len > sizeof(attrib_name)-1)
1539  len = sizeof(attrib_name)-1;
1540  memcpy(attrib_name, p, len);
1541  attrib_name[len] = 0;
1542  mxml_decode(attrib_name);
1543 
1544  p = pv;
1545  while (*p && isspace((unsigned char)*p)) {
1546  if (*p == '\n')
1547  line_number++;
1548  p++;
1549  }
1550  if (!*p)
1551  return read_error(HERE, "Unexpected end of file");
1552  if (*p != '=')
1553  return read_error(HERE, "Expect \"=\" here");
1554 
1555  p++;
1556  while (*p && isspace((unsigned char)*p)) {
1557  if (*p == '\n')
1558  line_number++;
1559  p++;
1560  }
1561  if (!*p)
1562  return read_error(HERE, "Unexpected end of file");
1563  if (*p != '\"' && *p != '\'')
1564  return read_error(HERE, "Expect \" or \' here");
1565  quote = *p;
1566  p++;
1567 
1568  /* extract attribute value */
1569  pv = p;
1570  while (*pv && *pv != quote)
1571  pv++;
1572  if (!*pv)
1573  return read_error(HERE, "Unexpected end of file");
1574 
1575  len = (size_t)pv - (size_t)p;
1576  if (len > sizeof(attrib_value)-1)
1577  len = sizeof(attrib_value)-1;
1578  memcpy(attrib_value, p, len);
1579  attrib_value[len] = 0;
1580  mxml_decode(attrib_value);
1581 
1582  /* add attribute to current node */
1583  mxml_add_attribute(pnew, attrib_name, attrib_value);
1584 
1585  p = pv+1;
1586  while (*p && isspace((unsigned char)*p)) {
1587  if (*p == '\n')
1588  line_number++;
1589  p++;
1590  }
1591  if (!*p)
1592  return read_error(HERE, "Unexpected end of file");
1593  }
1594 
1595  if (*p == '/') {
1596 
1597  /* found empty node, like <node/>, just skip closing bracket */
1598  p++;
1599 
1600  while (*p && isspace((unsigned char)*p)) {
1601  if (*p == '\n')
1602  line_number++;
1603  p++;
1604  }
1605  if (!*p)
1606  return read_error(HERE, "Unexpected end of file");
1607  if (*p != '>')
1608  return read_error(HERE, "Expected \">\" after \"/\"");
1609  p++;
1610  }
1611 
1612  if (*p == '>') {
1613 
1614  p++;
1615 
1616  /* check if we have sub-element or value */
1617  pv = p;
1618  while (*pv && isspace((unsigned char)*pv)) {
1619  if (*pv == '\n')
1620  line_number++;
1621  pv++;
1622  }
1623  if (!*pv)
1624  return read_error(HERE, "Unexpected end of file");
1625 
1626  if (*pv == '<' && *(pv+1) != '/') {
1627 
1628  /* start new subtree */
1629  ptree = pnew;
1630  p = pv;
1631 
1632  } else {
1633 
1634  /* extract value */
1635  while (*pv && *pv != '<') {
1636  if (*pv == '\n')
1637  line_number++;
1638  pv++;
1639  }
1640  if (!*pv)
1641  return read_error(HERE, "Unexpected end of file");
1642 
1643  len = (size_t)pv - (size_t)p;
1644  pnew->value = (char *)mxml_malloc(len+1);
1645  memcpy(pnew->value, p, len);
1646  pnew->value[len] = 0;
1647  mxml_decode(pnew->value);
1648  p = pv;
1649 
1650  ptree = pnew;
1651  }
1652  }
1653  }
1654  }
1655  }
1656 
1657  /* go to next element */
1658  while (*p && *p != '<') {
1659  if (*p == '\n')
1660  line_number++;
1661  p++;
1662  }
1663  } while (*p);
1664 
1665  return root;
1666 }
1667 
1668 /*------------------------------------------------------------------*/
1669 
1670 /**
1671  * parse !ENTYTY entries of XML files and replace with references.
1672  * Return 0 in case of no errors, return error description.
1673  * Optional file_name is used for error reporting if called from mxml_parse_file()
1674  */
1675 int mxml_parse_entity(char **buf, const char *file_name, char *error, int error_size, int *error_line)
1676 {
1677  char *p;
1678  char *pv;
1679  char delimiter;
1680  int i, j, k, line_number, status;
1681  char *replacement;
1682  char entity_name[MXML_MAX_ENTITY][256];
1683  char entity_reference_name[MXML_MAX_ENTITY][256];
1684  char *entity_value[MXML_MAX_ENTITY];
1685  int entity_type[MXML_MAX_ENTITY]; /* internal or external */
1686  int entity_line_number[MXML_MAX_ENTITY];
1687  int nentity;
1688  int fh, length;
1689  int entity_value_length[MXML_MAX_ENTITY];
1690  int entity_name_length[MXML_MAX_ENTITY];
1691 
1692  PMXML_NODE root = mxml_create_root_node(); /* dummy for 'HERE' */
1693 
1694  for (int ip = 0; ip < MXML_MAX_ENTITY; ip++)
1695  entity_value[ip] = NULL;
1696 
1697  line_number = 1;
1698  nentity = -1;
1699  status = 0;
1700 
1701  if (!buf || !(*buf) || !strlen(*buf))
1702  return 0;
1703 
1704  char directoryname[FILENAME_MAX];
1705  mxml_strlcpy(directoryname, file_name, FILENAME_MAX);
1706  mxml_dirname(directoryname);
1707 
1708  /* copy string to temporary space */
1709  int len = strlen(*buf);
1710  char* buffer = (char *) mxml_malloc(len+1);
1711  if (buffer == NULL) {
1712  read_error(HERE, "Cannot allocate memory.");
1713  status = 1;
1714  goto error;
1715  }
1716  memcpy(buffer, *buf, len+1);
1717 
1718  p = strstr(buffer, "!DOCTYPE");
1719  if (p == NULL) { /* no entities */
1720  status = 0;
1721  goto error;
1722  }
1723 
1724  pv = strstr(p, "[");
1725  if (pv == NULL) { /* no entities */
1726  status = 1;
1727  goto error;
1728  }
1729 
1730  p = pv + 1;
1731 
1732  /* search !ENTITY */
1733  do {
1734  if (*p == ']')
1735  break;
1736 
1737  if (*p == '<') {
1738 
1739  /* found new entity */
1740  p++;
1741  while (*p && isspace((unsigned char)*p)) {
1742  if (*p == '\n')
1743  line_number++;
1744  p++;
1745  }
1746  if (!*p) {
1747  read_error(HERE, "Unexpected end of file");
1748  status = 1;
1749  goto error;
1750  }
1751 
1752  if (strncmp(p, "!--", 3) == 0) {
1753  /* found comment */
1754  p += 3;
1755  if (strstr(p, "-->") == NULL) {
1756  read_error(HERE, "Unterminated comment");
1757  status = 1;
1758  goto error;
1759  }
1760 
1761  while (strncmp(p, "-->", 3) != 0) {
1762  if (*p == '\n')
1763  line_number++;
1764  p++;
1765  }
1766  p += 3;
1767  }
1768 
1769  else if (strncmp(p, "!ENTITY", 7) == 0) {
1770  /* found entity */
1771  nentity++;
1772  if (nentity >= MXML_MAX_ENTITY) {
1773  read_error(HERE, "Too much entities");
1774  status = 1;
1775  goto error;
1776  }
1777 
1778  entity_line_number[nentity] = line_number;
1779 
1780  pv = p + 7;
1781  while (*pv == ' ')
1782  pv++;
1783 
1784  /* extract entity name */
1785  p = pv;
1786 
1787  while (*p && isspace((unsigned char)*p) && *p != '<' && *p != '>') {
1788  if (*p == '\n')
1789  line_number++;
1790  p++;
1791  }
1792  if (!*p) {
1793  read_error(HERE, "Unexpected end of file");
1794  status = 1;
1795  goto error;
1796  }
1797  if (*p == '<' || *p == '>') {
1798  read_error(HERE, "Unexpected \'%c\' inside !ENTITY", *p);
1799  status = 1;
1800  goto error;
1801  }
1802 
1803  pv = p;
1804  while (*pv && !isspace((unsigned char)*pv) && *pv != '<' && *pv != '>')
1805  pv++;
1806 
1807  if (!*pv) {
1808  read_error(HERE, "Unexpected end of file");
1809  status = 1;
1810  goto error;
1811  }
1812  if (*pv == '<' || *pv == '>') {
1813  read_error(HERE, "Unexpected \'%c\' inside entity \"%s\"", *pv, &entity_name[nentity][1]);
1814  status = 1;
1815  goto error;
1816  }
1817 
1818  entity_name[nentity][0] = '&';
1819  i = 1;
1820  entity_name[nentity][i] = 0;
1821  while (*p && !isspace((unsigned char)*p) && *p != '/' && *p != '>' && *p != '<' && i < 253)
1822  entity_name[nentity][i++] = *p++;
1823  entity_name[nentity][i++] = ';';
1824  entity_name[nentity][i] = 0;
1825 
1826  if (!*p) {
1827  read_error(HERE, "Unexpected end of file");
1828  status = 1;
1829  goto error;
1830  }
1831  if (*p == '<') {
1832  read_error(HERE, "Unexpected \'<\' inside entity \"%s\"", &entity_name[nentity][1]);
1833  status = 1;
1834  goto error;
1835  }
1836 
1837  /* extract replacement or SYSTEM */
1838  while (*p && isspace((unsigned char)*p)) {
1839  if (*p == '\n')
1840  line_number++;
1841  p++;
1842  }
1843  if (!*p) {
1844  read_error(HERE, "Unexpected end of file");
1845  status = 1;
1846  goto error;
1847  }
1848  if (*p == '>') {
1849  read_error(HERE, "Unexpected \'>\' inside entity \"%s\"", &entity_name[nentity][1]);
1850  status = 1;
1851  goto error;
1852  }
1853 
1854  /* check if SYSTEM */
1855  if (strncmp(p, "SYSTEM", 6) == 0) {
1856  entity_type[nentity] = EXTERNAL_ENTITY;
1857  p += 6;
1858  } else {
1859  entity_type[nentity] = INTERNAL_ENTITY;
1860  }
1861 
1862  /* extract replacement */
1863  while (*p && isspace((unsigned char)*p)) {
1864  if (*p == '\n')
1865  line_number++;
1866  p++;
1867  }
1868  if (!*p) {
1869  read_error(HERE, "Unexpected end of file");
1870  status = 1;
1871  goto error;
1872  }
1873  if (*p == '>') {
1874  read_error(HERE, "Unexpected \'>\' inside entity \"%s\"", &entity_name[nentity][1]);
1875  status = 1;
1876  goto error;
1877  }
1878 
1879  if (*p != '\"' && *p != '\'') {
1880  read_error(HERE, "Replacement was not found for entity \"%s\"", &entity_name[nentity][1]);
1881  status = 1;
1882  goto error;
1883  }
1884  delimiter = *p;
1885  p++;
1886  if (!*p) {
1887  read_error(HERE, "Unexpected end of file");
1888  status = 1;
1889  goto error;
1890  }
1891  pv = p;
1892  while (*pv && *pv != delimiter)
1893  pv++;
1894 
1895  if (!*pv) {
1896  read_error(HERE, "Unexpected end of file");
1897  status = 1;
1898  goto error;
1899  }
1900  if (*pv == '<') {
1901  read_error(HERE, "Unexpected \'%c\' inside entity \"%s\"", *pv, &entity_name[nentity][1]);
1902  status = 1;
1903  goto error;
1904  }
1905 
1906  len = (int)((size_t) pv - (size_t) p);
1907  replacement = (char *) mxml_malloc(len + 1);
1908  if (replacement == NULL) {
1909  read_error(HERE, "Cannot allocate memory.");
1910  status = 1;
1911  goto error;
1912  }
1913 
1914  memcpy(replacement, p, len);
1915  replacement[len] = 0;
1916  mxml_decode(replacement);
1917 
1918  if (entity_type[nentity] == EXTERNAL_ENTITY) {
1919  mxml_strlcpy(entity_reference_name[nentity], replacement, sizeof(entity_reference_name[nentity]));
1920  } else {
1921  int rlen = strlen(replacement);
1922  entity_value[nentity] = (char *) mxml_malloc(rlen+1);
1923  if (entity_value[nentity] == NULL) {
1924  read_error(HERE, "Cannot allocate memory.");
1925  status = 1;
1926  goto error;
1927  }
1928  memcpy(entity_value[nentity], replacement, rlen+1);
1929  }
1930  mxml_free(replacement);
1931 
1932  p = pv;
1933  while (*p && isspace((unsigned char)*p)) {
1934  if (*p == '\n')
1935  line_number++;
1936  p++;
1937  }
1938  if (!*p) {
1939  read_error(HERE, "Unexpected end of file");
1940  status = 1;
1941  goto error;
1942  }
1943  }
1944  }
1945 
1946  /* go to next element */
1947  while (*p && *p != '<') {
1948  if (*p == '\n')
1949  line_number++;
1950  p++;
1951  }
1952  } while (*p);
1953  nentity++;
1954 
1955  /* read external file */
1956  for (i = 0; i < nentity; i++) {
1957  if (entity_type[i] == EXTERNAL_ENTITY) {
1958  std::string filename;
1959  if ( entity_reference_name[i][0] == DIR_SEPARATOR ) { /* absolute path */
1960  filename = entity_reference_name[i];
1961  } else { /* relative path */
1962  //sprintf(filename, "%s%c%s", directoryname, DIR_SEPARATOR, entity_reference_name[i]);
1963  filename += directoryname;
1964  filename += DIR_SEPARATOR;
1965  filename += entity_reference_name[i];
1966  }
1967  fh = open(filename.c_str(), O_RDONLY | O_TEXT, 0644);
1968 
1969  if (fh == -1) {
1970  line_number = entity_line_number[i];
1971  read_error(HERE, "%s is missing", entity_reference_name[i]);
1972  status = 1;
1973  goto error;
1974  } else {
1975  length = (int)lseek(fh, 0, SEEK_END);
1976  lseek(fh, 0, SEEK_SET);
1977  if (length == 0) {
1978  entity_value[i] = (char *) mxml_malloc(1);
1979  if (entity_value[i] == NULL) {
1980  read_error(HERE, "Cannot allocate memory.");
1981  close(fh);
1982  status = 1;
1983  goto error;
1984  }
1985  entity_value[i][0] = 0;
1986  } else {
1987  entity_value[i] = (char *) mxml_malloc(length);
1988  if (entity_value[i] == NULL) {
1989  read_error(HERE, "Cannot allocate memory.");
1990  close(fh);
1991  status = 1;
1992  goto error;
1993  }
1994 
1995  /* read complete file at once */
1996  length = (int)read(fh, entity_value[i], length);
1997  entity_value[i][length - 1] = 0;
1998  close(fh);
1999 
2000  /* recursive parse */
2001  if (mxml_parse_entity(&entity_value[i], filename.c_str(), error, error_size, error_line) != 0) {
2002  status = 1;
2003  goto error;
2004  }
2005  }
2006  }
2007  }
2008  }
2009 
2010  /* count length of output string */
2011  length = (int)strlen(buffer);
2012  for (i = 0; i < nentity; i++) {
2013  p = buffer;
2014  entity_value_length[i] = (int)strlen(entity_value[i]);
2015  entity_name_length[i] = (int)strlen(entity_name[i]);
2016  while (1) {
2017  pv = strstr(p, entity_name[i]);
2018  if (pv) {
2019  length += entity_value_length[i] - entity_name_length[i];
2020  p = pv + 1;
2021  } else {
2022  break;
2023  }
2024  }
2025  }
2026 
2027  /* re-allocate memory */
2028  *buf = (char *) mxml_realloc(*buf, length + 1);
2029  if (*buf == NULL) {
2030  read_error(HERE, "Cannot allocate memory.");
2031  status = 1;
2032  goto error;
2033  }
2034 
2035  /* replace entities */
2036  p = buffer;
2037  pv = *buf;
2038  do {
2039  if (*p == '&') {
2040  /* found entity */
2041  for (j = 0; j < nentity; j++) {
2042  if (strncmp(p, entity_name[j], entity_name_length[j]) == 0) {
2043  for (k = 0; k < (int) entity_value_length[j]; k++)
2044  *pv++ = entity_value[j][k];
2045  p += entity_name_length[j];
2046  break;
2047  }
2048  }
2049  }
2050  *pv++ = *p++;
2051  } while (*p);
2052  *pv = 0;
2053 
2054  mxml_free_tree(root);
2055 
2056 error:
2057 
2058  if (buffer != NULL)
2059  mxml_free(buffer);
2060  for (int ip = 0; ip < MXML_MAX_ENTITY; ip++)
2061  if (entity_value[ip] != NULL)
2062  mxml_free(entity_value[ip]);
2063 
2064  return status;
2065 }
2066 
2067 /*------------------------------------------------------------------*/
2068 
2069 /**
2070  * parse a XML file and convert it into a tree of MXML_NODE's.
2071  * Return NULL in case of an error, return error description
2072  */
2073 PMXML_NODE mxml_parse_file(const char *file_name, char *error, int error_size, int *error_line)
2074 {
2075  if (error)
2076  error[0] = 0;
2077 
2078  int fh = open(file_name, O_RDONLY | O_TEXT, 0644);
2079 
2080  if (fh == -1) {
2081  std::string msg = "";
2082  msg += "Cannot open file \"";
2083  msg += file_name;
2084  msg += "\": ";
2085  msg += toStrerror(errno);
2086  mxml_strlcpy(error, msg.c_str(), error_size);
2087  return NULL;
2088  }
2089 
2090  off_t length = lseek(fh, 0, SEEK_END);
2091  lseek(fh, 0, SEEK_SET);
2092 
2093  char* buf = (char *)mxml_malloc(length+1);
2094  if (buf == NULL) {
2095  close(fh);
2096  std::string msg = "";
2097  msg += "Cannot allocate buffer size ";
2098  msg += toString(length);
2099  msg += " for file \"";
2100  msg += file_name;
2101  msg += "\": ";
2102  msg += toStrerror(errno);
2103  mxml_strlcpy(error, msg.c_str(), error_size);
2104  return NULL;
2105  }
2106 
2107  /* read complete file at once */
2108  int rd = read(fh, buf, length);
2109  if (rd != length) {
2110  std::string msg = "";
2111  msg += "Cannot read file \"";
2112  msg += file_name;
2113  msg += "\", read of ";
2114  msg += toString(length);
2115  msg += " returned ";
2116  msg += toString(rd);
2117  msg += ": ";
2118  msg += toStrerror(errno);
2119  mxml_strlcpy(error, msg.c_str(), error_size);
2120  mxml_free(buf);
2121  return NULL;
2122  }
2123 
2124  buf[length] = 0;
2125  close(fh);
2126 
2127  if (mxml_parse_entity(&buf, file_name, error, error_size, error_line) != 0) {
2128  mxml_free(buf);
2129  return NULL;
2130  }
2131 
2132  PMXML_NODE root = mxml_parse_buffer(buf, error, error_size, error_line);
2133 
2134  mxml_free(buf);
2135 
2136  return root;
2137 }
2138 
2139 /*------------------------------------------------------------------*/
2140 
2141 /**
2142  * write complete subtree recursively into file opened with mxml_open_document()
2143  */
2144 int mxml_write_subtree(MXML_WRITER *writer, PMXML_NODE tree, int indent)
2145 {
2146  int i;
2147 
2148  mxml_start_element1(writer, tree->name, indent);
2149  for (i=0 ; i<tree->n_attributes ; i++)
2150  if (!mxml_write_attribute(writer, tree->attribute_name+i*MXML_NAME_LENGTH, tree->attribute_value[i]))
2151  return FALSE;
2152 
2153  if (tree->value)
2154  if (!mxml_write_value(writer, tree->value))
2155  return FALSE;
2156 
2157  for (i=0 ; i<tree->n_children ; i++)
2158  if (!mxml_write_subtree(writer, &tree->child[i], (tree->value == NULL) || i > 0))
2159  return FALSE;
2160 
2161  return mxml_end_element(writer);
2162 }
2163 
2164 /*------------------------------------------------------------------*/
2165 
2166 /**
2167  * write a complete XML tree to a file
2168  */
2169 int mxml_write_tree(const char *file_name, PMXML_NODE tree)
2170 {
2171  MXML_WRITER *writer;
2172  int i;
2173 
2174  assert(tree);
2175  writer = mxml_open_file(file_name);
2176  if (!writer)
2177  return FALSE;
2178 
2179  for (i=0 ; i<tree->n_children ; i++)
2180  if (tree->child[i].node_type == ELEMENT_NODE) /* skip PI and comments */
2181  if (!mxml_write_subtree(writer, &tree->child[i], TRUE))
2182  return FALSE;
2183 
2184  if (!mxml_close_file(writer))
2185  return FALSE;
2186 
2187  return TRUE;
2188 }
2189 
2190 /*------------------------------------------------------------------*/
2191 
2193 {
2194  PMXML_NODE clone;
2195  int i;
2196 
2197  clone = (PMXML_NODE)calloc(sizeof(MXML_NODE), 1);
2198 
2199  /* copy name, node_type, n_attributes and n_children */
2200  memcpy(clone, tree, sizeof(MXML_NODE));
2201 
2202  clone->value = NULL;
2203  mxml_replace_node_value(clone, tree->value);
2204 
2205  clone->attribute_name = NULL;
2206  clone->attribute_value = NULL;
2207  for (i=0 ; i<tree->n_attributes ; i++)
2209 
2210  clone->child = NULL;
2211  clone->n_children = 0;
2212  for (i=0 ; i<tree->n_children ; i++)
2213  mxml_add_tree(clone, mxml_clone_tree(mxml_subnode(tree, i)));
2214 
2215  return clone;
2216 }
2217 
2218 /*------------------------------------------------------------------*/
2219 
2220 /**
2221  * print XML tree for debugging
2222  */
2223 void mxml_debug_tree(PMXML_NODE tree, int level)
2224 {
2225  int i, j;
2226 
2227  for (i=0 ; i<level ; i++)
2228  printf(" ");
2229  printf("Name: %s\n", tree->name);
2230  for (i=0 ; i<level ; i++)
2231  printf(" ");
2232  printf("Valu: %s\n", tree->value);
2233  for (i=0 ; i<level ; i++)
2234  printf(" ");
2235  printf("Type: %d\n", tree->node_type);
2236  for (i=0 ; i<level ; i++)
2237  printf(" ");
2238  printf("Lin1: %d\n", tree->line_number_start);
2239  for (i=0 ; i<level ; i++)
2240  printf(" ");
2241  printf("Lin2: %d\n", tree->line_number_end);
2242 
2243  for (j=0 ; j<tree->n_attributes ; j++) {
2244  for (i=0 ; i<level ; i++)
2245  printf(" ");
2246  printf("%s: %s\n", tree->attribute_name+j*MXML_NAME_LENGTH,
2247  tree->attribute_value[j]);
2248  }
2249 
2250  for (i=0 ; i<level ; i++)
2251  printf(" ");
2252  printf("Addr: %08zX\n", (size_t)tree);
2253  for (i=0 ; i<level ; i++)
2254  printf(" ");
2255  printf("Prnt: %08zX\n", (size_t)tree->parent);
2256  for (i=0 ; i<level ; i++)
2257  printf(" ");
2258  printf("NCld: %d\n", tree->n_children);
2259 
2260  for (i=0 ; i<tree->n_children ; i++)
2261  mxml_debug_tree(tree->child+i, level+1);
2262 
2263  if (level == 0)
2264  printf("\n");
2265 }
2266 
2267 /*------------------------------------------------------------------*/
2268 
2269 /**
2270  * free memory of XML tree, must be called after any
2271  * mxml_create_root_node() or mxml_parse_file()
2272  */
2274 {
2275  int i;
2276 
2277  /* first free children recursively */
2278  for (i=0 ; i<tree->n_children ; i++)
2279  mxml_free_tree(&tree->child[i]);
2280  if (tree->n_children)
2281  mxml_free(tree->child);
2282 
2283  /* now free dynamic data */
2284  for (i=0 ; i<tree->n_attributes ; i++)
2285  mxml_free(tree->attribute_value[i]);
2286 
2287  if (tree->n_attributes) {
2288  mxml_free(tree->attribute_name);
2289  mxml_free(tree->attribute_value);
2290  }
2291 
2292  if (tree->value)
2293  mxml_free(tree->value);
2294 
2295  /* if we are the root node, free it */
2296  if (tree->parent == NULL)
2297  mxml_free(tree);
2298 }
2299 
2300 /*------------------------------------------------------------------*/
2301 
2302 /*
2303 void mxml_test()
2304 {
2305  char err[256];
2306  PMXML_NODE tree, tree2, node;
2307 
2308  tree = mxml_parse_file("c:\\tmp\\test.xml", err, sizeof(err));
2309  tree2 = mxml_clone_tree(tree);
2310 
2311  printf("Orig:\n");
2312  mxml_debug_tree(tree, 0);
2313 
2314  printf("\nClone:\n");
2315  mxml_debug_tree(tree2, 0);
2316 
2317  printf("\nCombined:\n");
2318  node = mxml_find_node(tree2, "cddb");
2319  mxml_add_tree(tree, node);
2320  mxml_debug_tree(tree, 0);
2321 
2322  mxml_free_tree(tree);
2323 }
2324 */
2325 
2326 /*------------------------------------------------------------------*/
2327  /**
2328  mxml_basename deletes any prefix ending with the last slash '/' character
2329  present in path. mxml_dirname deletes the filename portion, beginning with
2330  the last slash '/' character to the end of path. Followings are examples
2331  from these functions
2332 
2333  path dirname basename
2334  "/" "/" ""
2335  "." "." "."
2336  "" "" ""
2337  "/test.txt" "/" "test.txt"
2338  "path/to/test.txt" "path/to" "test.txt"
2339  "test.txt "." "test.txt"
2340 
2341  Under Windows, '\\' and ':' are recognized ad separator too.
2342  */
2343 
2344 void mxml_basename(char *path)
2345 {
2346  char str[FILENAME_MAX];
2347  char *p;
2348  char *name;
2349 
2350  if (path) {
2351  strcpy(str, path);
2352  p = str;
2353  name = str;
2354  while (1) {
2355  if (*p == 0)
2356  break;
2357  if (*p == '/'
2358 #ifdef _MSC_VER
2359  || *p == ':' || *p == '\\'
2360 #endif
2361  )
2362  name = p + 1;
2363  p++;
2364  }
2365  strcpy(path, name);
2366  }
2367 
2368  return;
2369 }
2370 
2371 void mxml_dirname(char *path)
2372 {
2373  char *p;
2374 #ifdef _MSC_VER
2375  char *pv;
2376 #endif
2377 
2378  if (!path || strlen(path) == 0)
2379  return;
2380 
2381  p = strrchr(path, '/');
2382 #ifdef _MSC_VER
2383  pv = strrchr(path, ':');
2384  if (pv > p)
2385  p = pv;
2386  pv = strrchr(path, '\\');
2387  if (pv > p)
2388  p = pv;
2389 #endif
2390 
2391  if (p == 0) /* current directory */
2392  strcpy(path, ".");
2393  else if (p == path) /* root directory */
2394  sprintf(path, "%c", *p);
2395  else
2396  *p = 0;
2397 
2398  return;
2399 }
2400 
2401 /*------------------------------------------------------------------*/
2402 
2403 /**
2404  * Retieve node at a certain line number
2405  */
2407 {
2408  int i;
2409  PMXML_NODE pn;
2410 
2411  if (tree->line_number_start == line_number)
2412  return tree;
2413 
2414  for (i=0 ; i<tree->n_children ; i++) {
2415  pn = mxml_get_node_at_line(&tree->child[i], line_number);
2416  if (pn)
2417  return pn;
2418  }
2419 
2420  return NULL;
2421 }
2422 
2423 /* emacs
2424  * Local Variables:
2425  * tab-width: 8
2426  * c-basic-offset: 3
2427  * indent-tabs-mode: nil
2428  * End:
2429  */
char * dst
Definition: lz4.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:2273
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:2073
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:2371
#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:262
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:1357
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:272
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:2169
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:2192
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:1675
int mxml_write_value(MXML_WRITER *writer, const char *data)
Definition: mxml.cxx:547
void mxml_basename(char *path)
Definition: mxml.cxx:2344
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:2144
PMXML_NODE mxml_get_node_at_line(PMXML_NODE tree, int line_number)
Definition: mxml.cxx:2406
static PMXML_NODE static void mxml_encode(char *buf, int buf_size, const char *src, int src_len, int translate)
Definition: mxml.cxx:318
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:2223
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_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