From 159e54f5d2df4eb811be0d40c50a16bb215acd7b Mon Sep 17 00:00:00 2001 From: Michael R Sweet Date: Mon, 8 Nov 2010 16:07:05 +0000 Subject: [PATCH] Add mxmlFindValue API (STR #110) --- CHANGES | 4 ++- README | 7 +++- doc/basics.html | 13 ++++++++ doc/intro.man | 7 ++++ doc/reference.html | 23 +++++++++++++ doc/relnotes.html | 6 ++++ mxml-search.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++ mxml.h | 1 + mxml.xml | 21 ++++++++++++ mxmldoc.c | 13 ++++++-- testmxml.c | 54 ++++++++++++++++++++++++++++-- 11 files changed, 224 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index c962908..a7453f0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,10 +1,12 @@ -CHANGES - 2010-11-07 +CHANGES - 2010-11-08 -------------------- CHANGES IN Mini-XML 2.7 - Updated the source headers to reference the Mini-XML license and its exceptions to the LGPL2 (STR #108) + - Added a new mxmlFindValue() function to find the value node of a + named element (STR #110) - Building a static version of the library did not work on Windows (STR #112) - The shared library did not include a destructor for the thread- diff --git a/README b/README index ed5af99..620c5d7 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -README - 2010-09-19 +README - 2010-11-08 ------------------- @@ -156,6 +156,11 @@ DOCUMENTATION ... do something ... } + The "mxmlFindValue()" function finds the (first) value node under a specific + element using a "path": + + mxml_node_t *value = mxmlFindValue(tree, "path/to/*/foo/bar"); + Finally, once you are done with the XML data, use the "mxmlDelete()" function to recursively free the memory that is used for a particular node or the entire tree: diff --git a/doc/basics.html b/doc/basics.html index 6804a3a..9ca0838 100644 --- a/doc/basics.html +++ b/doc/basics.html @@ -522,5 +522,18 @@ three constants:

+

Finding Value Nodes

+ +

You can find the value of a specific node in the tree using the mxmlFindValue, for example: + +

+    mxml_node_t *value = mxmlFindValue(tree, "path/to/*/foo/bar");
+
+ +

The second argument is a "path" to the parent node. Each component of the +path is separated by a slash (/) and represents a named element in the document +tree or a wildcard (*) path representing 0 or more intervening nodes.

+ diff --git a/doc/intro.man b/doc/intro.man index 134dacb..7d05617 100644 --- a/doc/intro.man +++ b/doc/intro.man @@ -141,6 +141,13 @@ You can also iterate with the same function: } .fi .PP +To find the value of a specific node in the tree, use the "mxmlFindValue()" +function: +.nf + + mxml_node_t *value = mxmlFindValue(tree, "path/to/*/foo/bar"); +.fi +.PP Finally, once you are done with the XML data, use the "mxmlDelete()" function to recursively free the memory that is used for a particular node or the entire tree: diff --git a/doc/reference.html b/doc/reference.html index 25c4ebc..231778d 100644 --- a/doc/reference.html +++ b/doc/reference.html @@ -155,6 +155,7 @@ Reference
  • mxmlEntityGetValue
  • mxmlEntityRemoveCallback
  • mxmlFindElement
  • +
  • mxmlFindValue
  • mxmlIndexDelete
  • mxmlIndexEnum
  • mxmlIndexFind
  • @@ -450,6 +451,28 @@ 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.

    +

     Mini-XML 2.7 mxmlFindValue

    +

    Find a value with the given path.

    +

    +mxml_node_t *mxmlFindValue (
    +    mxml_node_t *top,
    +    const char *path
    +);

    +

    Parameters

    +
    +
    top
    +
    Top node
    +
    path
    +
    Path to element
    +
    +

    Return Value

    +

    First value node or NULL

    +

    Discussion

    +

    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. + +

    mxmlIndexDelete

    Delete an index.

    diff --git a/doc/relnotes.html b/doc/relnotes.html index 177726a..c409162 100644 --- a/doc/relnotes.html +++ b/doc/relnotes.html @@ -12,6 +12,12 @@ hspace="10" width="100" height="100" alt="B">Release Notes

  • Updated the source headers to reference the Mini-XML license and its exceptions to the LGPL2 (STR #108)
  • +
  • Added a new mxmlFindValue() function to find the value node of a + named element (STR #110)
  • + +
  • Building a static version of the library did not work on Windows + (STR #112)
  • +
  • The shared library did not include a destructor for the thread- specific data key on UNIX-based operating systems (STR #103)
  • diff --git a/mxml-search.c b/mxml-search.c index 58d7fba..ab0e1b3 100644 --- a/mxml-search.c +++ b/mxml-search.c @@ -17,6 +17,7 @@ * 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. */ @@ -116,6 +117,87 @@ mxmlFindElement(mxml_node_t *node, /* I - Current node */ } +/* + * 'mxmlFindValue()' - Find a value 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 - First value node or NULL */ +mxmlFindValue(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 */ + *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 first child of the current node... + */ + + return (node->child); +} + + /* * 'mxmlWalkNext()' - Walk to the next logical node in the tree. * diff --git a/mxml.h b/mxml.h index 1c74de7..f58f2bd 100644 --- a/mxml.h +++ b/mxml.h @@ -205,6 +205,7 @@ extern void mxmlEntityRemoveCallback(mxml_entity_cb_t cb); extern mxml_node_t *mxmlFindElement(mxml_node_t *node, mxml_node_t *top, const char *name, const char *attr, const char *value, int descend); +extern mxml_node_t *mxmlFindValue(mxml_node_t *node, const char *path); extern void mxmlIndexDelete(mxml_index_t *ind); extern mxml_node_t *mxmlIndexEnum(mxml_index_t *ind); extern mxml_node_t *mxmlIndexFind(mxml_index_t *ind, diff --git a/mxml.xml b/mxml.xml index 6da1941..8842167 100644 --- a/mxml.xml +++ b/mxml.xml @@ -197,6 +197,27 @@ constrains the search to a particular node's children. Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST + + + mxml_node_t * + First value node or NULL + + Find a value 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 * + Top node + + + const char * + Path to element + + Delete an index. diff --git a/mxmldoc.c b/mxmldoc.c index a994bbd..330335f 100644 --- a/mxmldoc.c +++ b/mxmldoc.c @@ -2627,7 +2627,16 @@ update_comment(mxml_node_t *parent, /* I - Parent node */ if (!parent || !comment) return; - + + /* + * Convert "\/" to "/"... + */ + + for (ptr = strstr(comment->value.text.string, "\\/"); + ptr; + ptr = strstr(ptr, "\\/")) + safe_strcpy(ptr, ptr + 1); + /* * Update the comment... */ @@ -2662,7 +2671,7 @@ update_comment(mxml_node_t *parent, /* I - Parent node */ { /* * 'Convert "I - description", "IO - description", or "O - description" - * to description + directory attribute. + * to description + direction attribute. */ ptr = strchr(ptr, ' '); diff --git a/testmxml.c b/testmxml.c index ef75bc6..2fc1329 100644 --- a/testmxml.c +++ b/testmxml.c @@ -130,6 +130,8 @@ main(int argc, /* I - Number of command-line args */ MXML_REAL_CALLBACK); mxmlLoadString(tree, "opaque opaque opaque", MXML_OPAQUE_CALLBACK); + mxmlLoadString(tree, "valuevalue2" + "", MXML_OPAQUE_CALLBACK); node = tree->child; @@ -258,6 +260,52 @@ main(int argc, /* I - Number of command-line args */ } } + /* + * Test mxmlFindValue... + */ + + node = mxmlFindValue(tree, "*/two"); + if (!node) + { + fputs("ERROR: Unable to find value for \"*/two\".\n", stderr); + mxmlDelete(tree); + return (1); + } + else if (node->type != MXML_OPAQUE || strcmp(node->value.opaque, "value")) + { + fputs("ERROR: Bad value for \"*/two\".\n", stderr); + mxmlDelete(tree); + return (1); + } + + node = mxmlFindValue(tree, "foo/*/two"); + if (!node) + { + fputs("ERROR: Unable to find value for \"foo/*/two\".\n", stderr); + mxmlDelete(tree); + return (1); + } + else if (node->type != MXML_OPAQUE || strcmp(node->value.opaque, "value")) + { + fputs("ERROR: Bad value for \"foo/*/two\".\n", stderr); + mxmlDelete(tree); + return (1); + } + + node = mxmlFindValue(tree, "foo/bar/one/two"); + if (!node) + { + fputs("ERROR: Unable to find value for \"foo/bar/one/two\".\n", stderr); + mxmlDelete(tree); + return (1); + } + else if (node->type != MXML_OPAQUE || strcmp(node->value.opaque, "value")) + { + fputs("ERROR: Bad value for \"foo/bar/one/two\".\n", stderr); + mxmlDelete(tree); + return (1); + } + /* * Test indices... */ @@ -270,10 +318,10 @@ main(int argc, /* I - Number of command-line args */ return (1); } - if (ind->num_nodes != 5) + if (ind->num_nodes != 10) { fprintf(stderr, "ERROR: Index of all nodes contains %d " - "nodes; expected 5!\n", ind->num_nodes); + "nodes; expected 10!\n", ind->num_nodes); mxmlIndexDelete(ind); mxmlDelete(tree); return (1); @@ -378,7 +426,7 @@ main(int argc, /* I - Number of command-line args */ * Check the mxmlDelete() works properly... */ - for (i = 0; i < 8; i ++) + for (i = 0; i < 9; i ++) { if (tree->child) mxmlDelete(tree->child);