dahdi-tools/menuselect/mxml/mxml-index.c
Asterisk Development Team a885b2c253 Import menuselect r1110
Menuselect was originally included in the DAHDI-Tools repository with an svn
external. Since git does not handle externals so well, menuselect is being
brought into the tree directly. This allows menuselect to be present for all the
commits on the 2.4, 2.5, and 2.6 releases.

The command is:
  $ svn export http://svn.asterisk.org/svn/menuselect/trunk menuselect

Signed-off-by: Shaun Ruffell <sruffell@digium.com>
2013-02-05 14:28:29 -06:00

650 lines
14 KiB
C

/*
* "$Id: mxml-index.c 22267 2006-04-24 17:11:45Z kpfleming $"
*
* Index support code for Mini-XML, a small XML-like file parsing library.
*
* Copyright 2003-2005 by Michael Sweet.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Contents:
*
* mxmlIndexDelete() - Delete an index.
* mxmlIndexEnum() - Return the next node in the index.
* mxmlIndexFind() - Find the next matching node.
* mxmlIndexNew() - Create a new index.
* mxmlIndexReset() - Reset the enumeration/find pointer in the index and
* return the first node in the index.
* index_compare() - Compare two nodes.
* index_find() - Compare a node with index values.
* index_sort() - Sort the nodes in the index...
*/
/*
* Include necessary headers...
*/
#include "config.h"
#include "mxml.h"
/*
* Sort functions...
*/
static int index_compare(mxml_index_t *ind, mxml_node_t *first,
mxml_node_t *second);
static int index_find(mxml_index_t *ind, const char *element,
const char *value, mxml_node_t *node);
static void index_sort(mxml_index_t *ind, int left, int right);
/*
* 'mxmlIndexDelete()' - Delete an index.
*/
void
mxmlIndexDelete(mxml_index_t *ind) /* I - Index to delete */
{
/*
* Range check input..
*/
if (!ind)
return;
/*
* Free memory...
*/
if (ind->attr)
free(ind->attr);
if (ind->alloc_nodes)
free(ind->nodes);
free(ind);
}
/*
* 'mxmlIndexEnum()' - Return the next node in the index.
*
* Nodes are returned in the sorted order of the index.
*/
mxml_node_t * /* O - Next node or NULL if there is none */
mxmlIndexEnum(mxml_index_t *ind) /* I - Index to enumerate */
{
/*
* Range check input...
*/
if (!ind)
return (NULL);
/*
* Return the next node...
*/
if (ind->cur_node < ind->num_nodes)
return (ind->nodes[ind->cur_node ++]);
else
return (NULL);
}
/*
* 'mxmlIndexFind()' - Find the next matching node.
*
* You should call mxmlIndexReset() prior to using this function for
* the first time with a particular set of "element" and "value"
* strings. Passing NULL for both "element" and "value" is equivalent
* to calling mxmlIndexEnum().
*/
mxml_node_t * /* O - Node or NULL if none found */
mxmlIndexFind(mxml_index_t *ind, /* I - Index to search */
const char *element, /* I - Element name to find, if any */
const char *value) /* I - Attribute value, if any */
{
int diff, /* Difference between names */
current, /* Current entity in search */
first, /* First entity in search */
last; /* Last entity in search */
#ifdef DEBUG
printf("mxmlIndexFind(ind=%p, element=\"%s\", value=\"%s\")\n",
ind, element ? element : "(null)", value ? value : "(null)");
#endif /* DEBUG */
/*
* Range check input...
*/
if (!ind || (!ind->attr && value))
{
#ifdef DEBUG
puts(" returning NULL...");
printf(" ind->attr=\"%s\"\n", ind->attr ? ind->attr : "(null)");
#endif /* DEBUG */
return (NULL);
}
/*
* If both element and value are NULL, just enumerate the nodes in the
* index...
*/
if (!element && !value)
return (mxmlIndexEnum(ind));
/*
* If there are no nodes in the index, return NULL...
*/
if (!ind->num_nodes)
{
#ifdef DEBUG
puts(" returning NULL...");
puts(" no nodes!");
#endif /* DEBUG */
return (NULL);
}
/*
* If cur_node == 0, then find the first matching node...
*/
if (ind->cur_node == 0)
{
/*
* Find the first node using a modified binary search algorithm...
*/
first = 0;
last = ind->num_nodes - 1;
#ifdef DEBUG
printf(" find first time, num_nodes=%d...\n", ind->num_nodes);
#endif /* DEBUG */
while ((last - first) > 1)
{
current = (first + last) / 2;
#ifdef DEBUG
printf(" first=%d, last=%d, current=%d\n", first, last, current);
#endif /* DEBUG */
if ((diff = index_find(ind, element, value, ind->nodes[current])) == 0)
{
/*
* Found a match, move back to find the first...
*/
#ifdef DEBUG
puts(" match!");
#endif /* DEBUG */
while (current > 0 &&
!index_find(ind, element, value, ind->nodes[current - 1]))
current --;
#ifdef DEBUG
printf(" returning first match=%d\n", current);
#endif /* DEBUG */
/*
* Return the first match and save the index to the next...
*/
ind->cur_node = current + 1;
return (ind->nodes[current]);
}
else if (diff < 0)
last = current;
else
first = current;
#ifdef DEBUG
printf(" diff=%d\n", diff);
#endif /* DEBUG */
}
/*
* If we get this far, then we found exactly 0 or 1 matches...
*/
for (current = first; current <= last; current ++)
if (!index_find(ind, element, value, ind->nodes[current]))
{
/*
* Found exactly one (or possibly two) match...
*/
#ifdef DEBUG
printf(" returning only match %d...\n", current);
#endif /* DEBUG */
ind->cur_node = current + 1;
return (ind->nodes[current]);
}
/*
* No matches...
*/
ind->cur_node = ind->num_nodes;
#ifdef DEBUG
puts(" returning NULL...");
#endif /* DEBUG */
return (NULL);
}
else if (ind->cur_node < ind->num_nodes &&
!index_find(ind, element, value, ind->nodes[ind->cur_node]))
{
/*
* Return the next matching node...
*/
#ifdef DEBUG
printf(" returning next match %d...\n", ind->cur_node);
#endif /* DEBUG */
return (ind->nodes[ind->cur_node ++]);
}
/*
* If we get this far, then we have no matches...
*/
ind->cur_node = ind->num_nodes;
#ifdef DEBUG
puts(" returning NULL...");
#endif /* DEBUG */
return (NULL);
}
/*
* 'mxmlIndexNew()' - Create a new index.
*
* The index will contain all nodes that contain the named element and/or
* attribute. If both "element" and "attr" are NULL, then the index will
* contain a sorted list of the elements in the node tree. Nodes are
* sorted by element name and optionally by attribute value if the "attr"
* argument is not NULL.
*/
mxml_index_t * /* O - New index */
mxmlIndexNew(mxml_node_t *node, /* I - XML node tree */
const char *element, /* I - Element to index or NULL for all */
const char *attr) /* I - Attribute to index or NULL for none */
{
mxml_index_t *ind; /* New index */
mxml_node_t *current, /* Current node in index */
**temp; /* Temporary node pointer array */
/*
* Range check input...
*/
#ifdef DEBUG
printf("mxmlIndexNew(node=%p, element=\"%s\", attr=\"%s\")\n",
node, element ? element : "(null)", attr ? attr : "(null)");
#endif /* DEBUG */
if (!node)
return (NULL);
/*
* Create a new index...
*/
if ((ind = calloc(1, sizeof(mxml_index_t))) == NULL)
{
mxml_error("Unable to allocate %d bytes for index - %s",
sizeof(mxml_index_t), strerror(errno));
return (NULL);
}
if (attr)
ind->attr = strdup(attr);
if (!element && !attr)
current = node;
else
current = mxmlFindElement(node, node, element, attr, NULL, MXML_DESCEND);
while (current)
{
if (ind->num_nodes >= ind->alloc_nodes)
{
if (!ind->alloc_nodes)
temp = malloc(64 * sizeof(mxml_node_t *));
else
temp = realloc(ind->nodes, (ind->alloc_nodes + 64) * sizeof(mxml_node_t *));
if (!temp)
{
/*
* Unable to allocate memory for the index, so abort...
*/
mxml_error("Unable to allocate %d bytes for index: %s",
(ind->alloc_nodes + 64) * sizeof(mxml_node_t *),
strerror(errno));
mxmlIndexDelete(ind);
return (NULL);
}
ind->nodes = temp;
ind->alloc_nodes += 64;
}
ind->nodes[ind->num_nodes ++] = current;
current = mxmlFindElement(current, node, element, attr, NULL, MXML_DESCEND);
}
/*
* Sort nodes based upon the search criteria...
*/
#ifdef DEBUG
{
int i; /* Looping var */
printf("%d node(s) in index.\n\n", ind->num_nodes);
if (attr)
{
printf("Node Address Element %s\n", attr);
puts("-------- -------- -------------- ------------------------------");
for (i = 0; i < ind->num_nodes; i ++)
printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i],
ind->nodes[i]->value.element.name,
mxmlElementGetAttr(ind->nodes[i], attr));
}
else
{
puts("Node Address Element");
puts("-------- -------- --------------");
for (i = 0; i < ind->num_nodes; i ++)
printf("%8d %-8p %s\n", i, ind->nodes[i],
ind->nodes[i]->value.element.name);
}
putchar('\n');
}
#endif /* DEBUG */
if (ind->num_nodes > 1)
index_sort(ind, 0, ind->num_nodes - 1);
#ifdef DEBUG
{
int i; /* Looping var */
puts("After sorting:\n");
if (attr)
{
printf("Node Address Element %s\n", attr);
puts("-------- -------- -------------- ------------------------------");
for (i = 0; i < ind->num_nodes; i ++)
printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i],
ind->nodes[i]->value.element.name,
mxmlElementGetAttr(ind->nodes[i], attr));
}
else
{
puts("Node Address Element");
puts("-------- -------- --------------");
for (i = 0; i < ind->num_nodes; i ++)
printf("%8d %-8p %s\n", i, ind->nodes[i],
ind->nodes[i]->value.element.name);
}
putchar('\n');
}
#endif /* DEBUG */
/*
* Return the new index...
*/
return (ind);
}
/*
* 'mxmlIndexReset()' - Reset the enumeration/find pointer in the index and
* return the first node in the index.
*
* This function should be called prior to using mxmlIndexEnum() or
* mxmlIndexFind() for the first time.
*/
mxml_node_t * /* O - First node or NULL if there is none */
mxmlIndexReset(mxml_index_t *ind) /* I - Index to reset */
{
#ifdef DEBUG
printf("mxmlIndexReset(ind=%p)\n", ind);
#endif /* DEBUG */
/*
* Range check input...
*/
if (!ind)
return (NULL);
/*
* Set the index to the first element...
*/
ind->cur_node = 0;
/*
* Return the first node...
*/
if (ind->num_nodes)
return (ind->nodes[0]);
else
return (NULL);
}
/*
* 'index_compare()' - Compare two nodes.
*/
static int /* O - Result of comparison */
index_compare(mxml_index_t *ind, /* I - Index */
mxml_node_t *first, /* I - First node */
mxml_node_t *second) /* I - Second node */
{
int diff; /* Difference */
/*
* Check the element name...
*/
if ((diff = strcmp(first->value.element.name,
second->value.element.name)) != 0)
return (diff);
/*
* Check the attribute value...
*/
if (ind->attr)
{
if ((diff = strcmp(mxmlElementGetAttr(first, ind->attr),
mxmlElementGetAttr(second, ind->attr))) != 0)
return (diff);
}
/*
* No difference, return 0...
*/
return (0);
}
/*
* 'index_find()' - Compare a node with index values.
*/
static int /* O - Result of comparison */
index_find(mxml_index_t *ind, /* I - Index */
const char *element, /* I - Element name or NULL */
const char *value, /* I - Attribute value or NULL */
mxml_node_t *node) /* I - Node */
{
int diff; /* Difference */
/*
* Check the element name...
*/
if (element)
{
if ((diff = strcmp(element, node->value.element.name)) != 0)
return (diff);
}
/*
* Check the attribute value...
*/
if (value)
{
if ((diff = strcmp(value, mxmlElementGetAttr(node, ind->attr))) != 0)
return (diff);
}
/*
* No difference, return 0...
*/
return (0);
}
/*
* 'index_sort()' - Sort the nodes in the index...
*
* This function implements the classic quicksort algorithm...
*/
static void
index_sort(mxml_index_t *ind, /* I - Index to sort */
int left, /* I - Left node in partition */
int right) /* I - Right node in partition */
{
mxml_node_t *pivot, /* Pivot node */
*temp; /* Swap node */
int templ, /* Temporary left node */
tempr; /* Temporary right node */
/*
* Loop until we have sorted all the way to the right...
*/
do
{
/*
* Sort the pivot in the current partition...
*/
pivot = ind->nodes[left];
for (templ = left, tempr = right; templ < tempr;)
{
/*
* Move left while left node <= pivot node...
*/
while ((templ < right) &&
index_compare(ind, ind->nodes[templ], pivot) <= 0)
templ ++;
/*
* Move right while right node > pivot node...
*/
while ((tempr > left) &&
index_compare(ind, ind->nodes[tempr], pivot) > 0)
tempr --;
/*
* Swap nodes if needed...
*/
if (templ < tempr)
{
temp = ind->nodes[templ];
ind->nodes[templ] = ind->nodes[tempr];
ind->nodes[tempr] = temp;
}
}
/*
* When we get here, the right (tempr) node is the new position for the
* pivot node...
*/
if (index_compare(ind, pivot, ind->nodes[tempr]) > 0)
{
ind->nodes[left] = ind->nodes[tempr];
ind->nodes[tempr] = pivot;
}
/*
* Recursively sort the left partition as needed...
*/
if (left < (tempr - 1))
index_sort(ind, left, tempr - 1);
}
while (right > (left = tempr + 1));
}
/*
* End of "$Id: mxml-index.c 22267 2006-04-24 17:11:45Z kpfleming $".
*/