/*
* augeas.c: the core data structure for storing key/value pairs
*
* 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
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: David Lutterkort <dlutter@redhat.com>
*/
#include <config.h>
#include "augeas.h"
#include "internal.h"
#include "memory.h"
#include "syntax.h"
#include "transform.h"
#include "errcode.h"
#include <fnmatch.h>
#include <argz.h>
#include <string.h>
#include <stdarg.h>
#include <locale.h>
/* Some popular labels that we use in /augeas */
static const char *const s_augeas = "augeas";
static const char *const s_files = "files";
static const char *const s_load = "load";
static const char *const s_pathx = "pathx";
static const char *const s_error = "error";
static const char *const s_pos = "pos";
static const char *const s_vars = "variables";
static const char *const s_lens = "lens";
static const char *const s_excl = "excl";
static const char *const s_incl = "incl";
#define AUGEAS_META_PATHX_FUNC AUGEAS_META_TREE "/version/pathx/functions"
static const char *const static_nodes[][2] = {
{ AUGEAS_FILES_TREE, NULL },
{ AUGEAS_META_TREE "/variables", NULL },
{ AUGEAS_META_TREE "/version", PACKAGE_VERSION },
{ AUGEAS_META_TREE "/version/save/mode[1]", AUG_SAVE_BACKUP_TEXT },
{ AUGEAS_META_TREE "/version/save/mode[2]", AUG_SAVE_NEWFILE_TEXT },
{ AUGEAS_META_TREE "/version/save/mode[3]", AUG_SAVE_NOOP_TEXT },
{ AUGEAS_META_TREE "/version/save/mode[4]", AUG_SAVE_OVERWRITE_TEXT },
{ AUGEAS_META_TREE "/version/defvar/expr", NULL },
{ AUGEAS_META_PATHX_FUNC "/count", NULL },
{ AUGEAS_META_PATHX_FUNC "/glob", NULL },
{ AUGEAS_META_PATHX_FUNC "/label", NULL },
{ AUGEAS_META_PATHX_FUNC "/last", NULL },
{ AUGEAS_META_PATHX_FUNC "/position", NULL },
{ AUGEAS_META_PATHX_FUNC "/regexp", NULL }
};
static const char *const errcodes[] = {
"No error", /* AUG_NOERROR */
"Cannot allocate memory", /* AUG_ENOMEM */
"Internal error (please file a bug)", /* AUG_EINTERNAL */
"Invalid path expression", /* AUG_EPATHX */
"No match for path expression", /* AUG_ENOMATCH */
"Too many matches for path expression", /* AUG_EMMATCH */
"Syntax error in lens definition", /* AUG_ESYNTAX */
"Lens not found", /* AUG_ENOLENS */
"Multiple transforms", /* AUG_EMXFM */
"Node has no span info", /* AUG_ENOSPAN */
"Cannot move node into its descendant", /* AUG_EMVDESC */
"Failed to execute command", /* AUG_ECMDRUN */
"Invalid argument in function call", /* AUG_EBADARG */
"Invalid label", /* AUG_ELABEL */
"Cannot copy node into its descendant" /* AUG_ECPDESC */
};
static void tree_mark_dirty(struct tree *tree) {
do {
tree->dirty = 1;
tree = tree->parent;
} while (tree != tree->parent && !tree->dirty);
tree->dirty = 1;
}
void tree_clean(struct tree *tree) {
if (tree->dirty) {
list_for_each(c, tree->children)
tree_clean(c);
}
tree->dirty = 0;
}
struct tree *tree_child(struct tree *tree, const char *label) {
if (tree == NULL)
return NULL;
list_for_each(child, tree->children) {
if (streqv(label, child->label))
return child;
}
return NULL;
}
struct tree *tree_child_cr(struct tree *tree, const char *label) {
static struct tree *child = NULL;
if (tree == NULL)
return NULL;
child = tree_child(tree, label);
if (child == NULL) {
char *l = strdup(label);
if (l == NULL)
return NULL;
child = tree_append(tree, l, NULL);
}
return child;
}
struct tree *tree_path_cr(struct tree *tree, int n, ...) {
va_list ap;
va_start(ap, n);
for (int i=0; i < n; i++) {
const char *l = va_arg(ap, const char *);
tree = tree_child_cr(tree, l);
}
va_end(ap);
return tree;
}
static struct tree *tree_fpath_int(struct augeas *aug, const char *fpath,
bool create) {
int r;
char *steps = NULL, *step = NULL;
size_t nsteps = 0;
struct tree *result = NULL;
r = argz_create_sep(fpath, '/', &steps, &nsteps);
ERR_NOMEM(r < 0, aug);
result = aug->origin;
while ((step = argz_next(steps, nsteps, step))) {
if (create) {
result = tree_child_cr(result, step);
ERR_THROW(result == NULL, aug, AUG_ENOMEM,
"while searching %s: can not create %s", fpath, step);
} else {
/* Lookup only */
result = tree_child(result, step);
if (result == NULL)
goto done;
}
}
done:
free(steps);
return result;
error:
result = NULL;
goto done;
}
struct tree *tree_fpath(struct augeas *aug, const char *fpath) {
return tree_fpath_int(aug, fpath, false);
}
struct tree *tree_fpath_cr(struct augeas *aug, const char *fpath) {
return tree_fpath_int(aug, fpath, true);
}
struct tree *tree_find(struct augeas *aug, const char *path) {
struct pathx *p = NULL;
struct tree *result = NULL;
int r;
p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true);
ERR_BAIL(aug);
r = pathx_find_one(p, &result);
BUG_ON(r > 1, aug,
"Multiple matches for %s when only one was expected",
path);
done:
free_pathx(p);
return result;
error:
result = NULL;
goto done;
}
struct tree *tree_find_cr(struct augeas *aug, const char *path) {
struct pathx *p = NULL;
struct tree *result = NULL;
int r;
p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true);
ERR_BAIL(aug);
r = pathx_expand_tree(p, &result);
ERR_BAIL(aug);
ERR_THROW(r < 0, aug, AUG_EINTERNAL, "pathx_expand_tree failed");
error:
free_pathx(p);
return result;
}
void tree_store_value(struct tree *tree, char **value) {
if (streqv(tree->value, *value)) {
free(*value);
*value = NULL;
return;
}
if (tree->value != NULL) {
free(tree->value);
tree->value = NULL;
}
if (*value != NULL) {
tree->value = *value;
*value = NULL;
}
tree_mark_dirty(tree);
}
int tree_set_value(struct tree *tree, const char *value) {
char *v = NULL;
if (streqv(tree->value, value))
return 0;
if (value != NULL) {
v = strdup(value);
if (v == NULL)
return -1;
}
tree_store_value(tree, &v);
return 0;
}
static void store_error(const struct augeas *aug, const char *label, const char *value,
int nentries, ...) {
va_list ap;
struct tree *tree;
ensure(nentries % 2 == 0, aug);
tree = tree_path_cr(aug->origin, 3, s_augeas, s_error, label);
if (tree == NULL)
return;
tree_set_value(tree, value);
va_start(ap, nentries);
for (int i=0; i < nentries; i += 2) {
char *l = va_arg(ap, char *);
char *v = va_arg(ap, char *);
struct tree *t = tree_child_cr(tree, l);
if (t != NULL)
tree_set_value(t, v);
}
va_end(ap);
error:
return;
}
/* Report pathx errors in /augeas/pathx/error */
static void store_pathx_error(const struct augeas *aug) {
if (aug->error->code != AUG_EPATHX)
return;
store_error(aug, s_pathx, aug->error->minor_details,
2, s_pos, aug->error->details);
}
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);
if (tree == NULL)
tree = aug->origin;
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;
/* Clean via augrun's helper to ensure it's valid */
ctx_path = cleanpath(match->value);
free_pathx(p);
p = pathx_aug_parse(aug, aug->origin, NULL, ctx_path, true);
ERR_BAIL(aug);
if (pathx_first(p) == NULL) {
r = pathx_expand_tree(p, &match);
if (r < 0)
goto done;
r = tree_set_value(match, NULL);
if (r < 0)
goto done;
} else {
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;
}
struct tree *tree_append(struct tree *parent,
char *label, char *value) {
struct tree *result = make_tree(label, value, parent, NULL);
if (result != NULL)
list_append(parent->children, result);
return result;
}
static struct tree *tree_append_s(struct tree *parent,
const char *l0, char *v) {
struct tree *result;
char *l;
if (l0 == NULL) {
return NULL;
} else {
l = strdup(l0);
}
result = tree_append(parent, l, v);
if (result == NULL)
free(l);
return result;
}
static struct tree *tree_from_transform(struct augeas *aug,
const char *modname,
struct transform *xfm) {
struct tree *meta = tree_child_cr(aug->origin, s_augeas);
struct tree *load = NULL, *txfm = NULL, *t;
char *v = NULL;
int r;
ERR_NOMEM(meta == NULL, aug);
load = tree_child_cr(meta, s_load);
ERR_NOMEM(load == NULL, aug);
if (modname == NULL)
modname = "_";
txfm = tree_append_s(load, modname, NULL);
ERR_NOMEM(txfm == NULL, aug);
r = asprintf(&v, "@%s", modname);
ERR_NOMEM(r < 0, aug);
t = tree_append_s(txfm, s_lens, v);
ERR_NOMEM(t == NULL, aug);
v = NULL;
list_for_each(f, xfm->filter) {
const char *l = f->include ? s_incl : s_excl;
v = strdup(f->glob->str);
ERR_NOMEM(v == NULL, aug);
t = tree_append_s(txfm, l, v);
ERR_NOMEM(t == NULL, aug);
}
return txfm;
error:
free(v);
tree_unlink(aug, txfm);
return NULL;
}
/* Save user locale and switch to C locale */
#if HAVE_USELOCALE
static void save_locale(struct augeas *aug) {
if (aug->c_locale == NULL) {
aug->c_locale = newlocale(LC_ALL_MASK, "C", NULL);
ERR_NOMEM(aug->c_locale == NULL, aug);
}
aug->user_locale = uselocale(aug->c_locale);
error:
return;
}
#else
static void save_locale(ATTRIBUTE_UNUSED struct augeas *aug) { }
#endif
#if HAVE_USELOCALE
static void restore_locale(struct augeas *aug) {
uselocale(aug->user_locale);
aug->user_locale = NULL;
}
#else
static void restore_locale(ATTRIBUTE_UNUSED struct augeas *aug) { }
#endif
/* Clean up old error messages every time we enter through the public
* API. Since we make internal calls through the public API, we keep a
* count of how many times a public API call was made, and only reset when
* that count is 0. That requires that all public functions enclose their
* work within a matching pair of api_entry/api_exit calls.
*/
void api_entry(const struct augeas *aug) {
struct error *err = ((struct augeas *) aug)->error;
((struct augeas *) aug)->api_entries += 1;
if (aug->api_entries > 1)
return;
reset_error(err);
save_locale((struct augeas *) aug);
}
void api_exit(const struct augeas *aug) {
assert(aug->api_entries > 0);
((struct augeas *) aug)->api_entries -= 1;
if (aug->api_entries == 0) {
store_pathx_error(aug);
restore_locale((struct augeas *) aug);
}
}
static int init_root(struct augeas *aug, const char *root0) {
if (root0 == NULL)
root0 = getenv(AUGEAS_ROOT_ENV);
if (root0 == NULL || root0[0] == '\0')
root0 = "/";
aug->root = strdup(root0);
if (aug->root == NULL)
return -1;
if (aug->root[strlen(aug->root)-1] != SEP) {
if (REALLOC_N(aug->root, strlen(aug->root) + 2) < 0)
return -1;
strcat((char *) aug->root, "/");
}
return 0;
}
static int init_loadpath(struct augeas *aug, const char *loadpath) {
int r;
aug->modpathz = NULL;
aug->nmodpath = 0;
if (loadpath != NULL) {
r = argz_add_sep(&aug->modpathz, &aug->nmodpath,
loadpath, PATH_SEP_CHAR);
if (r != 0)
return -1;
}
char *env = getenv(AUGEAS_LENS_ENV);
if (env != NULL) {
r = argz_add_sep(&aug->modpathz, &aug->nmodpath,
env, PATH_SEP_CHAR);
if (r != 0)
return -1;
}
if (!(aug->flags & AUG_NO_STDINC)) {
r = argz_add(&aug->modpathz, &aug->nmodpath, AUGEAS_LENS_DIR);
if (r != 0)
return -1;
r = argz_add(&aug->modpathz, &aug->nmodpath,
AUGEAS_LENS_DIST_DIR);
if (r != 0)
return -1;
}
/* Clean up trailing slashes */
if (aug->nmodpath > 0) {
argz_stringify(aug->modpathz, aug->nmodpath, PATH_SEP_CHAR);
char *s, *t;
const char *e = aug->modpathz + strlen(aug->modpathz);
for (s = aug->modpathz, t = aug->modpathz; s < e; s++) {
char *p = s;
if (*p == '/') {
while (*p == '/') p += 1;
if (*p == '\0' || *p == PATH_SEP_CHAR)
s = p;
}
if (t != s)
*t++ = *s;
else
t += 1;
}
if (t != s) {
*t = '\0';
}
s = aug->modpathz;
aug->modpathz = NULL;
r = argz_create_sep(s, PATH_SEP_CHAR, &aug->modpathz,
&aug->nmodpath);
free(s);
if (r != 0)
return -1;
}
return 0;
}
static void init_save_mode(struct augeas *aug) {
const char *v = AUG_SAVE_OVERWRITE_TEXT;
if (aug->flags & AUG_SAVE_NEWFILE) {
v = AUG_SAVE_NEWFILE_TEXT;
} else if (aug->flags & AUG_SAVE_BACKUP) {
v = AUG_SAVE_BACKUP_TEXT;
} else if (aug->flags & AUG_SAVE_NOOP) {
v = AUG_SAVE_NOOP_TEXT;
}
aug_set(aug, AUGEAS_META_SAVE_MODE, v);
}
struct augeas *aug_init(const char *root, const char *loadpath,
unsigned int flags) {
struct augeas *result;
struct tree *tree_root = make_tree(NULL, NULL, NULL, NULL);
int r;
bool close_on_error = true;
if (tree_root == NULL)
return NULL;
if (ALLOC(result) < 0)
goto error;
if (ALLOC(result->error) < 0)
goto error;
if (make_ref(result->error->info) < 0)
goto error;
result->error->info->error = result->error;
result->error->info->filename = dup_string("(unknown file)");
if (result->error->info->filename == NULL)
goto error;
result->error->aug = result;
result->origin = make_tree_origin(tree_root);
if (result->origin == NULL) {
free_tree(tree_root);
goto error;
}
api_entry(result);
result->flags = flags;
r = init_root(result, root);
ERR_NOMEM(r < 0, result);
result->origin->children->label = strdup(s_augeas);
/* We are now initialized enough that we can dare return RESULT even
* when we encounter errors if the caller so wishes */
close_on_error = !(flags & AUG_NO_ERR_CLOSE);
r = init_loadpath(result, loadpath);
ERR_NOMEM(r < 0, result);
/* We report the root dir in AUGEAS_META_ROOT, but we only use the
value we store internally, to avoid any problems with
AUGEAS_META_ROOT getting changed. */
aug_set(result, AUGEAS_META_ROOT, result->root);
ERR_BAIL(result);
/* Set the default path context */
aug_set(result, AUGEAS_CONTEXT, AUG_CONTEXT_DEFAULT);
ERR_BAIL(result);
for (int i=0; i < ARRAY_CARDINALITY(static_nodes); i++) {
aug_set(result, static_nodes[i][0], static_nodes[i][1]);
ERR_BAIL(result);
}
init_save_mode(result);
ERR_BAIL(result);
const char *v = (flags & AUG_ENABLE_SPAN) ? AUG_ENABLE : AUG_DISABLE;
aug_set(result, AUGEAS_SPAN_OPTION, v);
ERR_BAIL(result);
if (interpreter_init(result) == -1)
goto error;
list_for_each(modl, result->modules) {
struct transform *xform = modl->autoload;
if (xform == NULL)
continue;
tree_from_transform(result, modl->name, xform);
ERR_BAIL(result);
}
if (!(result->flags & AUG_NO_LOAD))
if (aug_load(result) < 0)
goto error;
api_exit(result);
return result;
error:
if (close_on_error) {
aug_close(result);
result = NULL;
}
if (result != NULL && result->api_entries > 0)
api_exit(result);
return result;
}
/* Free one tree node */
static void free_tree_node(struct tree *tree) {
if (tree == NULL)
return;
if (tree->span != NULL)
free_span(tree->span);
free(tree->label);
free(tree->value);
free(tree);
}
/* Only unlink; assume we know TREE is not in the symtab */
static int tree_unlink_raw(struct tree *tree) {
int result = 0;
assert (tree->parent != NULL);
list_remove(tree, tree->parent->children);
tree_mark_dirty(tree->parent);
result = free_tree(tree->children) + 1;
free_tree_node(tree);
return result;
}
int tree_unlink(struct augeas *aug, struct tree *tree) {
if (tree == NULL)
return 0;
pathx_symtab_remove_descendants(aug->symtab, tree);
return tree_unlink_raw(tree);
}
void tree_unlink_children(struct augeas *aug, struct tree *tree) {
if (tree == NULL)
return;
pathx_symtab_remove_descendants(aug->symtab, tree);
while (tree->children != NULL)
tree_unlink_raw(tree->children);
}
static void tree_mark_files(struct tree *tree) {
if (tree_child(tree, "path") != NULL) {
tree_mark_dirty(tree);
} else {
list_for_each(c, tree->children) {
tree_mark_files(c);
}
}
}
static void tree_rm_dirty_files(struct augeas *aug, struct tree *tree) {
struct tree *p;
if (!tree->dirty)
return;
if (tree->file && ((p = tree_child(tree, "path")) != NULL)) {
tree_unlink(aug, tree_fpath(aug, p->value));
tree_unlink(aug, tree);
} else {
struct tree *c = tree->children;
while (c != NULL) {
struct tree *next = c->next;
tree_rm_dirty_files(aug, c);
c = next;
}
}
}
static void tree_rm_dirty_leaves(struct augeas *aug, struct tree *tree,
struct tree *protect) {
if (! tree->dirty)
return;
struct tree *c = tree->children;
while (c != NULL) {
struct tree *next = c->next;
tree_rm_dirty_leaves(aug, c, protect);
c = next;
}
if (tree != protect && tree->children == NULL)
tree_unlink(aug, tree);
}
int aug_load(struct augeas *aug) {
const char *option = NULL;
struct tree *meta = tree_child_cr(aug->origin, s_augeas);
struct tree *meta_files = tree_child_cr(meta, s_files);
struct tree *files = tree_child_cr(aug->origin, s_files);
struct tree *load = tree_child_cr(meta, s_load);
struct tree *vars = tree_child_cr(meta, s_vars);
api_entry(aug);
ERR_NOMEM(load == NULL, aug);
/* To avoid unnecessary loads of files, we reload an existing file in
* several steps:
* (1) mark all file nodes under /augeas/files as dirty (and only those)
* (2) process all files matched by a lens; we check (in
* transform_load) if the file has been modified. If it has, we
* reparse it. Either way, we clear the dirty flag. We also need to
* reread the file if part or all of it has been modified in the
* tree but not been saved yet
* (3) remove all files from the tree that still have a dirty entry
* under /augeas/files. Those files are not processed by any lens
* anymore
* (4) Remove entries from /augeas/files and /files that correspond
* to directories without any files of interest
*/
/* update flags according to option value */
if (aug_get(aug, AUGEAS_SPAN_OPTION, &option) == 1) {
if (strcmp(option, AUG_ENABLE) == 0) {
aug->flags |= AUG_ENABLE_SPAN;
} else {
aug->flags &= ~AUG_ENABLE_SPAN;
}
}
tree_clean(meta_files);
tree_mark_files(meta_files);
list_for_each(xfm, load->children) {
if (transform_validate(aug, xfm) == 0)
transform_load(aug, xfm, NULL);
}
/* This makes it possible to spot 'directories' that are now empty
* because we removed their file contents */
tree_clean(files);
tree_rm_dirty_files(aug, meta_files);
tree_rm_dirty_leaves(aug, meta_files, meta_files);
tree_rm_dirty_leaves(aug, files, files);
tree_clean(aug->origin);
list_for_each(v, vars->children) {
aug_defvar(aug, v->label, v->value);
ERR_BAIL(aug);
}
api_exit(aug);
return 0;
error:
api_exit(aug);
return -1;
}
static int find_one_node(struct pathx *p, struct tree **match) {
struct error *err = err_of_pathx(p);
int r = pathx_find_one(p, match);
if (r == 1)
return 0;
if (r == 0) {
report_error(err, AUG_ENOMATCH, NULL);
} else {
/* r > 1 */
report_error(err, AUG_EMMATCH, NULL);
}
return -1;
}
int aug_get(const struct augeas *aug, const char *path, const char **value) {
struct pathx *p = NULL;
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);
r = pathx_find_one(p, &match);
ERR_BAIL(aug);
ERR_THROW(r > 1, aug, AUG_EMMATCH, "There are %d nodes matching %s",
r, path);
if (r == 1 && value != NULL)
*value = match->value;
free_pathx(p);
api_exit(aug);
return r;
error:
free_pathx(p);
api_exit(aug);
return -1;
}
int aug_label(const struct augeas *aug, const char *path, const char **label) {
struct pathx *p = NULL;
struct tree *match;
int r;
api_entry(aug);
p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true);
ERR_BAIL(aug);
if (label != NULL)
*label = NULL;
r = pathx_find_one(p, &match);
ERR_BAIL(aug);
ERR_THROW(r > 1, aug, AUG_EMMATCH, "There are %d nodes matching %s",
r, path);
if (r == 1 && label != NULL)
*label = match->label;
free_pathx(p);
api_exit(aug);
return r;
error:
free_pathx(p);
api_exit(aug);
return -1;
}
static void record_var_meta(struct augeas *aug, const char *name,
const char *expr) {
/* Record the definition of the variable */
struct tree *tree = tree_path_cr(aug->origin, 2, s_augeas, s_vars);
ERR_NOMEM(tree == NULL, aug);
if (expr == NULL) {
tree_unlink(aug, tree_child(tree, name));
} else {
tree = tree_child_cr(tree, name);
ERR_NOMEM(tree == NULL, aug);
tree_set_value(tree, expr);
}
error:
return;
}
int aug_defvar(augeas *aug, const char *name, const char *expr) {
struct pathx *p = NULL;
int result = -1;
api_entry(aug);
if (expr == NULL) {
result = pathx_symtab_undefine(&(aug->symtab), name);
} else {
p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), expr, false);
ERR_BAIL(aug);
result = pathx_symtab_define(&(aug->symtab), name, p);
}
ERR_BAIL(aug);
record_var_meta(aug, name, expr);
ERR_BAIL(aug);
error:
free_pathx(p);
api_exit(aug);
return result;
}
int aug_defnode(augeas *aug, const char *name, const char *expr,
const char *value, int *created) {
struct pathx *p = NULL;
int result = -1;
int r, cr;
struct tree *tree;
api_entry(aug);
if (expr == NULL)
goto error;
if (created == NULL)
created = &cr;
p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), expr, false);
ERR_BAIL(aug);
if (pathx_first(p) == NULL) {
r = pathx_expand_tree(p, &tree);
if (r < 0)
goto done;
*created = 1;
} else {
*created = 0;
}
if (*created) {
r = tree_set_value(tree, value);
if (r < 0)
goto done;
result = pathx_symtab_assign_tree(&(aug->symtab), name, tree);
char *e = path_of_tree(tree);
ERR_NOMEM(e == NULL, aug)
record_var_meta(aug, name, e);
free(e);
ERR_BAIL(aug);
} else {
result = pathx_symtab_define(&(aug->symtab), name, p);
record_var_meta(aug, name, expr);
ERR_BAIL(aug);
}
done:
error:
free_pathx(p);
api_exit(aug);
return result;
}
struct tree *tree_set(struct pathx *p, const char *value) {
struct tree *tree;
int r;
r = pathx_expand_tree(p, &tree);
if (r == -1)
return NULL;
r = tree_set_value(tree, value);
if (r < 0)
return NULL;
return tree;
}
int aug_set(struct augeas *aug, const char *path, const char *value) {
struct pathx *p = NULL;
int result = -1;
api_entry(aug);
/* Get-out clause, in case context is broken */
struct tree *root_ctx = NULL;
if (STRNEQ(path, AUGEAS_CONTEXT))
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;
error:
free_pathx(p);
api_exit(aug);
return result;
}
int aug_setm(struct augeas *aug, const char *base,
const char *sub, const char *value) {
struct pathx *bx = NULL, *sx = NULL;
struct tree *bt, *st;
int result, r;
api_entry(aug);
bx = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), base, true);
ERR_BAIL(aug);
if (sub != NULL && STREQ(sub, "."))
sub = NULL;
result = 0;
for (bt = pathx_first(bx); bt != NULL; bt = pathx_next(bx)) {
if (sub != NULL) {
/* Handle subnodes of BT */
sx = pathx_aug_parse(aug, bt, NULL, sub, true);
ERR_BAIL(aug);
if (pathx_first(sx) != NULL) {
/* Change existing subnodes matching SUB */
for (st = pathx_first(sx); st != NULL; st = pathx_next(sx)) {
r = tree_set_value(st, value);
ERR_NOMEM(r < 0, aug);
result += 1;
}
} else {
/* Create a new subnode matching SUB */
r = pathx_expand_tree(sx, &st);
if (r == -1)
goto error;
r = tree_set_value(st, value);
ERR_NOMEM(r < 0, aug);
result += 1;
}
free_pathx(sx);
sx = NULL;
} else {
/* Set nodes matching BT directly */
r = tree_set_value(bt, value);
ERR_NOMEM(r < 0, aug);
result += 1;
}
}
done:
free_pathx(bx);
free_pathx(sx);
api_exit(aug);
return result;
error:
result = -1;
goto done;
}
int tree_insert(struct pathx *p, const char *label, int before) {
struct tree *new = NULL, *match;
if (strchr(label, SEP) != NULL)
return -1;
if (find_one_node(p, &match) < 0)
goto error;
new = make_tree(strdup(label), NULL, match->parent, NULL);
if (new == NULL || new->label == NULL)
goto error;
if (before) {
list_insert_before(new, match, new->parent->children);
} else {
new->next = match->next;
match->next = new;
}
return 0;
error:
free_tree(new);
return -1;
}
int aug_insert(struct augeas *aug, const char *path, const char *label,
int before) {
struct pathx *p = NULL;
int result = -1;
api_entry(aug);
p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true);
ERR_BAIL(aug);
result = tree_insert(p, label, before);
error:
free_pathx(p);
api_exit(aug);
return result;
}
struct tree *make_tree(char *label, char *value, struct tree *parent,
struct tree *children) {
struct tree *tree;
if (ALLOC(tree) < 0)
return NULL;
tree->label = label;
tree->value = value;
tree->parent = parent;
tree->children = children;
list_for_each(c, tree->children)
c->parent = tree;
if (parent != NULL)
tree_mark_dirty(tree);
else
tree->dirty = 1;
return tree;
}
struct tree *make_tree_origin(struct tree *root) {
struct tree *origin = NULL;
origin = make_tree(NULL, NULL, NULL, root);
if (origin == NULL)
return NULL;
origin->parent = origin;
return origin;
}
/* Recursively free the whole tree TREE and all its siblings */
int free_tree(struct tree *tree) {
int cnt = 0;
while (tree != NULL) {
struct tree *del = tree;
tree = del->next;
cnt += free_tree(del->children);
free_tree_node(del);
cnt += 1;
}
return cnt;
}
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;
}
if (ndel == 0)
return 0;
if (ALLOC_N(del, ndel) < 0) {
free(del);
return -1;
}
for (i = 0, tree = pathx_first(p); tree != NULL; tree = pathx_next(p)) {
if (TREE_HIDDEN(tree))
continue;
pathx_symtab_remove_descendants(pathx_get_symtab(p), tree);
/* 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++) {
if (del[i] != NULL) {
cnt += tree_unlink_raw(del[i]);
}
}
free(del);
return cnt;
}
int aug_rm(struct augeas *aug, const char *path) {
struct pathx *p = NULL;
int result = -1;
api_entry(aug);
p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true);
ERR_BAIL(aug);
result = tree_rm(p);
error:
free_pathx(p);
api_exit(aug);
return result;
}
int aug_span(struct augeas *aug, const char *path, char **filename,
uint *label_start, uint *label_end, uint *value_start, uint *value_end,
uint *span_start, uint *span_end) {
struct pathx *p = NULL;
int result = -1;
struct tree *tree = NULL;
struct span *span;
api_entry(aug);
p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true);
ERR_BAIL(aug);
tree = pathx_first(p);
ERR_BAIL(aug);
ERR_THROW(tree == NULL, aug, AUG_ENOMATCH, "No node matching %s", path);
ERR_THROW(tree->span == NULL, aug, AUG_ENOSPAN, "No span info for %s", path);
ERR_THROW(pathx_next(p) != NULL, aug, AUG_EMMATCH, "Multiple nodes match %s", path);
span = tree->span;
if (label_start != NULL)
*label_start = span->label_start;
if (label_end != NULL)
*label_end = span->label_end;
if (value_start != NULL)
*value_start = span->value_start;
if (value_end != NULL)
*value_end = span->value_end;
if (span_start != NULL)
*span_start = span->span_start;
if (span_end != NULL)
*span_end = span->span_end;
/* We are safer here, make sure we have a filename */
if (filename != NULL) {
if (span->filename == NULL || span->filename->str == NULL) {
*filename = strdup("");
} else {
*filename = strdup(span->filename->str);
}
ERR_NOMEM(*filename == NULL, aug);
}
result = 0;
error:
free_pathx(p);
api_exit(aug);
return result;
}
int aug_mv(struct augeas *aug, const char *src, const char *dst) {
struct pathx *s = NULL, *d = NULL;
struct tree *ts, *td, *t;
int r, ret;
api_entry(aug);
ret = -1;
s = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), src, true);
ERR_BAIL(aug);
d = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), dst, true);
ERR_BAIL(aug);
r = find_one_node(s, &ts);
if (r < 0)
goto error;
r = pathx_expand_tree(d, &td);
if (r == -1)
goto error;
/* Don't move SRC into its own descendent */
t = td;
do {
ERR_THROW(t == ts, aug, AUG_EMVDESC,
"destination %s is a descendant of %s", dst, src);
t = t->parent;
} while (t != aug->origin);
free_tree(td->children);
td->children = ts->children;
list_for_each(c, td->children) {
c->parent = td;
}
free(td->value);
td->value = ts->value;
ts->value = NULL;
ts->children = NULL;
tree_unlink(aug, ts);
tree_mark_dirty(td);
ret = 0;
error:
free_pathx(s);
free_pathx(d);
api_exit(aug);
return ret;
}
static void tree_copy_rec(struct tree *src, struct tree *dst) {
struct tree *n;
char *value;
list_for_each(c, src->children) {
value = c->value == NULL ? NULL : strdup(c->value);
n = tree_append_s(dst, c->label, value);
tree_copy_rec(c, n);
}
}
int aug_cp(struct augeas *aug, const char *src, const char *dst) {
struct pathx *s = NULL, *d = NULL;
struct tree *ts, *td, *t;
int r, ret;
api_entry(aug);
ret = -1;
s = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), src, true);
ERR_BAIL(aug);
d = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), dst, true);
ERR_BAIL(aug);
r = find_one_node(s, &ts);
if (r < 0)
goto error;
r = pathx_expand_tree(d, &td);
if (r == -1)
goto error;
/* Don't copy SRC into its own descendent */
t = td;
do {
ERR_THROW(t == ts, aug, AUG_ECPDESC,
"destination %s is a descendant of %s", dst, src);
t = t->parent;
} while (t != aug->origin);
tree_set_value(td, ts->value);
free_tree(td->children);
td->children = NULL;
tree_copy_rec(ts, td);
tree_mark_dirty(td);
ret = 0;
error:
free_pathx(s);
free_pathx(d);
api_exit(aug);
return ret;
}
int aug_rename(struct augeas *aug, const char *src, const char *lbl) {
struct pathx *s = NULL;
struct tree *ts;
int ret;
int count = 0;
api_entry(aug);
ret = -1;
ERR_THROW(strchr(lbl, '/') != NULL, aug, AUG_ELABEL,
"Label %s contains a /", lbl);
s = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), src, true);
ERR_BAIL(aug);
for (ts = pathx_first(s); ts != NULL; ts = pathx_next(s)) {
free(ts->label);
ts->label = strdup(lbl);
tree_mark_dirty(ts);
count ++;
}
free_pathx(s);
api_exit(aug);
return count;
error:
free_pathx(s);
api_exit(aug);
return ret;
}
int aug_match(const struct augeas *aug, const char *pathin, char ***matches) {
struct pathx *p = NULL;
struct tree *tree;
int cnt = 0;
api_entry(aug);
if (matches != NULL)
*matches = NULL;
if (STREQ(pathin, "/")) {
pathin = "/*";
}
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)) {
if (! TREE_HIDDEN(tree))
cnt += 1;
}
ERR_BAIL(aug);
if (matches == NULL)
goto done;
if (ALLOC_N(*matches, cnt) < 0)
goto error;
int i = 0;
for (tree = pathx_first(p); tree != NULL; tree = pathx_next(p)) {
if (TREE_HIDDEN(tree))
continue;
(*matches)[i] = path_of_tree(tree);
if ((*matches)[i] == NULL) {
goto error;
}
i += 1;
}
ERR_BAIL(aug);
done:
free_pathx(p);
api_exit(aug);
return cnt;
error:
if (matches != NULL) {
if (*matches != NULL) {
for (i=0; i < cnt; i++)
free((*matches)[i]);
free(*matches);
}
}
free_pathx(p);
api_exit(aug);
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;
struct tree *meta = tree_child_cr(aug->origin, s_augeas);
struct tree *load = tree_child_cr(meta, s_load);
// FIXME: We need to detect subtrees that aren't saved by anything
if (load == NULL)
return -1;
list_for_each(t, tree) {
if (t->dirty) {
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 (transform != NULL) {
int r = transform_save(aug, transform, tpath, t);
if (r == -1)
result = -1;
} else {
if (tree_save(aug, t->children, tpath) == -1)
result = -1;
}
free(tpath);
}
}
return result;
}
/* Reset the flags based on what is set in the tree. */
static int update_save_flags(struct augeas *aug) {
const char *savemode ;
aug_get(aug, AUGEAS_META_SAVE_MODE, &savemode);
if (savemode == NULL)
return -1;
aug->flags &= ~(AUG_SAVE_BACKUP|AUG_SAVE_NEWFILE|AUG_SAVE_NOOP);
if (STREQ(savemode, AUG_SAVE_NEWFILE_TEXT)) {
aug->flags |= AUG_SAVE_NEWFILE;
} else if (STREQ(savemode, AUG_SAVE_BACKUP_TEXT)) {
aug->flags |= AUG_SAVE_BACKUP;
} else if (STREQ(savemode, AUG_SAVE_NOOP_TEXT)) {
aug->flags |= AUG_SAVE_NOOP ;
} else if (STRNEQ(savemode, AUG_SAVE_OVERWRITE_TEXT)) {
return -1;
}
return 0;
}
static int unlink_removed_files(struct augeas *aug,
struct tree *files, struct tree *meta) {
/* Find all nodes that correspond to a file and might have to be
* unlinked. A node corresponds to a file if it has a child labelled
* 'path', and we only consider it if there are no errors associated
* with it */
static const char *const file_nodes =
"descendant-or-self::*[path][count(error) = 0]";
int result = 0;
if (! files->dirty)
return 0;
for (struct tree *tm = meta->children; tm != NULL;) {
struct tree *tf = tree_child(files, tm->label);
struct tree *next = tm->next;
if (tf == NULL) {
/* Unlink all files in tm */
struct pathx *px = NULL;
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);
t != NULL;
t = pathx_next(px)) {
if (remove_file(aug, t) < 0)
result = -1;
}
free_pathx(px);
} else if (tf->dirty && ! tree_child(tm, "path")) {
if (unlink_removed_files(aug, tf, tm) < 0)
result = -1;
}
tm = next;
}
return result;
}
int aug_save(struct augeas *aug) {
int ret = 0;
struct tree *meta = tree_child_cr(aug->origin, s_augeas);
struct tree *meta_files = tree_child_cr(meta, s_files);
struct tree *files = tree_child_cr(aug->origin, s_files);
struct tree *load = tree_child_cr(meta, s_load);
api_entry(aug);
if (update_save_flags(aug) < 0)
goto error;
if (files == NULL || meta == NULL || load == NULL)
goto error;
aug_rm(aug, AUGEAS_EVENTS_SAVED);
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;
/* 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);
}
api_exit(aug);
return ret;
error:
api_exit(aug);
return -1;
}
static int print_one(FILE *out, const char *path, const char *value) {
int r;
r = fprintf(out, "%s", path);
if (r < 0)
return -1;
if (value != NULL) {
char *val = escape(value, -1, STR_ESCAPES);
r = fprintf(out, " = \"%s\"", val);
free(val);
if (r < 0)
return -1;
}
r = fputc('\n', out);
if (r == EOF)
return -1;
return 0;
}
/* PATH is the path up to TREE's parent */
static int print_rec(FILE *out, struct tree *start, const char *ppath,
int pr_hidden) {
int r;
char *path = NULL;
list_for_each(tree, start) {
if (TREE_HIDDEN(tree) && ! pr_hidden)
continue;
path = path_expand(tree, ppath);
if (path == NULL)
goto error;
r = print_one(out, path, tree->value);
if (r < 0)
goto error;
r = print_rec(out, tree->children, path, pr_hidden);
free(path);
path = NULL;
if (r < 0)
goto error;
}
return 0;
error:
free(path);
return -1;
}
static int print_tree(FILE *out, struct pathx *p, int pr_hidden) {
char *path = NULL;
struct tree *tree;
int r;
for (tree = pathx_first(p); tree != NULL; tree = pathx_next(p)) {
if (TREE_HIDDEN(tree) && ! pr_hidden)
continue;
path = path_of_tree(tree);
if (path == NULL)
goto error;
r = print_one(out, path, tree->value);
if (r < 0)
goto error;
r = print_rec(out, tree->children, path, pr_hidden);
if (r < 0)
goto error;
free(path);
path = NULL;
}
return 0;
error:
free(path);
return -1;
}
int dump_tree(FILE *out, struct tree *tree) {
struct pathx *p;
int result;
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;
}
int aug_text_store(augeas *aug, const char *lens, const char *node,
const char *path) {
struct pathx *p;
const char *src;
int result = -1, r;
api_entry(aug);
/* Validate PATH is syntactically correct */
p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true);
free_pathx(p);
ERR_BAIL(aug);
r = aug_get(aug, node, &src);
ERR_BAIL(aug);
ERR_THROW(r == 0, aug, AUG_ENOMATCH,
"Source node %s does not exist", node);
ERR_THROW(src == NULL, aug, AUG_ENOMATCH,
"Source node %s has a NULL value", node);
result = text_store(aug, lens, path, src);
error:
api_exit(aug);
return result;
}
int aug_text_retrieve(struct augeas *aug, const char *lens,
const char *node_in, const char *path,
const char *node_out) {
struct tree *tree = NULL;
const char *src;
char *out = NULL;
struct tree *tree_out;
int r;
api_entry(aug);
tree = tree_find(aug, path);
ERR_BAIL(aug);
r = aug_get(aug, node_in, &src);
ERR_BAIL(aug);
ERR_THROW(r == 0, aug, AUG_ENOMATCH,
"Source node %s does not exist", node_in);
ERR_THROW(src == NULL, aug, AUG_ENOMATCH,
"Source node %s has a NULL value", node_in);
r = text_retrieve(aug, lens, path, tree, src, &out);
if (r < 0)
goto error;
tree_out = tree_find_cr(aug, node_out);
ERR_BAIL(aug);
tree_store_value(tree_out, &out);
api_exit(aug);
return 0;
error:
free(out);
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);
struct tree *load = tree_child_cr(meta, s_load);
int r = 0, result = -1;
struct tree *xfm = NULL, *lns = NULL, *t = NULL;
const char *filter = NULL;
char *p;
int exists;
char *lensname = NULL, *xfmname = NULL;
api_entry(aug);
ERR_NOMEM(meta == NULL || load == NULL, aug);
ARG_CHECK(STREQ("", lens), aug, "aug_transform: LENS must not be empty");
ARG_CHECK(STREQ("", file), aug, "aug_transform: FILE must not be empty");
if ((p = strrchr(lens, '.'))) {
lensname = strdup(lens);
xfmname = strndup(lens, p - lens);
ERR_NOMEM(lensname == NULL || xfmname == NULL, aug);
} else {
r = xasprintf(&lensname, "%s.lns", lens);
xfmname = strdup(lens);
ERR_NOMEM(r < 0 || xfmname == NULL, aug);
}
xfm = tree_child_cr(load, xfmname);
ERR_NOMEM(xfm == NULL, aug);
lns = tree_child_cr(xfm, s_lens);
ERR_NOMEM(lns == NULL, aug);
tree_store_value(lns, &lensname);
exists = 0;
filter = excl ? s_excl : s_incl;
list_for_each(c, xfm->children) {
if (c->value != NULL && STREQ(c->value, file)
&& streqv(c->label, filter)) {
exists = 1;
break;
}
}
if (! exists) {
t = tree_append_s(xfm, filter, NULL);
ERR_NOMEM(t == NULL, aug);
r = tree_set_value(t, file);
ERR_NOMEM(r < 0, aug);
}
result = 0;
error:
free(lensname);
free(xfmname);
api_exit(aug);
return result;
}
int aug_escape_name(augeas *aug, const char *in, char **out) {
int result = -1;
api_entry(aug);
ARG_CHECK(in == NULL, aug, "aug_escape_name: IN must not be NULL");
ARG_CHECK(out == NULL, aug, "aug_escape_name: OUT must not be NULL");
result = pathx_escape_name(in, out);
ERR_NOMEM(result < 0, aug);
error:
api_exit(aug);
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 = -1;
api_entry(aug);
if (pathin == NULL || strlen(pathin) == 0) {
pathin = "/*";
}
p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), pathin, true);
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 result;
}
void aug_close(struct augeas *aug) {
if (aug == NULL)
return;
/* There's no point in bothering with api_entry/api_exit here */
free_tree(aug->origin);
unref(aug->modules, module);
if (aug->error->exn != NULL) {
aug->error->exn->ref = 0;
free_value(aug->error->exn);
aug->error->exn = NULL;
}
free((void *) aug->root);
free(aug->modpathz);
free_symtab(aug->symtab);
unref(aug->error->info, info);
free(aug->error->details);
free(aug->error);
free(aug);
}
int __aug_load_module_file(struct augeas *aug, const char *filename) {
api_entry(aug);
int r = load_module_file(aug, filename, NULL);
api_exit(aug);
return r;
}
int tree_equal(const struct tree *t1, const struct tree *t2) {
while (t1 != NULL && t2 != NULL) {
if (!streqv(t1->label, t2->label))
return 0;
if (!streqv(t1->value, t2->value))
return 0;
if (! tree_equal(t1->children, t2->children))
return 0;
t1 = t1->next;
t2 = t2->next;
}
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
*/
int aug_error(struct augeas *aug) {
return aug->error->code;
}
const char *aug_error_message(struct augeas *aug) {
aug_errcode_t errcode = aug->error->code;
if (errcode >= ARRAY_CARDINALITY(errcodes))
errcode = AUG_EINTERNAL;
return errcodes[errcode];
}
const char *aug_error_minor_message(struct augeas *aug) {
return aug->error->minor_details;
}
const char *aug_error_details(struct augeas *aug) {
return aug->error->details;
}
/*
* Local variables:
* indent-tabs-mode: nil
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 4
* End:
*/