blob: 128cda1a4cf2f87f22bb7eda0a0a01a956871249 [file] [log] [blame]
Jon Medhurstaaf37a32013-06-11 12:10:56 +01001/*
Jon Medhurst96b56152014-10-30 18:01:15 +00002 * "$Id: mxml-node.c 451 2014-01-04 21:50:06Z msweet $"
Jon Medhurstaaf37a32013-06-11 12:10:56 +01003 *
4 * Node support code for Mini-XML, a small XML-like file parsing library.
5 *
Jon Medhurst96b56152014-10-30 18:01:15 +00006 * Copyright 2003-2014 by Michael R Sweet.
Jon Medhurstaaf37a32013-06-11 12:10:56 +01007 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Michael R Sweet and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "COPYING"
11 * which should have been included with this file. If this file is
12 * missing or damaged, see the license at:
13 *
Jon Medhurst96b56152014-10-30 18:01:15 +000014 * http://www.msweet.org/projects.php/Mini-XML
Jon Medhurstaaf37a32013-06-11 12:10:56 +010015 */
16
17/*
18 * Include necessary headers...
19 */
20
21#include "config.h"
22#include "mxml.h"
23
24
25/*
26 * Local functions...
27 */
28
29static mxml_node_t *mxml_new(mxml_node_t *parent, mxml_type_t type);
30
31
32/*
33 * 'mxmlAdd()' - Add a node to a tree.
34 *
35 * Adds the specified node to the parent. If the child argument is not
36 * NULL, puts the new node before or after the specified child depending
37 * on the value of the where argument. If the child argument is NULL,
38 * puts the new node at the beginning of the child list (MXML_ADD_BEFORE)
39 * or at the end of the child list (MXML_ADD_AFTER). The constant
40 * MXML_ADD_TO_PARENT can be used to specify a NULL child pointer.
41 */
42
43void
44mxmlAdd(mxml_node_t *parent, /* I - Parent node */
45 int where, /* I - Where to add, MXML_ADD_BEFORE or MXML_ADD_AFTER */
46 mxml_node_t *child, /* I - Child node for where or MXML_ADD_TO_PARENT */
47 mxml_node_t *node) /* I - Node to add */
48{
49#ifdef DEBUG
50 fprintf(stderr, "mxmlAdd(parent=%p, where=%d, child=%p, node=%p)\n", parent,
51 where, child, node);
52#endif /* DEBUG */
53
54 /*
55 * Range check input...
56 */
57
58 if (!parent || !node)
59 return;
60
61#if DEBUG > 1
62 fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent);
63 if (parent)
64 {
65 fprintf(stderr, " BEFORE: parent->child=%p\n", parent->child);
66 fprintf(stderr, " BEFORE: parent->last_child=%p\n", parent->last_child);
67 fprintf(stderr, " BEFORE: parent->prev=%p\n", parent->prev);
68 fprintf(stderr, " BEFORE: parent->next=%p\n", parent->next);
69 }
70#endif /* DEBUG > 1 */
71
72 /*
73 * Remove the node from any existing parent...
74 */
75
76 if (node->parent)
77 mxmlRemove(node);
78
79 /*
80 * Reset pointers...
81 */
82
83 node->parent = parent;
84
85 switch (where)
86 {
87 case MXML_ADD_BEFORE :
88 if (!child || child == parent->child || child->parent != parent)
89 {
90 /*
91 * Insert as first node under parent...
92 */
93
94 node->next = parent->child;
95
96 if (parent->child)
97 parent->child->prev = node;
98 else
99 parent->last_child = node;
100
101 parent->child = node;
102 }
103 else
104 {
105 /*
106 * Insert node before this child...
107 */
108
109 node->next = child;
110 node->prev = child->prev;
111
112 if (child->prev)
113 child->prev->next = node;
114 else
115 parent->child = node;
116
117 child->prev = node;
118 }
119 break;
120
121 case MXML_ADD_AFTER :
122 if (!child || child == parent->last_child || child->parent != parent)
123 {
124 /*
125 * Insert as last node under parent...
126 */
127
128 node->parent = parent;
129 node->prev = parent->last_child;
130
131 if (parent->last_child)
132 parent->last_child->next = node;
133 else
134 parent->child = node;
135
136 parent->last_child = node;
137 }
138 else
139 {
140 /*
141 * Insert node after this child...
142 */
143
144 node->prev = child;
145 node->next = child->next;
146
147 if (child->next)
148 child->next->prev = node;
149 else
150 parent->last_child = node;
151
152 child->next = node;
153 }
154 break;
155 }
156
157#if DEBUG > 1
158 fprintf(stderr, " AFTER: node->parent=%p\n", node->parent);
159 if (parent)
160 {
161 fprintf(stderr, " AFTER: parent->child=%p\n", parent->child);
162 fprintf(stderr, " AFTER: parent->last_child=%p\n", parent->last_child);
163 fprintf(stderr, " AFTER: parent->prev=%p\n", parent->prev);
164 fprintf(stderr, " AFTER: parent->next=%p\n", parent->next);
165 }
166#endif /* DEBUG > 1 */
167}
168
169
170/*
171 * 'mxmlDelete()' - Delete a node and all of its children.
172 *
173 * If the specified node has a parent, this function first removes the
174 * node from its parent using the mxmlRemove() function.
175 */
176
177void
178mxmlDelete(mxml_node_t *node) /* I - Node to delete */
179{
180 int i; /* Looping var */
181
182
183#ifdef DEBUG
184 fprintf(stderr, "mxmlDelete(node=%p)\n", node);
185#endif /* DEBUG */
186
187 /*
188 * Range check input...
189 */
190
191 if (!node)
192 return;
193
194 /*
195 * Remove the node from its parent, if any...
196 */
197
198 mxmlRemove(node);
199
200 /*
201 * Delete children...
202 */
203
204 while (node->child)
205 mxmlDelete(node->child);
206
207 /*
208 * Now delete any node data...
209 */
210
211 switch (node->type)
212 {
213 case MXML_ELEMENT :
214 if (node->value.element.name)
215 free(node->value.element.name);
216
217 if (node->value.element.num_attrs)
218 {
219 for (i = 0; i < node->value.element.num_attrs; i ++)
220 {
221 if (node->value.element.attrs[i].name)
222 free(node->value.element.attrs[i].name);
223 if (node->value.element.attrs[i].value)
224 free(node->value.element.attrs[i].value);
225 }
226
227 free(node->value.element.attrs);
228 }
229 break;
230 case MXML_INTEGER :
231 /* Nothing to do */
232 break;
233 case MXML_OPAQUE :
234 if (node->value.opaque)
235 free(node->value.opaque);
236 break;
237 case MXML_REAL :
238 /* Nothing to do */
239 break;
240 case MXML_TEXT :
241 if (node->value.text.string)
242 free(node->value.text.string);
243 break;
244 case MXML_CUSTOM :
245 if (node->value.custom.data &&
246 node->value.custom.destroy)
247 (*(node->value.custom.destroy))(node->value.custom.data);
248 break;
249 default :
250 break;
251 }
252
253 /*
254 * Free this node...
255 */
256
257 free(node);
258}
259
260
261/*
262 * 'mxmlGetRefCount()' - Get the current reference (use) count for a node.
263 *
264 * The initial reference count of new nodes is 1. Use the @link mxmlRetain@
265 * and @link mxmlRelease@ functions to increment and decrement a node's
266 * reference count.
267 *
268 * @since Mini-XML 2.7@.
269 */
270
271int /* O - Reference count */
272mxmlGetRefCount(mxml_node_t *node) /* I - Node */
273{
274 /*
275 * Range check input...
276 */
277
278 if (!node)
279 return (0);
280
281 /*
282 * Return the reference count...
283 */
284
285 return (node->ref_count);
286}
287
288
289/*
290 * 'mxmlNewCDATA()' - Create a new CDATA node.
291 *
292 * The new CDATA node is added to the end of the specified parent's child
293 * list. The constant MXML_NO_PARENT can be used to specify that the new
294 * CDATA node has no parent. The data string must be nul-terminated and
295 * is copied into the new node. CDATA nodes use the MXML_ELEMENT type.
296 *
297 * @since Mini-XML 2.3@
298 */
299
300mxml_node_t * /* O - New node */
301mxmlNewCDATA(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
302 const char *data) /* I - Data string */
303{
304 mxml_node_t *node; /* New node */
305
306
307#ifdef DEBUG
308 fprintf(stderr, "mxmlNewCDATA(parent=%p, data=\"%s\")\n",
309 parent, data ? data : "(null)");
310#endif /* DEBUG */
311
312 /*
313 * Range check input...
314 */
315
316 if (!data)
317 return (NULL);
318
319 /*
320 * Create the node and set the name value...
321 */
322
323 if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL)
324 node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data);
325
326 return (node);
327}
328
329
330/*
331 * 'mxmlNewCustom()' - Create a new custom data node.
332 *
333 * The new custom node is added to the end of the specified parent's child
334 * list. The constant MXML_NO_PARENT can be used to specify that the new
335 * element node has no parent. NULL can be passed when the data in the
336 * node is not dynamically allocated or is separately managed.
337 *
338 * @since Mini-XML 2.1@
339 */
340
341mxml_node_t * /* O - New node */
342mxmlNewCustom(
343 mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
344 void *data, /* I - Pointer to data */
345 mxml_custom_destroy_cb_t destroy) /* I - Function to destroy data */
346{
347 mxml_node_t *node; /* New node */
348
349
350#ifdef DEBUG
351 fprintf(stderr, "mxmlNewCustom(parent=%p, data=%p, destroy=%p)\n", parent,
352 data, destroy);
353#endif /* DEBUG */
354
355 /*
356 * Create the node and set the value...
357 */
358
359 if ((node = mxml_new(parent, MXML_CUSTOM)) != NULL)
360 {
361 node->value.custom.data = data;
362 node->value.custom.destroy = destroy;
363 }
364
365 return (node);
366}
367
368
369/*
370 * 'mxmlNewElement()' - Create a new element node.
371 *
372 * The new element node is added to the end of the specified parent's child
373 * list. The constant MXML_NO_PARENT can be used to specify that the new
374 * element node has no parent.
375 */
376
377mxml_node_t * /* O - New node */
378mxmlNewElement(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
379 const char *name) /* I - Name of element */
380{
381 mxml_node_t *node; /* New node */
382
383
384#ifdef DEBUG
385 fprintf(stderr, "mxmlNewElement(parent=%p, name=\"%s\")\n", parent,
386 name ? name : "(null)");
387#endif /* DEBUG */
388
389 /*
390 * Range check input...
391 */
392
393 if (!name)
394 return (NULL);
395
396 /*
397 * Create the node and set the element name...
398 */
399
400 if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL)
401 node->value.element.name = strdup(name);
402
403 return (node);
404}
405
406
407/*
408 * 'mxmlNewInteger()' - Create a new integer node.
409 *
410 * The new integer node is added to the end of the specified parent's child
411 * list. The constant MXML_NO_PARENT can be used to specify that the new
412 * integer node has no parent.
413 */
414
415mxml_node_t * /* O - New node */
416mxmlNewInteger(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
417 int integer) /* I - Integer value */
418{
419 mxml_node_t *node; /* New node */
420
421
422#ifdef DEBUG
423 fprintf(stderr, "mxmlNewInteger(parent=%p, integer=%d)\n", parent, integer);
424#endif /* DEBUG */
425
426 /*
427 * Create the node and set the element name...
428 */
429
430 if ((node = mxml_new(parent, MXML_INTEGER)) != NULL)
431 node->value.integer = integer;
432
433 return (node);
434}
435
436
437/*
438 * 'mxmlNewOpaque()' - Create a new opaque string.
439 *
440 * The new opaque node is added to the end of the specified parent's child
441 * list. The constant MXML_NO_PARENT can be used to specify that the new
442 * opaque node has no parent. The opaque string must be nul-terminated and
443 * is copied into the new node.
444 */
445
446mxml_node_t * /* O - New node */
447mxmlNewOpaque(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
448 const char *opaque) /* I - Opaque string */
449{
450 mxml_node_t *node; /* New node */
451
452
453#ifdef DEBUG
454 fprintf(stderr, "mxmlNewOpaque(parent=%p, opaque=\"%s\")\n", parent,
455 opaque ? opaque : "(null)");
456#endif /* DEBUG */
457
458 /*
459 * Range check input...
460 */
461
462 if (!opaque)
463 return (NULL);
464
465 /*
466 * Create the node and set the element name...
467 */
468
469 if ((node = mxml_new(parent, MXML_OPAQUE)) != NULL)
470 node->value.opaque = strdup(opaque);
471
472 return (node);
473}
474
475
476/*
477 * 'mxmlNewReal()' - Create a new real number node.
478 *
479 * The new real number node is added to the end of the specified parent's
480 * child list. The constant MXML_NO_PARENT can be used to specify that
481 * the new real number node has no parent.
482 */
483
484mxml_node_t * /* O - New node */
485mxmlNewReal(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
486 double real) /* I - Real number value */
487{
488 mxml_node_t *node; /* New node */
489
490
491#ifdef DEBUG
492 fprintf(stderr, "mxmlNewReal(parent=%p, real=%g)\n", parent, real);
493#endif /* DEBUG */
494
495 /*
496 * Create the node and set the element name...
497 */
498
499 if ((node = mxml_new(parent, MXML_REAL)) != NULL)
500 node->value.real = real;
501
502 return (node);
503}
504
505
506/*
507 * 'mxmlNewText()' - Create a new text fragment node.
508 *
509 * The new text node is added to the end of the specified parent's child
510 * list. The constant MXML_NO_PARENT can be used to specify that the new
511 * text node has no parent. The whitespace parameter is used to specify
512 * whether leading whitespace is present before the node. The text
Jon Medhurst96b56152014-10-30 18:01:15 +0000513 * string must be nul-terminated and is copied into the new node.
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100514 */
515
516mxml_node_t * /* O - New node */
517mxmlNewText(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
518 int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */
519 const char *string) /* I - String */
520{
521 mxml_node_t *node; /* New node */
522
523
524#ifdef DEBUG
525 fprintf(stderr, "mxmlNewText(parent=%p, whitespace=%d, string=\"%s\")\n",
526 parent, whitespace, string ? string : "(null)");
527#endif /* DEBUG */
528
529 /*
530 * Range check input...
531 */
532
533 if (!string)
534 return (NULL);
535
536 /*
537 * Create the node and set the text value...
538 */
539
540 if ((node = mxml_new(parent, MXML_TEXT)) != NULL)
541 {
542 node->value.text.whitespace = whitespace;
543 node->value.text.string = strdup(string);
544 }
545
546 return (node);
547}
548
549
550/*
551 * 'mxmlNewTextf()' - Create a new formatted text fragment node.
552 *
553 * The new text node is added to the end of the specified parent's child
554 * list. The constant MXML_NO_PARENT can be used to specify that the new
555 * text node has no parent. The whitespace parameter is used to specify
556 * whether leading whitespace is present before the node. The format
Jon Medhurst96b56152014-10-30 18:01:15 +0000557 * string must be nul-terminated and is formatted into the new node.
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100558 */
559
560mxml_node_t * /* O - New node */
561mxmlNewTextf(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
562 int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */
563 const char *format, /* I - Printf-style frmat string */
564 ...) /* I - Additional args as needed */
565{
566 mxml_node_t *node; /* New node */
567 va_list ap; /* Pointer to arguments */
568
569
570#ifdef DEBUG
571 fprintf(stderr, "mxmlNewTextf(parent=%p, whitespace=%d, format=\"%s\", ...)\n",
572 parent, whitespace, format ? format : "(null)");
573#endif /* DEBUG */
574
575 /*
576 * Range check input...
577 */
578
579 if (!format)
580 return (NULL);
581
582 /*
583 * Create the node and set the text value...
584 */
585
586 if ((node = mxml_new(parent, MXML_TEXT)) != NULL)
587 {
588 va_start(ap, format);
589
590 node->value.text.whitespace = whitespace;
591 node->value.text.string = _mxml_vstrdupf(format, ap);
592
593 va_end(ap);
594 }
595
596 return (node);
597}
598
599
600/*
601 * 'mxmlRemove()' - Remove a node from its parent.
602 *
603 * Does not free memory used by the node - use mxmlDelete() for that.
604 * This function does nothing if the node has no parent.
605 */
606
607void
608mxmlRemove(mxml_node_t *node) /* I - Node to remove */
609{
610#ifdef DEBUG
611 fprintf(stderr, "mxmlRemove(node=%p)\n", node);
612#endif /* DEBUG */
613
614 /*
615 * Range check input...
616 */
617
618 if (!node || !node->parent)
619 return;
620
621 /*
622 * Remove from parent...
623 */
624
625#if DEBUG > 1
626 fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent);
627 if (node->parent)
628 {
629 fprintf(stderr, " BEFORE: node->parent->child=%p\n", node->parent->child);
630 fprintf(stderr, " BEFORE: node->parent->last_child=%p\n", node->parent->last_child);
631 }
632 fprintf(stderr, " BEFORE: node->child=%p\n", node->child);
633 fprintf(stderr, " BEFORE: node->last_child=%p\n", node->last_child);
634 fprintf(stderr, " BEFORE: node->prev=%p\n", node->prev);
635 fprintf(stderr, " BEFORE: node->next=%p\n", node->next);
636#endif /* DEBUG > 1 */
637
638 if (node->prev)
639 node->prev->next = node->next;
640 else
641 node->parent->child = node->next;
642
643 if (node->next)
644 node->next->prev = node->prev;
645 else
646 node->parent->last_child = node->prev;
647
648 node->parent = NULL;
649 node->prev = NULL;
650 node->next = NULL;
651
652#if DEBUG > 1
653 fprintf(stderr, " AFTER: node->parent=%p\n", node->parent);
654 if (node->parent)
655 {
656 fprintf(stderr, " AFTER: node->parent->child=%p\n", node->parent->child);
657 fprintf(stderr, " AFTER: node->parent->last_child=%p\n", node->parent->last_child);
658 }
659 fprintf(stderr, " AFTER: node->child=%p\n", node->child);
660 fprintf(stderr, " AFTER: node->last_child=%p\n", node->last_child);
661 fprintf(stderr, " AFTER: node->prev=%p\n", node->prev);
662 fprintf(stderr, " AFTER: node->next=%p\n", node->next);
663#endif /* DEBUG > 1 */
664}
665
666
667/*
668 * 'mxmlNewXML()' - Create a new XML document tree.
669 *
670 * The "version" argument specifies the version number to put in the
671 * ?xml element node. If NULL, version 1.0 is assumed.
672 *
673 * @since Mini-XML 2.3@
674 */
675
676mxml_node_t * /* O - New ?xml node */
677mxmlNewXML(const char *version) /* I - Version number to use */
678{
679 char element[1024]; /* Element text */
680
681
682 snprintf(element, sizeof(element), "?xml version=\"%s\" encoding=\"utf-8\"?",
683 version ? version : "1.0");
684
685 return (mxmlNewElement(NULL, element));
686}
687
688
689/*
690 * 'mxmlRelease()' - Release a node.
691 *
692 * When the reference count reaches zero, the node (and any children)
693 * is deleted via mxmlDelete().
694 *
695 * @since Mini-XML 2.3@
696 */
697
698int /* O - New reference count */
699mxmlRelease(mxml_node_t *node) /* I - Node */
700{
701 if (node)
702 {
703 if ((-- node->ref_count) <= 0)
704 {
705 mxmlDelete(node);
706 return (0);
707 }
708 else
709 return (node->ref_count);
710 }
711 else
712 return (-1);
713}
714
715
716/*
717 * 'mxmlRetain()' - Retain a node.
718 *
719 * @since Mini-XML 2.3@
720 */
721
722int /* O - New reference count */
723mxmlRetain(mxml_node_t *node) /* I - Node */
724{
725 if (node)
726 return (++ node->ref_count);
727 else
728 return (-1);
729}
730
731
732/*
733 * 'mxml_new()' - Create a new node.
734 */
735
736static mxml_node_t * /* O - New node */
737mxml_new(mxml_node_t *parent, /* I - Parent node */
738 mxml_type_t type) /* I - Node type */
739{
740 mxml_node_t *node; /* New node */
741
742
743#if DEBUG > 1
744 fprintf(stderr, "mxml_new(parent=%p, type=%d)\n", parent, type);
745#endif /* DEBUG > 1 */
746
747 /*
748 * Allocate memory for the node...
749 */
750
751 if ((node = calloc(1, sizeof(mxml_node_t))) == NULL)
752 {
753#if DEBUG > 1
754 fputs(" returning NULL\n", stderr);
755#endif /* DEBUG > 1 */
756
757 return (NULL);
758 }
759
760#if DEBUG > 1
761 fprintf(stderr, " returning %p\n", node);
762#endif /* DEBUG > 1 */
763
764 /*
765 * Set the node type...
766 */
767
768 node->type = type;
769 node->ref_count = 1;
770
771 /*
772 * Add to the parent if present...
773 */
774
775 if (parent)
776 mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node);
777
778 /*
779 * Return the new node...
780 */
781
782 return (node);
783}
784
785
786/*
Jon Medhurst96b56152014-10-30 18:01:15 +0000787 * End of "$Id: mxml-node.c 451 2014-01-04 21:50:06Z msweet $".
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100788 */