1 /*
2 * "$Id: mxml-search.c 427 2011-01-03 02:03:29Z mike $"
3 *
4 * Search/navigation functions for Mini-XML, a small XML-like file
5 * parsing library.
6 *
7 * Copyright 2003-2010 by Michael R Sweet.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Michael R Sweet and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "COPYING"
12 * which should have been included with this file. If this file is
13 * missing or damaged, see the license at:
14 *
15 * http://www.minixml.org/
16 *
17 * Contents:
18 *
19 * mxmlFindElement() - Find the named element.
20 * mxmlFindValue() - Find a value with the given path.
21 * mxmlWalkNext() - Walk to the next logical node in the tree.
22 * mxmlWalkPrev() - Walk to the previous logical node in the tree.
23 */
25 /*
26 * Include necessary headers...
27 */
29 #include "config.h"
30 #include "mxml.h"
33 /*
34 * 'mxmlFindElement()' - Find the named element.
35 *
36 * The search is constrained by the name, attribute name, and value; any
37 * NULL names or values are treated as wildcards, so different kinds of
38 * searches can be implemented by looking for all elements of a given name
39 * or all elements with a specific attribute. The descend argument determines
40 * whether the search descends into child nodes; normally you will use
41 * MXML_DESCEND_FIRST for the initial search and MXML_NO_DESCEND to find
42 * additional direct descendents of the node. The top node argument
43 * constrains the search to a particular node's children.
44 */
46 mxml_node_t * /* O - Element node or NULL */
47 mxmlFindElement(mxml_node_t *node, /* I - Current node */
48 mxml_node_t *top, /* I - Top node */
49 const char *name, /* I - Element name or NULL for any */
50 const char *attr, /* I - Attribute name, or NULL for none */
51 const char *value, /* I - Attribute value, or NULL for any */
52 int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */
53 {
54 const char *temp; /* Current attribute value */
57 /*
58 * Range check input...
59 */
61 if (!node || !top || (!attr && value))
62 return (NULL);
64 /*
65 * Start with the next node...
66 */
68 node = mxmlWalkNext(node, top, descend);
70 /*
71 * Loop until we find a matching element...
72 */
74 while (node != NULL)
75 {
76 /*
77 * See if this node matches...
78 */
80 if (node->type == MXML_ELEMENT &&
81 node->value.element.name &&
82 (!name || !strcmp(node->value.element.name, name)))
83 {
84 /*
85 * See if we need to check for an attribute...
86 */
88 if (!attr)
89 return (node); /* No attribute search, return it... */
91 /*
92 * Check for the attribute...
93 */
95 if ((temp = mxmlElementGetAttr(node, attr)) != NULL)
96 {
97 /*
98 * OK, we have the attribute, does it match?
99 */
101 if (!value || !strcmp(value, temp))
102 return (node); /* Yes, return it... */
103 }
104 }
106 /*
107 * No match, move on to the next node...
108 */
110 if (descend == MXML_DESCEND)
111 node = mxmlWalkNext(node, top, MXML_DESCEND);
112 else
113 node = node->next;
114 }
116 return (NULL);
117 }
120 /*
121 * 'mxmlFindPath()' - Find a node with the given path.
122 *
123 * The "path" is a slash-separated list of element names. The name "*" is
124 * considered a wildcard for one or more levels of elements. For example,
125 * "foo/one/two", "bar/two/one", "*\/one", and so forth.
126 *
127 * The first child node of the found node is returned if the given node has
128 * children and the first child is a value node.
129 *
130 * @since Mini-XML 2.7@
131 */
133 mxml_node_t * /* O - Found node or NULL */
134 mxmlFindPath(mxml_node_t *top, /* I - Top node */
135 const char *path) /* I - Path to element */
136 {
137 mxml_node_t *node; /* Current node */
138 char element[256]; /* Current element name */
139 const char *pathsep; /* Separator in path */
140 int descend; /* mxmlFindElement option */
143 /*
144 * Range check input...
145 */
147 if (!top || !path || !*path)
148 return (NULL);
150 /*
151 * Search each element in the path...
152 */
154 node = top;
155 while (*path)
156 {
157 /*
158 * Handle wildcards...
159 */
161 if (!strncmp(path, "*/", 2))
162 {
163 path += 2;
164 descend = MXML_DESCEND;
165 }
166 else
167 descend = MXML_DESCEND_FIRST;
169 /*
170 * Get the next element in the path...
171 */
173 if ((pathsep = strchr(path, '/')) == NULL)
174 pathsep = path + strlen(path);
176 if (pathsep == path || (pathsep - path) >= sizeof(element))
177 return (NULL);
179 memcpy(element, path, pathsep - path);
180 element[pathsep - path] = '\0';
182 if (*pathsep)
183 path = pathsep + 1;
184 else
185 path = pathsep;
187 /*
188 * Search for the element...
189 */
191 if ((node = mxmlFindElement(node, node, element, NULL, NULL,
192 descend)) == NULL)
193 return (NULL);
194 }
196 /*
197 * If we get this far, return the node or its first child...
198 */
200 if (node->child && node->child->type != MXML_ELEMENT)
201 return (node->child);
202 else
203 return (node);
204 }
207 /*
208 * 'mxmlWalkNext()' - Walk to the next logical node in the tree.
209 *
210 * The descend argument controls whether the first child is considered
211 * to be the next node. The top node argument constrains the walk to
212 * the node's children.
213 */
215 mxml_node_t * /* O - Next node or NULL */
216 mxmlWalkNext(mxml_node_t *node, /* I - Current node */
217 mxml_node_t *top, /* I - Top node */
218 int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */
219 {
220 if (!node)
221 return (NULL);
222 else if (node->child && descend)
223 return (node->child);
224 else if (node == top)
225 return (NULL);
226 else if (node->next)
227 return (node->next);
228 else if (node->parent && node->parent != top)
229 {
230 node = node->parent;
232 while (!node->next)
233 if (node->parent == top || !node->parent)
234 return (NULL);
235 else
236 node = node->parent;
238 return (node->next);
239 }
240 else
241 return (NULL);
242 }
245 /*
246 * 'mxmlWalkPrev()' - Walk to the previous logical node in the tree.
247 *
248 * The descend argument controls whether the previous node's last child
249 * is considered to be the previous node. The top node argument constrains
250 * the walk to the node's children.
251 */
253 mxml_node_t * /* O - Previous node or NULL */
254 mxmlWalkPrev(mxml_node_t *node, /* I - Current node */
255 mxml_node_t *top, /* I - Top node */
256 int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */
257 {
258 if (!node || node == top)
259 return (NULL);
260 else if (node->prev)
261 {
262 if (node->prev->last_child && descend)
263 {
264 /*
265 * Find the last child under the previous node...
266 */
268 node = node->prev->last_child;
270 while (node->last_child)
271 node = node->last_child;
273 return (node);
274 }
275 else
276 return (node->prev);
277 }
278 else if (node->parent != top)
279 return (node->parent);
280 else
281 return (NULL);
282 }
285 /*
286 * End of "$Id: mxml-search.c 427 2011-01-03 02:03:29Z mike $".
287 */