[augeas-devel] [PATCH 1/2] path expressions: prepend context from /augeas/context to all relative paths

Dominic Cleal dcleal at redhat.com
Sun Nov 6 11:39:51 UTC 2011


A path context is prepended to all paths that do not begin at the root,
changing relative paths into absolute paths.  The context is stored in
/augeas/context and defaults to the value "/files".
---
 src/augeas.c      |   84 +++++++++++++++++++++++++++++++++++++++++------------
 src/augeas.h      |    2 +-
 src/builtin.c     |    2 +-
 src/internal.h    |   18 +++++++++++-
 src/pathx.c       |   48 +++++++++++++++++++++++++++++-
 src/transform.c   |    4 +-
 tests/test-api.c  |   48 ++++++++++++++++++++++++++++++
 tests/xpath.tests |    7 ++++
 8 files changed, 188 insertions(+), 25 deletions(-)

diff --git a/src/augeas.c b/src/augeas.c
index 35721fc..a8bed99 100644
--- a/src/augeas.c
+++ b/src/augeas.c
@@ -137,7 +137,7 @@ struct tree *tree_find(struct augeas *aug, const char *path) {
     struct tree *result = NULL;
     int r;
 
-    p = pathx_aug_parse(aug, aug->origin, path, true);
+    p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true);
     ERR_BAIL(aug);
 
     r = pathx_find_one(p, &result);
@@ -157,7 +157,7 @@ struct tree *tree_find_cr(struct augeas *aug, const char *path) {
     struct tree *result = NULL;
     int r;
 
-    p = pathx_aug_parse(aug, aug->origin, path, true);
+    p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true);
     ERR_BAIL(aug);
 
     r = pathx_expand_tree(p, &result);
