RPM Package Manager, CVS Repository http://rpm5.org/cvs/ ____________________________________________________________________________
Server: rpm5.org Name: Jeff Johnson Root: /v/rpm/cvs Email: j...@rpm5.org Module: rpm Date: 14-Jun-2009 18:13:21 Branch: HEAD Handle: 2009061416132100 Added files: rpm/tools augtool.c augtool.h Modified files: rpm/tools .cvsignore Makefile.am Log: - augeas: swipe a copy of augtool.c/internal.h for reworking. Summary: Revision Changes Path 1.31 +1 -0 rpm/tools/.cvsignore 2.127 +5 -2 rpm/tools/Makefile.am 2.1 +717 -0 rpm/tools/augtool.c 2.1 +525 -0 rpm/tools/augtool.h ____________________________________________________________________________ patch -p0 <<'@@ .' Index: rpm/tools/.cvsignore ============================================================================ $ cvs diff -u -r1.30 -r1.31 .cvsignore --- rpm/tools/.cvsignore 13 May 2009 23:54:57 -0000 1.30 +++ rpm/tools/.cvsignore 14 Jun 2009 16:13:21 -0000 1.31 @@ -5,6 +5,7 @@ .libs *.gcda *.gcno +augtool convertdb1 db_tool debugedit @@ . patch -p0 <<'@@ .' Index: rpm/tools/Makefile.am ============================================================================ $ cvs diff -u -r2.126 -r2.127 Makefile.am --- rpm/tools/Makefile.am 3 Jun 2009 09:20:58 -0000 2.126 +++ rpm/tools/Makefile.am 14 Jun 2009 16:13:21 -0000 2.127 @@ -18,9 +18,9 @@ @WITH_PCRE_CPPFLAGS@ \ @WITH_XAR_CPPFLAGS@ -EXTRA_DIST = hashtab.h +EXTRA_DIST = augtool.h hashtab.h -EXTRA_PROGRAMS = rpmkey debugedit +EXTRA_PROGRAMS = augtool debugedit rpmkey RPMMISC_LDADD_COMMON = \ $(top_builddir)/misc/librpmmisc.la \ @@ -50,6 +50,9 @@ rpmcmp rpmdeps @WITH_KEYUTILS_RPMKEY@ @WITH_LIBELF_DEBUGEDIT@ dist_man_MANS = rpmgrep.1 +augtool_SOURCES = augtool.c +augtool_LDADD = $(RPM_LDADD_COMMON) -lreadline + debugedit_SOURCES = debugedit.c hashtab.c debugedit_LDADD = $(RPM_LDADD_COMMON) @@ . patch -p0 <<'@@ .' Index: rpm/tools/augtool.c ============================================================================ $ cvs diff -u -r0 -r2.1 augtool.c --- /dev/null 2009-06-14 18:11:00 +0200 +++ augtool.c 2009-06-14 18:13:21 +0200 @@ -0,0 +1,717 @@ +/* + * augtool.c: + * + * Copyright (C) 2007, 2008 Red Hat Inc. + * + * 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 <dlut...@redhat.com> + */ + +#include <config.h> +#include "augeas.h" +#include "augtool.h" + +#include <readline/readline.h> +#include <readline/history.h> +#include <argz.h> +#include <getopt.h> +#include <limits.h> +#include <ctype.h> + +struct command { + const char *name; + int minargs; + int maxargs; + int(*handler) (char *args[]); + const char *synopsis; + const char *help; +}; + +static const struct command const commands[]; + +static augeas *aug = NULL; +static const char *const progname = "augtool"; +static unsigned int flags = AUG_NONE; +const char *root = NULL; +char *loadpath = NULL; + + +static char *cleanstr(char *path, const char sep) { + if (path == NULL || strlen(path) == 0) + return path; + char *e = path + strlen(path) - 1; + while (e >= path && (*e == sep || isspace(*e))) + *e-- = '\0'; + return path; +} + +static char *cleanpath(char *path) { + return cleanstr(path, SEP); +} + +/* + * Dup PATH and split it into a directory and basename. The returned value + * points to the copy of PATH. Adding strlen(PATH)+1 to it gives the + * basename. + * + * If PATH can not be split, returns NULL + */ +ATTRIBUTE_UNUSED +static char *pathsplit(const char *path) { + char *ppath = strdup(path); + char *pend = strrchr(ppath, SEP); + + if (pend == NULL || pend == ppath) { + free(ppath); + return NULL; + } + *pend = '\0'; + return ppath; +} + +static char *ls_pattern(const char *path) { + char *q; + int r; + + if (path[strlen(path)-1] == SEP) + r = asprintf(&q, "%s*", path); + else + r = asprintf(&q, "%s/*", path); + if (r == -1) + return NULL; + return q; +} + +static int child_count(const char *path) { + char *q = ls_pattern(path); + int cnt; + + if (q == NULL) + return 0; + cnt = aug_match(aug, q, NULL); + free(q); + return cnt; +} + +static int cmd_ls(char *args[]) { + int cnt; + char *path = cleanpath(args[0]); + char **paths; + int i; + + path = ls_pattern(path); + if (path == NULL) + return -1; + cnt = aug_match(aug, path, &paths); + for (i=0; i < cnt; i++) { + const char *val; + const char *basnam = strrchr(paths[i], SEP); + int dir = child_count(paths[i]); + aug_get(aug, paths[i], &val); + basnam = (basnam == NULL) ? paths[i] : basnam + 1; + if (val == NULL) + val = "(none)"; + printf("%s%s= %s\n", basnam, dir ? "/ " : " ", val); + free(paths[i]); + } + if (cnt > 0) + free(paths); + free(path); + return 0; +} + +static int cmd_match(char *args[]) { + int cnt; + const char *pattern = cleanpath(args[0]); + char **matches; + int filter = (args[1] != NULL) && (strlen(args[1]) > 0); + int result = 0; + int i; + + cnt = aug_match(aug, pattern, &matches); + if (cnt < 0) { + printf(" (error matching %s)\n", pattern); + result = -1; + goto done; + } + if (cnt == 0) { + printf(" (no matches)\n"); + goto done; + } + + for (i=0; i < cnt; i++) { + const char *val; + aug_get(aug, matches[i], &val); + if (val == NULL) + val = "(none)"; + if (filter) { + if (STREQ(args[1], val)) + printf("%s\n", matches[i]); + } else { + printf("%s = %s\n", matches[i], val); + } + } + done: + for (i=0; i < cnt; i++) + free(matches[i]); + free(matches); + return result; +} + +static int cmd_rm(char *args[]) { + int cnt; + const char *path = cleanpath(args[0]); + printf("rm : %s", path); + cnt = aug_rm(aug, path); + printf(" %d\n", cnt); + return 0; +} + +static int cmd_mv(char *args[]) { + const char *src = cleanpath(args[0]); + const char *dst = cleanpath(args[1]); + int r; + + r = aug_mv(aug, src, dst); + if (r == -1) + printf("Failed\n"); + return r; +} + +static int cmd_set(char *args[]) { + const char *path = cleanpath(args[0]); + const char *val = args[1]; + int r; + + r = aug_set(aug, path, val); + if (r == -1) + printf ("Failed\n"); + return r; +} + +static int cmd_defvar(char *args[]) { + const char *name = args[0]; + const char *path = cleanpath(args[1]); + int r; + + r = aug_defvar(aug, name, path); + if (r == -1) + printf ("Failed\n"); + return r; +} + +static int cmd_defnode(char *args[]) { + const char *name = args[0]; + const char *path = cleanpath(args[1]); + const char *value = args[2]; + int r; + + /* Our simple minded line parser treats non-existant and empty values + * the same. We choose to take the empty string to mean NULL */ + if (value != NULL && strlen(value) == 0) + value = NULL; + r = aug_defnode(aug, name, path, value, NULL); + if (r == -1) + printf ("Failed\n"); + return r; +} + +static int cmd_clear(char *args[]) { + const char *path = cleanpath(args[0]); + int r; + + r = aug_set(aug, path, NULL); + if (r == -1) + printf ("Failed\n"); + return r; +} + +static int cmd_get(char *args[]) { + const char *path = cleanpath(args[0]); + const char *val; + + printf("%s", path); + if (aug_get(aug, path, &val) != 1) { + printf(" (o)\n"); + } else if (val == NULL) { + printf(" (none)\n"); + } else { + printf(" = %s\n", val); + } + return 0; +} + +static int cmd_print(char *args[]) { + return aug_print(aug, stdout, cleanpath(args[0])); +} + +static int cmd_save(ATTRIBUTE_UNUSED char *args[]) { + int r; + r = aug_save(aug); + if (r == -1) { + printf("Saving failed\n"); + } else { + r = aug_match(aug, "/augeas/events/saved", NULL); + if (r > 0) { + printf("Saved %d file(s)\n", r); + } else if (r < 0) { + printf("Error during match: %d\n", r); + } + } + return r; +} + +static int cmd_load(ATTRIBUTE_UNUSED char *args[]) { + int r; + r = aug_load(aug); + if (r == -1) { + printf("Loading failed\n"); + } else { + r = aug_match(aug, "/augeas/events/saved", NULL); + if (r > 0) { + printf("Saved %d file(s)\n", r); + } else if (r < 0) { + printf("Error during match: %d\n", r); + } + } + return r; +} + +static int cmd_ins(char *args[]) { + const char *label = args[0]; + const char *where = args[1]; + const char *path = cleanpath(args[2]); + int before; + int r; + + if (STREQ(where, "after")) + before = 0; + else if (STREQ(where, "before")) + before = 1; + else { + printf("The <WHERE> argument must be either 'before' or 'after'."); + return -1; + } + + r = aug_insert(aug, path, label, before); + if (r == -1) + printf ("Failed\n"); + return r; +} + +static int cmd_help(ATTRIBUTE_UNUSED char *args[]) { + const struct command *c; + + printf("Commands:\n\n"); + printf(" exit, quit\n Exit the program\n\n"); + for (c=commands; c->name != NULL; c++) { + printf(" %s\n %s\n\n", c->synopsis, c->help); + } + printf("\nEnvironment:\n\n"); + printf(" AUGEAS_ROOT\n the file system root, defaults to '/'\n\n"); + printf(" AUGEAS_LENS_LIB\n colon separated list of directories with lenses,\n\ + defaults to " AUGEAS_LENS_DIR "\n\n"); + return 0; +} + +static int chk_args(const struct command *cmd, int maxargs, char *args[]) { + int i; + + for (i=0; i < cmd->minargs; i++) { + if (args[i] == NULL || strlen(args[i]) == 0) { + fprintf(stderr, "Not enough arguments for %s\n", cmd->name); + return -1; + } + } + for (i = cmd->maxargs; i < maxargs; i++) { + if (args[i] != NULL && strlen(args[i]) > 0) { + fprintf(stderr, "Too many arguments for %s\n", cmd->name); + return -1; + } + } + return 0; +} + +static char *nexttoken(char **line) { + char *r, *s; + char quot = '\0'; + + s = *line; + + while (*s && isblank(*s)) s+= 1; + if (*s == '\'' || *s == '"') { + quot = *s; + s += 1; + } + r = s; + while (*s) { + if ((quot && *s == quot) || (!quot && isblank(*s))) + break; + s += 1; + } + if (*s) + *s++ = '\0'; + *line = s; + return r; +} + +static char *parseline(char *line, int maxargs, char *args[]) { + char *cmd; + int argc; + + MEMZERO(args, maxargs); + cmd = nexttoken(&line); + + for (argc=0; argc < maxargs; argc++) { + args[argc] = nexttoken(&line); + } + + if (*line) { + fprintf(stderr, "Too many arguments: '%s' not used\n", line); + } + return cmd; +} + +static const struct command const commands[] = { + { "ls", 1, 1, cmd_ls, "ls <PATH>", + "List the direct children of PATH" + }, + { "match", 1, 2, cmd_match, "match <PATH> [<VALUE>]", + "Find all paths that match the path expression PATH. If VALUE is given,\n" + " only the matching paths whose value equals VALUE are printed" + }, + { "rm", 1, 1, cmd_rm, "rm <PATH>", + "Delete PATH and all its children from the tree" + }, + { "mv", 2, 2, cmd_mv, "mv <SRC> <DST>", + "Move node SRC to DST. SRC must match exactly one node in the tree.\n" + " DST must either match exactly one node in the tree, or may not\n" + " exist yet. If DST exists already, it and all its descendants are\n" + " deleted. If DST does not exist yet, it and all its missing \n" + " ancestors are created." }, + { "set", 1, 2, cmd_set, "set <PATH> <VALUE>", + "Associate VALUE with PATH. If PATH is not in the tree yet,\n" + " it and all its ancestors will be created. These new tree entries\n" + " will appear last amongst their siblings" + }, + { "clear", 1, 1, cmd_clear, "clear <PATH>", + "Set the value for PATH to NULL. If PATH is not in the tree yet,\n" + " it and all its ancestors will be created. These new tree entries\n" + " will appear last amongst their siblings" + }, + { "get", 1, 1, cmd_get, "get <PATH>", + "Print the value associated with PATH" + }, + { "print", 0, 1, cmd_print, "print [<PATH>]", + "Print entries in the tree. If PATH is given, printing starts there,\n" + " otherwise the whole tree is printed" + }, + { "ins", 3, 3, cmd_ins, "ins <LABEL> <WHERE> <PATH>", + "Insert a new node with label LABEL right before or after PATH into\n" + " the tree. WHERE must be either 'before' or 'after'." + }, + { "save", 0, 0, cmd_save, "save", + "Save all pending changes to disk. For now, files are not overwritten.\n" + " Instead, new files with extension .augnew are created" + }, + { "load", 0, 0, cmd_load, "load", + "Load files accordig to the transforms in /augeas/load." + }, + { "defvar", 2, 2, cmd_defvar, "defvar <NAME> <EXPR>", + "Define the variable NAME to the result of evalutating EXPR. The\n" + " variable can be used in path expressions as $NAME. Note that EXPR\n" + " is evaluated when the variable is defined, not when it is used." + }, + { "defnode", 2, 3, cmd_defnode, "defnode <NAME> <EXPR> [<VALUE>]", + "Define the variable NAME to the result of evalutating EXPR, which\n" + " must be a nodeset. If no node matching EXPR exists yet, one\n" + " is created and NAME will refer to it. If VALUE is given, this\n" + " is the same as 'set EXPR VALUE'; if VALUE is not given, the\n" + " node is created as if with 'clear EXPR' would and NAME refers\n" + " to that node." + }, + { "help", 0, 0, cmd_help, "help", + "Print this help text" + }, + { NULL, -1, -1, NULL, NULL, NULL } +}; + +static int run_command(char *cmd, int maxargs, char **args) { + int r = 0; + const struct command *c; + + if (STREQ("exit", cmd) || STREQ("quit", cmd)) { + exit(EXIT_SUCCESS); + } + for (c = commands; c->name; c++) { + if (STREQ(cmd, c->name)) + break; + } + if (c->name) { + r = chk_args(c, maxargs, args); + if (r == 0) { + r = (*c->handler)(args); + } + } else { + fprintf(stderr, "Unknown command '%s'\n", cmd); + r = -1; + } + + return r; +} + +static char *readline_path_generator(const char *text, int state) { + static int current = 0; + static char **children = NULL; + static int nchildren = 0; + + if (state == 0) { + char *end = strrchr(text, SEP); + char *path; + if (end == NULL) { + if ((path = strdup("/*")) == NULL) + return NULL; + } else { + end += 1; + CALLOC(path, end - text + 2); + if (path == NULL) + return NULL; + strncpy(path, text, end - text); + strcat(path, "*"); + } + + for (;current < nchildren; current++) + free((void *) children[current]); + free((void *) children); + nchildren = aug_match(aug, path, &children); + current = 0; + free(path); + } + + while (current < nchildren) { + char *child = children[current]; + current += 1; + if (STREQLEN(child, text, strlen(text))) { + if (child_count(child) > 0) { + char *c = realloc(child, strlen(child)+2); + if (c == NULL) + return NULL; + child = c; + strcat(child, "/"); + } + rl_filename_completion_desired = 1; + rl_completion_append_character = '\0'; + return child; + } else { + free(child); + } + } + return NULL; +} + +static char *readline_command_generator(const char *text, int state) { + static int current = 0; + const char *name; + + if (state == 0) + current = 0; + + rl_completion_append_character = ' '; + while ((name = commands[current].name) != NULL) { + current += 1; + if (STREQLEN(text, name, strlen(text))) + return strdup(name); + } + return NULL; +} + +#define HAVE_RL_COMPLETION_MATCHES /* XXX no AutoFu yet */ +#ifndef HAVE_RL_COMPLETION_MATCHES +typedef char *rl_compentry_func_t(const char *, int); +static char **rl_completion_matches(ATTRIBUTE_UNUSED const char *text, + ATTRIBUTE_UNUSED rl_compentry_func_t *func) { + return NULL; +} +#endif + +static char **readline_completion(const char *text, int start, + ATTRIBUTE_UNUSED int end) { + if (start == 0) + return rl_completion_matches(text, readline_command_generator); + else + return rl_completion_matches(text, readline_path_generator); + + return NULL; +} + +static void readline_init(void) { + rl_readline_name = "augtool"; + rl_attempted_completion_function = readline_completion; + rl_completion_entry_function = readline_path_generator; +} + +__attribute__((noreturn)) +static void usage(void) { + fprintf(stderr, "Usage: %s [OPTIONS] [COMMAND]\n", progname); + fprintf(stderr, "Load the Augeas tree and modify it. If no COMMAND is given, run interactively\n"); + fprintf(stderr, "Run '%s help' to get a list of possible commands.\n", + progname); + fprintf(stderr, "\nOptions:\n\n"); + fprintf(stderr, " -c, --typecheck typecheck lenses\n"); + fprintf(stderr, " -b, --backup preserve originals of modified files with\n" + " extension '.augsave'\n"); + fprintf(stderr, " -n, --new save changes in files with extension '.augnew',\n" + " leave original unchanged\n"); + fprintf(stderr, " -r, --root ROOT use ROOT as the root of the filesystem\n"); + fprintf(stderr, " -I, --include DIR search DIR for modules; can be given mutiple times\n"); + fprintf(stderr, " --nostdinc do not search the builtin default directories for modules\n"); + fprintf(stderr, " --noload do not load any files into the tree on startup\n"); + fprintf(stderr, " --noautoload do not autoload modules from the search path\n"); + + exit(EXIT_FAILURE); +} + +static void parse_opts(int argc, char **argv) { + int opt; + size_t loadpathlen = 0; + enum { + VAL_NO_STDINC = CHAR_MAX + 1, + VAL_NO_LOAD = VAL_NO_STDINC + 1, + VAL_NO_AUTOLOAD = VAL_NO_LOAD + 1 + }; + struct option options[] = { + { "help", 0, 0, 'h' }, + { "typecheck", 0, 0, 'c' }, + { "backup", 0, 0, 'b' }, + { "new", 0, 0, 'n' }, + { "root", 1, 0, 'r' }, + { "include", 1, 0, 'I' }, + { "nostdinc", 0, 0, VAL_NO_STDINC }, + { "noload", 0, 0, VAL_NO_LOAD }, + { "noautoload", 0, 0, VAL_NO_AUTOLOAD }, + { 0, 0, 0, 0} + }; + int idx; + + while ((opt = getopt_long(argc, argv, "hnbcr:I:", options, &idx)) != -1) { + switch(opt) { + case 'c': + flags |= AUG_TYPE_CHECK; + break; + case 'b': + flags |= AUG_SAVE_BACKUP; + break; + case 'n': + flags |= AUG_SAVE_NEWFILE; + break; + case 'h': + usage(); + break; + case 'r': + root = optarg; + break; + case 'I': + argz_add(&loadpath, &loadpathlen, optarg); + break; + case VAL_NO_STDINC: + flags |= AUG_NO_STDINC; + break; + case VAL_NO_LOAD: + flags |= AUG_NO_LOAD; + break; + case VAL_NO_AUTOLOAD: + flags |= AUG_NO_MODL_AUTOLOAD; + break; + default: + usage(); + break; + } + } + argz_stringify(loadpath, loadpathlen, PATH_SEP_CHAR); +} + +static int main_loop(void) { + static const int maxargs = 3; + char *line = NULL; + char *cmd, *args[maxargs]; + int ret = 0; + size_t len = 0; + + while(1) { + char *dup_line; + + if (isatty(fileno(stdin))) { + line = readline("augtool> "); + } else if (getline(&line, &len, stdin) == -1) { + return ret; + } + cleanstr(line, '\n'); + if (line == NULL) { + printf("\n"); + return ret; + } + if (line[0] == '#') + continue; + + dup_line = strdup(line); + if (dup_line == NULL) { + fprintf(stderr, "Out of memory\n"); + return -1; + } + + cmd = parseline(dup_line, maxargs, args); + if (cmd != NULL && strlen(cmd) > 0) { + int r; + r = run_command(cmd, maxargs, args); + if (r < 0) + ret = -1; + if (isatty(fileno(stdin))) + add_history(line); + } + free(dup_line); + } +} + +int main(int argc, char **argv) { + int r; + + parse_opts(argc, argv); + + aug = aug_init(root, loadpath, flags); + if (aug == NULL) { + fprintf(stderr, "Failed to initialize Augeas\n"); + exit(EXIT_FAILURE); + } + readline_init(); + if (optind < argc) { + // Accept one command from the command line + r = run_command(argv[optind], argc - optind, argv+optind+1); + } else { + r = main_loop(); + } + + return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ @@ . patch -p0 <<'@@ .' Index: rpm/tools/augtool.h ============================================================================ $ cvs diff -u -r0 -r2.1 augtool.h --- /dev/null 2009-06-14 18:11:00 +0200 +++ augtool.h 2009-06-14 18:13:21 +0200 @@ -0,0 +1,525 @@ +/* + * internal.h: Useful definitions + * + * Copyright (C) 2007, 2008 Red Hat Inc. + * + * 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 <dlut...@redhat.com> + */ + +#ifndef H_AUGTOOL +#define H_AUGTOOL + +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <stdlib.h> +#include <stdbool.h> +#include <unistd.h> +#include <errno.h> +#include <assert.h> + +/* ===== list.h */ + +#define list_append(head, tail) \ + do { \ + if ((head) == NULL) { \ + head = tail; \ + break; \ + } \ + typeof(head) _p; \ + for (_p = (head); _p->next != NULL; _p = _p->next); \ + _p->next = (tail); \ + } while (0) + +#define list_for_each(iter, list) \ + for (typeof(list) (iter) = list; (iter) != NULL; (iter) = (iter)->next) + +#define list_remove(elt, list) \ + do { \ + typeof(elt) _e = (elt); \ + if (_e == (list)) { \ + (list) = _e->next; \ + } else { \ + typeof(_e) _p; \ + for (_p = (list); _p != NULL && _p->next != _e; _p = _p->next); \ + if (_p != NULL) \ + _p->next = _e->next; \ + } \ + _e->next = NULL; \ + } while(0) + +/* Insert NEW in list LIST before element AC. NEW->next must be null, + and ELT must really be on LIST, otherwise chaos will ensue +*/ +#define list_insert_before(new, elt, list) \ + do { \ + if ((list) == NULL) { \ + (list) = (new); \ + } else if ((elt) == (list)) { \ + (new)->next = (elt); \ + (list) = (new); \ + } else { \ + typeof(elt) _p; \ + for (_p = (list); _p != NULL && _p->next != (elt); _p = _p->next); \ + if (_p != NULL) { \ + (new)->next = (elt); \ + _p->next = (new); \ + } \ + } \ + } while(0) + +#define list_free(list) \ + while ((list) != NULL) { \ + typeof(list) _p = list; \ + (list) = (list)->next; \ + free((void *) _p); \ + } + +#define list_length(len, list) \ + do { \ + typeof(list) _p; \ + for (len=0, _p = (list); _p != NULL; len += 1, _p = _p->next); \ + } while(0) + +/* Make ELT the new head of LIST and set LIST to it */ +#define list_cons(list, elt) \ + do { \ + typeof(elt) _e = (elt); \ + _e->next = (list); \ + (list) = _e; \ + } while(0) + +#define list_reverse(list) \ + do { \ + typeof(list) _head = (list); \ + typeof(list) _prev = NULL; \ + while (_head != NULL) { \ + typeof(list) _next = _head->next; \ + _head->next = _prev; \ + _prev = _head; \ + _head = _next; \ + } \ + (list) = _prev; \ + } while (0) + +/* Append ELT to the end of LIST. TAIL must be NULL or a pointer to + the last element of LIST +*/ +#define list_tail_cons(list, tail, elt) \ + do { \ + if ((list) == NULL) { \ + (list) = (elt); \ + (tail) = (list); \ + } else { \ + if ((tail) == NULL) \ + for ((tail) = (list); (tail)->next != NULL; \ + (tail) = (tail)->next); \ + (tail)->next = (elt); \ + (tail) = (elt); \ + } \ + } while(0) + +/* ===== datadir.h */ +#define DATADIR "/usr/share" + +/* ===== internal.h */ + +/* + * Various parameters about env vars, special tree nodes etc. + */ + +/* Define: AUGEAS_LENS_DIR + * The default location for lens definitions */ +#define AUGEAS_LENS_DIR DATADIR "/augeas/lenses" + +/* The directory where we install lenses distribute with Augeas */ +#define AUGEAS_LENS_DIST_DIR DATADIR "/augeas/lenses/dist" + +/* Define: AUGEAS_ROOT_ENV + * The env var that points to the chroot holding files we may modify. + * Mostly useful for testing */ +#define AUGEAS_ROOT_ENV "AUGEAS_ROOT" + +/* Define: AUGEAS_FILES_TREE + * The root for actual file contents */ +#define AUGEAS_FILES_TREE "/files" + +/* Define: AUGEAS_META_TREE + * Augeas reports some information in this subtree */ +#define AUGEAS_META_TREE "/augeas" + +/* Define: AUGEAS_META_FILES + * Information about files */ +#define AUGEAS_META_FILES AUGEAS_META_TREE AUGEAS_FILES_TREE + +/* Define: AUGEAS_META_ROOT + * The root directory */ +#define AUGEAS_META_ROOT AUGEAS_META_TREE "/root" + +/* Define: AUGEAS_META_SAVE_MODE + * How we save files. One of 'backup', 'overwrite' or 'newfile' */ +#define AUGEAS_META_SAVE_MODE AUGEAS_META_TREE "/save" + +/* Define: AUGEAS_CLONE_IF_RENAME_FAILS + * Control what save does when renaming the temporary file to its final + * destination fails with EXDEV or EBUSY: when this tree node exists, copy + * the file contents. If it is not present, simply give up and report an + * error. */ +#define AUGEAS_COPY_IF_RENAME_FAILS \ + AUGEAS_META_SAVE_MODE "/copy_if_rename_fails" + +/* A hierarchy where we record certain 'events', e.g. which tree + * nodes actually gotsaved into files */ +#define AUGEAS_EVENTS AUGEAS_META_TREE "/events" + +#define AUGEAS_EVENTS_SAVED AUGEAS_EVENTS "/saved" + +/* Where to put information about parsing of path expressions */ +#define AUGEAS_META_PATHX AUGEAS_META_TREE "/pathx" + +/* Define: AUGEAS_LENS_ENV + * Name of env var that contains list of paths to search for additional + spec files */ +#define AUGEAS_LENS_ENV "AUGEAS_LENS_LIB" + +/* Define: MAX_ENV_SIZE + * Fairly arbitrary bound on the length of the path we + * accept from AUGEAS_SPEC_ENV */ +#define MAX_ENV_SIZE 4096 + +/* Define: PATH_SEP_CHAR + * Character separating paths in a list of paths */ +#define PATH_SEP_CHAR ':' + +/* Constants for setting the save mode via the augeas path at + * AUGEAS_META_SAVE_MODE */ +#define AUG_SAVE_BACKUP_TEXT "backup" +#define AUG_SAVE_NEWFILE_TEXT "newfile" +#define AUG_SAVE_NOOP_TEXT "noop" +#define AUG_SAVE_OVERWRITE_TEXT "overwrite" + +#ifdef __GNUC__ + +#ifndef __GNUC_PREREQ +#define __GNUC_PREREQ(maj,min) 0 +#endif + +/** + * ATTRIBUTE_UNUSED: + * + * Macro to flag conciously unused parameters to functions + */ +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED __attribute__((__unused__)) +#endif + +/** + * ATTRIBUTE_FORMAT + * + * Macro used to check printf/scanf-like functions, if compiling + * with gcc. + */ +#ifndef ATTRIBUTE_FORMAT +#define ATTRIBUTE_FORMAT(args...) __attribute__((__format__ (args))) +#endif + +#ifndef ATTRIBUTE_PURE +#define ATTRIBUTE_PURE __attribute__((pure)) +#endif + +#ifndef ATTRIBUTE_RETURN_CHECK +#if __GNUC_PREREQ (3, 4) +#define ATTRIBUTE_RETURN_CHECK __attribute__((__warn_unused_result__)) +#else +#define ATTRIBUTE_RETURN_CHECK +#endif +#endif + +#else +#define ATTRIBUTE_UNUSED +#define ATTRIBUTE_FORMAT(...) +#define ATTRIBUTE_PURE +#define ATTRIBUTE_RETURN_CHECK +#endif /* __GNUC__ */ + +#define ARRAY_CARDINALITY(array) (sizeof (array) / sizeof *(array)) + +/* String equality tests, suggested by Jim Meyering. */ +#define STREQ(a,b) (strcmp((a),(b)) == 0) +#define STRCASEEQ(a,b) (strcasecmp((a),(b)) == 0) +#define STRCASEEQLEN(a,b,n) (strncasecmp((a),(b),(n)) == 0) +#define STRNEQ(a,b) (strcmp((a),(b)) != 0) +#define STRCASENEQ(a,b) (strcasecmp((a),(b)) != 0) +#define STREQLEN(a,b,n) (strncmp((a),(b),(n)) == 0) +#define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0) + +ATTRIBUTE_PURE +static inline int streqv(const char *a, const char *b) { + if (a == NULL || b == NULL) + return a == b; + return STREQ(a,b); +} + +/* Path length and comparison */ + +#define SEP '/' + +/* Length of PATH without any trailing '/' */ +ATTRIBUTE_PURE +static inline int pathlen(const char *path) { + int len = strlen(path); + + if (len > 0 && path[len-1] == SEP) + len--; + + return len; +} + +/* Return 1 if P1 is a prefix of P2. P1 as a string must have length <= P2 */ +ATTRIBUTE_PURE +static inline int pathprefix(const char *p1, const char *p2) { + if (p1 == NULL || p2 == NULL) + return 0; + int l1 = pathlen(p1); + + return STREQLEN(p1, p2, l1) && (p2[l1] == '\0' || p2[l1] == SEP); +} + +static inline int pathendswith(const char *path, const char *basenam) { + const char *p = strrchr(path, SEP); + if (p == NULL) + return 0; + return streqv(p+1, basenam); +} + +/* Join NSEG path components (passed as const char *) into one PATH. + Allocate as needed. Return 0 on success, -1 on failure */ +int pathjoin(char **path, int nseg, ...); + +/* Call calloc to allocate an array of N instances of *VAR */ +#define CALLOC(Var,N) do { (Var) = calloc ((N), sizeof (*(Var))); } while (0) + +#define MEMZERO(ptr, n) memset((ptr), 0, (n) * sizeof(*(ptr))); + +/** + * TODO: + * + * macro to flag unimplemented blocks + */ +#define TODO \ + fprintf(stderr, "%s:%d Unimplemented block\n", \ + __FILE__, __LINE__); + +#define FIXME(msg, args ...) \ + do { \ + fprintf(stderr, "%s:%d Fixme: ", \ + __FILE__, __LINE__); \ + fprintf(stderr, msg, ## args); \ + fputc('\n', stderr); \ + } while(0) + +/* + * Internal data structures + */ + +// internal.c + +/* Function: escape + * Escape nonprintable characters within TEXT, similar to how it's done in + * C string literals. Caller must free the returned string. + */ +char *escape(const char *text, int cnt); + +/* Function: unescape */ +char *unescape(const char *s, int len); + +/* Function: print_chars */ +int print_chars(FILE *out, const char *text, int cnt); + +/* Function: print_pos + * Print a pretty representation of being at position POS within TEXT */ +void print_pos(FILE *out, const char *text, int pos); +char *format_pos(const char *text, int pos); + +/* Function: read_file + * Read the contents of file PATH and return them as one long string. The + * caller must free the result. Return NULL if any error occurs. + */ +char* read_file(const char *path); + +/* Get the error message for ERRNUM in a threadsafe way. Based on libvirt's + * virStrError + */ +const char *xstrerror(int errnum, char *buf, size_t len); + +/* Struct: augeas + * The data structure representing a connection to Augeas. */ +struct augeas { + struct tree *origin; /* Actual tree root is origin->children */ + const char *root; /* Filesystem root for all files */ + /* always ends with '/' */ + unsigned int flags; /* Flags passed to AUG_INIT */ + struct module *modules; /* Loaded modules */ + size_t nmodpath; + char *modpathz; /* The search path for modules as a + glibc argz vector */ + struct pathx_symtab *symtab; +}; + +/* Struct: tree + * An entry in the global config tree. The data structure allows associating + * values with interior nodes, but the API currently marks that as an error. + * + * To make dealing with parents uniform, even for the root, we create + * standalone trees with a fake root, called origin. That root is generally + * not referenced from anywhere. Standalone trees should be created with + * MAKE_TREE_ORIGIN. + * + * The DIRTY flag is used to track which parts of the tree might need to be + * saved. For any node that is marked dirty, all of its ancestors must be + * marked dirty, too. Instead of setting this flag directly, the function + * TREE_MARK_DIRTY in augeas.c should be used (and only functions in that + * file should have a need to mark nodes as dirty) + */ +struct tree { + struct tree *next; + struct tree *parent; /* Points to self for root */ + char *label; /* Last component of PATH */ + struct tree *children; /* List of children through NEXT */ + char *value; + int dirty; +}; + +/* The opaque structure used to represent path expressions. API's + * using STRUCT PATHX are declared farther below + */ +struct pathx; + +#define ROOT_P(t) ((t) != NULL && (t)->parent == (t)->parent->parent) + +/* Function: make_tree + * Allocate a new tree node with the given LABEL, VALUE, and CHILDREN, + * which are not copied. The new tree is marked as dirty + */ +struct tree *make_tree(char *label, char *value, + struct tree *parent, struct tree *children); + +/* Mark a tree as a standalone tree; this creates a fake parent for ROOT, + * so that even ROOT has a parent. A new node with only child ROOT is + * returned on success, and NULL on failure. + */ +struct tree *make_tree_origin(struct tree *root); + +int tree_replace(struct tree *origin, const char *path, struct tree *sub); +/* Make a new tree node and append it to parent's children */ +struct tree *tree_append(struct tree *parent, char *label, char *value); + +int tree_rm(struct pathx *p); +int tree_unlink(struct tree *tree); +struct tree *tree_set(struct pathx *p, const char *value); +int tree_insert(struct pathx *p, const char *label, int before); +int free_tree(struct tree *tree); +int dump_tree(FILE *out, struct tree *tree); +int tree_equal(const struct tree *t1, const struct tree *t2); +char *path_expand(struct tree *tree, const char *ppath); +char *path_of_tree(struct tree *tree); + +/* Struct: memstream + * Wrappers to simulate OPEN_MEMSTREAM where that's not available. The + * STREAM member is opened by INIT_MEMSTREAM and closed by + * CLOSE_MEMSTREAM. The BUF is allocated automatically, but can not be used + * until after CLOSE_MEMSTREAM has been called. It is the callers + * responsibility to free up BUF. + */ +struct memstream { + FILE *stream; + char *buf; + size_t size; +}; + +/* Function: init_memstream + * Initialize a memstream. On systems that have OPEN_MEMSTREAM, it is used + * to open MS->STREAM. On systems without OPEN_MEMSTREAM, MS->STREAM is + * backed by a temporary file. + * + * MS must be allocated in advance; INIT_MEMSTREAM initializes it. + */ +int init_memstream(struct memstream *ms); + +/* Function: close_memstream + * Close a memstream. After calling this, MS->STREAM can not be used + * anymore and a string representing whatever was written to it is + * available in MS->BUF. The caller must free MS->BUF. + * + * The caller must free the MEMSTREAM structure. + */ +int close_memstream(struct memstream *ms); + +/* + * Path expressions + */ + +typedef enum { + PATHX_NOERROR = 0, + PATHX_ENAME, + PATHX_ESTRING, + PATHX_ENUMBER, + PATHX_EDELIM, + PATHX_ENOEQUAL, + PATHX_ENOMEM, + PATHX_EPRED, + PATHX_EPAREN, + PATHX_ESLASH, + PATHX_EINTERNAL, + PATHX_ETYPE, + PATHX_ENOVAR, + PATHX_EEND, + PATHX_ENONODES +} pathx_errcode_t; + +struct pathx; +struct pathx_symtab; + +const char *pathx_error(struct pathx *pathx, const char **txt, int *pos); + +/* Parse the string PATH into a path expression PX that will be evaluated + * against the tree ORIGIN. + * + * If NEED_NODESET is true, the resulting path expression must evaluate toa + * nodeset, otherwise it can evaluate to a value of any type. + * + * Returns 0 on success, and -1 on error + */ +int pathx_parse(const struct tree *origin, const char *path, + bool need_nodeset, + struct pathx_symtab *symtab, + struct pathx **px); +struct tree *pathx_first(struct pathx *path); +struct tree *pathx_next(struct pathx *path); +int pathx_find_one(struct pathx *path, struct tree **match); +int pathx_expand_tree(struct pathx *path, struct tree **tree); +void free_pathx(struct pathx *path); + +int pathx_symtab_init(struct pathx_symtab **symtab); +int pathx_symtab_define(struct pathx_symtab **symtab, + const char *name, struct pathx *px); +int pathx_symtab_undefine(struct pathx_symtab **symtab, const char *name); +void pathx_symtab_remove_descendants(struct pathx *pathx, + const struct tree *tree); +void free_symtab(struct pathx_symtab *symtab); + +#endif /* H_AUGTOOL */ @@ . ______________________________________________________________________ RPM Package Manager http://rpm5.org CVS Sources Repository rpm-cvs@rpm5.org