mxml-search.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. /*
  2. * Search/navigation functions for Mini-XML, a small XML file parsing library.
  3. *
  4. * https://www.msweet.org/mxml
  5. *
  6. * Copyright © 2003-2019 by Michael R Sweet.
  7. *
  8. * Licensed under Apache License v2.0. See the file "LICENSE" for more
  9. * information.
  10. */
  11. /*
  12. * Include necessary headers...
  13. */
  14. #include "config.h"
  15. #include "mxml-private.h"
  16. /*
  17. * 'mxmlFindElement()' - Find the named element.
  18. *
  19. * The search is constrained by the name, attribute name, and value; any
  20. * @code NULL@ names or values are treated as wildcards, so different kinds of
  21. * searches can be implemented by looking for all elements of a given name
  22. * or all elements with a specific attribute. The descend argument determines
  23. * whether the search descends into child nodes; normally you will use
  24. * @code MXML_DESCEND_FIRST@ for the initial search and @code MXML_NO_DESCEND@
  25. * to find additional direct descendents of the node. The top node argument
  26. * constrains the search to a particular node's children.
  27. */
  28. mxml_node_t * /* O - Element node or @code NULL@ */
  29. mxmlFindElement(mxml_node_t *node, /* I - Current node */
  30. mxml_node_t *top, /* I - Top node */
  31. const char *element, /* I - Element name or @code NULL@ for any */
  32. const char *attr, /* I - Attribute name, or @code NULL@ for none */
  33. const char *value, /* I - Attribute value, or @code NULL@ for any */
  34. int descend) /* I - Descend into tree - @code MXML_DESCEND@, @code MXML_NO_DESCEND@, or @code MXML_DESCEND_FIRST@ */
  35. {
  36. const char *temp; /* Current attribute value */
  37. /*
  38. * Range check input...
  39. */
  40. if (!node || !top || (!attr && value))
  41. return (NULL);
  42. /*
  43. * Start with the next node...
  44. */
  45. node = mxmlWalkNext(node, top, descend);
  46. /*
  47. * Loop until we find a matching element...
  48. */
  49. while (node != NULL)
  50. {
  51. /*
  52. * See if this node matches...
  53. */
  54. if (node->type == MXML_ELEMENT &&
  55. node->value.element.name &&
  56. (!element || !strcmp(node->value.element.name, element)))
  57. {
  58. /*
  59. * See if we need to check for an attribute...
  60. */
  61. if (!attr)
  62. return (node); /* No attribute search, return it... */
  63. /*
  64. * Check for the attribute...
  65. */
  66. if ((temp = mxmlElementGetAttr(node, attr)) != NULL)
  67. {
  68. /*
  69. * OK, we have the attribute, does it match?
  70. */
  71. if (!value || !strcmp(value, temp))
  72. return (node); /* Yes, return it... */
  73. }
  74. }
  75. /*
  76. * No match, move on to the next node...
  77. */
  78. if (descend == MXML_DESCEND)
  79. node = mxmlWalkNext(node, top, MXML_DESCEND);
  80. else
  81. node = node->next;
  82. }
  83. return (NULL);
  84. }
  85. /*
  86. * 'mxmlFindPath()' - Find a node with the given path.
  87. *
  88. * The "path" is a slash-separated list of element names. The name "*" is
  89. * considered a wildcard for one or more levels of elements. For example,
  90. * "foo/one/two", "bar/two/one", "*\/one", and so forth.
  91. *
  92. * The first child node of the found node is returned if the given node has
  93. * children and the first child is a value node.
  94. *
  95. * @since Mini-XML 2.7@
  96. */
  97. mxml_node_t * /* O - Found node or @code NULL@ */
  98. mxmlFindPath(mxml_node_t *top, /* I - Top node */
  99. const char *path) /* I - Path to element */
  100. {
  101. mxml_node_t *node; /* Current node */
  102. char element[256]; /* Current element name */
  103. const char *pathsep; /* Separator in path */
  104. int descend; /* mxmlFindElement option */
  105. /*
  106. * Range check input...
  107. */
  108. if (!top || !path || !*path)
  109. return (NULL);
  110. /*
  111. * Search each element in the path...
  112. */
  113. node = top;
  114. while (*path)
  115. {
  116. /*
  117. * Handle wildcards...
  118. */
  119. if (!strncmp(path, "*/", 2))
  120. {
  121. path += 2;
  122. descend = MXML_DESCEND;
  123. }
  124. else
  125. descend = MXML_DESCEND_FIRST;
  126. /*
  127. * Get the next element in the path...
  128. */
  129. if ((pathsep = strchr(path, '/')) == NULL)
  130. pathsep = path + strlen(path);
  131. if (pathsep == path || (pathsep - path) >= sizeof(element))
  132. return (NULL);
  133. memcpy(element, path, pathsep - path);
  134. element[pathsep - path] = '\0';
  135. if (*pathsep)
  136. path = pathsep + 1;
  137. else
  138. path = pathsep;
  139. /*
  140. * Search for the element...
  141. */
  142. if ((node = mxmlFindElement(node, node, element, NULL, NULL,
  143. descend)) == NULL)
  144. return (NULL);
  145. }
  146. /*
  147. * If we get this far, return the node or its first child...
  148. */
  149. if (node->child && node->child->type != MXML_ELEMENT)
  150. return (node->child);
  151. else
  152. return (node);
  153. }
  154. /*
  155. * 'mxmlWalkNext()' - Walk to the next logical node in the tree.
  156. *
  157. * The descend argument controls whether the first child is considered
  158. * to be the next node. The top node argument constrains the walk to
  159. * the node's children.
  160. */
  161. mxml_node_t * /* O - Next node or @code NULL@ */
  162. mxmlWalkNext(mxml_node_t *node, /* I - Current node */
  163. mxml_node_t *top, /* I - Top node */
  164. int descend) /* I - Descend into tree - @code MXML_DESCEND@, @code MXML_NO_DESCEND@, or @code MXML_DESCEND_FIRST@ */
  165. {
  166. if (!node)
  167. return (NULL);
  168. else if (node->child && descend)
  169. return (node->child);
  170. else if (node == top)
  171. return (NULL);
  172. else if (node->next)
  173. return (node->next);
  174. else if (node->parent && node->parent != top)
  175. {
  176. node = node->parent;
  177. while (!node->next)
  178. if (node->parent == top || !node->parent)
  179. return (NULL);
  180. else
  181. node = node->parent;
  182. return (node->next);
  183. }
  184. else
  185. return (NULL);
  186. }
  187. /*
  188. * 'mxmlWalkPrev()' - Walk to the previous logical node in the tree.
  189. *
  190. * The descend argument controls whether the previous node's last child
  191. * is considered to be the previous node. The top node argument constrains
  192. * the walk to the node's children.
  193. */
  194. mxml_node_t * /* O - Previous node or @code NULL@ */
  195. mxmlWalkPrev(mxml_node_t *node, /* I - Current node */
  196. mxml_node_t *top, /* I - Top node */
  197. int descend) /* I - Descend into tree - @code MXML_DESCEND@, @code MXML_NO_DESCEND@, or @code MXML_DESCEND_FIRST@ */
  198. {
  199. if (!node || node == top)
  200. return (NULL);
  201. else if (node->prev)
  202. {
  203. if (node->prev->last_child && descend)
  204. {
  205. /*
  206. * Find the last child under the previous node...
  207. */
  208. node = node->prev->last_child;
  209. while (node->last_child)
  210. node = node->last_child;
  211. return (node);
  212. }
  213. else
  214. return (node->prev);
  215. }
  216. else if (node->parent != top)
  217. return (node->parent);
  218. else
  219. return (NULL);
  220. }