xml.c: support numerically indexed elements in wimlib_{get,set}_image_property()
authorEric Biggers <ebiggers3@gmail.com>
Sat, 19 Dec 2015 22:32:22 +0000 (16:32 -0600)
committerEric Biggers <ebiggers3@gmail.com>
Sat, 19 Dec 2015 22:32:22 +0000 (16:32 -0600)
include/wimlib.h
src/xml.c

index 0946227..10f9bb7 100644 (file)
@@ -3186,7 +3186,11 @@ wimlib_get_image_name(const WIMStruct *wim, int image);
  *     "TOTALBYTES".  The name can contain forward slashes to indicate a nested
  *     XML element; for example, "WINDOWS/VERSION/BUILD" indicates the BUILD
  *     element nested within the VERSION element nested within the WINDOWS
- *     element.  The <tt>[</tt> character is reserved for future use.
+ *     element.  Since wimlib v1.8.4, a bracketed number can be used to
+ *     indicate one of several identically-named elements; for example,
+ *     "WINDOWS/LANGUAGES/LANGUAGE[2]" indicates the second "LANGUAGE" element
+ *     nested within the "WINDOWS/LANGUAGES" element.  Note that element names
+ *     are case sensitive.
  *
  * @return
  *     The property's value as a ::wimlib_tchar string, or @c NULL if there is
@@ -4043,6 +4047,13 @@ wimlib_set_image_name(WIMStruct *wim, int image, const wimlib_tchar *name);
  * @param property_name
  *     The name of the image property in the same format documented for
  *     wimlib_get_image_property().
+ *     <br/>
+ *     Note: if creating a new element using a bracketed index such as
+ *     "WINDOWS/LANGUAGES/LANGUAGE[2]", the highest index that can be specified
+ *     is one greater than the number of existing elements with that same name,
+ *     excluding the index.  That means that if you are adding a list of new
+ *     elements, they must be added sequentially from the first index (1) to
+ *     the last index (n).
  * @param property_value
  *     If not NULL and not empty, the property is set to this value.
  *     Otherwise, the property is removed from the XML document.
@@ -4052,7 +4063,8 @@ wimlib_set_image_name(WIMStruct *wim, int image, const wimlib_tchar *name);
  * @retval ::WIMLIB_ERR_INVALID_IMAGE
  *     @p image does not exist in @p wim.
  * @retval ::WIMLIB_ERR_INVALID_PARAM
- *     @p property_name has an unsupported format.
+ *     @p property_name has an unsupported format, or @p property_name included
+ *     a bracketed index that was too high.
  */
 extern int
 wimlib_set_image_property(WIMStruct *wim, int image,
index 8280e8e..d464706 100644 (file)
--- a/src/xml.c
+++ b/src/xml.c
@@ -342,6 +342,32 @@ alloc_wim_xml_info(void)
        return info;
 }
 
+static bool
+parse_index(xmlChar **pp, uint32_t *index_ret)
+{
+       xmlChar *p = *pp;
+       uint32_t index = 0;
+
+       *p++ = '\0'; /* overwrite '[' */
+       while (*p >= '0' && *p <= '9') {
+               uint32_t n = (index * 10) + (*p++ - '0');
+               if (n < index)
+                       return false;
+               index = n;
+       }
+       if (index == 0)
+               return false;
+       if (*p != ']')
+               return false;
+       p++;
+       if (*p != '/' && *p != '\0')
+               return false;
+
+       *pp = p;
+       *index_ret = index;
+       return true;
+}
+
 static int
 do_xml_path_walk(xmlNode *node, const xmlChar *path, bool create,
                 xmlNode **result_ret)
@@ -362,34 +388,44 @@ do_xml_path_walk(xmlNode *node, const xmlChar *path, bool create,
 
        if (*p == '/')
                goto bad_syntax;
-       if (strchr(p, '[')) /* reserved for future use */
-               goto bad_syntax;
        c = *p;
 
        while (c != '\0') {
                const xmlChar *name;
                xmlNode *child;
+               uint32_t index = 1;
 
                /* We have another path component.  */
 
                /* Parse the element name.  */
                name = p;
-               while (*p != '/' && *p != '\0')
+               while (*p != '/' && *p != '\0' && *p != '[')
                        p++;
                if (p == name) /* empty name?  */
                        goto bad_syntax;
+
+               /* Handle a bracketed index, if one was specified.  */
+               if (*p == '[' && !parse_index(&p, &index))
+                       goto bad_syntax;
+
                c = *p;
                *p = '\0';
 
                /* Look for a matching child.  */
                node_for_each_child(node, child)
-                       if (node_is_element(child, name))
+                       if (node_is_element(child, name) && !--index)
                                goto next_step;
 
                /* No child matched the path.  If create=false, the lookup
                 * failed.  If create=true, create the needed element.  */
                if (!create)
                        return 0;
+
+               /* We can't create an element at index 'n' if indices 1...n-1
+                * didn't already exist.  */
+               if (index != 1)
+                       return WIMLIB_ERR_INVALID_PARAM;
+
                child = xmlNewChild(node, NULL, name, NULL);
                if (!child)
                        return WIMLIB_ERR_NOMEM;