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