2 * "$Id: mxml-node.c 451 2014-01-04 21:50:06Z msweet $"
4 * Node support code for Mini-XML, a small XML-like file parsing library.
6 * Copyright 2003-2014 by Michael R Sweet.
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:
14 * http://www.msweet.org/projects.php/Mini-XML
18 * Include necessary headers...
29 static mxml_node_t *mxml_new(mxml_node_t *parent, mxml_type_t type);
33 * 'mxmlAdd()' - Add a node to a tree.
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.
44 mxmlAdd(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 */
50 fprintf(stderr, "mxmlAdd(parent=%p, where=%d, child=%p, node=%p)\n", parent,
55 * Range check input...
62 fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent);
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);
70 #endif /* DEBUG > 1 */
73 * Remove the node from any existing parent...
83 node->parent = parent;
87 case MXML_ADD_BEFORE :
88 if (!child || child == parent->child || child->parent != parent)
91 * Insert as first node under parent...
94 node->next = parent->child;
97 parent->child->prev = node;
99 parent->last_child = node;
101 parent->child = node;
106 * Insert node before this child...
110 node->prev = child->prev;
113 child->prev->next = node;
115 parent->child = node;
121 case MXML_ADD_AFTER :
122 if (!child || child == parent->last_child || child->parent != parent)
125 * Insert as last node under parent...
128 node->parent = parent;
129 node->prev = parent->last_child;
131 if (parent->last_child)
132 parent->last_child->next = node;
134 parent->child = node;
136 parent->last_child = node;
141 * Insert node after this child...
145 node->next = child->next;
148 child->next->prev = node;
150 parent->last_child = node;
158 fprintf(stderr, " AFTER: node->parent=%p\n", node->parent);
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);
166 #endif /* DEBUG > 1 */
171 * 'mxmlDelete()' - Delete a node and all of its children.
173 * If the specified node has a parent, this function first removes the
174 * node from its parent using the mxmlRemove() function.
178 mxmlDelete(mxml_node_t *node) /* I - Node to delete */
180 int i; /* Looping var */
184 fprintf(stderr, "mxmlDelete(node=%p)\n", node);
188 * Range check input...
195 * Remove the node from its parent, if any...
205 mxmlDelete(node->child);
208 * Now delete any node data...
214 if (node->value.element.name)
215 free(node->value.element.name);
217 if (node->value.element.num_attrs)
219 for (i = 0; i < node->value.element.num_attrs; i ++)
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);
227 free(node->value.element.attrs);
234 if (node->value.opaque)
235 free(node->value.opaque);
241 if (node->value.text.string)
242 free(node->value.text.string);
245 if (node->value.custom.data &&
246 node->value.custom.destroy)
247 (*(node->value.custom.destroy))(node->value.custom.data);
262 * 'mxmlGetRefCount()' - Get the current reference (use) count for a node.
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
268 * @since Mini-XML 2.7@.
271 int /* O - Reference count */
272 mxmlGetRefCount(mxml_node_t *node) /* I - Node */
275 * Range check input...
282 * Return the reference count...
285 return (node->ref_count);
290 * 'mxmlNewCDATA()' - Create a new CDATA node.
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.
297 * @since Mini-XML 2.3@
300 mxml_node_t * /* O - New node */
301 mxmlNewCDATA(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
302 const char *data) /* I - Data string */
304 mxml_node_t *node; /* New node */
308 fprintf(stderr, "mxmlNewCDATA(parent=%p, data=\"%s\")\n",
309 parent, data ? data : "(null)");
313 * Range check input...
320 * Create the node and set the name value...
323 if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL)
324 node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data);
331 * 'mxmlNewCustom()' - Create a new custom data node.
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.
338 * @since Mini-XML 2.1@
341 mxml_node_t * /* O - New node */
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 */
347 mxml_node_t *node; /* New node */
351 fprintf(stderr, "mxmlNewCustom(parent=%p, data=%p, destroy=%p)\n", parent,
356 * Create the node and set the value...
359 if ((node = mxml_new(parent, MXML_CUSTOM)) != NULL)
361 node->value.custom.data = data;
362 node->value.custom.destroy = destroy;
370 * 'mxmlNewElement()' - Create a new element node.
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.
377 mxml_node_t * /* O - New node */
378 mxmlNewElement(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
379 const char *name) /* I - Name of element */
381 mxml_node_t *node; /* New node */
385 fprintf(stderr, "mxmlNewElement(parent=%p, name=\"%s\")\n", parent,
386 name ? name : "(null)");
390 * Range check input...
397 * Create the node and set the element name...
400 if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL)
401 node->value.element.name = strdup(name);
408 * 'mxmlNewInteger()' - Create a new integer node.
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.
415 mxml_node_t * /* O - New node */
416 mxmlNewInteger(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
417 int integer) /* I - Integer value */
419 mxml_node_t *node; /* New node */
423 fprintf(stderr, "mxmlNewInteger(parent=%p, integer=%d)\n", parent, integer);
427 * Create the node and set the element name...
430 if ((node = mxml_new(parent, MXML_INTEGER)) != NULL)
431 node->value.integer = integer;
438 * 'mxmlNewOpaque()' - Create a new opaque string.
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.
446 mxml_node_t * /* O - New node */
447 mxmlNewOpaque(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
448 const char *opaque) /* I - Opaque string */
450 mxml_node_t *node; /* New node */
454 fprintf(stderr, "mxmlNewOpaque(parent=%p, opaque=\"%s\")\n", parent,
455 opaque ? opaque : "(null)");
459 * Range check input...
466 * Create the node and set the element name...
469 if ((node = mxml_new(parent, MXML_OPAQUE)) != NULL)
470 node->value.opaque = strdup(opaque);
477 * 'mxmlNewReal()' - Create a new real number node.
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.
484 mxml_node_t * /* O - New node */
485 mxmlNewReal(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
486 double real) /* I - Real number value */
488 mxml_node_t *node; /* New node */
492 fprintf(stderr, "mxmlNewReal(parent=%p, real=%g)\n", parent, real);
496 * Create the node and set the element name...
499 if ((node = mxml_new(parent, MXML_REAL)) != NULL)
500 node->value.real = real;
507 * 'mxmlNewText()' - Create a new text fragment node.
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
513 * string must be nul-terminated and is copied into the new node.
516 mxml_node_t * /* O - New node */
517 mxmlNewText(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 */
521 mxml_node_t *node; /* New node */
525 fprintf(stderr, "mxmlNewText(parent=%p, whitespace=%d, string=\"%s\")\n",
526 parent, whitespace, string ? string : "(null)");
530 * Range check input...
537 * Create the node and set the text value...
540 if ((node = mxml_new(parent, MXML_TEXT)) != NULL)
542 node->value.text.whitespace = whitespace;
543 node->value.text.string = strdup(string);
551 * 'mxmlNewTextf()' - Create a new formatted text fragment node.
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
557 * string must be nul-terminated and is formatted into the new node.
560 mxml_node_t * /* O - New node */
561 mxmlNewTextf(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 */
566 mxml_node_t *node; /* New node */
567 va_list ap; /* Pointer to arguments */
571 fprintf(stderr, "mxmlNewTextf(parent=%p, whitespace=%d, format=\"%s\", ...)\n",
572 parent, whitespace, format ? format : "(null)");
576 * Range check input...
583 * Create the node and set the text value...
586 if ((node = mxml_new(parent, MXML_TEXT)) != NULL)
588 va_start(ap, format);
590 node->value.text.whitespace = whitespace;
591 node->value.text.string = _mxml_vstrdupf(format, ap);
601 * 'mxmlRemove()' - Remove a node from its parent.
603 * Does not free memory used by the node - use mxmlDelete() for that.
604 * This function does nothing if the node has no parent.
608 mxmlRemove(mxml_node_t *node) /* I - Node to remove */
611 fprintf(stderr, "mxmlRemove(node=%p)\n", node);
615 * Range check input...
618 if (!node || !node->parent)
622 * Remove from parent...
626 fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent);
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);
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 */
639 node->prev->next = node->next;
641 node->parent->child = node->next;
644 node->next->prev = node->prev;
646 node->parent->last_child = node->prev;
653 fprintf(stderr, " AFTER: node->parent=%p\n", node->parent);
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);
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 */
668 * 'mxmlNewXML()' - Create a new XML document tree.
670 * The "version" argument specifies the version number to put in the
671 * ?xml element node. If NULL, version 1.0 is assumed.
673 * @since Mini-XML 2.3@
676 mxml_node_t * /* O - New ?xml node */
677 mxmlNewXML(const char *version) /* I - Version number to use */
679 char element[1024]; /* Element text */
682 snprintf(element, sizeof(element), "?xml version=\"%s\" encoding=\"utf-8\"?",
683 version ? version : "1.0");
685 return (mxmlNewElement(NULL, element));
690 * 'mxmlRelease()' - Release a node.
692 * When the reference count reaches zero, the node (and any children)
693 * is deleted via mxmlDelete().
695 * @since Mini-XML 2.3@
698 int /* O - New reference count */
699 mxmlRelease(mxml_node_t *node) /* I - Node */
703 if ((-- node->ref_count) <= 0)
709 return (node->ref_count);
717 * 'mxmlRetain()' - Retain a node.
719 * @since Mini-XML 2.3@
722 int /* O - New reference count */
723 mxmlRetain(mxml_node_t *node) /* I - Node */
726 return (++ node->ref_count);
733 * 'mxml_new()' - Create a new node.
736 static mxml_node_t * /* O - New node */
737 mxml_new(mxml_node_t *parent, /* I - Parent node */
738 mxml_type_t type) /* I - Node type */
740 mxml_node_t *node; /* New node */
744 fprintf(stderr, "mxml_new(parent=%p, type=%d)\n", parent, type);
745 #endif /* DEBUG > 1 */
748 * Allocate memory for the node...
751 if ((node = calloc(1, sizeof(mxml_node_t))) == NULL)
754 fputs(" returning NULL\n", stderr);
755 #endif /* DEBUG > 1 */
761 fprintf(stderr, " returning %p\n", node);
762 #endif /* DEBUG > 1 */
765 * Set the node type...
772 * Add to the parent if present...
776 mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node);
779 * Return the new node...
787 * End of "$Id: mxml-node.c 451 2014-01-04 21:50:06Z msweet $".