Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add function modified() to select nodes which are marked as dirty #691

Merged
merged 2 commits into from
Aug 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 34 additions & 29 deletions src/augeas.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ static const char *const static_nodes[][2] = {
{ AUGEAS_META_PATHX_FUNC "/glob", NULL },
{ AUGEAS_META_PATHX_FUNC "/label", NULL },
{ AUGEAS_META_PATHX_FUNC "/last", NULL },
{ AUGEAS_META_PATHX_FUNC "/modified", NULL },
{ AUGEAS_META_PATHX_FUNC "/position", NULL },
{ AUGEAS_META_PATHX_FUNC "/regexp", NULL }
};
Expand All @@ -84,18 +85,21 @@ static const char *const errcodes[] = {
};

static void tree_mark_dirty(struct tree *tree) {
do {
tree->dirty = 1;
tree = tree->parent;
} while (tree != tree->parent && !tree->dirty);
tree->dirty = 1;
while (tree != tree->parent ) {
if ( tree->file ) {
tree->dirty = 1;
break;
}
tree = tree->parent;
}
}

void tree_clean(struct tree *tree) {
if (tree->dirty) {
list_for_each(c, tree->children)
tree_clean(c);
}
if ( tree->file && ! tree->dirty )
return;
list_for_each(c, tree->children)
tree_clean(c);
tree->dirty = 0;
}

