mxml-index.c

Go to the documentation of this file.
00001 /*
00002  * "$Id: mxml-index.c 184 2005-01-29 07:21:44Z mike $"
00003  *
00004  * Index support code for Mini-XML, a small XML-like file parsing library.
00005  *
00006  * Copyright 2003-2005 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  *   mxmlIndexDelete()   - Delete an index.
00021  *   mxmlIndexEnum()     - Return the next node in the index.
00022  *   mxmlIndexFind()     - Find the next matching node.
00023  *   mxmlIndexNew()      - Create a new index.
00024  *   mxmlIndexReset()    - Reset the enumeration/find pointer in the index and
00025  *                         return the first node in the index.
00026  *   index_compare()     - Compare two nodes.
00027  *   index_find()        - Compare a node with index values.
00028  *   index_sort()        - Sort the nodes in the index...
00029  */
00030 
00031 /*
00032  * Include necessary headers...
00033  */
00034 
00035 #include "config.h"
00036 #include "mxml.h"
00037 
00038 
00039 /*
00040  * Sort functions...
00041  */
00042 
00043 static int  index_compare(mxml_index_t *ind, mxml_node_t *first,
00044                       mxml_node_t *second);
00045 static int  index_find(mxml_index_t *ind, const char *element,
00046                    const char *value, mxml_node_t *node);
00047 static void index_sort(mxml_index_t *ind, int left, int right);
00048 
00049 
00050 /*
00051  * 'mxmlIndexDelete()' - Delete an index.
00052  */
00053 
00054 void
00055 mxmlIndexDelete(mxml_index_t *ind)  /* I - Index to delete */
00056 {
00057  /*
00058   * Range check input..
00059   */
00060 
00061   if (!ind)
00062     return;
00063 
00064  /*
00065   * Free memory...
00066   */
00067 
00068   if (ind->attr)
00069     free(ind->attr);
00070 
00071   if (ind->alloc_nodes)
00072     free(ind->nodes);
00073 
00074   free(ind);
00075 }
00076 
00077 
00078 /*
00079  * 'mxmlIndexEnum()' - Return the next node in the index.
00080  *
00081  * Nodes are returned in the sorted order of the index.
00082  */
00083 
00084 mxml_node_t *               /* O - Next node or NULL if there is none */
00085 mxmlIndexEnum(mxml_index_t *ind)    /* I - Index to enumerate */
00086 {
00087  /*
00088   * Range check input...
00089   */
00090 
00091   if (!ind)
00092     return (NULL);
00093 
00094  /*
00095   * Return the next node...
00096   */
00097 
00098   if (ind->cur_node < ind->num_nodes)
00099     return (ind->nodes[ind->cur_node ++]);
00100   else
00101     return (NULL);
00102 }
00103 
00104 
00105 /*
00106  * 'mxmlIndexFind()' - Find the next matching node.
00107  *
00108  * You should call mxmlIndexReset() prior to using this function for
00109  * the first time with a particular set of "element" and "value"
00110  * strings. Passing NULL for both "element" and "value" is equivalent
00111  * to calling mxmlIndexEnum().
00112  */
00113 
00114 mxml_node_t *               /* O - Node or NULL if none found */
00115 mxmlIndexFind(mxml_index_t *ind,    /* I - Index to search */
00116               const char   *element,    /* I - Element name to find, if any */
00117           const char   *value)  /* I - Attribute value, if any */
00118 {
00119   int       diff,           /* Difference between names */
00120         current,        /* Current entity in search */
00121         first,          /* First entity in search */
00122         last;           /* Last entity in search */
00123 
00124 
00125 #ifdef DEBUG
00126   printf("mxmlIndexFind(ind=%p, element=\"%s\", value=\"%s\")\n",
00127          ind, element ? element : "(null)", value ? value : "(null)");
00128 #endif /* DEBUG */
00129 
00130  /*
00131   * Range check input...
00132   */
00133 
00134   if (!ind || (!ind->attr && value))
00135   {
00136 #ifdef DEBUG
00137     puts("    returning NULL...");
00138     printf("    ind->attr=\"%s\"\n", ind->attr ? ind->attr : "(null)");
00139 #endif /* DEBUG */
00140 
00141     return (NULL);
00142   }
00143 
00144  /*
00145   * If both element and value are NULL, just enumerate the nodes in the
00146   * index...
00147   */
00148 
00149   if (!element && !value)
00150     return (mxmlIndexEnum(ind));
00151 
00152  /*
00153   * If there are no nodes in the index, return NULL...
00154   */
00155 
00156   if (!ind->num_nodes)
00157   {
00158 #ifdef DEBUG
00159     puts("    returning NULL...");
00160     puts("    no nodes!");
00161 #endif /* DEBUG */
00162 
00163     return (NULL);
00164   }
00165 
00166  /*
00167   * If cur_node == 0, then find the first matching node...
00168   */
00169 
00170   if (ind->cur_node == 0)
00171   {
00172    /*
00173     * Find the first node using a modified binary search algorithm...
00174     */
00175 
00176     first = 0;
00177     last  = ind->num_nodes - 1;
00178 
00179 #ifdef DEBUG
00180     printf("    find first time, num_nodes=%d...\n", ind->num_nodes);
00181 #endif /* DEBUG */
00182 
00183     while ((last - first) > 1)
00184     {
00185       current = (first + last) / 2;
00186 
00187 #ifdef DEBUG
00188       printf("    first=%d, last=%d, current=%d\n", first, last, current);
00189 #endif /* DEBUG */
00190 
00191       if ((diff = index_find(ind, element, value, ind->nodes[current])) == 0)
00192       {
00193        /*
00194         * Found a match, move back to find the first...
00195     */
00196 
00197 #ifdef DEBUG
00198         puts("    match!");
00199 #endif /* DEBUG */
00200 
00201         while (current > 0 &&
00202            !index_find(ind, element, value, ind->nodes[current - 1]))
00203       current --;
00204 
00205 #ifdef DEBUG
00206         printf("    returning first match=%d\n", current);
00207 #endif /* DEBUG */
00208 
00209        /*
00210         * Return the first match and save the index to the next...
00211     */
00212 
00213         ind->cur_node = current + 1;
00214 
00215     return (ind->nodes[current]);
00216       }
00217       else if (diff < 0)
00218     last = current;
00219       else
00220     first = current;
00221 
00222 #ifdef DEBUG
00223       printf("    diff=%d\n", diff);
00224 #endif /* DEBUG */
00225     }
00226 
00227    /*
00228     * If we get this far, then we found exactly 0 or 1 matches...
00229     */
00230 
00231     for (current = first; current <= last; current ++)
00232       if (!index_find(ind, element, value, ind->nodes[current]))
00233       {
00234        /*
00235     * Found exactly one (or possibly two) match...
00236     */
00237 
00238 #ifdef DEBUG
00239     printf("    returning only match %d...\n", current);
00240 #endif /* DEBUG */
00241 
00242     ind->cur_node = current + 1;
00243 
00244     return (ind->nodes[current]);
00245       }
00246 
00247    /*
00248     * No matches...
00249     */
00250 
00251     ind->cur_node = ind->num_nodes;
00252 
00253 #ifdef DEBUG
00254     puts("    returning NULL...");
00255 #endif /* DEBUG */
00256 
00257     return (NULL);
00258   }
00259   else if (ind->cur_node < ind->num_nodes &&
00260            !index_find(ind, element, value, ind->nodes[ind->cur_node]))
00261   {
00262    /*
00263     * Return the next matching node...
00264     */
00265 
00266 #ifdef DEBUG
00267     printf("    returning next match %d...\n", ind->cur_node);
00268 #endif /* DEBUG */
00269 
00270     return (ind->nodes[ind->cur_node ++]);
00271   }
00272 
00273  /*
00274   * If we get this far, then we have no matches...
00275   */
00276 
00277   ind->cur_node = ind->num_nodes;
00278 
00279 #ifdef DEBUG
00280   puts("    returning NULL...");
00281 #endif /* DEBUG */
00282 
00283   return (NULL);
00284 }
00285 
00286 
00287 /*
00288  * 'mxmlIndexNew()' - Create a new index.
00289  *
00290  * The index will contain all nodes that contain the named element and/or
00291  * attribute. If both "element" and "attr" are NULL, then the index will
00292  * contain a sorted list of the elements in the node tree.  Nodes are
00293  * sorted by element name and optionally by attribute value if the "attr"
00294  * argument is not NULL.
00295  */
00296 
00297 mxml_index_t *              /* O - New index */
00298 mxmlIndexNew(mxml_node_t *node,     /* I - XML node tree */
00299              const char  *element,  /* I - Element to index or NULL for all */
00300              const char  *attr)     /* I - Attribute to index or NULL for none */
00301 {
00302   mxml_index_t  *ind;           /* New index */
00303   mxml_node_t   *current,       /* Current node in index */
00304         **temp;         /* Temporary node pointer array */
00305 
00306 
00307  /*
00308   * Range check input...
00309   */
00310 
00311 #ifdef DEBUG
00312   printf("mxmlIndexNew(node=%p, element=\"%s\", attr=\"%s\")\n",
00313          node, element ? element : "(null)", attr ? attr : "(null)");
00314 #endif /* DEBUG */
00315 
00316   if (!node)
00317     return (NULL);
00318 
00319  /*
00320   * Create a new index...
00321   */
00322 
00323   if ((ind = calloc(1, sizeof(mxml_index_t))) == NULL)
00324   {
00325     mxml_error("Unable to allocate %d bytes for index - %s",
00326                sizeof(mxml_index_t), strerror(errno));
00327     return (NULL);
00328   }
00329 
00330   if (attr)
00331     ind->attr = strdup(attr);
00332 
00333   if (!element && !attr)
00334     current = node;
00335   else
00336     current = mxmlFindElement(node, node, element, attr, NULL, MXML_DESCEND);
00337 
00338   while (current)
00339   {
00340     if (ind->num_nodes >= ind->alloc_nodes)
00341     {
00342       if (!ind->alloc_nodes)
00343         temp = malloc(64 * sizeof(mxml_node_t *));
00344       else
00345         temp = realloc(ind->nodes, (ind->alloc_nodes + 64) * sizeof(mxml_node_t *));
00346 
00347       if (!temp)
00348       {
00349        /*
00350         * Unable to allocate memory for the index, so abort...
00351     */
00352 
00353         mxml_error("Unable to allocate %d bytes for index: %s",
00354                (ind->alloc_nodes + 64) * sizeof(mxml_node_t *),
00355            strerror(errno));
00356 
00357         mxmlIndexDelete(ind);
00358     return (NULL);
00359       }
00360 
00361       ind->nodes       = temp;
00362       ind->alloc_nodes += 64;
00363     }
00364 
00365     ind->nodes[ind->num_nodes ++] = current;
00366 
00367     current = mxmlFindElement(current, node, element, attr, NULL, MXML_DESCEND);
00368   }
00369 
00370  /*
00371   * Sort nodes based upon the search criteria...
00372   */
00373 
00374 #ifdef DEBUG
00375   {
00376     int i;              /* Looping var */
00377 
00378 
00379     printf("%d node(s) in index.\n\n", ind->num_nodes);
00380 
00381     if (attr)
00382     {
00383       printf("Node      Address   Element         %s\n", attr);
00384       puts("--------  --------  --------------  ------------------------------");
00385 
00386       for (i = 0; i < ind->num_nodes; i ++)
00387     printf("%8d  %-8p  %-14.14s  %s\n", i, ind->nodes[i],
00388            ind->nodes[i]->value.element.name,
00389            mxmlElementGetAttr(ind->nodes[i], attr));
00390     }
00391     else
00392     {
00393       puts("Node      Address   Element");
00394       puts("--------  --------  --------------");
00395 
00396       for (i = 0; i < ind->num_nodes; i ++)
00397     printf("%8d  %-8p  %s\n", i, ind->nodes[i],
00398            ind->nodes[i]->value.element.name);
00399     }
00400 
00401     putchar('\n');
00402   }
00403 #endif /* DEBUG */
00404 
00405   if (ind->num_nodes > 1)
00406     index_sort(ind, 0, ind->num_nodes - 1);
00407 
00408 #ifdef DEBUG
00409   {
00410     int i;              /* Looping var */
00411 
00412 
00413     puts("After sorting:\n");
00414 
00415     if (attr)
00416     {
00417       printf("Node      Address   Element         %s\n", attr);
00418       puts("--------  --------  --------------  ------------------------------");
00419 
00420       for (i = 0; i < ind->num_nodes; i ++)
00421     printf("%8d  %-8p  %-14.14s  %s\n", i, ind->nodes[i],
00422            ind->nodes[i]->value.element.name,
00423            mxmlElementGetAttr(ind->nodes[i], attr));
00424     }
00425     else
00426     {
00427       puts("Node      Address   Element");
00428       puts("--------  --------  --------------");
00429 
00430       for (i = 0; i < ind->num_nodes; i ++)
00431     printf("%8d  %-8p  %s\n", i, ind->nodes[i],
00432            ind->nodes[i]->value.element.name);
00433     }
00434 
00435     putchar('\n');
00436   }
00437 #endif /* DEBUG */
00438 
00439  /*
00440   * Return the new index...
00441   */
00442 
00443   return (ind);
00444 }
00445 
00446 
00447 /*
00448  * 'mxmlIndexReset()' - Reset the enumeration/find pointer in the index and
00449  *                      return the first node in the index.
00450  *
00451  * This function should be called prior to using mxmlIndexEnum() or
00452  * mxmlIndexFind() for the first time.
00453  */
00454 
00455 mxml_node_t *               /* O - First node or NULL if there is none */
00456 mxmlIndexReset(mxml_index_t *ind)   /* I - Index to reset */
00457 {
00458 #ifdef DEBUG
00459   printf("mxmlIndexReset(ind=%p)\n", ind);
00460 #endif /* DEBUG */
00461 
00462  /*
00463   * Range check input...
00464   */
00465 
00466   if (!ind)
00467     return (NULL);
00468 
00469  /*
00470   * Set the index to the first element...
00471   */
00472 
00473   ind->cur_node = 0;
00474 
00475  /*
00476   * Return the first node...
00477   */
00478 
00479   if (ind->num_nodes)
00480     return (ind->nodes[0]);
00481   else
00482     return (NULL);
00483 }
00484 
00485 
00486 /*
00487  * 'index_compare()' - Compare two nodes.
00488  */
00489 
00490 static int              /* O - Result of comparison */
00491 index_compare(mxml_index_t *ind,    /* I - Index */
00492               mxml_node_t  *first,  /* I - First node */
00493               mxml_node_t  *second) /* I - Second node */
00494 {
00495   int   diff;               /* Difference */
00496 
00497 
00498  /*
00499   * Check the element name...
00500   */
00501 
00502   if ((diff = strcmp(first->value.element.name,
00503                      second->value.element.name)) != 0)
00504     return (diff);
00505 
00506  /*
00507   * Check the attribute value...
00508   */
00509 
00510   if (ind->attr)
00511   {
00512     if ((diff = strcmp(mxmlElementGetAttr(first, ind->attr),
00513                        mxmlElementGetAttr(second, ind->attr))) != 0)
00514       return (diff);
00515   }
00516 
00517  /*
00518   * No difference, return 0...
00519   */
00520 
00521   return (0);
00522 }
00523 
00524 
00525 /*
00526  * 'index_find()' - Compare a node with index values.
00527  */
00528 
00529 static int              /* O - Result of comparison */
00530 index_find(mxml_index_t *ind,       /* I - Index */
00531            const char   *element,   /* I - Element name or NULL */
00532        const char   *value,     /* I - Attribute value or NULL */
00533            mxml_node_t  *node)      /* I - Node */
00534 {
00535   int   diff;               /* Difference */
00536 
00537 
00538  /*
00539   * Check the element name...
00540   */
00541 
00542   if (element)
00543   {
00544     if ((diff = strcmp(element, node->value.element.name)) != 0)
00545       return (diff);
00546   }
00547 
00548  /*
00549   * Check the attribute value...
00550   */
00551 
00552   if (value)
00553   {
00554     if ((diff = strcmp(value, mxmlElementGetAttr(node, ind->attr))) != 0)
00555       return (diff);
00556   }
00557 
00558  /*
00559   * No difference, return 0...
00560   */
00561 
00562   return (0);
00563 }
00564 
00565 
00566 /*
00567  * 'index_sort()' - Sort the nodes in the index...
00568  *
00569  * This function implements the classic quicksort algorithm...
00570  */
00571 
00572 static void
00573 index_sort(mxml_index_t *ind,       /* I - Index to sort */
00574            int          left,       /* I - Left node in partition */
00575        int          right)      /* I - Right node in partition */
00576 {
00577   mxml_node_t   *pivot,         /* Pivot node */
00578         *temp;          /* Swap node */
00579   int       templ,          /* Temporary left node */
00580         tempr;          /* Temporary right node */
00581 
00582 
00583  /*
00584   * Loop until we have sorted all the way to the right...
00585   */
00586 
00587   do
00588   {
00589    /*
00590     * Sort the pivot in the current partition...
00591     */
00592 
00593     pivot = ind->nodes[left];
00594 
00595     for (templ = left, tempr = right; templ < tempr;)
00596     {
00597      /*
00598       * Move left while left node <= pivot node...
00599       */
00600 
00601       while ((templ < right) &&
00602              index_compare(ind, ind->nodes[templ], pivot) <= 0)
00603     templ ++;
00604 
00605      /*
00606       * Move right while right node > pivot node...
00607       */
00608 
00609       while ((tempr > left) &&
00610              index_compare(ind, ind->nodes[tempr], pivot) > 0)
00611     tempr --;
00612 
00613      /*
00614       * Swap nodes if needed...
00615       */
00616 
00617       if (templ < tempr)
00618       {
00619     temp              = ind->nodes[templ];
00620     ind->nodes[templ] = ind->nodes[tempr];
00621     ind->nodes[tempr] = temp;
00622       }
00623     }
00624 
00625    /*
00626     * When we get here, the right (tempr) node is the new position for the
00627     * pivot node...
00628     */
00629 
00630     if (index_compare(ind, pivot, ind->nodes[tempr]) > 0)
00631     {
00632       ind->nodes[left]  = ind->nodes[tempr];
00633       ind->nodes[tempr] = pivot;
00634     }
00635 
00636    /*
00637     * Recursively sort the left partition as needed...
00638     */
00639 
00640     if (left < (tempr - 1))
00641       index_sort(ind, left, tempr - 1);
00642   }
00643   while (right > (left = tempr + 1));
00644 }

Generated by  doxygen 1.6.2