Subversion Repositories f9daq

Rev

Rev 197 | Details | Compare with Previous | Last modification | View Log | RSS feed

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