/*
* 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
#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";
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] = {
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 {
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
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",
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;
}
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) {
}
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);
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,
}
done:
+ free_pathx(bx);
+ free_pathx(sx);
api_exit(aug);
return result;
error:
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 (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;
int aug_rm(struct augeas *aug, const char *path) {
struct pathx *p = NULL;
- int result;
+ int result = -1;
api_entry(aug);
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,
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;
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);
}
}
}
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);
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) {
/* 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);
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);
}
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");
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);
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) {
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
*/