diff options
Diffstat (limited to 'xml/mxml-search.c')
-rw-r--r-- | xml/mxml-search.c | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/xml/mxml-search.c b/xml/mxml-search.c new file mode 100644 index 0000000..3b4eb3f --- /dev/null +++ b/xml/mxml-search.c @@ -0,0 +1,363 @@ +/* + * "$Id: mxml-search.c 427 2011-01-03 02:03:29Z mike $" + * + * Search/navigation functions for Mini-XML, a small XML-like file + * parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlFindElement() - Find the named element. + * mxmlFindValue() - Find a value with the given path. + * mxmlWalkNext() - Walk to the next logical node in the tree. + * mxmlWalkPrev() - Walk to the previous logical node in the tree. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * 'mxmlFindElement()' - Find the named element. + * + * The search is constrained by the name, attribute name, and value; any + * NULL names or values are treated as wildcards, so different kinds of + * searches can be implemented by looking for all elements of a given name + * or all elements with a specific attribute. The descend argument determines + * whether the search descends into child nodes; normally you will use + * MXML_DESCEND_FIRST for the initial search and MXML_NO_DESCEND to find + * additional direct descendents of the node. The top node argument + * constrains the search to a particular node's children. + */ + +mxml_node_t * /* O - Element node or NULL */ +mxmlFindElement(mxml_node_t *node, /* I - Current node */ + mxml_node_t *top, /* I - Top node */ + const char *name, /* I - Element name or NULL for any */ + const char *attr, /* I - Attribute name, or NULL for none */ + const char *value, /* I - Attribute value, or NULL for any */ + int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */ +{ + const char *temp; /* Current attribute value */ + + + /* + * Range check input... + */ + + if (!node || !top || (!attr && value)) + return (NULL); + + /* + * Start with the next node... + */ + + node = mxmlWalkNext(node, top, descend); + + /* + * Loop until we find a matching element... + */ + + while (node != NULL) + { + /* + * See if this node matches... + */ + + if (node->type == MXML_ELEMENT && + node->value.element.name && + (!name || !strcmp(node->value.element.name, name))) + { + /* + * See if we need to check for an attribute... + */ + + if (!attr) + return (node); /* No attribute search, return it... */ + + /* + * Check for the attribute... + */ + + if ((temp = mxmlElementGetAttr(node, attr)) != NULL) + { + /* + * OK, we have the attribute, does it match? + */ + + if (!value || !strcmp(value, temp)) + return (node); /* Yes, return it... */ + } + } + + /* + * No match, move on to the next node... + */ + + if (descend == MXML_DESCEND) + node = mxmlWalkNext(node, top, MXML_DESCEND); + else + node = node->next; + } + + return (NULL); +} + +/* + * 'mxmlFindPathNode()' - Find a node with the given path. + * + * The "path" is a slash-separated list of element names. The name "*" is + * considered a wildcard for one or more levels of elements. For example, + * "foo/one/two", "bar/two/one", "*\/one", and so forth. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * /* O - Found node or NULL */ +mxmlFindPathNode(mxml_node_t *top, /* I - Top node */ + const char *path) /* I - Path to element */ +{ + mxml_node_t *node; /* Current node */ + char element[256]; /* Current element name */ + const char *pathsep; /* Separator in path */ + int descend; /* mxmlFindElement option */ + + + /* + * Range check input... + */ + + if (!top || !path || !*path) + return (NULL); + + /* + * Search each element in the path... + */ + + node = top; + while (*path) + { + /* + * Handle wildcards... + */ + + if (!strncmp(path, "*/", 2)) + { + path += 2; + descend = MXML_DESCEND; + } + else + descend = MXML_DESCEND_FIRST; + + /* + * Get the next element in the path... + */ + + if ((pathsep = strchr(path, '/')) == NULL) + pathsep = path + strlen(path); + + if (pathsep == path || (pathsep - path) >= sizeof(element)) + return (NULL); + + memcpy(element, path, pathsep - path); + element[pathsep - path] = '\0'; + + if (*pathsep) + path = pathsep + 1; + else + path = pathsep; + + /* + * Search for the element... + */ + + if ((node = mxmlFindElement(node, node, element, NULL, NULL, + descend)) == NULL) + return (NULL); + } + + return (node); +} + + +/* + * 'mxmlFindPath()' - Find a node with the given path. + * + * The "path" is a slash-separated list of element names. The name "*" is + * considered a wildcard for one or more levels of elements. For example, + * "foo/one/two", "bar/two/one", "*\/one", and so forth. + * + * The first child node of the found node is returned if the given node has + * children and the first child is a value node. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * /* O - Found node or NULL */ +mxmlFindPath(mxml_node_t *top, /* I - Top node */ + const char *path) /* I - Path to element */ +{ + mxml_node_t *node; /* Current node */ + char element[256]; /* Current element name */ + const char *pathsep; /* Separator in path */ + int descend; /* mxmlFindElement option */ + + + /* + * Range check input... + */ + + if (!top || !path || !*path) + return (NULL); + + /* + * Search each element in the path... + */ + + node = top; + while (*path) + { + /* + * Handle wildcards... + */ + + if (!strncmp(path, "*/", 2)) + { + path += 2; + descend = MXML_DESCEND; + } + else + descend = MXML_DESCEND_FIRST; + + /* + * Get the next element in the path... + */ + + if ((pathsep = strchr(path, '/')) == NULL) + pathsep = path + strlen(path); + + if (pathsep == path || (pathsep - path) >= sizeof(element)) + return (NULL); + + memcpy(element, path, pathsep - path); + element[pathsep - path] = '\0'; + + if (*pathsep) + path = pathsep + 1; + else + path = pathsep; + + /* + * Search for the element... + */ + + if ((node = mxmlFindElement(node, node, element, NULL, NULL, + descend)) == NULL) + return (NULL); + } + + /* + * If we get this far, return the node or its first child... + */ + + if (node->child && node->child->type != MXML_ELEMENT) + return (node->child); + else + return (node); +} + + +/* + * 'mxmlWalkNext()' - Walk to the next logical node in the tree. + * + * The descend argument controls whether the first child is considered + * to be the next node. The top node argument constrains the walk to + * the node's children. + */ + +mxml_node_t * /* O - Next node or NULL */ +mxmlWalkNext(mxml_node_t *node, /* I - Current node */ + mxml_node_t *top, /* I - Top node */ + int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */ +{ + if (!node) + return (NULL); + else if (node->child && descend) + return (node->child); + else if (node == top) + return (NULL); + else if (node->next) + return (node->next); + else if (node->parent && node->parent != top) + { + node = node->parent; + + while (!node->next) + if (node->parent == top || !node->parent) + return (NULL); + else + node = node->parent; + + return (node->next); + } + else + return (NULL); +} + + +/* + * 'mxmlWalkPrev()' - Walk to the previous logical node in the tree. + * + * The descend argument controls whether the previous node's last child + * is considered to be the previous node. The top node argument constrains + * the walk to the node's children. + */ + +mxml_node_t * /* O - Previous node or NULL */ +mxmlWalkPrev(mxml_node_t *node, /* I - Current node */ + mxml_node_t *top, /* I - Top node */ + int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */ +{ + if (!node || node == top) + return (NULL); + else if (node->prev) + { + if (node->prev->last_child && descend) + { + /* + * Find the last child under the previous node... + */ + + node = node->prev->last_child; + + while (node->last_child) + node = node->last_child; + + return (node); + } + else + return (node->prev); + } + else if (node->parent != top) + return (node->parent); + else + return (NULL); +} + + +/* + * End of "$Id: mxml-search.c 427 2011-01-03 02:03:29Z mike $". + */ |