xpath UPDATE root node string conversion

Fixes sysrepo/sysrepo#2864
diff --git a/src/xpath.c b/src/xpath.c
index 369e83c..328fb3a 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -47,6 +47,8 @@
         struct lyxp_set *set, uint32_t options);
 static LY_ERR moveto_resolve_model(const char **qname, uint16_t *qname_len, const struct lyxp_set *set,
         const struct lysc_node *ctx_scnode, const struct lys_module **moveto_mod);
+static LY_ERR moveto_axis_node_next(const struct lyd_node **iter, enum lyxp_node_type *iter_type,
+        const struct lyd_node *node, enum lyxp_node_type node_type, enum lyxp_axis axis, struct lyxp_set *set);
 static LY_ERR moveto_node(struct lyxp_set *set, const struct lys_module *moveto_mod, const char *ncname,
         enum lyxp_axis axis, uint32_t options);
 static LY_ERR moveto_scnode(struct lyxp_set *set, const struct lys_module *moveto_mod, const char *ncname,
@@ -396,148 +398,151 @@
 /**
  * @brief Cast nodes recursively to one string @p str.
  *
- * @param[in] node Node to cast.
- * @param[in] fake_cont Whether to put the data into a "fake" container.
- * @param[in] root_type Type of the XPath root.
+ * @param[in] node Node to cast, NULL if root.
+ * @param[in] set XPath set.
  * @param[in] indent Current indent.
  * @param[in,out] str Resulting string.
  * @param[in,out] used Used bytes in @p str.
  * @param[in,out] size Allocated bytes in @p str.
- * @return LY_ERR
+ * @return LY_ERR value.
  */
 static LY_ERR
-cast_string_recursive(const struct lyd_node *node, ly_bool fake_cont, enum lyxp_node_type root_type, uint16_t indent,
-        char **str, uint16_t *used, uint16_t *size)
+cast_string_recursive(const struct lyd_node *node, struct lyxp_set *set, uint16_t indent, char **str, uint16_t *used,
+        uint16_t *size)
 {
     char *buf, *line, *ptr = NULL;
     const char *value_str;
     const struct lyd_node *child;
+    enum lyxp_node_type child_type;
     struct lyd_node *tree;
     struct lyd_node_any *any;
     LY_ERR rc;
 
-    if ((root_type == LYXP_NODE_ROOT_CONFIG) && (node->schema->flags & LYS_CONFIG_R)) {
+    if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && node && (node->schema->flags & LYS_CONFIG_R)) {
         return LY_SUCCESS;
     }
 
-    if (fake_cont) {
-        rc = cast_string_realloc(LYD_CTX(node), 1, str, used, size);
-        LY_CHECK_RET(rc);
+    if (!node) {
+        /* fake container */
+        LY_CHECK_RET(cast_string_realloc(set->ctx, 1, str, used, size));
         strcpy(*str + (*used - 1), "\n");
         ++(*used);
 
         ++indent;
-    }
 
-    switch (node->schema->nodetype) {
-    case LYS_CONTAINER:
-    case LYS_LIST:
-    case LYS_RPC:
-    case LYS_NOTIF:
-        rc = cast_string_realloc(LYD_CTX(node), 1, str, used, size);
-        LY_CHECK_RET(rc);
-        strcpy(*str + (*used - 1), "\n");
-        ++(*used);
-
-        for (child = lyd_child(node); child; child = child->next) {
-            rc = cast_string_recursive(child, 0, root_type, indent + 1, str, used, size);
-            LY_CHECK_RET(rc);
+        /* print all the top-level nodes */
+        child = NULL;
+        child_type = 0;
+        while (!moveto_axis_node_next(&child, &child_type, NULL, set->root_type, LYXP_AXIS_CHILD, set)) {
+            LY_CHECK_RET(cast_string_recursive(child, set, indent, str, used, size));
         }
 
-        break;
-
-    case LYS_LEAF:
-    case LYS_LEAFLIST:
-        value_str = lyd_get_value(node);
-
-        /* print indent */
-        LY_CHECK_RET(cast_string_realloc(LYD_CTX(node), indent * 2 + strlen(value_str) + 1, str, used, size));
-        memset(*str + (*used - 1), ' ', indent * 2);
-        *used += indent * 2;
-
-        /* print value */
-        if (*used == 1) {
-            sprintf(*str + (*used - 1), "%s", value_str);
-            *used += strlen(value_str);
-        } else {
-            sprintf(*str + (*used - 1), "%s\n", value_str);
-            *used += strlen(value_str) + 1;
-        }
-
-        break;
-
-    case LYS_ANYXML:
-    case LYS_ANYDATA:
-        any = (struct lyd_node_any *)node;
-        if (!(void *)any->value.tree) {
-            /* no content */
-            buf = strdup("");
-            LY_CHECK_ERR_RET(!buf, LOGMEM(LYD_CTX(node)), LY_EMEM);
-        } else {
-            struct ly_out *out;
-
-            if (any->value_type == LYD_ANYDATA_LYB) {
-                /* try to parse it into a data tree */
-                if (lyd_parse_data_mem((struct ly_ctx *)LYD_CTX(node), any->value.mem, LYD_LYB,
-                        LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree) == LY_SUCCESS) {
-                    /* successfully parsed */
-                    free(any->value.mem);
-                    any->value.tree = tree;
-                    any->value_type = LYD_ANYDATA_DATATREE;
-                }
-                /* error is covered by the following switch where LYD_ANYDATA_LYB causes failure */
-            }
-
-            switch (any->value_type) {
-            case LYD_ANYDATA_STRING:
-            case LYD_ANYDATA_XML:
-            case LYD_ANYDATA_JSON:
-                buf = strdup(any->value.json);
-                LY_CHECK_ERR_RET(!buf, LOGMEM(LYD_CTX(node)), LY_EMEM);
-                break;
-            case LYD_ANYDATA_DATATREE:
-                LY_CHECK_RET(ly_out_new_memory(&buf, 0, &out));
-                rc = lyd_print_all(out, any->value.tree, LYD_XML, 0);
-                ly_out_free(out, NULL, 0);
-                LY_CHECK_RET(rc < 0, -rc);
-                break;
-            case LYD_ANYDATA_LYB:
-                LOGERR(LYD_CTX(node), LY_EINVAL, "Cannot convert LYB anydata into string.");
-                return LY_EINVAL;
-            }
-        }
-
-        line = strtok_r(buf, "\n", &ptr);
-        do {
-            rc = cast_string_realloc(LYD_CTX(node), indent * 2 + strlen(line) + 1, str, used, size);
-            if (rc != LY_SUCCESS) {
-                free(buf);
-                return rc;
-            }
-            memset(*str + (*used - 1), ' ', indent * 2);
-            *used += indent * 2;
-
-            strcpy(*str + (*used - 1), line);
-            *used += strlen(line);
-
-            strcpy(*str + (*used - 1), "\n");
-            *used += 1;
-        } while ((line = strtok_r(NULL, "\n", &ptr)));
-
-        free(buf);
-        break;
-
-    default:
-        LOGINT_RET(LYD_CTX(node));
-    }
-
-    if (fake_cont) {
-        rc = cast_string_realloc(LYD_CTX(node), 1, str, used, size);
-        LY_CHECK_RET(rc);
+        /* end fake container */
+        LY_CHECK_RET(cast_string_realloc(set->ctx, 1, str, used, size));
         strcpy(*str + (*used - 1), "\n");
         ++(*used);
 
         --indent;
+    } else {
+        switch (node->schema->nodetype) {
+        case LYS_CONTAINER:
+        case LYS_LIST:
+        case LYS_RPC:
+        case LYS_NOTIF:
+            LY_CHECK_RET(cast_string_realloc(set->ctx, 1, str, used, size));
+            strcpy(*str + (*used - 1), "\n");
+            ++(*used);
+
+            for (child = lyd_child(node); child; child = child->next) {
+                LY_CHECK_RET(cast_string_recursive(child, set, indent + 1, str, used, size));
+            }
+
+            break;
+
+        case LYS_LEAF:
+        case LYS_LEAFLIST:
+            value_str = lyd_get_value(node);
+
+            /* print indent */
+            LY_CHECK_RET(cast_string_realloc(set->ctx, indent * 2 + strlen(value_str) + 1, str, used, size));
+            memset(*str + (*used - 1), ' ', indent * 2);
+            *used += indent * 2;
+
+            /* print value */
+            if (*used == 1) {
+                sprintf(*str + (*used - 1), "%s", value_str);
+                *used += strlen(value_str);
+            } else {
+                sprintf(*str + (*used - 1), "%s\n", value_str);
+                *used += strlen(value_str) + 1;
+            }
+
+            break;
+
+        case LYS_ANYXML:
+        case LYS_ANYDATA:
+            any = (struct lyd_node_any *)node;
+            if (!(void *)any->value.tree) {
+                /* no content */
+                buf = strdup("");
+                LY_CHECK_ERR_RET(!buf, LOGMEM(set->ctx), LY_EMEM);
+            } else {
+                struct ly_out *out;
+
+                if (any->value_type == LYD_ANYDATA_LYB) {
+                    /* try to parse it into a data tree */
+                    if (lyd_parse_data_mem((struct ly_ctx *)set->ctx, any->value.mem, LYD_LYB,
+                            LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree) == LY_SUCCESS) {
+                        /* successfully parsed */
+                        free(any->value.mem);
+                        any->value.tree = tree;
+                        any->value_type = LYD_ANYDATA_DATATREE;
+                    }
+                    /* error is covered by the following switch where LYD_ANYDATA_LYB causes failure */
+                }
+
+                switch (any->value_type) {
+                case LYD_ANYDATA_STRING:
+                case LYD_ANYDATA_XML:
+                case LYD_ANYDATA_JSON:
+                    buf = strdup(any->value.json);
+                    LY_CHECK_ERR_RET(!buf, LOGMEM(set->ctx), LY_EMEM);
+                    break;
+                case LYD_ANYDATA_DATATREE:
+                    LY_CHECK_RET(ly_out_new_memory(&buf, 0, &out));
+                    rc = lyd_print_all(out, any->value.tree, LYD_XML, 0);
+                    ly_out_free(out, NULL, 0);
+                    LY_CHECK_RET(rc < 0, -rc);
+                    break;
+                case LYD_ANYDATA_LYB:
+                    LOGERR(set->ctx, LY_EINVAL, "Cannot convert LYB anydata into string.");
+                    return LY_EINVAL;
+                }
+            }
+
+            line = strtok_r(buf, "\n", &ptr);
+            do {
+                rc = cast_string_realloc(set->ctx, indent * 2 + strlen(line) + 1, str, used, size);
+                if (rc != LY_SUCCESS) {
+                    free(buf);
+                    return rc;
+                }
+                memset(*str + (*used - 1), ' ', indent * 2);
+                *used += indent * 2;
+
+                strcpy(*str + (*used - 1), line);
+                *used += strlen(line);
+
+                strcpy(*str + (*used - 1), "\n");
+                *used += 1;
+            } while ((line = strtok_r(NULL, "\n", &ptr)));
+
+            free(buf);
+            break;
+
+        default:
+            LOGINT_RET(set->ctx);
+        }
     }
 
     return LY_SUCCESS;
@@ -546,25 +551,24 @@
 /**
  * @brief Cast an element into a string.
  *
- * @param[in] node Node to cast.
- * @param[in] fake_cont Whether to put the data into a "fake" container.
- * @param[in] root_type Type of the XPath root.
+ * @param[in] node Node to cast, NULL if root.
+ * @param[in] set XPath set.
  * @param[out] str Element cast to dynamically-allocated string.
  * @return LY_ERR
  */
 static LY_ERR
-cast_string_elem(struct lyd_node *node, ly_bool fake_cont, enum lyxp_node_type root_type, char **str)
+cast_string_elem(const struct lyd_node *node, struct lyxp_set *set, char **str)
 {
     uint16_t used, size;
     LY_ERR rc;
 
     *str = malloc(LYXP_STRING_CAST_SIZE_START * sizeof(char));
-    LY_CHECK_ERR_RET(!*str, LOGMEM(LYD_CTX(node)), LY_EMEM);
+    LY_CHECK_ERR_RET(!*str, LOGMEM(set->ctx), LY_EMEM);
     (*str)[0] = '\0';
     used = 1;
     size = LYXP_STRING_CAST_SIZE_START;
 
-    rc = cast_string_recursive(node, fake_cont, root_type, 0, str, &used, &size);
+    rc = cast_string_recursive(node, set, 0, str, &used, &size);
     if (rc != LY_SUCCESS) {
         free(*str);
         return rc;
@@ -572,7 +576,7 @@
 
     if (size > used) {
         *str = ly_realloc(*str, used * sizeof(char));
-        LY_CHECK_ERR_RET(!*str, LOGMEM(LYD_CTX(node)), LY_EMEM);
+        LY_CHECK_ERR_RET(!*str, LOGMEM(set->ctx), LY_EMEM);
     }
     return LY_SUCCESS;
 }
@@ -602,10 +606,9 @@
         LOGINT_RET(set->ctx);
     case LYXP_NODE_ROOT:
     case LYXP_NODE_ROOT_CONFIG:
-        return cast_string_elem(set->val.nodes[0].node, 1, set->root_type, str);
     case LYXP_NODE_ELEM:
     case LYXP_NODE_TEXT:
-        return cast_string_elem(set->val.nodes[0].node, 0, set->root_type, str);
+        return cast_string_elem(set->val.nodes[0].node, set, str);
     case LYXP_NODE_META:
         *str = strdup(lyd_get_meta_value(set->val.meta[0].meta));
         if (!*str) {