mxml-node.c

Go to the documentation of this file.
00001 /*
00002  * "$Id: mxml-node.c 270 2007-04-23 21:48:03Z mike $"
00003  *
00004  * Node support code for Mini-XML, a small XML-like file parsing library.
00005  *
00006  * Copyright 2003-2007 by Michael Sweet.
00007  *
00008  * This program is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Library General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2, or (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * Contents:
00019  *
00020  *   mxmlAdd()        - Add a node to a tree.
00021  *   mxmlDelete()     - Delete a node and all of its children.
00022  *   mxmlNewCDATA()   - Create a new CDATA node.
00023  *   mxmlNewCustom()  - Create a new custom data node.
00024  *   mxmlNewElement() - Create a new element node.
00025  *   mxmlNewInteger() - Create a new integer node.
00026  *   mxmlNewOpaque()  - Create a new opaque string.
00027  *   mxmlNewReal()    - Create a new real number node.
00028  *   mxmlNewText()    - Create a new text fragment node.
00029  *   mxmlNewTextf()   - Create a new formatted text fragment node.
00030  *   mxmlNewXML()     - Create a new XML document tree.
00031  *   mxmlRelease()    - Release a node.
00032  *   mxmlRemove()     - Remove a node from its parent.
00033  *   mxmlRetain()     - Retain a node.
00034  *   mxml_new()       - Create a new node.
00035  */
00036 
00037 /*
00038  * Include necessary headers...
00039  */
00040 
00041 #include "config.h"
00042 #include "mxml.h"
00043 
00044 
00045 /*
00046  * Local functions...
00047  */
00048 
00049 static mxml_node_t  *mxml_new(mxml_node_t *parent, mxml_type_t type);
00050 
00051 
00052 /*
00053  * 'mxmlAdd()' - Add a node to a tree.
00054  *
00055  * Adds the specified node to the parent. If the child argument is not
00056  * NULL, puts the new node before or after the specified child depending
00057  * on the value of the where argument. If the child argument is NULL,
00058  * puts the new node at the beginning of the child list (MXML_ADD_BEFORE)
00059  * or at the end of the child list (MXML_ADD_AFTER). The constant
00060  * MXML_ADD_TO_PARENT can be used to specify a NULL child pointer.
00061  */
00062 
00063 void
00064 mxmlAdd(mxml_node_t *parent,        /* I - Parent node */
00065         int         where,      /* I - Where to add, MXML_ADD_BEFORE or MXML_ADD_AFTER */
00066         mxml_node_t *child,     /* I - Child node for where or MXML_ADD_TO_PARENT */
00067     mxml_node_t *node)      /* I - Node to add */
00068 {
00069 #ifdef DEBUG
00070   fprintf(stderr, "mxmlAdd(parent=%p, where=%d, child=%p, node=%p)\n", parent,
00071           where, child, node);
00072 #endif /* DEBUG */
00073 
00074  /*
00075   * Range check input...
00076   */
00077 
00078   if (!parent || !node)
00079     return;
00080 
00081 #if DEBUG > 1
00082   fprintf(stderr, "    BEFORE: node->parent=%p\n", node->parent);
00083   if (parent)
00084   {
00085     fprintf(stderr, "    BEFORE: parent->child=%p\n", parent->child);
00086     fprintf(stderr, "    BEFORE: parent->last_child=%p\n", parent->last_child);
00087     fprintf(stderr, "    BEFORE: parent->prev=%p\n", parent->prev);
00088     fprintf(stderr, "    BEFORE: parent->next=%p\n", parent->next);
00089   }
00090 #endif /* DEBUG > 1 */
00091 
00092  /*
00093   * Remove the node from any existing parent...
00094   */
00095 
00096   if (node->parent)
00097     mxmlRemove(node);
00098 
00099  /*
00100   * Reset pointers...
00101   */
00102 
00103   node->parent = parent;
00104 
00105   switch (where)
00106   {
00107     case MXML_ADD_BEFORE :
00108         if (!child || child == parent->child || child->parent != parent)
00109     {
00110      /*
00111       * Insert as first node under parent...
00112       */
00113 
00114       node->next = parent->child;
00115 
00116       if (parent->child)
00117         parent->child->prev = node;
00118       else
00119         parent->last_child = node;
00120 
00121       parent->child = node;
00122     }
00123     else
00124     {
00125      /*
00126       * Insert node before this child...
00127       */
00128 
00129       node->next = child;
00130       node->prev = child->prev;
00131 
00132       if (child->prev)
00133         child->prev->next = node;
00134       else
00135         parent->child = node;
00136 
00137       child->prev = node;
00138     }
00139         break;
00140 
00141     case MXML_ADD_AFTER :
00142         if (!child || child == parent->last_child || child->parent != parent)
00143     {
00144      /*
00145       * Insert as last node under parent...
00146       */
00147 
00148       node->parent = parent;
00149       node->prev   = parent->last_child;
00150 
00151       if (parent->last_child)
00152         parent->last_child->next = node;
00153       else
00154         parent->child = node;
00155 
00156       parent->last_child = node;
00157         }
00158     else
00159     {
00160      /*
00161       * Insert node after this child...
00162       */
00163 
00164       node->prev = child;
00165       node->next = child->next;
00166 
00167       if (child->next)
00168         child->next->prev = node;
00169       else
00170         parent->last_child = node;
00171 
00172       child->next = node;
00173     }
00174         break;
00175   }
00176 
00177 #if DEBUG > 1
00178   fprintf(stderr, "    AFTER: node->parent=%p\n", node->parent);
00179   if (parent)
00180   {
00181     fprintf(stderr, "    AFTER: parent->child=%p\n", parent->child);
00182     fprintf(stderr, "    AFTER: parent->last_child=%p\n", parent->last_child);
00183     fprintf(stderr, "    AFTER: parent->prev=%p\n", parent->prev);
00184     fprintf(stderr, "    AFTER: parent->next=%p\n", parent->next);
00185   }
00186 #endif /* DEBUG > 1 */
00187 }
00188 
00189 
00190 /*
00191  * 'mxmlDelete()' - Delete a node and all of its children.
00192  *
00193  * If the specified node has a parent, this function first removes the
00194  * node from its parent using the mxmlRemove() function.
00195  */
00196 
00197 void
00198 mxmlDelete(mxml_node_t *node)       /* I - Node to delete */
00199 {
00200   int   i;              /* Looping var */
00201 
00202 
00203 #ifdef DEBUG
00204   fprintf(stderr, "mxmlDelete(node=%p)\n", node);
00205 #endif /* DEBUG */
00206 
00207  /*
00208   * Range check input...
00209   */
00210 
00211   if (!node)
00212     return;
00213 
00214  /*
00215   * Remove the node from its parent, if any...
00216   */
00217 
00218   mxmlRemove(node);
00219 
00220  /*
00221   * Delete children...
00222   */
00223 
00224   while (node->child)
00225     mxmlDelete(node->child);
00226 
00227  /*
00228   * Now delete any node data...
00229   */
00230 
00231   switch (node->type)
00232   {
00233     case MXML_ELEMENT :
00234         if (node->value.element.name)
00235       free(node->value.element.name);
00236 
00237     if (node->value.element.num_attrs)
00238     {
00239       for (i = 0; i < node->value.element.num_attrs; i ++)
00240       {
00241         if (node->value.element.attrs[i].name)
00242           free(node->value.element.attrs[i].name);
00243         if (node->value.element.attrs[i].value)
00244           free(node->value.element.attrs[i].value);
00245       }
00246 
00247           free(node->value.element.attrs);
00248     }
00249         break;
00250     case MXML_INTEGER :
00251        /* Nothing to do */
00252         break;
00253     case MXML_OPAQUE :
00254         if (node->value.opaque)
00255       free(node->value.opaque);
00256         break;
00257     case MXML_REAL :
00258        /* Nothing to do */
00259         break;
00260     case MXML_TEXT :
00261         if (node->value.text.string)
00262       free(node->value.text.string);
00263         break;
00264     case MXML_CUSTOM :
00265         if (node->value.custom.data &&
00266         node->value.custom.destroy)
00267       (*(node->value.custom.destroy))(node->value.custom.data);
00268     break;
00269     default :
00270         break;
00271   }
00272 
00273  /*
00274   * Free this node...
00275   */
00276 
00277   free(node);
00278 }
00279 
00280 
00281 /*
00282  * 'mxmlNewCDATA()' - Create a new CDATA node.
00283  *
00284  * The new CDATA node is added to the end of the specified parent's child
00285  * list. The constant MXML_NO_PARENT can be used to specify that the new
00286  * CDATA node has no parent. The data string must be nul-terminated and
00287  * is copied into the new node. CDATA nodes use the MXML_ELEMENT type.
00288  *
00289  * @since Mini-XML 2.3@
00290  */
00291 
00292 mxml_node_t *               /* O - New node */
00293 mxmlNewCDATA(mxml_node_t *parent,   /* I - Parent node or MXML_NO_PARENT */
00294          const char  *data)     /* I - Data string */
00295 {
00296   mxml_node_t   *node;          /* New node */
00297 
00298 
00299 #ifdef DEBUG
00300   fprintf(stderr, "mxmlNewCDATA(parent=%p, data=\"%s\")\n",
00301           parent, data ? data : "(null)");
00302 #endif /* DEBUG */
00303 
00304  /*
00305   * Range check input...
00306   */
00307 
00308   if (!data)
00309     return (NULL);
00310 
00311  /*
00312   * Create the node and set the name value...
00313   */
00314 
00315   if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL)
00316     node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data);
00317 
00318   return (node);
00319 }
00320 
00321 
00322 /*
00323  * 'mxmlNewCustom()' - Create a new custom data node.
00324  *
00325  * The new custom node is added to the end of the specified parent's child
00326  * list. The constant MXML_NO_PARENT can be used to specify that the new
00327  * element node has no parent. NULL can be passed when the data in the
00328  * node is not dynamically allocated or is separately managed.
00329  *
00330  * @since Mini-XML 2.1@
00331  */
00332 
00333 mxml_node_t *               /* O - New node */
00334 mxmlNewCustom(
00335     mxml_node_t              *parent,   /* I - Parent node or MXML_NO_PARENT */
00336     void                     *data, /* I - Pointer to data */
00337     mxml_custom_destroy_cb_t destroy)   /* I - Function to destroy data */
00338 {
00339   mxml_node_t   *node;          /* New node */
00340 
00341 
00342 #ifdef DEBUG
00343   fprintf(stderr, "mxmlNewCustom(parent=%p, data=%p, destroy=%p)\n", parent,
00344           data, destroy);
00345 #endif /* DEBUG */
00346 
00347  /*
00348   * Create the node and set the value...
00349   */
00350 
00351   if ((node = mxml_new(parent, MXML_CUSTOM)) != NULL)
00352   {
00353     node->value.custom.data    = data;
00354     node->value.custom.destroy = destroy;
00355   }
00356 
00357   return (node);
00358 }
00359 
00360 
00361 /*
00362  * 'mxmlNewElement()' - Create a new element node.
00363  *
00364  * The new element node is added to the end of the specified parent's child
00365  * list. The constant MXML_NO_PARENT can be used to specify that the new
00366  * element node has no parent.
00367  */
00368 
00369 mxml_node_t *               /* O - New node */
00370 mxmlNewElement(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
00371                const char  *name)   /* I - Name of element */
00372 {
00373   mxml_node_t   *node;          /* New node */
00374 
00375 
00376 #ifdef DEBUG
00377   fprintf(stderr, "mxmlNewElement(parent=%p, name=\"%s\")\n", parent,
00378           name ? name : "(null)");
00379 #endif /* DEBUG */
00380 
00381  /*
00382   * Range check input...
00383   */
00384 
00385   if (!name)
00386     return (NULL);
00387 
00388  /*
00389   * Create the node and set the element name...
00390   */
00391 
00392   if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL)
00393     node->value.element.name = strdup(name);
00394 
00395   return (node);
00396 }
00397 
00398 
00399 /*
00400  * 'mxmlNewInteger()' - Create a new integer node.
00401  *
00402  * The new integer node is added to the end of the specified parent's child
00403  * list. The constant MXML_NO_PARENT can be used to specify that the new
00404  * integer node has no parent.
00405  */
00406 
00407 mxml_node_t *               /* O - New node */
00408 mxmlNewInteger(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */
00409                int         integer) /* I - Integer value */
00410 {
00411   mxml_node_t   *node;          /* New node */
00412 
00413 
00414 #ifdef DEBUG
00415   fprintf(stderr, "mxmlNewInteger(parent=%p, integer=%d)\n", parent, integer);
00416 #endif /* DEBUG */
00417 
00418  /*
00419   * Create the node and set the element name...
00420   */
00421 
00422   if ((node = mxml_new(parent, MXML_INTEGER)) != NULL)
00423     node->value.integer = integer;
00424 
00425   return (node);
00426 }
00427 
00428 
00429 /*
00430  * 'mxmlNewOpaque()' - Create a new opaque string.
00431  *
00432  * The new opaque node is added to the end of the specified parent's child
00433  * list. The constant MXML_NO_PARENT can be used to specify that the new
00434  * opaque node has no parent. The opaque string must be nul-terminated and
00435  * is copied into the new node.
00436  */
00437 
00438 mxml_node_t *               /* O - New node */
00439 mxmlNewOpaque(mxml_node_t *parent,  /* I - Parent node or MXML_NO_PARENT */
00440               const char  *opaque)  /* I - Opaque string */
00441 {
00442   mxml_node_t   *node;          /* New node */
00443 
00444 
00445 #ifdef DEBUG
00446   fprintf(stderr, "mxmlNewOpaque(parent=%p, opaque=\"%s\")\n", parent,
00447           opaque ? opaque : "(null)");
00448 #endif /* DEBUG */
00449 
00450  /*
00451   * Range check input...
00452   */
00453 
00454   if (!opaque)
00455     return (NULL);
00456 
00457  /*
00458   * Create the node and set the element name...
00459   */
00460 
00461   if ((node = mxml_new(parent, MXML_OPAQUE)) != NULL)
00462     node->value.opaque = strdup(opaque);
00463 
00464   return (node);
00465 }
00466 
00467 
00468 /*
00469  * 'mxmlNewReal()' - Create a new real number node.
00470  *
00471  * The new real number node is added to the end of the specified parent's
00472  * child list. The constant MXML_NO_PARENT can be used to specify that
00473  * the new real number node has no parent.
00474  */
00475 
00476 mxml_node_t *               /* O - New node */
00477 mxmlNewReal(mxml_node_t *parent,    /* I - Parent node or MXML_NO_PARENT */
00478             double      real)       /* I - Real number value */
00479 {
00480   mxml_node_t   *node;          /* New node */
00481 
00482 
00483 #ifdef DEBUG
00484   fprintf(stderr, "mxmlNewReal(parent=%p, real=%g)\n", parent, real);
00485 #endif /* DEBUG */
00486 
00487  /*
00488   * Create the node and set the element name...
00489   */
00490 
00491   if ((node = mxml_new(parent, MXML_REAL)) != NULL)
00492     node->value.real = real;
00493 
00494   return (node);
00495 }
00496 
00497 
00498 /*
00499  * 'mxmlNewText()' - Create a new text fragment node.
00500  *
00501  * The new text node is added to the end of the specified parent's child
00502  * list. The constant MXML_NO_PARENT can be used to specify that the new
00503  * text node has no parent. The whitespace parameter is used to specify
00504  * whether leading whitespace is present before the node. The text
00505  * string must be nul-terminated and is copied into the new node.
00506  */
00507 
00508 mxml_node_t *               /* O - New node */
00509 mxmlNewText(mxml_node_t *parent,    /* I - Parent node or MXML_NO_PARENT */
00510             int         whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */
00511         const char  *string)    /* I - String */
00512 {
00513   mxml_node_t   *node;          /* New node */
00514 
00515 
00516 #ifdef DEBUG
00517   fprintf(stderr, "mxmlNewText(parent=%p, whitespace=%d, string=\"%s\")\n",
00518           parent, whitespace, string ? string : "(null)");
00519 #endif /* DEBUG */
00520 
00521  /*
00522   * Range check input...
00523   */
00524 
00525   if (!string)
00526     return (NULL);
00527 
00528  /*
00529   * Create the node and set the text value...
00530   */
00531 
00532   if ((node = mxml_new(parent, MXML_TEXT)) != NULL)
00533   {
00534     node->value.text.whitespace = whitespace;
00535     node->value.text.string     = strdup(string);
00536   }
00537 
00538   return (node);
00539 }
00540 
00541 
00542 /*
00543  * 'mxmlNewTextf()' - Create a new formatted text fragment node.
00544  *
00545  * The new text node is added to the end of the specified parent's child
00546  * list. The constant MXML_NO_PARENT can be used to specify that the new
00547  * text node has no parent. The whitespace parameter is used to specify
00548  * whether leading whitespace is present before the node. The format
00549  * string must be nul-terminated and is formatted into the new node.
00550  */
00551 
00552 mxml_node_t *               /* O - New node */
00553 mxmlNewTextf(mxml_node_t *parent,   /* I - Parent node or MXML_NO_PARENT */
00554              int         whitespace,    /* I - 1 = leading whitespace, 0 = no whitespace */
00555          const char  *format,   /* I - Printf-style frmat string */
00556          ...)           /* I - Additional args as needed */
00557 {
00558   mxml_node_t   *node;          /* New node */
00559   va_list   ap;         /* Pointer to arguments */
00560 
00561 
00562 #ifdef DEBUG
00563   fprintf(stderr, "mxmlNewTextf(parent=%p, whitespace=%d, format=\"%s\", ...)\n",
00564           parent, whitespace, format ? format : "(null)");
00565 #endif /* DEBUG */
00566 
00567  /*
00568   * Range check input...
00569   */
00570 
00571   if (!format)
00572     return (NULL);
00573 
00574  /*
00575   * Create the node and set the text value...
00576   */
00577 
00578   if ((node = mxml_new(parent, MXML_TEXT)) != NULL)
00579   {
00580     va_start(ap, format);
00581 
00582     node->value.text.whitespace = whitespace;
00583     node->value.text.string     = _mxml_vstrdupf(format, ap);
00584 
00585     va_end(ap);
00586   }
00587 
00588   return (node);
00589 }
00590 
00591 
00592 /*
00593  * 'mxmlRemove()' - Remove a node from its parent.
00594  *
00595  * Does not free memory used by the node - use mxmlDelete() for that.
00596  * This function does nothing if the node has no parent.
00597  */
00598 
00599 void
00600 mxmlRemove(mxml_node_t *node)       /* I - Node to remove */
00601 {
00602 #ifdef DEBUG
00603   fprintf(stderr, "mxmlRemove(node=%p)\n", node);
00604 #endif /* DEBUG */
00605 
00606  /*
00607   * Range check input...
00608   */
00609 
00610   if (!node || !node->parent)
00611     return;
00612 
00613  /*
00614   * Remove from parent...
00615   */
00616 
00617 #if DEBUG > 1
00618   fprintf(stderr, "    BEFORE: node->parent=%p\n", node->parent);
00619   if (node->parent)
00620   {
00621     fprintf(stderr, "    BEFORE: node->parent->child=%p\n", node->parent->child);
00622     fprintf(stderr, "    BEFORE: node->parent->last_child=%p\n", node->parent->last_child);
00623   }
00624   fprintf(stderr, "    BEFORE: node->child=%p\n", node->child);
00625   fprintf(stderr, "    BEFORE: node->last_child=%p\n", node->last_child);
00626   fprintf(stderr, "    BEFORE: node->prev=%p\n", node->prev);
00627   fprintf(stderr, "    BEFORE: node->next=%p\n", node->next);
00628 #endif /* DEBUG > 1 */
00629 
00630   if (node->prev)
00631     node->prev->next = node->next;
00632   else
00633     node->parent->child = node->next;
00634 
00635   if (node->next)
00636     node->next->prev = node->prev;
00637   else
00638     node->parent->last_child = node->prev;
00639 
00640   node->parent = NULL;
00641   node->prev   = NULL;
00642   node->next   = NULL;
00643 
00644 #if DEBUG > 1
00645   fprintf(stderr, "    AFTER: node->parent=%p\n", node->parent);
00646   if (node->parent)
00647   {
00648     fprintf(stderr, "    AFTER: node->parent->child=%p\n", node->parent->child);
00649     fprintf(stderr, "    AFTER: node->parent->last_child=%p\n", node->parent->last_child);
00650   }
00651   fprintf(stderr, "    AFTER: node->child=%p\n", node->child);
00652   fprintf(stderr, "    AFTER: node->last_child=%p\n", node->last_child);
00653   fprintf(stderr, "    AFTER: node->prev=%p\n", node->prev);
00654   fprintf(stderr, "    AFTER: node->next=%p\n", node->next);
00655 #endif /* DEBUG > 1 */
00656 }
00657 
00658 
00659 /*
00660  * 'mxmlNewXML()' - Create a new XML document tree.
00661  *
00662  * The "version" argument specifies the version number to put in the
00663  * ?xml element node. If NULL, version 1.0 is assumed.
00664  *
00665  * @since Mini-XML 2.3@
00666  */
00667 
00668 mxml_node_t *               /* O - New ?xml node */
00669 mxmlNewXML(const char *version)     /* I - Version number to use */
00670 {
00671   char  element[1024];          /* Element text */
00672 
00673 
00674   snprintf(element, sizeof(element), "?xml version=\"%s\"?",
00675            version ? version : "1.0");
00676 
00677   return (mxmlNewElement(NULL, element));
00678 }
00679 
00680 
00681 /*
00682  * 'mxmlRelease()' - Release a node.
00683  *
00684  * When the reference count reaches zero, the node (and any children)
00685  * is deleted via mxmlDelete().
00686  *
00687  * @since Mini-XML 2.3@
00688  */
00689 
00690 int                 /* O - New reference count */
00691 mxmlRelease(mxml_node_t *node)      /* I - Node */
00692 {
00693   if (node)
00694   {
00695     if ((-- node->ref_count) <= 0)
00696     {
00697       mxmlDelete(node);
00698       return (0);
00699     }
00700     else
00701       return (node->ref_count);
00702   }
00703   else
00704     return (-1);
00705 }
00706 
00707 
00708 /*
00709  * 'mxmlRetain()' - Retain a node.
00710  *
00711  * @since Mini-XML 2.3@
00712  */
00713 
00714 int                 /* O - New reference count */
00715 mxmlRetain(mxml_node_t *node)       /* I - Node */
00716 {
00717   if (node)
00718     return (++ node->ref_count);
00719   else
00720     return (-1);
00721 }
00722 
00723 
00724 /*
00725  * 'mxml_new()' - Create a new node.
00726  */
00727 
00728 static mxml_node_t *            /* O - New node */
00729 mxml_new(mxml_node_t *parent,       /* I - Parent node */
00730          mxml_type_t type)      /* I - Node type */
00731 {
00732   mxml_node_t   *node;          /* New node */
00733 
00734 
00735 #if DEBUG > 1
00736   fprintf(stderr, "mxml_new(parent=%p, type=%d)\n", parent, type);
00737 #endif /* DEBUG > 1 */
00738 
00739  /*
00740   * Allocate memory for the node...
00741   */
00742 
00743   if ((node = calloc(1, sizeof(mxml_node_t))) == NULL)
00744   {
00745 #if DEBUG > 1
00746     fputs("    returning NULL\n", stderr);
00747 #endif /* DEBUG > 1 */
00748 
00749     return (NULL);
00750   }
00751 
00752 #if DEBUG > 1
00753   fprintf(stderr, "    returning %p\n", node);
00754 #endif /* DEBUG > 1 */
00755 
00756  /*
00757   * Set the node type...
00758   */
00759 
00760   node->type      = type;
00761   node->ref_count = 1;
00762 
00763  /*
00764   * Add to the parent if present...
00765   */
00766 
00767   if (parent)
00768     mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node);
00769 
00770  /*
00771   * Return the new node...
00772   */
00773 
00774   return (node);
00775 }

Generated by  doxygen 1.6.2