Imported Upstream version 1.12.0
[platform/upstream/augeas.git] / src / augeas.c
index a1931f6..df760f5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * augeas.c: the core data structure for storing key/value pairs
  *
- * Copyright (C) 2007-2011 David Lutterkort
+ * Copyright (C) 2007-2017 David Lutterkort
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -33,7 +33,6 @@
 #include <string.h>
 #include <stdarg.h>
 #include <locale.h>
-#include <libxml/tree.h>
 
 /* Some popular labels that we use in /augeas */
 static const char *const s_augeas = "augeas";
@@ -47,8 +46,6 @@ static const char *const s_lens   = "lens";
 static const char *const s_excl   = "excl";
 static const char *const s_incl   = "incl";
 
-#define TREE_HIDDEN(tree) ((tree)->label == NULL)
-
 #define AUGEAS_META_PATHX_FUNC AUGEAS_META_TREE "/version/pathx/functions"
 
 static const char *const static_nodes[][2] = {
@@ -697,7 +694,7 @@ static void tree_rm_dirty_files(struct augeas *aug, struct tree *tree) {
     if (!tree->dirty)
         return;
 
-    if ((p = tree_child(tree, "path")) != NULL) {
+    if (tree->file && ((p = tree_child(tree, "path")) != NULL)) {
         tree_unlink(aug, tree_fpath(aug, p->value));
         tree_unlink(aug, tree);
     } else {
@@ -767,7 +764,7 @@ int aug_load(struct augeas *aug) {
 
     list_for_each(xfm, load->children) {
         if (transform_validate(aug, xfm) == 0)
-            transform_load(aug, xfm);
+            transform_load(aug, xfm, NULL);
     }
 
     /* This makes it possible to spot 'directories' that are now empty
@@ -814,14 +811,14 @@ int aug_get(const struct augeas *aug, const char *path, const char **value) {
     struct tree *match;
     int r;
 
+    if (value != NULL)
+        *value = NULL;
+
     api_entry(aug);
 
     p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true);
     ERR_BAIL(aug);
 
-    if (value != NULL)
-        *value = NULL;
-
     r = pathx_find_one(p, &match);
     ERR_BAIL(aug);
     ERR_THROW(r > 1, aug, AUG_EMMATCH, "There are %d nodes matching %s",
@@ -910,7 +907,7 @@ int aug_defvar(augeas *aug, const char *name, const char *expr) {
 
 int aug_defnode(augeas *aug, const char *name, const char *expr,
                 const char *value, int *created) {
-    struct pathx *p;
+    struct pathx *p = NULL;
     int result = -1;
     int r, cr;
     struct tree *tree;
@@ -951,12 +948,10 @@ int aug_defnode(augeas *aug, const char *name, const char *expr,
     }
 
  done:
+ error:
     free_pathx(p);
     api_exit(aug);
     return result;
- error:
-    api_exit(aug);
-    return -1;
 }
 
 struct tree *tree_set(struct pathx *p, const char *value) {
@@ -974,8 +969,8 @@ struct tree *tree_set(struct pathx *p, const char *value) {
 }
 
 int aug_set(struct augeas *aug, const char *path, const char *value) {
-    struct pathx *p;
-    int result;
+    struct pathx *p = NULL;
+    int result = -1;
 
     api_entry(aug);
 
@@ -988,13 +983,10 @@ int aug_set(struct augeas *aug, const char *path, const char *value) {
     ERR_BAIL(aug);
 
     result = tree_set(p, value) == NULL ? -1 : 0;
+ error:
     free_pathx(p);
-
     api_exit(aug);
     return result;
- error:
-    api_exit(aug);
-    return -1;
 }
 
 int aug_setm(struct augeas *aug, const char *base,
@@ -1044,6 +1036,8 @@ int aug_setm(struct augeas *aug, const char *base,
     }
 
  done:
+    free_pathx(bx);
+    free_pathx(sx);
     api_exit(aug);
     return result;
  error:
@@ -1142,6 +1136,7 @@ int tree_rm(struct pathx *p) {
     struct tree *tree, **del;
     int cnt = 0, ndel = 0, i;
 
+    /* set ndel to the number of trees we could possibly delete */
     for (tree = pathx_first(p); tree != NULL; tree = pathx_next(p)) {
         if (! TREE_HIDDEN(tree))
             ndel += 1;
@@ -1159,12 +1154,32 @@ int tree_rm(struct pathx *p) {
         if (TREE_HIDDEN(tree))
             continue;
         pathx_symtab_remove_descendants(pathx_get_symtab(p), tree);
-        del[i] = tree;
-        i += 1;
+        /* Collect the tree nodes that actually need to be deleted in
+           del. Mark the root of every subtree we are going to free by
+           setting tree->added. Only add a node to del if none of its
+           ancestors would have been freed by the time we get to freeing
+           that node; this avoids double frees for situations where the
+           path expression matches both /node and /node/child as unlinking
+           /node implicitly unlinks /node/child */
+        int live = 1;
+        for (struct tree *t = tree; live && ! ROOT_P(t); t = t->parent) {
+            if (t->added)
+                live = 0;
+        }
+        if (live) {
+            del[i] = tree;
+            i += 1;
+            tree->added = 1;
+        }
     }
+    /* ndel now means: the number of trees we are actually going to delete */
+    ndel = i;
 
-    for (i = 0; i < ndel; i++)
-        cnt += tree_unlink_raw(del[i]);
+    for (i = 0; i < ndel; i++) {
+        if (del[i] != NULL) {
+            cnt += tree_unlink_raw(del[i]);
+        }
+    }
     free(del);
 
     return cnt;
@@ -1172,7 +1187,7 @@ int tree_rm(struct pathx *p) {
 
 int aug_rm(struct augeas *aug, const char *path) {
     struct pathx *p = NULL;
-    int result;
+    int result = -1;
 
     api_entry(aug);
 
@@ -1180,14 +1195,11 @@ int aug_rm(struct augeas *aug, const char *path) {
     ERR_BAIL(aug);
 
     result = tree_rm(p);
-    free_pathx(p);
-    ERR_BAIL(aug);
 
-    api_exit(aug);
-    return result;
  error:
+    free_pathx(p);
     api_exit(aug);
-    return -1;
+    return result;
 }
 
 int aug_span(struct augeas *aug, const char *path, char **filename,
@@ -1444,6 +1456,29 @@ int aug_match(const struct augeas *aug, const char *pathin, char ***matches) {
     return -1;
 }
 
+/* XFM1 and XFM2 can both be used to save the same file. That is an error
+   only if the two lenses in the two transforms are actually different. */
+static int check_save_dup(struct augeas *aug, const char *path,
+                          struct tree *xfm1, struct tree *xfm2) {
+    int result = 0;
+    struct lens *l1 = xfm_lens(aug, xfm1, NULL);
+    struct lens *l2 = xfm_lens(aug, xfm2, NULL);
+    if (l1 != l2) {
+        const char *filename = path + strlen(AUGEAS_FILES_TREE) + 1;
+        transform_file_error(aug, "mxfm_save", filename,
+                             "Lenses %s and %s could be used to save this file",
+                             xfm_lens_name(xfm1),
+                             xfm_lens_name(xfm2));
+        ERR_REPORT(aug, AUG_EMXFM,
+                   "Path %s transformable by lens %s and %s",
+                   path,
+                   xfm_lens_name(xfm1),
+                   xfm_lens_name(xfm2));
+        result = -1;
+    }
+    return result;
+}
+
 static int tree_save(struct augeas *aug, struct tree *tree,
                      const char *path) {
     int result = 0;
@@ -1468,18 +1503,7 @@ static int tree_save(struct augeas *aug, struct tree *tree,
                     if (transform == NULL || transform == xfm) {
                         transform = xfm;
                     } else {
-                        const char *filename =
-                            tpath + strlen(AUGEAS_FILES_TREE) + 1;
-                        transform_file_error(aug, "mxfm_save", filename,
-                           "Lenses %s and %s could be used to save this file",
-                                             xfm_lens_name(transform),
-                                             xfm_lens_name(xfm));
-                        ERR_REPORT(aug, AUG_EMXFM,
-                                   "Path %s transformable by lens %s and %s",
-                                   tpath,
-                                   xfm_lens_name(transform),
-                                   xfm_lens_name(xfm));
-                        result = -1;
+                        result = check_save_dup(aug, tpath, transform, xfm);
                     }
                 }
             }
@@ -1542,6 +1566,7 @@ static int unlink_removed_files(struct augeas *aug,
             if (pathx_parse(tm, err_of_aug(aug), file_nodes, true,
                             aug->symtab, NULL, &px) != PATHX_NOERROR) {
                 result = -1;
+                free_pathx(px);
                 continue;
             }
             for (struct tree *t = pathx_first(px);
@@ -1680,155 +1705,16 @@ int dump_tree(FILE *out, struct tree *tree) {
     struct pathx *p;
     int result;
 
-    if (pathx_parse(tree, NULL, "/*", true, NULL, NULL, &p) != PATHX_NOERROR)
+    if (pathx_parse(tree, NULL, "/*", true, NULL, NULL, &p) != PATHX_NOERROR) {
+        free_pathx(p);
         return -1;
+    }
 
     result = print_tree(out, p, 1);
     free_pathx(p);
     return result;
 }
 
-static int to_xml_span(xmlNodePtr elem, const char *pfor, int start, int end) {
-    int r;
-    char *buf;
-    xmlAttrPtr prop;
-    xmlNodePtr span_elem;
-
-    span_elem = xmlNewChild(elem, NULL, BAD_CAST "span", NULL);
-    if (span_elem == NULL)
-        return -1;
-
-    prop = xmlSetProp(span_elem, BAD_CAST "for", BAD_CAST pfor);
-    if (prop == NULL)
-        return -1;
-
-    /* Format and set the start property */
-    r = xasprintf(&buf, "%d", start);
-    if (r < 0)
-        return -1;
-
-    prop = xmlSetProp(span_elem, BAD_CAST "start", BAD_CAST buf);
-    FREE(buf);
-    if (prop == NULL)
-        return -1;
-
-    /* Format and set the end property */
-    r = xasprintf(&buf, "%d", end);
-    if (r < 0)
-        return -1;
-
-    prop = xmlSetProp(span_elem, BAD_CAST "end", BAD_CAST buf);
-    FREE(buf);
-    if (prop == NULL)
-        return -1;
-
-    return 0;
-}
-
-static int to_xml_one(xmlNodePtr elem, const struct tree *tree,
-                      const char *pathin) {
-    xmlNodePtr value;
-    xmlAttrPtr prop;
-    int r;
-
-    prop = xmlSetProp(elem, BAD_CAST "label", BAD_CAST tree->label);
-    if (prop == NULL)
-        goto error;
-
-    if (tree->span) {
-        struct span *span = tree->span;
-
-        prop = xmlSetProp(elem, BAD_CAST "file",
-                          BAD_CAST span->filename->str);
-        if (prop == NULL)
-            goto error;
-
-        r = to_xml_span(elem, "label", span->label_start, span->label_end);
-        if (r < 0)
-            goto error;
-
-        r = to_xml_span(elem, "value", span->value_start, span->value_end);
-        if (r < 0)
-            goto error;
-
-        r = to_xml_span(elem, "node", span->span_start, span->span_end);
-        if (r < 0)
-            goto error;
-    }
-
-    if (pathin != NULL) {
-        prop = xmlSetProp(elem, BAD_CAST "path", BAD_CAST pathin);
-        if (prop == NULL)
-            goto error;
-    }
-    if (tree->value != NULL) {
-        value = xmlNewTextChild(elem, NULL, BAD_CAST "value",
-                                BAD_CAST tree->value);
-        if (value == NULL)
-            goto error;
-    }
-    return 0;
- error:
-    return -1;
-}
-
-static int to_xml_rec(xmlNodePtr pnode, struct tree *start,
-                      const char *pathin) {
-    int r;
-    xmlNodePtr elem;
-
-    elem = xmlNewChild(pnode, NULL, BAD_CAST "node", NULL);
-    if (elem == NULL)
-        goto error;
-    r = to_xml_one(elem, start, pathin);
-    if (r < 0)
-        goto error;
-
-    list_for_each(tree, start->children) {
-        if (TREE_HIDDEN(tree))
-            continue;
-        r = to_xml_rec(elem, tree, NULL);
-        if (r < 0)
-            goto error;
-    }
-
-    return 0;
- error:
-    return -1;
-}
-
-static int tree_to_xml(struct pathx *p, xmlNode **xml, const char *pathin) {
-    char *path = NULL;
-    struct tree *tree;
-    xmlAttrPtr expr;
-    int r;
-
-    *xml = xmlNewNode(NULL, BAD_CAST "augeas");
-    if (*xml == NULL)
-        goto error;
-    expr = xmlSetProp(*xml, BAD_CAST "match", BAD_CAST pathin);
-    if (expr == NULL)
-        goto error;
-
-    for (tree = pathx_first(p); tree != NULL; tree = pathx_next(p)) {
-        if (TREE_HIDDEN(tree))
-            continue;
-        path = path_of_tree(tree);
-        if (path == NULL)
-            goto error;
-        r = to_xml_rec(*xml, tree, path);
-        if (r < 0)
-            goto error;
-        FREE(path);
-    }
-    return 0;
- error:
-    free(path);
-    xmlFree(*xml);
-    *xml = NULL;
-    return -1;
-}
-
 int aug_text_store(augeas *aug, const char *lens, const char *node,
                    const char *path) {
 
@@ -1840,8 +1726,8 @@ int aug_text_store(augeas *aug, const char *lens, const char *node,
 
     /* Validate PATH is syntactically correct */
     p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true);
-    ERR_BAIL(aug);
     free_pathx(p);
+    ERR_BAIL(aug);
 
     r = aug_get(aug, node, &src);
     ERR_BAIL(aug);
@@ -1894,35 +1780,6 @@ int aug_text_retrieve(struct augeas *aug, const char *lens,
     return -1;
 }
 
-int aug_to_xml(const struct augeas *aug, const char *pathin,
-               xmlNode **xmldoc, unsigned int flags) {
-    struct pathx *p;
-    int result;
-
-    api_entry(aug);
-
-    ARG_CHECK(flags != 0, aug, "aug_to_xml: FLAGS must be 0");
-    ARG_CHECK(xmldoc == NULL, aug, "aug_to_xml: XMLDOC must be non-NULL");
-
-    if (pathin == NULL || strlen(pathin) == 0 || strcmp(pathin, "/") == 0) {
-        pathin = "/*";
-    }
-
-    p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), pathin, true);
-    ERR_BAIL(aug);
-    result = tree_to_xml(p, xmldoc, pathin);
-    ERR_THROW(result < 0, aug, AUG_ENOMEM, NULL);
-    free_pathx(p);
-    api_exit(aug);
-
-    return result;
- error:
-    if (xmldoc !=NULL)
-        *xmldoc = NULL;
-    api_exit(aug);
-    return -1;
-}
-
 int aug_transform(struct augeas *aug, const char *lens,
                   const char *file, int excl) {
     struct tree *meta = tree_child_cr(aug->origin, s_augeas);
@@ -1986,7 +1843,7 @@ int aug_transform(struct augeas *aug, const char *lens,
 }
 
 int aug_escape_name(augeas *aug, const char *in, char **out) {
-    int result;
+    int result = -1;
 
     api_entry(aug);
     ARG_CHECK(in == NULL, aug, "aug_escape_name: IN must not be NULL");
@@ -1999,9 +1856,48 @@ int aug_escape_name(augeas *aug, const char *in, char **out) {
     return result;
 }
 
+int aug_load_file(struct augeas *aug, const char *file) {
+    int result = -1, r;
+    struct tree *meta = tree_child_cr(aug->origin, s_augeas);
+    struct tree *load = tree_child_cr(meta, s_load);
+    char *tree_path = NULL;
+    bool found = false;
+
+    api_entry(aug);
+
+    ERR_NOMEM(load == NULL, aug);
+
+    list_for_each(xfm, load->children)  {
+        if (filter_matches(xfm, file)) {
+            transform_load(aug, xfm, file);
+            found = true;
+            break;
+        }
+    }
+
+    ERR_THROW(!found, aug, AUG_ENOLENS,
+              "can not determine lens to load file %s", file);
+
+    /* Mark the nodes we just loaded as clean so they won't get saved
+       without additional modifications */
+    r = xasprintf(&tree_path, "/files/%s", file);
+    ERR_NOMEM(r < 0, aug);
+
+    struct tree *t = tree_fpath(aug, tree_path);
+    if (t != NULL) {
+        tree_clean(t);
+    }
+
+    result = 0;
+error:
+    api_exit(aug);
+    free(tree_path);
+    return result;
+}
+
 int aug_print(const struct augeas *aug, FILE *out, const char *pathin) {
     struct pathx *p;
-    int result;
+    int result = -1;
 
     api_entry(aug);
 
@@ -2013,13 +1909,63 @@ int aug_print(const struct augeas *aug, FILE *out, const char *pathin) {
     ERR_BAIL(aug);
 
     result = print_tree(out, p, 0);
+ error:
     free_pathx(p);
-
     api_exit(aug);
     return result;
+}
+
+static char *
+tree_source(const augeas *aug, struct tree *tree) {
+    char *result = NULL;
+
+    while (!(ROOT_P(tree) || tree->file))
+        tree = tree->parent;
+
+    if (tree->file) {
+        if (tree->span == NULL) {
+            int r;
+            r = ALLOC(tree->span);
+            ERR_NOMEM(r < 0, aug);
+            tree->span->filename = make_string(path_of_tree(tree));
+            ERR_NOMEM(tree->span->filename == NULL, aug);
+        }
+        result = strdup(tree->span->filename->str);
+        ERR_NOMEM(result == NULL, aug);
+    }
+ error:
+    return result;
+}
+
+int aug_source(const augeas *aug, const char *path, char **file_path) {
+    int result = -1, r;
+    struct pathx *p = NULL;
+    struct tree *match;
+
+    api_entry(aug);
+
+    ARG_CHECK(file_path == NULL, aug,
+              "aug_source_file: FILE_PATH must not be NULL");
+    *file_path = NULL;
+
+    p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true);
+    ERR_BAIL(aug);
+
+    r = pathx_find_one(p, &match);
+    ERR_BAIL(aug);
+    ERR_THROW(r > 1, aug, AUG_EMMATCH, "There are %d nodes matching %s",
+              r, path);
+    ERR_THROW(r == 0, aug, AUG_ENOMATCH, "There is no node matching %s",
+              path);
+
+    *file_path = tree_source(aug, match);
+    ERR_BAIL(aug);
+
+    result = 0;
  error:
+    free_pathx(p);
     api_exit(aug);
-    return -1;
+    return result;
 }
 
 void aug_close(struct augeas *aug) {
@@ -2064,6 +2010,128 @@ int tree_equal(const struct tree *t1, const struct tree *t2) {
     return t1 == t2;
 }
 
+int aug_ns_attr(const augeas* aug, const char *var, int i,
+                const char **value, const char **label, char **file_path) {
+    int result = -1;
+
+    if (value != NULL)
+        *value = NULL;
+
+    if (label != NULL)
+        *label = NULL;
+
+    if (file_path != NULL)
+        *file_path = NULL;
+
+    api_entry(aug);
+
+    struct tree *tree = pathx_symtab_get_tree(aug->symtab, var, i);
+    ERR_THROW(tree == NULL, aug, AUG_ENOMATCH,
+              "Node %s[%d] does not exist", var, i);
+
+    if (file_path != NULL) {
+        *file_path = tree_source(aug, tree);
+        ERR_BAIL(aug);
+    }
+
+    if (value != NULL)
+        *value = tree->value;
+
+    if (label != NULL)
+        *label = tree->label;
+
+    result = 1;
+
+ error:
+    api_exit(aug);
+    return result;
+}
+
+int aug_ns_label(const augeas* aug, const char *var, int i,
+                 const char **label, int *index) {
+    int result = -1;
+
+    if (label != NULL)
+        *label = NULL;
+
+    if (index != NULL)
+        *index = -1;
+
+    api_entry(aug);
+
+    struct tree *tree = pathx_symtab_get_tree(aug->symtab, var, i);
+    ERR_THROW(tree == NULL, aug, AUG_ENOMATCH,
+              "Node %s[%d] does not exist", var, i);
+
+    if (label != NULL)
+        *label = tree->label;
+
+    if (index != NULL) {
+        *index = tree_sibling_index(tree);
+    }
+
+    result = 1;
+
+ error:
+    api_exit(aug);
+    return result;
+}
+
+int aug_ns_value(const augeas* aug, const char *var, int i,
+                 const char **value) {
+    int result = -1;
+
+    if (value != NULL)
+        *value = NULL;
+
+    api_entry(aug);
+
+    struct tree *tree = pathx_symtab_get_tree(aug->symtab, var, i);
+    ERR_THROW(tree == NULL, aug, AUG_ENOMATCH,
+              "Node %s[%d] does not exist", var, i);
+
+    if (value != NULL)
+        *value = tree->value;
+
+    result = 1;
+
+ error:
+    api_exit(aug);
+    return result;
+}
+
+int aug_ns_count(const augeas *aug, const char *var) {
+    int result = -1;
+
+    api_entry(aug);
+
+    result = pathx_symtab_count(aug->symtab, var);
+
+    api_exit(aug);
+
+    return result;
+}
+
+int aug_ns_path(const augeas *aug, const char *var, int i, char **path) {
+    int result = -1;
+
+    *path = NULL;
+
+    api_entry(aug);
+
+    struct tree *tree = pathx_symtab_get_tree(aug->symtab, var, i);
+    ERR_THROW(tree == NULL, aug, AUG_ENOMATCH,
+              "Node %s[%d] does not exist", var, i);
+
+    *path = path_of_tree(tree);
+
+    result = 0;
+
+ error:
+    api_exit(aug);
+    return result;
+}
+
 /*
  * Error reporting API
  */