mxml-search.c 6.63 KB
Newer Older
Michael R Sweet's avatar
Michael R Sweet committed
1
/*
Michael Sweet's avatar
Michael Sweet committed
2
 * Search/navigation functions for Mini-XML, a small XML file parsing library.
Michael R Sweet's avatar
Michael R Sweet committed
3
 *
Michael Sweet's avatar
Michael Sweet committed
4
 * Copyright 2003-2017 by Michael R Sweet.
Michael R Sweet's avatar
Michael R Sweet committed
5
 *
6 7 8 9 10
 * 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:
Michael R Sweet's avatar
Michael R Sweet committed
11
 *
Michael Sweet's avatar
Michael Sweet committed
12
 *     https://michaelrsweet.github.io/mxml
Michael R Sweet's avatar
Michael R Sweet committed
13 14 15 16 17 18
 */

/*
 * Include necessary headers...
 */

19
#include "config.h"
20
#include "mxml.h"
Michael R Sweet's avatar
Michael R Sweet committed
21 22 23 24


/*
 * 'mxmlFindElement()' - Find the named element.
25 26
 *
 * The search is constrained by the name, attribute name, and value; any
27
 * @code NULL@ names or values are treated as wildcards, so different kinds of
28 29 30
 * 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
31 32
 * @code MXML_DESCEND_FIRST@ for the initial search and @code MXML_NO_DESCEND@
 * to find additional direct descendents of the node. The top node argument
33
 * constrains the search to a particular node's children.
Michael R Sweet's avatar
Michael R Sweet committed
34 35
 */

36
mxml_node_t *				/* O - Element node or @code NULL@ */
Michael R Sweet's avatar
Michael R Sweet committed
37 38
mxmlFindElement(mxml_node_t *node,	/* I - Current node */
                mxml_node_t *top,	/* I - Top node */
39 40 41 42
                const char  *element,	/* I - Element name or @code NULL@ for any */
		const char  *attr,	/* I - Attribute name, or @code NULL@ for none */
		const char  *value,	/* I - Attribute value, or @code NULL@ for any */
		int         descend)	/* I - Descend into tree - @code MXML_DESCEND@, @code MXML_NO_DESCEND@, or @code MXML_DESCEND_FIRST@ */
Michael R Sweet's avatar
Michael R Sweet committed
43
{
44 45 46 47 48 49 50 51 52 53
  const char	*temp;			/* Current attribute value */


 /*
  * Range check input...
  */

  if (!node || !top || (!attr && value))
    return (NULL);

Michael R Sweet's avatar
Michael R Sweet committed
54 55 56 57
 /*
  * Start with the next node...
  */

58
  node = mxmlWalkNext(node, top, descend);
Michael R Sweet's avatar
Michael R Sweet committed
59 60 61 62 63 64 65 66 67 68 69 70 71

 /*
  * Loop until we find a matching element...
  */

  while (node != NULL)
  {
   /*
    * See if this node matches...
    */

    if (node->type == MXML_ELEMENT &&
        node->value.element.name &&
72
	(!element || !strcmp(node->value.element.name, element)))
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
    {
     /*
      * 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... */
      }
    }
Michael R Sweet's avatar
Michael R Sweet committed
95 96

   /*
97
    * No match, move on to the next node...
Michael R Sweet's avatar
Michael R Sweet committed
98 99
    */

100 101 102 103
    if (descend == MXML_DESCEND)
      node = mxmlWalkNext(node, top, MXML_DESCEND);
    else
      node = node->next;
Michael R Sweet's avatar
Michael R Sweet committed
104 105 106 107 108 109
  }

  return (NULL);
}


110
/*
111
 * 'mxmlFindPath()' - Find a node with the given path.
112 113 114 115 116
 *
 * 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.
 *
117 118
 * The first child node of the found node is returned if the given node has
 * children and the first child is a value node.
119
 *
120 121 122
 * @since Mini-XML 2.7@
 */

