don't blow up if a jitterbuffer is not in use
[asterisk/asterisk.git] / mxml / mxml-node.c
1 /*
2  * "$Id$"
3  *
4  * Node support code for Mini-XML, a small XML-like file parsing library.
5  *
6  * Copyright 2003-2005 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  *   mxmlNewElement() - Create a new element node.
23  *   mxmlNewInteger() - Create a new integer node.
24  *   mxmlNewOpaque()  - Create a new opaque string.
25  *   mxmlNewReal()    - Create a new real number node.
26  *   mxmlNewText()    - Create a new text fragment node.
27  *   mxmlNewTextf()   - Create a new formatted text fragment node.
28  *   mxmlRemove()     - Remove a node from its parent.
29  *   mxml_new()       - Create a new node.
30  */
31
32 /*
33  * Include necessary headers...
34  */
35
36 #include "config.h"
37 #include "mxml.h"
38
39
40 /*
41  * Local functions...
42  */
43
44 static mxml_node_t      *mxml_new(mxml_node_t *parent, mxml_type_t type);
45
46
47 /*
48  * 'mxmlAdd()' - Add a node to a tree.
49  *
50  * Adds the specified node to the parent. If the child argument is not
51  * NULL, puts the new node before or after the specified child depending
52  * on the value of the where argument. If the child argument is NULL,
53  * puts the new node at the beginning of the child list (MXML_ADD_BEFORE)
54  * or at the end of the child list (MXML_ADD_AFTER). The constant
55  * MXML_ADD_TO_PARENT can be used to specify a NULL child pointer.
56  */
57
58 void
59 mxmlAdd(mxml_node_t *parent,            /* I - Parent node */
60         int         where,              /* I - Where to add, MXML_ADD_BEFORE or MXML_ADD_AFTER */
61         mxml_node_t *child,             /* I - Child node for where or MXML_ADD_TO_PARENT */
62         mxml_node_t *node)              /* I - Node to add */
63 {
64 #ifdef DEBUG
65   fprintf(stderr, "mxmlAdd(parent=%p, where=%d, child=%p, node=%p)\n", parent,
66           where, child, node);
67 #endif /* DEBUG */
68
69  /*
70   * Range check input...
71   */
72
73   if (!parent || !node)
74     return;
75
76 #if DEBUG > 1
77   fprintf(stderr, "    BEFORE: node->parent=%p\n", node->parent);
78   if (parent)
79   {
80     fprintf(stderr, "    BEFORE: parent->child=%p\n", parent->child);
81     fprintf(stderr, "    BEFORE: parent->last_child=%p\n", parent->last_child);
82     fprintf(stderr, "    BEFORE: parent->prev=%p\n", parent->prev);
83     fprintf(stderr, "    BEFORE: parent->next=%p\n", parent->next);
84   }
85 #endif /* DEBUG > 1 */
86
87  /*
88   * Remove the node from any existing parent...
89   */
90
91   if (node->parent)
92     mxmlRemove(node);
93
94  /*
95   * Reset pointers...
96   */
97
98   node->parent = parent;
99
100   switch (where)
101   {
102     case MXML_ADD_BEFORE :
103         if (!child || child == parent->child || child->parent != parent)
104         {
105          /*
106           * Insert as first node under parent...
107           */
108
109           node->next = parent->child;
110
111           if (parent->child)
112             parent->child->prev = node;
113           else
114             parent->last_child = node;
115
116           parent->child = node;
117         }
118         else
119         {
120          /*
121           * Insert node before this child...
122           */
123
124           node->next = child;
125           node->prev = child->prev;
126
127           if (child->prev)
128             child->prev->next = node;
129           else
130             parent->child = node;
131
132           child->prev = node;
133         }
134         break;
135
136     case MXML_ADD_AFTER :
137         if (!child || child == parent->last_child || child->parent != parent)
138         {
139          /*
140           * Insert as last node under parent...
141           */
142
143           node->parent = parent;
144           node->prev   = parent->last_child;
145
146           if (parent->last_child)
147             parent->last_child->next = node;
148           else
149             parent->child = node;
150
151           parent->last_child = node;
152         }
153         else
154         {
155          /*
156           * Insert node after this child...
157           */
158
159           node->prev = child;
160           node->next = child->next;
161
162           if (child->next)
163             child->next->prev = node;
164           else
165             parent->last_child = node;
166
167           child->next = node;
168         }
169         break;
170   }
171
172 #if DEBUG > 1
173   fprintf(stderr, "    AFTER: node->parent=%p\n", node->parent);
174   if (parent)
175   {
176     fprintf(stderr, "    AFTER: parent->child=%p\n", parent->child);
177     fprintf(stderr, "    AFTER: parent->last_child=%p\n", parent->last_child);
178     fprintf(stderr, "    AFTER: parent->prev=%p\n", parent->prev);
179     fprintf(stderr, "    AFTER: parent->next=%p\n", parent->next);
180   }
181 #endif /* DEBUG > 1 */
182 }
183
184
185 /*
186  * 'mxmlDelete()' - Delete a node and all of its children.
187  *
188  * If the specified node has a parent, this function first removes the
189  * node from its parent using the mxmlRemove() function.
190  */
191
192 void
193 mxmlDelete(mxml_node_t *node)           /* I - Node to delete */
194 {
195   int   i;                              /* Looping var */
196
197
198 #ifdef DEBUG
199   fprintf(stderr, "mxmlDelete(node=%p)\n", node);
200 #endif /* DEBUG */
201
202  /*
203   * Range check input...
204   */
205
206   if (!node)
207     return;
208
209  /*
210   * Remove the node from its parent, if any...
211   */
212
213   mxmlRemove(node);
214
215  /*
216   * Delete children...
217   */
218
219   while (node->child)
220     mxmlDelete(node->child);
221
222  /*
223   * Now delete any node data...
224   */
225
226   switch (node->type)
227   {
228     case MXML_ELEMENT :
229         if (node->value.element.name)
230           free(node->value.element.name);
231
232         if (node->value.element.num_attrs)
233         {
234           for (i = 0; i < node->value.element.num_attrs; i ++)
235           {
236             if (node->value.element.attrs[i].name)
237               free(node->value.element.attrs[i].name);
238             if (node->value.element.attrs[i].value)
239               free(node->value.element.attrs[i].value);
240           }
241
242           free(node->value.element.attrs);
243         }
244         break;
245     case MXML_INTEGER :
246        /* Nothing to do */
247         break;
248     case MXML_OPAQUE :
249         if (node->value.opaque)
250           free(node->value.opaque);
251         break;
252     case MXML_REAL :
253        /* Nothing to do */
254         break;
255     case MXML_TEXT :
256         if (node->value.text.string)
257           free(node->value.text.string);
258         break;
259     case MXML_CUSTOM :
260         if (node->value.custom.data &&
261             node->value.custom.destroy)
262           (*(node->value.custom.destroy))(node->value.custom.data);
263         break;
264   }
265
266  /*
267   * Free this node...
268   */
269
270   free(node);
271 }
272
273
274 /*
275  * 'mxmlNewCustom()' - Create a new custom data node.
276  *
277  * The new custom node is added to the end of the specified parent's child
278  * list. The constant MXML_NO_PARENT can be used to specify that the new
279  * element node has no parent. NULL can be passed when the data in the
280  * node is not dynamically allocated or is separately managed.
281  */
282
283 mxml_node_t *                           /* O - New node */
284 mxmlNewCustom(mxml_node_t *parent,      /* I - Parent node or MXML_NO_PARENT */
285               void        *data,        /* I - Pointer to data */
286               void        (*destroy)(void *))
287                                         /* I - Function to destroy data */
288 {
289   mxml_node_t   *node;                  /* New node */
290
291
292 #ifdef DEBUG
293   fprintf(stderr, "mxmlNewCustom(parent=%p, data=%p, destroy=%p)\n", parent,
294           data, destroy);
295 #endif /* DEBUG */
296
297  /*
298   * Create the node and set the value...
299   */
300
301   if ((node = mxml_new(parent, MXML_CUSTOM)) != NULL)
302   {
303     node->value.custom.data    = data;
304     node->value.custom.destroy = destroy;
305   }
306
307   return (node);
308 }
309
310
311 /*
312  * 'mxmlNewElement()' - Create a new element node.
313  *
314  * The new element node is added to the end of the specified parent's child
315  * list. The constant MXML_NO_PARENT can be used to specify that the new
316  * element node has no parent.
317  */
318
319 mxml_node_t *                           /* O - New node */
320 mxmlNewElement(mxml_node_t *parent,     /* I - Parent node or MXML_NO_PARENT */
321                const char  *name)       /* I - Name of element */
322 {
323   mxml_node_t   *node;                  /* New node */
324
325
326 #ifdef DEBUG
327   fprintf(stderr, "mxmlNewElement(parent=%p, name=\"%s\")\n", parent,
328           name ? name : "(null)");
329 #endif /* DEBUG */
330
331  /*
332   * Range check input...
333   */
334
335   if (!name)
336     return (NULL);
337
338  /*
339   * Create the node and set the element name...
340   */
341
342   if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL)
343     node->value.element.name = strdup(name);
344
345   return (node);
346 }
347
348
349 /*
350  * 'mxmlNewInteger()' - Create a new integer node.
351  *
352  * The new integer node is added to the end of the specified parent's child
353  * list. The constant MXML_NO_PARENT can be used to specify that the new
354  * integer node has no parent.
355  */
356
357 mxml_node_t *                           /* O - New node */
358 mxmlNewInteger(mxml_node_t *parent,     /* I - Parent node or MXML_NO_PARENT */
359                int         integer)     /* I - Integer value */
360 {
361   mxml_node_t   *node;                  /* New node */
362
363
364 #ifdef DEBUG
365   fprintf(stderr, "mxmlNewInteger(parent=%p, integer=%d)\n", parent, integer);
366 #endif /* DEBUG */
367
368  /*
369   * Create the node and set the element name...
370   */
371
372   if ((node = mxml_new(parent, MXML_INTEGER)) != NULL)
373     node->value.integer = integer;
374
375   return (node);
376 }
377
378
379 /*
380  * 'mxmlNewOpaque()' - Create a new opaque string.
381  *
382  * The new opaque node is added to the end of the specified parent's child
383  * list. The constant MXML_NO_PARENT can be used to specify that the new
384  * opaque node has no parent. The opaque string must be nul-terminated and
385  * is copied into the new node.
386  */
387
388 mxml_node_t *                           /* O - New node */
389 mxmlNewOpaque(mxml_node_t *parent,      /* I - Parent node or MXML_NO_PARENT */
390               const char  *opaque)      /* I - Opaque string */
391 {
392   mxml_node_t   *node;                  /* New node */
393
394
395 #ifdef DEBUG
396   fprintf(stderr, "mxmlNewOpaque(parent=%p, opaque=\"%s\")\n", parent,
397           opaque ? opaque : "(null)");
398 #endif /* DEBUG */
399
400  /*
401   * Range check input...
402   */
403
404   if (!opaque)
405     return (NULL);
406
407  /*
408   * Create the node and set the element name...
409   */
410
411   if ((node = mxml_new(parent, MXML_OPAQUE)) != NULL)
412     node->value.opaque = strdup(opaque);
413
414   return (node);
415 }
416
417
418 /*
419  * 'mxmlNewReal()' - Create a new real number node.
420  *
421  * The new real number node is added to the end of the specified parent's
422  * child list. The constant MXML_NO_PARENT can be used to specify that
423  * the new real number node has no parent.
424  */
425
426 mxml_node_t *                           /* O - New node */
427 mxmlNewReal(mxml_node_t *parent,        /* I - Parent node or MXML_NO_PARENT */
428             double      real)           /* I - Real number value */
429 {
430   mxml_node_t   *node;                  /* New node */
431
432
433 #ifdef DEBUG
434   fprintf(stderr, "mxmlNewReal(parent=%p, real=%g)\n", parent, real);
435 #endif /* DEBUG */
436
437  /*
438   * Create the node and set the element name...
439   */
440
441   if ((node = mxml_new(parent, MXML_REAL)) != NULL)
442     node->value.real = real;
443
444   return (node);
445 }
446
447
448 /*
449  * 'mxmlNewText()' - Create a new text fragment node.
450  *
451  * The new text node is added to the end of the specified parent's child
452  * list. The constant MXML_NO_PARENT can be used to specify that the new
453  * text node has no parent. The whitespace parameter is used to specify
454  * whether leading whitespace is present before the node. The text
455  * string must be nul-terminated and is copied into the new node.  
456  */
457
458 mxml_node_t *                           /* O - New node */
459 mxmlNewText(mxml_node_t *parent,        /* I - Parent node or MXML_NO_PARENT */
460             int         whitespace,     /* I - 1 = leading whitespace, 0 = no whitespace */
461             const char  *string)        /* I - String */
462 {
463   mxml_node_t   *node;                  /* New node */
464
465
466 #ifdef DEBUG
467   fprintf(stderr, "mxmlNewText(parent=%p, whitespace=%d, string=\"%s\")\n",
468           parent, whitespace, string ? string : "(null)");
469 #endif /* DEBUG */
470
471  /*
472   * Range check input...
473   */
474
475   if (!string)
476     return (NULL);
477
478  /*
479   * Create the node and set the text value...
480   */
481
482   if ((node = mxml_new(parent, MXML_TEXT)) != NULL)
483   {
484     node->value.text.whitespace = whitespace;
485     node->value.text.string     = strdup(string);
486   }
487
488   return (node);
489 }
490
491
492 /*
493  * 'mxmlNewTextf()' - Create a new formatted text fragment node.
494  *
495  * The new text node is added to the end of the specified parent's child
496  * list. The constant MXML_NO_PARENT can be used to specify that the new
497  * text node has no parent. The whitespace parameter is used to specify
498  * whether leading whitespace is present before the node. The format
499  * string must be nul-terminated and is formatted into the new node.  
500  */
501
502 mxml_node_t *                           /* O - New node */
503 mxmlNewTextf(mxml_node_t *parent,       /* I - Parent node or MXML_NO_PARENT */
504              int         whitespace,    /* I - 1 = leading whitespace, 0 = no whitespace */
505              const char  *format,       /* I - Printf-style frmat string */
506              ...)                       /* I - Additional args as needed */
507 {
508   mxml_node_t   *node;                  /* New node */
509   va_list       ap;                     /* Pointer to arguments */
510
511
512 #ifdef DEBUG
513   fprintf(stderr, "mxmlNewTextf(parent=%p, whitespace=%d, format=\"%s\", ...)\n",
514           parent, whitespace, format ? format : "(null)");
515 #endif /* DEBUG */
516
517  /*
518   * Range check input...
519   */
520
521   if (!format)
522     return (NULL);
523
524  /*
525   * Create the node and set the text value...
526   */
527
528   if ((node = mxml_new(parent, MXML_TEXT)) != NULL)
529   {
530     va_start(ap, format);
531
532     node->value.text.whitespace = whitespace;
533     node->value.text.string     = mxml_strdupf(format, ap);
534
535     va_end(ap);
536   }
537
538   return (node);
539 }
540
541
542 /*
543  * 'mxmlRemove()' - Remove a node from its parent.
544  *
545  * Does not free memory used by the node - use mxmlDelete() for that.
546  * This function does nothing if the node has no parent.
547  */
548
549 void
550 mxmlRemove(mxml_node_t *node)           /* I - Node to remove */
551 {
552 #ifdef DEBUG
553   fprintf(stderr, "mxmlRemove(node=%p)\n", node);
554 #endif /* DEBUG */
555
556  /*
557   * Range check input...
558   */
559
560   if (!node || !node->parent)
561     return;
562
563  /*
564   * Remove from parent...
565   */
566
567 #if DEBUG > 1
568   fprintf(stderr, "    BEFORE: node->parent=%p\n", node->parent);
569   if (node->parent)
570   {
571     fprintf(stderr, "    BEFORE: node->parent->child=%p\n", node->parent->child);
572     fprintf(stderr, "    BEFORE: node->parent->last_child=%p\n", node->parent->last_child);
573   }
574   fprintf(stderr, "    BEFORE: node->child=%p\n", node->child);
575   fprintf(stderr, "    BEFORE: node->last_child=%p\n", node->last_child);
576   fprintf(stderr, "    BEFORE: node->prev=%p\n", node->prev);
577   fprintf(stderr, "    BEFORE: node->next=%p\n", node->next);
578 #endif /* DEBUG > 1 */
579
580   if (node->prev)
581     node->prev->next = node->next;
582   else
583     node->parent->child = node->next;
584
585   if (node->next)
586     node->next->prev = node->prev;
587   else
588     node->parent->last_child = node->prev;
589
590   node->parent = NULL;
591   node->prev   = NULL;
592   node->next   = NULL;
593
594 #if DEBUG > 1
595   fprintf(stderr, "    AFTER: node->parent=%p\n", node->parent);
596   if (node->parent)
597   {
598     fprintf(stderr, "    AFTER: node->parent->child=%p\n", node->parent->child);
599     fprintf(stderr, "    AFTER: node->parent->last_child=%p\n", node->parent->last_child);
600   }
601   fprintf(stderr, "    AFTER: node->child=%p\n", node->child);
602   fprintf(stderr, "    AFTER: node->last_child=%p\n", node->last_child);
603   fprintf(stderr, "    AFTER: node->prev=%p\n", node->prev);
604   fprintf(stderr, "    AFTER: node->next=%p\n", node->next);
605 #endif /* DEBUG > 1 */
606 }
607
608
609 /*
610  * 'mxml_new()' - Create a new node.
611  */
612
613 static mxml_node_t *                    /* O - New node */
614 mxml_new(mxml_node_t *parent,           /* I - Parent node */
615          mxml_type_t type)              /* I - Node type */
616 {
617   mxml_node_t   *node;                  /* New node */
618
619
620 #if DEBUG > 1
621   fprintf(stderr, "mxml_new(parent=%p, type=%d)\n", parent, type);
622 #endif /* DEBUG > 1 */
623
624  /*
625   * Allocate memory for the node...
626   */
627
628   if ((node = calloc(1, sizeof(mxml_node_t))) == NULL)
629   {
630 #if DEBUG > 1
631     fputs("    returning NULL\n", stderr);
632 #endif /* DEBUG > 1 */
633
634     return (NULL);
635   }
636
637 #if DEBUG > 1
638   fprintf(stderr, "    returning %p\n", node);
639 #endif /* DEBUG > 1 */
640
641  /*
642   * Set the node type...
643   */
644
645   node->type = type;
646
647  /*
648   * Add to the parent if present...
649   */
650
651   if (parent)
652     mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node);
653
654  /*
655   * Return the new node...
656   */
657
658   return (node);
659 }
660
661
662 /*
663  * End of "$Id$".
664  */