source: drsdaq/DRS/mxml.c@ 840

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