@@ -235,6 +235,7 @@ static void store_pathx_error(const struct augeas *aug) {
 
 struct pathx *pathx_aug_parse(const struct augeas *aug,
                               struct tree *tree,
+                              struct tree *root_ctx,
                               const char *path, bool need_nodeset) {
     struct pathx *result;
     struct error *err = err_of_aug(aug);
@@ -242,10 +243,47 @@ struct pathx *pathx_aug_parse(const struct augeas *aug,
     if (tree == NULL)
         tree = aug->origin;
 
-    pathx_parse(tree, err, path, need_nodeset, aug->symtab, &result);
+    pathx_parse(tree, err, path, need_nodeset, aug->symtab, root_ctx, &result);
     return result;
 }
 
+/* Find the tree stored in AUGEAS_CONTEXT */
+struct tree *tree_root_ctx(const struct augeas *aug) {
+    struct pathx *p = NULL;
+    struct tree *match = NULL;
+    const char *ctx_path;
+    int r;
+
+    p = pathx_aug_parse(aug, aug->origin, NULL, AUGEAS_CONTEXT, true);
+    ERR_BAIL(aug);
+
+    r = pathx_find_one(p, &match);
+    ERR_THROW(r > 1, aug, AUG_EMMATCH,
+              "There are %d nodes matching %s, expecting one",
+              r, AUGEAS_CONTEXT);
+
+    if (match == NULL || match->value == NULL || *match->value == '\0')
+        goto error;
+
+    ctx_path = match->value;
+    free_pathx(p);
+
+    p = pathx_aug_parse(aug, aug->origin, NULL, ctx_path, true);
+    ERR_BAIL(aug);
+
+    r = pathx_find_one(p, &match);
+    ERR_THROW(r > 1, aug, AUG_EMMATCH,
+              "There are %d nodes matching the context %s, expecting one",
+              r, ctx_path);
+
+ done:
+    free_pathx(p);
+    return match;
+ error:
+    match = NULL;
+    goto done;
+}
+
 static const char *init_root(const char *root0) {
     char *root;
 
@@ -453,6 +491,9 @@ struct augeas *aug_init(const char *root, const char *loadpath,
        AUGEAS_META_ROOT getting changed. */
     aug_set(result, AUGEAS_META_ROOT, result->root);
 
+    /* Set the default path context */
+    aug_set(result, AUGEAS_CONTEXT, AUG_CONTEXT_DEFAULT);
+
     for (int i=0; i < ARRAY_CARDINALITY(static_nodes); i++)
         aug_set(result, static_nodes[i][0], static_nodes[i][1]);
 
@@ -643,7 +684,7 @@ int aug_get(const struct augeas *aug, const char *path, const char **value) {
 
     api_entry(aug);
 
-    p = pathx_aug_parse(aug, aug->origin, path, true);
+    p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true);
     ERR_BAIL(aug);
 
     if (value != NULL)
@@ -693,7 +734,7 @@ int aug_defvar(augeas *aug, const char *name, const char *expr) {
     if (expr == NULL) {
         result = pathx_symtab_undefine(&(aug->symtab), name);
     } else {
-        p = pathx_aug_parse(aug, aug->origin, expr, false);
+        p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), expr, false);
         ERR_BAIL(aug);
         result = pathx_symtab_define(&(aug->symtab), name, p);
     }
@@ -721,7 +762,7 @@ int aug_defnode(augeas *aug, const char *name, const char *expr,
     if (created == NULL)
         created = &cr;
 
-    p = pathx_aug_parse(aug, aug->origin, expr, false);
+    p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), expr, false);
     ERR_BAIL(aug);
 
     if (pathx_first(p) == NULL) {
@@ -778,7 +819,12 @@ int aug_set(struct augeas *aug, const char *path, const char *value) {
 
     api_entry(aug);
 
-    p = pathx_aug_parse(aug, aug->origin, path, true);
+    /* Get-out clause, in case context is broken */
+    struct tree *root_ctx = NULL;
+    if (strcmp(path, AUGEAS_CONTEXT) != 0)
+        root_ctx = tree_root_ctx(aug);
+
+    p = pathx_aug_parse(aug, aug->origin, root_ctx, path, true);
     ERR_BAIL(aug);
 
     result = tree_set(p, value) == NULL ? -1 : 0;
@@ -799,7 +845,7 @@ int aug_setm(struct augeas *aug, const char *base,
 
     api_entry(aug);
 
-    bx = pathx_aug_parse(aug, aug->origin, base, true);
+    bx = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), base, true);
     ERR_BAIL(aug);
 
     if (sub != NULL && STREQ(sub, "."))
@@ -809,7 +855,7 @@ int aug_setm(struct augeas *aug, const char *base,
     for (bt = pathx_first(bx); bt != NULL; bt = pathx_next(bx)) {
         if (sub != NULL) {
             /* Handle subnodes of BT */
-            sx = pathx_aug_parse(aug, bt, sub, true);
+            sx = pathx_aug_parse(aug, bt, NULL, sub, true);
             ERR_BAIL(aug);
             if (pathx_first(sx) != NULL) {
                 /* Change existing subnodes matching SUB */
@@ -877,7 +923,7 @@ int aug_insert(struct augeas *aug, const char *path, const char *label,
 
     api_entry(aug);
 
-    p = pathx_aug_parse(aug, aug->origin, path, true);
+    p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true);
     ERR_BAIL(aug);
 
     result = tree_insert(p, label, before);
@@ -993,7 +1039,7 @@ int aug_rm(struct augeas *aug, const char *path) {
 
     api_entry(aug);
 
-    p = pathx_aug_parse(aug, aug->origin, path, true);
+    p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true);
     ERR_BAIL(aug);
 
     result = tree_rm(p);
@@ -1017,7 +1063,7 @@ int aug_span(struct augeas *aug, const char *path, char **filename,
 
     api_entry(aug);
 
-    p = pathx_aug_parse(aug, aug->origin, path, true);
+    p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true);
     ERR_BAIL(aug);
 
     tree = pathx_first(p);
@@ -1069,7 +1115,7 @@ int tree_replace(struct augeas *aug, const char *path, struct tree *sub) {
     struct pathx *p = NULL;
     int r;
 
-    p = pathx_aug_parse(aug, aug->origin, path, true);
+    p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true);
     ERR_BAIL(aug);
 
     r = tree_rm(p);
@@ -1099,10 +1145,10 @@ int aug_mv(struct augeas *aug, const char *src, const char *dst) {
     api_entry(aug);
 
     ret = -1;
-    s = pathx_aug_parse(aug, aug->origin, src, true);
+    s = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), src, true);
     ERR_BAIL(aug);
 
-    d = pathx_aug_parse(aug, aug->origin, dst, true);
+    d = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), dst, true);
     ERR_BAIL(aug);
 
     r = find_one_node(s, &ts);
@@ -1158,7 +1204,7 @@ int aug_match(const struct augeas *aug, const char *pathin, char ***matches) {
         pathin = "/*";
     }
 
-    p = pathx_aug_parse(aug, aug->origin, pathin, true);
+    p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), pathin, true);
     ERR_BAIL(aug);
 
     for (tree = pathx_first(p); tree != NULL; tree = pathx_next(p)) {
@@ -1298,7 +1344,7 @@ static int unlink_removed_files(struct augeas *aug,
             /* Unlink all files in tm */
             struct pathx *px = NULL;
             if (pathx_parse(tm, err_of_aug(aug), file_nodes, true,
-                            aug->symtab, &px) != PATHX_NOERROR) {
+                            aug->symtab, NULL, &px) != PATHX_NOERROR) {
                 result = -1;
                 continue;
             }
@@ -1438,7 +1484,7 @@ int dump_tree(FILE *out, struct tree *tree) {
     struct pathx *p;
     int result;
 
-    if (pathx_parse(tree, NULL, "/*", true, NULL, &p) != PATHX_NOERROR)
+    if (pathx_parse(tree, NULL, "/*", true, NULL, NULL, &p) != PATHX_NOERROR)
         return -1;
 
     result = print_tree(out, p, 1);
@@ -1456,7 +1502,7 @@ int aug_print(const struct augeas *aug, FILE *out, const char *pathin) {
         pathin = "/*";
     }
 
-    p = pathx_aug_parse(aug, aug->origin, pathin, true);
+    p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), pathin, true);
     ERR_BAIL(aug);
 
     result = print_tree(out, p, 0);
diff --git a/src/augeas.h b/src/augeas.h
index 0a148ac..0e8f629 100644
--- a/src/augeas.h
+++ b/src/augeas.h
@@ -74,7 +74,7 @@ augeas *aug_init(const char *root, const char *loadpath, unsigned int flags);
  *
  * Define a variable NAME whose value is the result of evaluating EXPR. If
  * a variable NAME already exists, its name will be replaced with the
- * result of evaluating EXPR.
+ * result of evaluating EXPR.  Context will not be applied to EXPR.
  *
  * If EXPR is NULL, the variable NAME will be removed if it is defined.
  *
diff --git a/src/builtin.c b/src/builtin.c
index 1b339e3..6acf99e 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -163,7 +163,7 @@ static struct value *pathx_parse_glue(struct info *info, struct value *tree,
     assert(tree->tag == V_TREE);
 
     if (pathx_parse(tree->origin, info->error, path->string->str, true,
-                    NULL, p) != PATHX_NOERROR) {
+                    NULL, NULL, p) != PATHX_NOERROR) {
         return make_pathx_exn(ref(info), *p);
     } else {
         return NULL;
diff --git a/src/internal.h b/src/internal.h
index 8c58d37..80fd843 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -81,6 +81,10 @@
 #define AUGEAS_COPY_IF_RENAME_FAILS \
     AUGEAS_META_SAVE_MODE "/copy_if_rename_fails"
 
+/* Define: AUGEAS_CONTEXT
+ * Context prepended to all non-absolute paths */
+#define AUGEAS_CONTEXT AUGEAS_META_TREE "/context"
+
 /* A hierarchy where we record certain 'events', e.g. which tree
  * nodes actually gotsaved into files */
 #define AUGEAS_EVENTS AUGEAS_META_TREE "/events"
@@ -119,6 +123,9 @@
 #define AUG_ENABLE "enable"
 #define AUG_DISABLE "disable"
 
+/* default value for the relative path context */
+#define AUG_CONTEXT_DEFAULT "/files"
+
 #ifdef __GNUC__
 
 #ifndef __GNUC_PREREQ
@@ -424,6 +431,12 @@ struct tree *tree_find(struct augeas *aug, const char *path);
  * Returns the node or NULL on error
  */
 struct tree *tree_find_cr(struct augeas *aug, const char *path);
+/* Find the node at the path stored in AUGEAS_CONTEXT, i.e. the root context
+ * node for relative paths.
+ * Errors: EMMATCH - more than one node matches PATH
+ *         ENOMEM  - allocation error
+ */
+struct tree *tree_root_ctx(const struct augeas *aug);
 
 /* Struct: memstream
  * Wrappers to simulate OPEN_MEMSTREAM where that's not available. The
@@ -489,7 +502,8 @@ struct pathx_symtab;
 const char *pathx_error(struct pathx *pathx, const char **txt, int *pos);
 
 /* Parse a path expression PATH rooted at TREE, which is a node somewhere
- * in AUG->ORIGIN. If TREE is NULL, AUG->ORIGIN is used.
+ * in AUG->ORIGIN. If TREE is NULL, AUG->ORIGIN is used. If ROOT_CTX is not
+ * NULL and the PATH isn't absolute then it will be rooted at ROOT_CTX.
  *
  * Use this function rather than PATHX_PARSE for path expressions inside
  * the tree in AUG->ORIGIN.
@@ -502,6 +516,7 @@ const char *pathx_error(struct pathx *pathx, const char **txt, int *pos);
  */
 struct pathx *pathx_aug_parse(const struct augeas *aug,
                               struct tree *tree,
+                              struct tree *root_ctx,
                               const char *path, bool need_nodeset);
 
 /* Parse the string PATH into a path expression PX that will be evaluated
@@ -517,6 +532,7 @@ int pathx_parse(const struct tree *origin,
                 const char *path,
                 bool need_nodeset,
                 struct pathx_symtab *symtab,
+                struct tree *root_ctx,
                 struct pathx **px);
 /* Return the error struct that was passed into pathx_parse */
 struct error *err_of_pathx(struct pathx *px);
diff --git a/src/pathx.c b/src/pathx.c
index 6a9c377..ac9a802 100644
--- a/src/pathx.c
+++ b/src/pathx.c
@@ -134,6 +134,9 @@ struct step {
     struct pred *predicates;
 };
 
+/* Initialise the root nodeset with the first step */
+static struct tree *step_root(struct step *step, struct tree *ctx,
+                              struct tree *root_ctx);
 /* Iteration over the nodes on a step, ignoring the predicates */
 static struct tree *step_first(struct step *step, struct tree *ctx);
 static struct tree *step_next(struct step *step, struct tree *ctx,
@@ -221,6 +224,8 @@ struct state {
     uint            ctx_pos;
     uint            ctx_len;
 
+    struct tree *root_ctx; /* Root context for relative paths */
+
     /* A table of all values. The table is dynamically reallocated, i.e.
      * pointers to struct value should not be used across calls that
      * might allocate new values
@@ -1157,12 +1162,20 @@ static void ns_from_locpath(struct locpath *lp, uint *maxns,
         if (HAS_ERROR(state))
             goto error;
     }
+
     if (root == NULL) {
-        ns_add((*ns)[0], state->ctx, state);
+        struct step *first_step = NULL;
+        if (lp != NULL)
+            first_step = lp->steps;
+
+        struct tree *root_tree;
+        root_tree = step_root(first_step, state->ctx, state->root_ctx);
+        ns_add((*ns)[0], root_tree, state);
     } else {
         for (int i=0; i < root->used; i++)
             ns_add((*ns)[0], root->nodes[i], state);
     }
+
     if (HAS_ERROR(state))
         goto error;
 
@@ -2293,6 +2306,7 @@ int pathx_parse(const struct tree *tree,
                 const char *txt,
                 bool need_nodeset,
                 struct pathx_symtab *symtab,
+                struct tree *root_ctx,
                 struct pathx **pathx) {
     struct state *state = NULL;
 
@@ -2313,6 +2327,7 @@ int pathx_parse(const struct tree *tree,
     state->txt = txt;
     state->pos = txt;
     state->symtab = symtab;
+    state->root_ctx = root_ctx;
     state->error = err;
 
     if (ALLOC_N(state->value_pool, 8) < 0) {
@@ -2379,6 +2394,37 @@ static struct tree *tree_prev(struct tree *pos) {
     return node;
 }
 
+/* When the first step doesn't begin with ROOT then use relative root context
+ * instead. */
+static struct tree *step_root(struct step *step, struct tree *ctx,
+                              struct tree *root_ctx) {
+    struct tree *node = NULL;
+    switch (step->axis) {
+    case SELF:
+    case CHILD:
+    case DESCENDANT:
+    case PARENT:
+    case ANCESTOR:
+    case PRECEDING_SIBLING:
+    case FOLLOWING_SIBLING:
+        /* only use root_ctx when ctx is the absolute tree root */
+        if (ctx == ctx->parent && root_ctx != NULL)
+            node = root_ctx;
+        else
+            node = ctx;
+        break;
+    case ROOT:
+    case DESCENDANT_OR_SELF:
+        node = ctx;
+        break;
+    default:
+        assert(0);
+    }
+    if (node == NULL)
+        return NULL;
+    return node;
+}
+
 static struct tree *step_first(struct step *step, struct tree *ctx) {
     struct tree *node = NULL;
     switch (step->axis) {
diff --git a/src/transform.c b/src/transform.c
index b96650a..ef3f244 100644
--- a/src/transform.c
+++ b/src/transform.c
@@ -852,8 +852,8 @@ static int file_saved_event(struct augeas *aug, const char *path) {
     struct tree *dummy;
     int r;
 
-    px = pathx_aug_parse(aug, aug->origin, AUGEAS_EVENTS_SAVED "[last()]",
-                         true);
+    px = pathx_aug_parse(aug, aug->origin, NULL,
+                         AUGEAS_EVENTS_SAVED "[last()]", true);
     ERR_BAIL(aug);
 
     if (pathx_find_one(px, &dummy) == 1) {
diff --git a/tests/test-api.c b/tests/test-api.c
index 13e21d3..b046717 100644
--- a/tests/test-api.c
+++ b/tests/test-api.c
@@ -71,6 +71,53 @@ static void testGet(CuTest *tc) {
     CuAssertPtrEquals(tc, NULL, value);
     CuAssertIntEquals(tc, AUG_EMMATCH, aug_error(aug));
 
+    /* augeas should prepend context if relative path given */
+    r = aug_set(aug, "/augeas/context", "/augeas/version");
+    r = aug_get(aug, "save/*[1]", &value);
+    CuAssertIntEquals(tc, 1, r);
+    CuAssertPtrNotNull(tc, value);
+    CuAssertIntEquals(tc, AUG_NOERROR, aug_error(aug));
+
+    /* augeas should still work with an empty context */
+    r = aug_set(aug, "/augeas/context", "");
+    r = aug_get(aug, "/augeas/version", &value);
+    CuAssertIntEquals(tc, 1, r);
+    CuAssertPtrNotNull(tc, value);
+    CuAssertIntEquals(tc, AUG_NOERROR, aug_error(aug));
+
+    aug_close(aug);
+}
+
+static void testSet(CuTest *tc) {
+    int r;
+    const char *value;
+    struct augeas *aug;
+
+    aug = aug_init(root, loadpath, AUG_NO_STDINC|AUG_NO_LOAD);
+    CuAssertPtrNotNull(tc, aug);
+    CuAssertIntEquals(tc, AUG_NOERROR, aug_error(aug));
+
+    /* aug_set returns 0 for a simple set */
+    r = aug_set(aug, "/augeas/testSet", "foo");
+    CuAssertIntEquals(tc, 0, r);
+    CuAssertIntEquals(tc, AUG_NOERROR, aug_error(aug));
+
+    /* aug_set returns -1 when cannot set due to multiple nodes */
+    r = aug_set(aug, "/augeas/version/save/*", "foo");
+    CuAssertIntEquals(tc, -1, r);
+    CuAssertIntEquals(tc, AUG_EMMATCH, aug_error(aug));
+
+    /* aug_set is able to set the context, even when currently invalid */
+    r = aug_set(aug, "/augeas/context", "( /files | /augeas )");
+    CuAssertIntEquals(tc, 0, r);
+    CuAssertIntEquals(tc, AUG_NOERROR, aug_error(aug));
+    r = aug_get(aug, "/augeas/version", &value);
+    CuAssertIntEquals(tc, -1, r);
+    CuAssertIntEquals(tc, AUG_EMMATCH, aug_error(aug));
+    r = aug_set(aug, "/augeas/context", "/files");
+    CuAssertIntEquals(tc, 0, r);
+    CuAssertIntEquals(tc, AUG_NOERROR, aug_error(aug));
+
     aug_close(aug);
 }
 
@@ -357,6 +404,7 @@ int main(void) {
     CuSuiteSetup(suite, NULL, NULL);
 
     SUITE_ADD_TEST(suite, testGet);
+    SUITE_ADD_TEST(suite, testSet);
     SUITE_ADD_TEST(suite, testSetM);
     SUITE_ADD_TEST(suite, testDefVarMeta);
     SUITE_ADD_TEST(suite, testDefNodeExistingMeta);
diff --git a/tests/xpath.tests b/tests/xpath.tests
index e327062..742b9ff 100644
--- a/tests/xpath.tests
+++ b/tests/xpath.tests
@@ -300,3 +300,10 @@ test int-bool-f /files[int(1 = 0) > 0]
 
 test int-bool-t /files[int(1 = 1) > 0]
      /files
+
+# Relative paths, relying on default /files context
+test ctx_file etc/network/interfaces
+     /files/etc/network/interfaces
+
+test ctx_file_pred etc/network/interfaces/iface[. = "lo"]
+     /files/etc/network/interfaces/iface[1] = lo
-- 
1.7.4.4




More information about the augeas-devel mailing list