* "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
* @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.
* @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,
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)
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;