123
mxml_node_t *				/* O - Found node or @code NULL@ */
124 125
mxmlFindPath(mxml_node_t *top,		/* I - Top node */
	     const char  *path)		/* I - Path to element */
126 127
{
  mxml_node_t	*node;			/* Current node */
128 129
  char		element[256];		/* Current element name */
  const char	*pathsep;		/* Separator in path */
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
  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);
  }

 /*
187
  * If we get this far, return the node or its first child...
188 189
  */

190 191 192 193
  if (node->child && node->child->type != MXML_ELEMENT)
    return (node->child);
  else
    return (node);
194 195 196
}


Michael R Sweet's avatar
Michael R Sweet committed
197 198
/*
 * 'mxmlWalkNext()' - Walk to the next logical node in the tree.
199 200
 *
 * The descend argument controls whether the first child is considered
201
 * to be the next node.  The top node argument constrains the walk to
202
 * the node's children.
Michael R Sweet's avatar
Michael R Sweet committed
203 204
 */

205
mxml_node_t *				/* O - Next node or @code NULL@ */
Michael R Sweet's avatar
Michael R Sweet committed
206
mxmlWalkNext(mxml_node_t *node,		/* I - Current node */
207
             mxml_node_t *top,		/* I - Top node */
208
             int         descend)	/* I - Descend into tree - @code MXML_DESCEND@, @code MXML_NO_DESCEND@, or @code MXML_DESCEND_FIRST@ */
Michael R Sweet's avatar
Michael R Sweet committed
209
{
210 211 212 213
  if (!node)
    return (NULL);
  else if (node->child && descend)
    return (node->child);
214 215
  else if (node == top)
    return (NULL);
216 217
  else if (node->next)
    return (node->next);
218
  else if (node->parent && node->parent != top)
219 220 221 222
  {
    node = node->parent;

    while (!node->next)
223
      if (node->parent == top || !node->parent)
224 225 226 227 228 229 230 231
        return (NULL);
      else
        node = node->parent;

    return (node->next);
  }
  else
    return (NULL);
Michael R Sweet's avatar
Michael R Sweet committed
232 233 234 235 236
}


/*
 * 'mxmlWalkPrev()' - Walk to the previous logical node in the tree.
237 238
 *
 * The descend argument controls whether the previous node's last child
239
 * is considered to be the previous node.  The top node argument constrains
240
 * the walk to the node's children.
Michael R Sweet's avatar
Michael R Sweet committed
241 242
 */

243
mxml_node_t *				/* O - Previous node or @code NULL@ */
Michael R Sweet's avatar
Michael R Sweet committed
244
mxmlWalkPrev(mxml_node_t *node,		/* I - Current node */
245
             mxml_node_t *top,		/* I - Top node */
246
             int         descend)	/* I - Descend into tree - @code MXML_DESCEND@, @code MXML_NO_DESCEND@, or @code MXML_DESCEND_FIRST@ */
Michael R Sweet's avatar
Michael R Sweet committed
247
{
248
  if (!node || node == top)
Michael R Sweet's avatar
Michael R Sweet committed
249 250
    return (NULL);
  else if (node->prev)
251 252 253 254 255 256
  {
    if (node->prev->last_child && descend)
    {
     /*
      * Find the last child under the previous node...
      */
Michael R Sweet's avatar
Michael R Sweet committed
257

258
      node = node->prev->last_child;
Michael R Sweet's avatar
Michael R Sweet committed
259

260 261
      while (node->last_child)
        node = node->last_child;
Michael R Sweet's avatar
Michael R Sweet committed
262

263 264 265 266 267
      return (node);
    }
    else
      return (node->prev);
  }
Michael R Sweet's avatar
Michael R Sweet committed
268
  else if (node->parent != top)
269
    return (node->parent);
Michael R Sweet's avatar
Michael R Sweet committed
270 271 272
  else
    return (NULL);
}