2024-02-27 20:04:27 +00:00
|
|
|
|
//
|
|
|
|
|
// Search/navigation functions for Mini-XML, a small XML file parsing library.
|
|
|
|
|
//
|
|
|
|
|
// https://www.msweet.org/mxml
|
|
|
|
|
//
|
|
|
|
|
// Copyright © 2003-2024 by Michael R Sweet.
|
|
|
|
|
//
|
|
|
|
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
|
|
|
|
// information.
|
|
|
|
|
//
|
|
|
|
|
|
2019-01-05 01:02:48 +00:00
|
|
|
|
#include "mxml-private.h"
|
2003-06-03 19:46:29 +00:00
|
|
|
|
|
|
|
|
|
|
2024-02-27 20:04:27 +00:00
|
|
|
|
//
|
|
|
|
|
// 'mxmlFindElement()' - Find the named element.
|
|
|
|
|
//
|
2024-03-17 02:20:24 +00:00
|
|
|
|
// This function finds the named element `element` in XML tree `top` starting at
|
|
|
|
|
// node `node`. The search is constrained by element name `element`, attribute
|
|
|
|
|
// name `attr`, and attribute value `value` - `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_DESCEND_NONE` to find additional direct descendents of the node.
|
2024-02-27 20:04:27 +00:00
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
mxml_node_t * // O - Element node or `NULL`
|
2024-03-07 01:03:48 +00:00
|
|
|
|
mxmlFindElement(mxml_node_t *node, // I - Current node
|
|
|
|
|
mxml_node_t *top, // I - Top node
|
|
|
|
|
const char *element,// 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
|
|
|
|
|
mxml_descend_t descend) // I - Descend into tree - `MXML_DESCEND_ALL`, `MXML_DESCEND_NONE`, or `MXML_DESCEND_FIRST`
|
2003-06-03 19:46:29 +00:00
|
|
|
|
{
|
2024-02-27 20:04:27 +00:00
|
|
|
|
const char *temp; // Current attribute value
|
2003-06-04 16:30:40 +00:00
|
|
|
|
|
|
|
|
|
|
2024-02-27 20:04:27 +00:00
|
|
|
|
// Range check input...
|
2003-06-04 16:30:40 +00:00
|
|
|
|
if (!node || !top || (!attr && value))
|
|
|
|
|
return (NULL);
|
|
|
|
|
|
2024-02-27 20:04:27 +00:00
|
|
|
|
// Start with the next node...
|
2003-06-04 16:30:40 +00:00
|
|
|
|
node = mxmlWalkNext(node, top, descend);
|
2003-06-03 19:46:29 +00:00
|
|
|
|
|
2024-02-27 20:04:27 +00:00
|
|
|
|
// Loop until we find a matching element...
|
2003-06-03 19:46:29 +00:00
|
|
|
|
while (node != NULL)
|
|
|
|
|
{
|
2024-02-27 20:04:27 +00:00
|
|
|
|
// See if this node matches...
|
|
|
|
|
if (node->type == MXML_TYPE_ELEMENT && node->value.element.name && (!element || !strcmp(node->value.element.name, element)))
|
2003-06-04 16:30:40 +00:00
|
|
|
|
{
|
2024-02-27 20:04:27 +00:00
|
|
|
|
// See if we need to check for an attribute...
|
2003-06-04 16:30:40 +00:00
|
|
|
|
if (!attr)
|
2024-02-27 20:04:27 +00:00
|
|
|
|
return (node); // No attribute search, return it...
|
2003-06-04 16:30:40 +00:00
|
|
|
|
|
2024-02-27 20:04:27 +00:00
|
|
|
|
// Check for the attribute...
|
2003-06-04 16:30:40 +00:00
|
|
|
|
if ((temp = mxmlElementGetAttr(node, attr)) != NULL)
|
|
|
|
|
{
|
2024-02-27 20:04:27 +00:00
|
|
|
|
// OK, we have the attribute, does it match?
|
2003-06-04 16:30:40 +00:00
|
|
|
|
if (!value || !strcmp(value, temp))
|
2024-02-27 20:04:27 +00:00
|
|
|
|
return (node); // Yes, return it...
|
2003-06-04 16:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2003-06-03 19:46:29 +00:00
|
|
|
|
|
2024-02-27 20:04:27 +00:00
|
|
|
|
// No match, move on to the next node...
|
2024-03-07 01:03:48 +00:00
|
|
|
|
if (descend == MXML_DESCEND_ALL)
|
|
|
|
|
node = mxmlWalkNext(node, top, MXML_DESCEND_ALL);
|
2003-06-04 16:30:40 +00:00
|
|
|
|
else
|
|
|
|
|
node = node->next;
|
2003-06-03 19:46:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-02-27 20:04:27 +00:00
|
|
|
|
//
|
|
|
|
|
// 'mxmlFindPath()' - Find a node with the given path.
|
|
|
|
|
//
|
2024-03-17 02:20:24 +00:00
|
|
|
|
// This function finds a node in XML tree `top` using a slash-separated list of
|
|
|
|
|
// element names in `path`. 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.
|
2024-02-27 20:04:27 +00:00
|
|
|
|
//
|
|
|
|
|
// The first child node of the found node is returned if the given node has
|
|
|
|
|
// children and the first child is a value node.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
mxml_node_t * // O - Found node or `NULL`
|
|
|
|
|
mxmlFindPath(mxml_node_t *top, // I - Top node
|
|
|
|
|
const char *path) // I - Path to element
|
2010-11-08 16:07:05 +00:00
|
|
|
|
{
|
2024-02-27 20:04:27 +00:00
|
|
|
|
mxml_node_t *node; // Current node
|
|
|
|
|
char element[256]; // Current element name
|
|
|
|
|
const char *pathsep; // Separator in path
|
2024-03-07 01:03:48 +00:00
|
|
|
|
mxml_descend_t descend; // mxmlFindElement option
|
2010-11-08 16:07:05 +00:00
|
|
|
|
|
|
|
|
|
|
2024-02-27 20:04:27 +00:00
|
|
|
|
// Range check input...
|
2010-11-08 16:07:05 +00:00
|
|
|
|
if (!top || !path || !*path)
|
|
|
|
|
return (NULL);
|
|
|
|
|
|
2024-02-27 20:04:27 +00:00
|
|
|
|
// Search each element in the path...
|
2010-11-08 16:07:05 +00:00
|
|
|
|
node = top;
|
|
|
|
|
while (*path)
|
|
|
|
|
{
|
2024-02-27 20:04:27 +00:00
|
|
|
|
// Handle wildcards...
|
2010-11-08 16:07:05 +00:00
|
|
|
|
if (!strncmp(path, "*/", 2))
|
|
|
|
|
{
|
|
|
|
|
path += 2;
|
2024-03-07 01:03:48 +00:00
|
|
|
|
descend = MXML_DESCEND_ALL;
|
2010-11-08 16:07:05 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2024-02-27 20:04:27 +00:00
|
|
|
|
{
|
2010-11-08 16:07:05 +00:00
|
|
|
|
descend = MXML_DESCEND_FIRST;
|
2024-02-27 20:04:27 +00:00
|
|
|
|
}
|
2010-11-08 16:07:05 +00:00
|
|
|
|
|
2024-02-27 20:04:27 +00:00
|
|
|
|
// Get the next element in the path...
|
2010-11-08 16:07:05 +00:00
|
|
|
|
if ((pathsep = strchr(path, '/')) == NULL)
|
|
|
|
|
pathsep = path + strlen(path);
|
|
|
|
|
|
2024-03-06 21:48:06 +00:00
|
|
|
|
if (pathsep == path || (size_t)(pathsep - path) >= sizeof(element))
|
2010-11-08 16:07:05 +00:00
|
|
|
|
return (NULL);
|
|
|
|
|
|
|
|
|
|
memcpy(element, path, pathsep - path);
|
|
|
|
|
element[pathsep - path] = '\0';
|
|
|
|
|
|
|
|
|
|
if (*pathsep)
|
|
|
|
|
path = pathsep + 1;
|
|
|
|
|
else
|
|
|
|
|
path = pathsep;
|
|
|
|
|
|
2024-02-27 20:04:27 +00:00
|
|
|
|
// Search for the element...
|
|
|
|
|
if ((node = mxmlFindElement(node, node, element, NULL, NULL, descend)) == NULL)
|
2010-11-08 16:07:05 +00:00
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-27 20:04:27 +00:00
|
|
|
|
// If we get this far, return the node or its first child...
|
|
|
|
|
if (node->child && node->child->type != MXML_TYPE_ELEMENT)
|
2011-01-03 02:03:29 +00:00
|
|
|
|
return (node->child);
|
|
|
|
|
else
|
|
|
|
|
return (node);
|
2010-11-08 16:07:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-02-27 20:04:27 +00:00
|
|
|
|
//
|
|
|
|
|
// 'mxmlWalkNext()' - Walk to the next logical node in the tree.
|
|
|
|
|
//
|
2024-03-17 02:20:24 +00:00
|
|
|
|
// This function walks 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` argument constrains the walk to that node's children.
|
2024-02-27 20:04:27 +00:00
|
|
|
|
//
|
2003-06-03 19:46:29 +00:00
|
|
|
|
|
2024-02-27 20:04:27 +00:00
|
|
|
|
mxml_node_t * // O - Next node or `NULL`
|
2024-03-07 01:03:48 +00:00
|
|
|
|
mxmlWalkNext(mxml_node_t *node, // I - Current node
|
|
|
|
|
mxml_node_t *top, // I - Top node
|
|
|
|
|
mxml_descend_t descend) // I - Descend into tree - `MXML_DESCEND_ALL`, `MXML_DESCEND_NONE`, or `MXML_DESCEND_FIRST`
|
2003-06-03 19:46:29 +00:00
|
|
|
|
{
|
2003-06-04 16:30:40 +00:00
|
|
|
|
if (!node)
|
2024-02-27 20:04:27 +00:00
|
|
|
|
{
|
2003-06-04 16:30:40 +00:00
|
|
|
|
return (NULL);
|
2024-02-27 20:04:27 +00:00
|
|
|
|
}
|
2024-03-07 01:03:48 +00:00
|
|
|
|
else if (node->child && descend != MXML_DESCEND_NONE)
|
2024-02-27 20:04:27 +00:00
|
|
|
|
{
|
2003-06-04 16:30:40 +00:00
|
|
|
|
return (node->child);
|
2024-02-27 20:04:27 +00:00
|
|
|
|
}
|
2007-09-09 07:16:52 +00:00
|
|
|
|
else if (node == top)
|
2024-02-27 20:04:27 +00:00
|
|
|
|
{
|
2007-09-09 07:16:52 +00:00
|
|
|
|
return (NULL);
|
2024-02-27 20:04:27 +00:00
|
|
|
|
}
|
2003-06-04 16:30:40 +00:00
|
|
|
|
else if (node->next)
|
2024-02-27 20:04:27 +00:00
|
|
|
|
{
|
2003-06-04 16:30:40 +00:00
|
|
|
|
return (node->next);
|
2024-02-27 20:04:27 +00:00
|
|
|
|
}
|
2003-06-04 21:19:00 +00:00
|
|
|
|
else if (node->parent && node->parent != top)
|
2003-06-04 16:30:40 +00:00
|
|
|
|
{
|
|
|
|
|
node = node->parent;
|
|
|
|
|
|
|
|
|
|
while (!node->next)
|
2024-02-27 20:04:27 +00:00
|
|
|
|
{
|
2003-06-06 03:09:31 +00:00
|
|
|
|
if (node->parent == top || !node->parent)
|
2003-06-04 16:30:40 +00:00
|
|
|
|
return (NULL);
|
|
|
|
|
else
|
|
|
|
|
node = node->parent;
|
2024-02-27 20:04:27 +00:00
|
|
|
|
}
|
2003-06-04 16:30:40 +00:00
|
|
|
|
|
|
|
|
|
return (node->next);
|
|
|
|
|
}
|
|
|
|
|
else
|
2024-02-27 20:04:27 +00:00
|
|
|
|
{
|
2003-06-04 16:30:40 +00:00
|
|
|
|
return (NULL);
|
2024-02-27 20:04:27 +00:00
|
|
|
|
}
|
2003-06-03 19:46:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-02-27 20:04:27 +00:00
|
|
|
|
//
|
|
|
|
|
// 'mxmlWalkPrev()' - Walk to the previous logical node in the tree.
|
|
|
|
|
//
|
2024-03-17 02:20:24 +00:00
|
|
|
|
// This function walks to the previous logical node in the tree. The `descend`
|
|
|
|
|
// argument controls whether the first child is considered to be the next node.
|
|
|
|
|
// The `top` argument constrains the walk to that node's children.
|
2024-02-27 20:04:27 +00:00
|
|
|
|
//
|
2003-06-03 19:46:29 +00:00
|
|
|
|
|
2024-02-27 20:04:27 +00:00
|
|
|
|
mxml_node_t * // O - Previous node or `NULL`
|
2024-03-07 01:03:48 +00:00
|
|
|
|
mxmlWalkPrev(mxml_node_t *node, // I - Current node
|
|
|
|
|
mxml_node_t *top, // I - Top node
|
|
|
|
|
mxml_descend_t descend) // I - Descend into tree - `MXML_DESCEND_ALL`, `MXML_DESCEND_NONE`, or `MXML_DESCEND_FIRST`
|
2003-06-03 19:46:29 +00:00
|
|
|
|
{
|
2007-09-09 07:16:52 +00:00
|
|
|
|
if (!node || node == top)
|
2024-02-27 20:04:27 +00:00
|
|
|
|
{
|
2003-06-03 19:46:29 +00:00
|
|
|
|
return (NULL);
|
2024-02-27 20:04:27 +00:00
|
|
|
|
}
|
2003-06-03 19:46:29 +00:00
|
|
|
|
else if (node->prev)
|
2003-06-04 16:30:40 +00:00
|
|
|
|
{
|
2024-03-07 01:03:48 +00:00
|
|
|
|
if (node->prev->last_child && descend != MXML_DESCEND_NONE)
|
2003-06-04 16:30:40 +00:00
|
|
|
|
{
|
2024-02-27 20:04:27 +00:00
|
|
|
|
// Find the last child under the previous node...
|
2003-06-04 16:30:40 +00:00
|
|
|
|
node = node->prev->last_child;
|
2003-06-03 19:46:29 +00:00
|
|
|
|
|
2003-06-04 16:30:40 +00:00
|
|
|
|
while (node->last_child)
|
|
|
|
|
node = node->last_child;
|
2003-06-03 20:40:01 +00:00
|
|
|
|
|
2003-06-04 16:30:40 +00:00
|
|
|
|
return (node);
|
|
|
|
|
}
|
|
|
|
|
else
|
2024-02-27 20:04:27 +00:00
|
|
|
|
{
|
2003-06-04 16:30:40 +00:00
|
|
|
|
return (node->prev);
|
2024-02-27 20:04:27 +00:00
|
|
|
|
}
|
2003-06-04 16:30:40 +00:00
|
|
|
|
}
|
2003-06-03 20:40:01 +00:00
|
|
|
|
else if (node->parent != top)
|
2024-02-27 20:04:27 +00:00
|
|
|
|
{
|
2003-06-04 16:30:40 +00:00
|
|
|
|
return (node->parent);
|
2024-02-27 20:04:27 +00:00
|
|
|
|
}
|
2003-06-03 20:40:01 +00:00
|
|
|
|
else
|
2024-02-27 20:04:27 +00:00
|
|
|
|
{
|
2003-06-03 20:40:01 +00:00
|
|
|
|
return (NULL);
|
2024-02-27 20:04:27 +00:00
|
|
|
|
}
|
2003-06-03 20:40:01 +00:00
|
|
|
|
}
|