[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