Expand Down Expand Up @@ -691,10 +695,9 @@ static void tree_mark_files(struct tree *tree) {
static void tree_rm_dirty_files(struct augeas *aug, struct tree *tree) {
struct tree *p;

if (!tree->dirty)
if (tree->file && !tree->dirty) {
return;

if (tree->file && ((p = tree_child(tree, "path")) != NULL)) {
} else if (tree->file && tree->dirty && ((p = tree_child(tree, "path")) != NULL)) {
tree_unlink(aug, tree_fpath(aug, p->value));
tree_unlink(aug, tree);
} else {
Expand All @@ -709,7 +712,7 @@ static void tree_rm_dirty_files(struct augeas *aug, struct tree *tree) {

static void tree_rm_dirty_leaves(struct augeas *aug, struct tree *tree,
struct tree *protect) {
if (! tree->dirty)
if (tree->file && !tree->dirty)
return;

struct tree *c = tree->children;
Expand Down Expand Up @@ -1491,19 +1494,23 @@ static int tree_save(struct augeas *aug, struct tree *tree,
return -1;

list_for_each(t, tree) {
if (t->dirty) {
if (t->file && ! t->dirty) {
continue;
} else {
char *tpath = NULL;
struct tree *transform = NULL;
if (asprintf(&tpath, "%s/%s", path, t->label) == -1) {
result = -1;
continue;
}
list_for_each(xfm, load->children) {
if (transform_applies(xfm, tpath)) {
if (transform == NULL || transform == xfm) {
transform = xfm;
} else {
result = check_save_dup(aug, tpath, transform, xfm);
if ( t->dirty ) {
list_for_each(xfm, load->children) {
if (transform_applies(xfm, tpath)) {
if (transform == NULL || transform == xfm) {
transform = xfm;
} else {
result = check_save_dup(aug, tpath, transform, xfm);
}
}
}
}
Expand Down Expand Up @@ -1554,7 +1561,7 @@ static int unlink_removed_files(struct augeas *aug,

int result = 0;

if (! files->dirty)
if (files->file)
return 0;

for (struct tree *tm = meta->children; tm != NULL;) {
Expand All @@ -1576,7 +1583,7 @@ static int unlink_removed_files(struct augeas *aug,
result = -1;
}
free_pathx(px);
} else if (tf->dirty && ! tree_child(tm, "path")) {
} else if (! tree_child(tm, "path")) {
if (unlink_removed_files(aug, tf, tm) < 0)
result = -1;
}
Expand Down Expand Up @@ -1605,15 +1612,13 @@ int aug_save(struct augeas *aug) {
list_for_each(xfm, load->children)
transform_validate(aug, xfm);

if (files->dirty) {
if (tree_save(aug, files->children, AUGEAS_FILES_TREE) == -1)
ret = -1;
if (tree_save(aug, files->children, AUGEAS_FILES_TREE) == -1)
ret = -1;

/* Remove files whose entire subtree was removed. */
if (meta_files != NULL) {
if (unlink_removed_files(aug, files, meta_files) < 0)
ret = -1;
}
/* Remove files whose entire subtree was removed. */
if (meta_files != NULL) {
if (unlink_removed_files(aug, files, meta_files) < 0)
ret = -1;
}
if (!(aug->flags & AUG_SAVE_NOOP)) {
tree_clean(aug->origin);
Expand Down
9 changes: 9 additions & 0 deletions src/pathx.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ static void func_regexp_flag(struct state *state, int nargs);
static void func_glob(struct state *state, int nargs);
static void func_int(struct state *state, int nargs);
static void func_not(struct state *state, int nargs);
static void func_modified(struct state *state, int nargs);

static const enum type arg_types_nodeset[] = { T_NODESET };
static const enum type arg_types_string[] = { T_STRING };
Expand Down Expand Up @@ -341,6 +342,8 @@ static const struct func builtin_funcs[] = {
.arg_types = arg_types_nodeset, .impl = func_int, .pure = false },
{ .name = "int", .arity = 1, .type = T_NUMBER,
.arg_types = arg_types_bool, .impl = func_int, .pure = false },
{ .name = "modified", .arity = 0, .type = T_BOOLEAN,
.arg_types = NULL, .impl = func_modified, .pure = false },
{ .name = "not", .arity = 1, .type = T_BOOLEAN,
.arg_types = arg_types_bool, .impl = func_not, .pure = true }
};
Expand Down Expand Up @@ -724,6 +727,12 @@ static void func_int(struct state *state, int nargs) {
push_value(vind, state);
}

static void func_modified(struct state *state, int nargs) {
ensure_arity(0, 0);

push_boolean_value(state->ctx->dirty , state);
}

static void func_not(struct state *state, int nargs) {
ensure_arity(1, 1);
RET_ON_ERROR;
Expand Down
3 changes: 2 additions & 1 deletion tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,8 @@ check_SCRIPTS = \
test-save-empty.sh test-bug-1.sh test-idempotent.sh test-preserve.sh \
test-events-saved.sh test-save-mode.sh test-unlink-error.sh \
test-augtool-empty-line.sh test-augtool-modify-root.sh \
test-span-rec-lens.sh test-nonwritable.sh test-augmatch.sh
test-span-rec-lens.sh test-nonwritable.sh test-augmatch.sh \
test-function-modified.sh

EXTRA_DIST = \
test-augtool root lens-test-1 \
Expand Down
47 changes: 47 additions & 0 deletions tests/test-function-modified.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/bin/sh

# Test that changing the value of a node causes the function modified() to return true

root=$abs_top_builddir/build/test-function-modified
hosts=$root/etc/hosts

rm -rf $root
mkdir -p $(dirname $hosts)

cat <<EOF > $hosts
127.0.0.1 localhost
::1 localhost6
EOF

touch -r $0 $hosts

augtool --nostdinc -r $root -I $abs_top_srcdir/lenses > /dev/null <<EOF >$root/output.1
set /files/etc/hosts/1/ipaddr 127.0.0.4
match /files/etc/hosts//*[modified()]
EOF

[ "$(cat $root/output.1)" = '/files/etc/hosts/1/ipaddr = 127.0.0.4' ] || exit 1

# An empty value should return true, even if none of the child nodes are modified
augtool --nostdinc -r $root -I $abs_top_srcdir/lenses > /dev/null <<EOF >$root/output.2
set /files/etc/hosts/1 x
set /files/etc/hosts/1
match /files/etc/hosts//*[modified()]
EOF

[ "$(cat $root/output.2)" = '/files/etc/hosts/1 = (none)' ] || exit 1

# Test that changing a value does not change the parent node
# Test that adding a new node also marks the (new) parent node.
augtool --nostdinc -r $root -I $abs_top_srcdir/lenses > /dev/null <<EOF >$root/output.3
set /files/etc/hosts/1/ipaddr 127.1.1.1
set /files/etc/hosts/3/ipaddr 127.3.3.3
match /files/etc/hosts//*[modified()]
EOF

diff -q $root/output.3 - <<EOF || exit 1
/files/etc/hosts/3 = (none)
/files/etc/hosts/1/ipaddr = 127.1.1.1
/files/etc/hosts/3/ipaddr = 127.3.3.3
EOF