From a44fd676d46f0ef7882c6c87fe1e294a7a6bbf4d Mon Sep 17 00:00:00 2001 From: "jinwang.an" Date: Fri, 6 Apr 2018 13:04:39 +0900 Subject: [PATCH] Imported Upstream version 1.10.1 Change-Id: If6e444a746ef6b37fe35cecb0d1dc9d67ea9682d --- .gitignore | 1 + AUTHORS | 3 +- NEWS | 34 ++ README.md | 2 + configure.ac | 6 +- doc/lenses.tex | 4 +- doc/naturaldocs/conf/c_api/Topics.txt | 2 +- doc/naturaldocs/conf/lenses/Topics.txt | 2 +- examples/Makefile.am | 6 +- examples/fadot.c | 70 +++- lenses/aptconf.aug | 2 +- lenses/cobblersettings.aug | 4 +- lenses/cron_user.aug | 2 +- lenses/dhclient.aug | 2 +- lenses/dpkg.aug | 4 +- lenses/grubenv.aug | 19 + lenses/httpd.aug | 1 + lenses/mdadm_conf.aug | 2 +- lenses/nsswitch.aug | 2 +- lenses/ntp.aug | 3 +- lenses/properties.aug | 2 +- lenses/quote.aug | 6 +- lenses/rancid.aug | 34 ++ lenses/redis.aug | 2 +- lenses/resolv.aug | 17 +- lenses/shellvars_list.aug | 2 +- lenses/sudoers.aug | 2 +- lenses/syslog.aug | 2 +- lenses/systemd.aug | 1 + lenses/tests/test_afs_cellalias.aug | 4 +- lenses/tests/test_grubenv.aug | 36 ++ lenses/tests/test_nsswitch.aug | 4 + lenses/tests/test_ntp.aug | 2 + lenses/tests/test_opendkim.aug | 4 +- lenses/tests/test_properties.aug | 2 + lenses/tests/test_rancid.aug | 30 ++ lenses/tests/test_redis.aug | 4 +- lenses/tests/test_resolv.aug | 2 +- lenses/tests/test_shellvars_list.aug | 2 +- lenses/tests/test_yaml.aug | 19 + lenses/yaml.aug | 6 +- man/Makefile.am | 2 +- man/augmatch.pod | 119 ++++++ man/augtool.1 | 2 +- man/augtool.pod | 2 +- src/Makefile.am | 5 +- src/augeas.c | 85 ++++ src/augeas.h | 75 +++- src/augeas_sym.version | 8 + src/augmatch.c | 433 +++++++++++++++++++++ src/augparse.c | 2 +- src/augrun.c | 35 ++ src/augtool.c | 2 +- src/builtin.c | 8 +- src/fa.c | 107 ++++- src/fa.h | 50 ++- src/fa_sym.version | 10 + src/get.c | 10 +- src/hash.h | 2 +- src/info.c | 7 + src/info.h | 5 +- src/internal.c | 25 +- src/internal.h | 10 +- src/lens.c | 38 +- src/lens.h | 7 +- src/pathx.c | 31 +- src/put.c | 4 +- src/syntax.c | 9 +- src/transform.c | 11 +- tests/Makefile.am | 3 + tests/fatest.c | 3 +- tests/root/etc/apt/apt.conf.d/01autoremove | 40 ++ tests/root/etc/apt/apt.conf.d/01autoremove-kernels | 15 + .../root/etc/apt/apt.conf.d/50unattended-upgrades | 92 +++++ tests/root/etc/apt/apt.conf.d/70debconf | 3 + .../etc/apt/apt.conf.d/90cloud-init-pipelining | 2 + tests/root/etc/httpd/conf.modules.d/00-base.conf | 67 ++++ tests/root/etc/httpd/conf.modules.d/00-dav.conf | 3 + tests/root/etc/httpd/conf.modules.d/00-lua.conf | 1 + tests/root/etc/httpd/conf.modules.d/00-mpm.conf | 23 ++ .../root/etc/httpd/conf.modules.d/00-optional.conf | 18 + tests/root/etc/httpd/conf.modules.d/00-proxy.conf | 17 + .../root/etc/httpd/conf.modules.d/00-systemd.conf | 2 + tests/root/etc/httpd/conf.modules.d/01-cgi.conf | 14 + tests/root/etc/httpd/conf.modules.d/10-h2.conf | 1 + .../etc/httpd/conf.modules.d/10-mod_dnssd.conf | 1 + .../root/etc/httpd/conf.modules.d/10-proxy_h2.conf | 1 + tests/root/etc/httpd/conf.modules.d/README | 9 + tests/root/etc/resolv.conf | 6 + tests/root/etc/samba/smb.conf | 4 +- tests/root/etc/squid/squid.conf | 8 +- tests/root/etc/sysconfig/atd | 2 +- tests/root/etc/sysconfig/autofs | 2 +- tests/root/etc/sysconfig/hsqldb | 2 +- tests/root/etc/sysconfig/nfs | 2 +- tests/root/etc/sysconfig/readonly-root | 2 +- tests/root/etc/sysconfig/rsyslog | 2 +- tests/run.tests | 2 +- tests/test-api.c | 37 +- tests/test-save-empty.sh | 2 +- 100 files changed, 1721 insertions(+), 123 deletions(-) create mode 100644 lenses/grubenv.aug create mode 100644 lenses/rancid.aug create mode 100644 lenses/tests/test_grubenv.aug create mode 100644 lenses/tests/test_rancid.aug create mode 100644 man/augmatch.pod create mode 100644 src/augmatch.c create mode 100644 tests/root/etc/apt/apt.conf.d/01autoremove create mode 100644 tests/root/etc/apt/apt.conf.d/01autoremove-kernels create mode 100644 tests/root/etc/apt/apt.conf.d/50unattended-upgrades create mode 100644 tests/root/etc/apt/apt.conf.d/70debconf create mode 100644 tests/root/etc/apt/apt.conf.d/90cloud-init-pipelining create mode 100644 tests/root/etc/httpd/conf.modules.d/00-base.conf create mode 100644 tests/root/etc/httpd/conf.modules.d/00-dav.conf create mode 100644 tests/root/etc/httpd/conf.modules.d/00-lua.conf create mode 100644 tests/root/etc/httpd/conf.modules.d/00-mpm.conf create mode 100644 tests/root/etc/httpd/conf.modules.d/00-optional.conf create mode 100644 tests/root/etc/httpd/conf.modules.d/00-proxy.conf create mode 100644 tests/root/etc/httpd/conf.modules.d/00-systemd.conf create mode 100644 tests/root/etc/httpd/conf.modules.d/01-cgi.conf create mode 100644 tests/root/etc/httpd/conf.modules.d/10-h2.conf create mode 100644 tests/root/etc/httpd/conf.modules.d/10-mod_dnssd.conf create mode 100644 tests/root/etc/httpd/conf.modules.d/10-proxy_h2.conf create mode 100644 tests/root/etc/httpd/conf.modules.d/README create mode 100644 tests/root/etc/resolv.conf diff --git a/.gitignore b/.gitignore index 44ca4a0..27eeae2 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ ylwrap src/datadir.h src/parser.[ch] src/lexer.[ch] +src/augmatch src/augtool src/augparse src/callgrind.* diff --git a/AUTHORS b/AUTHORS index 9262220..6b4cfd2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -145,4 +145,5 @@ Contributions by: Anton Baranov Josef Reidinger James Valleroy - Pavel Chechetin \ No newline at end of file + Pavel Chechetin + Pedro Valero Mejia diff --git a/NEWS b/NEWS index 8941400..a03936b 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,37 @@ +1.10.1 - 2018-01-29 + - API changes + * Fix a symbol versioning mistake in libfa that unnecessarily broke ABI + +1.10.0 - 2018-01-25 + DO NOT USE THIS RELEASE, USE 1.10.1 INSTEAD + + - General changes/additions + * New CLI utility 'augmatch' to print the tree for a file and select + some of its contents + * New command 'count' in augtool + * New function 'not(bool) -> bool' for path expressions + * The path expression 'label[. = "value"]' can now be written more + concisely as 'label["value"]' + - API changes + * libfa has now a function fa_json to export an FA as a JSON file, and + fa_state_* functions that make it possible to iterate over the FA's + states and transitions. (Pedro Valero Mejia) + * Add functions aug_ns_label, aug_ns_value, aug_ns_count, and + aug_ns_path to get the label (with index), the value, the number of + nodes, and the fully qualified path for nodes stored in a nodeset in + a variable efficiently + - Lens changes/additions + * Grubenv: new lens to process /boot/grub/grubenv (omgold) + * Httpd: also read files from /etc/httpd/conf.modules.d/*.conf + (Tomas Meszaros) (Issue #537) + * Nsswitch: allow comments at the end of a line (Philip Hahn) (Issue #517) + * Ntp: accept 'ntpsigndsocket' statement (Philip Hahn) (Issue #516) + * Properties: accept empty comments with DOS line endings (Issue #161) + * Rancid: new lens for RANCiD router databases (Matt Dainty) + * Resolv: accept empty comments with DOS line endings (Issue #161) + * Systemd: also process /etc/systemd/logind.conf (Pat Riehecky) + * YAML: process a document that is just a sequence (John Vandenberg) + 1.9.0 - 2017-10-06 - General changes/additions * several improvements to the error messages when transforming a tree diff --git a/README.md b/README.md index 8ea1c36..f7cce48 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Build Status](https://travis-ci.org/hercules-team/augeas.svg?branch=master)](https://travis-ci.org/hercules-team/augeas) + Introduction ------------ diff --git a/configure.ac b/configure.ac index 8079574..41c8eb1 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT(augeas, 1.9.0) +AC_INIT(augeas, 1.10.1) AC_CONFIG_SRCDIR([src/augeas.c]) AC_CONFIG_AUX_DIR([build/ac-aux]) AM_CONFIG_HEADER([config.h]) @@ -65,8 +65,8 @@ if test x"$enable_debug" = x"yes"; then fi dnl Version info in libtool's notation -AC_SUBST([LIBAUGEAS_VERSION_INFO], [23:0:23]) -AC_SUBST([LIBFA_VERSION_INFO], [5:5:4]) +AC_SUBST([LIBAUGEAS_VERSION_INFO], [24:0:24]) +AC_SUBST([LIBFA_VERSION_INFO], [6:1:5]) AC_GNU_SOURCE diff --git a/doc/lenses.tex b/doc/lenses.tex index c9fae11..d00493d 100644 --- a/doc/lenses.tex +++ b/doc/lenses.tex @@ -76,7 +76,7 @@ in a number of ways: that an entry in the tree corresponds to text that was deleted. \item the children of a tree node form a list of subtrees, i.e. are ordered. In addition, several subtrees in such a list may use the same - label. This makes it possible to accomodate concrete files where + label. This makes it possible to accommodate concrete files where entries that are logically connected are stored scattered between unrelated entries like the {\tt AcceptEnv} entries in {\tt sshd\_config}. @@ -253,7 +253,7 @@ Copies a word into a leaf. Gets the next value from a sequence as the key. We assume there's a generator $\mathtt{nextval}: \Sigma^* \to \mathbb{N}$ that returns -successive numbers on each invocation. \redigits is the regular expresion +successive numbers on each invocation. \redigits is the regular expression {\tt [0-9]+} that matches positive numbers. \infrule{w\in\Sigma^* \andalso L\in\Dictlangs \andalso n = \mathtt{nextval}(w)} diff --git a/doc/naturaldocs/conf/c_api/Topics.txt b/doc/naturaldocs/conf/c_api/Topics.txt index 905270f..3a45bb3 100644 --- a/doc/naturaldocs/conf/c_api/Topics.txt +++ b/doc/naturaldocs/conf/c_api/Topics.txt @@ -24,7 +24,7 @@ Format: 1.52 # Alter Topic Type: [name] # Creates a new topic type or alters one from the main file. Each type gets # its own index and behavior settings. Its name can have letters, numbers, -# spaces, and these charaters: - / . ' +# spaces, and these characters: - / . ' # # Plural: [name] # Sets the plural name of the topic type, if different. diff --git a/doc/naturaldocs/conf/lenses/Topics.txt b/doc/naturaldocs/conf/lenses/Topics.txt index 7054ff0..c0657ec 100644 --- a/doc/naturaldocs/conf/lenses/Topics.txt +++ b/doc/naturaldocs/conf/lenses/Topics.txt @@ -17,7 +17,7 @@ Ignore Keywords: # Alter Topic Type: [name] # Creates a new topic type or alters one from the main file. Each type gets # its own index and behavior settings. Its name can have letters, numbers, -# spaces, and these charaters: - / . ' +# spaces, and these characters: - / . ' # # Plural: [name] # Sets the plural name of the topic type, if different. diff --git a/examples/Makefile.am b/examples/Makefile.am index cd59dd4..928eb51 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -5,12 +5,12 @@ GNULIB_CFLAGS= -I $(top_srcdir)/gnulib/lib AM_CFLAGS = @AUGEAS_CFLAGS@ @WARN_CFLAGS@ @LIBXML_CFLAGS@ $(GNULIB_CFLAGS) \ -I $(top_srcdir)/src -bin_PROGRAMS = fadot dump +bin_PROGRAMS = fadot +noinst_PROGRAMS = dump fadot_SOURCES = fadot.c fadot_LDADD = $(top_builddir)/src/libfa.la $(GNULIB) dump_sources = dump.c -dump_LDFLAGS = -static -dump_LDADD = $(top_builddir)/src/libaugeas.la $(top_builddir)/src/libfa.la +dump_LDADD = $(top_builddir)/src/libaugeas.la $(top_builddir)/src/libfa.la \ $(GNULIB) diff --git a/examples/fadot.c b/examples/fadot.c index f587412..666126c 100644 --- a/examples/fadot.c +++ b/examples/fadot.c @@ -31,9 +31,12 @@ #include #include #include +#include #include "fa.h" +#define UCHAR_NUM (UCHAR_MAX+1) + const char *progname; static const char *const escape_chars = "\"\a\b\t\n\v\f\r\\"; @@ -45,8 +48,8 @@ static void usage(void) { fprintf(stderr, "\nCompile REGEXP and apply operation to them. By default, just print\n"); fprintf(stderr, "the minimized regexp.\n"); fprintf(stderr, "\nOptions:\n\n"); - fprintf(stderr, " -o OPERATION one of : show concat union intersect\n"); - fprintf(stderr, " complement minus example\n"); + fprintf(stderr, " -o OPERATION one of : show concat union intersect json\n"); + fprintf(stderr, " complement minus example print\n"); fprintf(stderr, " -f DOT_FILE Path of output .dot file\n"); fprintf(stderr, " -n do not minimize resulting finite automaton\n"); @@ -196,6 +199,69 @@ int main (int argc, char **argv) { fa_example(fa_result, &word, &word_len); printf("Example word = %s\n", word); + } else if (!strcmp(operation, "json")) { + if (nb_regexp != 1) { + fprintf(stderr,"Please specify one regexp for operation example"); + return 1; + } + + fa_compile(argv[optind], strlen(argv[optind]), &fa_result); + + if (reduce) { + fa_minimize(fa_result); + } + + if (file_output != NULL) { + if ((fd = fopen(file_output, "w")) == NULL) { + fprintf(stderr, "Error while opening file %s \n", file_output); + return 1; + } + + fa_json(fd, fa_result); + fclose(fd); + } else { + fa_json(stdout, fa_result); + } + + return 0; + + } else if (!strcmp(operation, "print")) { + if (nb_regexp != 1) { + fprintf(stderr,"Please specify one regexp for operation example"); + return 1; + } + + fa_compile(argv[optind], strlen(argv[optind]), &fa_result); + + if (reduce) { + fa_minimize(fa_result); + } + + struct state *st, *st2; + uint32_t num_trans, i; + unsigned char begin, end; + + st = fa_state_initial(fa_result); + + printf("%s. Initial state: %p", fa_is_deterministic(fa_result) ? "DFA" : "NFA", fa_result); + + while (st != NULL) { + num_trans = fa_state_num_trans(st); + printf("\nFrom state %p (final = %s):\n", st, fa_state_is_accepting(st) == 1 ? "true" : "false"); + for (i = 0; i < num_trans; i++) { + if (fa_state_trans(st, i, &st2, &begin, &end) < 0) { + printf("Some error occur. \n"); + } + if (begin == end) + printf(" to: %p, label: %d\n", st2, begin); + else + printf(" to: %p, label: %d-%d\n", st2, begin, end); + } + st = fa_state_next(st); + } + + return 0; + } if (reduce) { diff --git a/lenses/aptconf.aug b/lenses/aptconf.aug index 99d5730..2131540 100644 --- a/lenses/aptconf.aug +++ b/lenses/aptconf.aug @@ -73,7 +73,7 @@ let name_re_colons = /[A-Za-z][A-Za-z:-]*/ since apt.conf allows for both APT { Clean-Installed { "true" } } and APT::Clean-Installed "true"; - but we're chosing to map them the same way + but we're choosing to map them the same way The recursive lens doesn't seem to care and defaults to the first diff --git a/lenses/cobblersettings.aug b/lenses/cobblersettings.aug index 2c74f94..6017000 100644 --- a/lenses/cobblersettings.aug +++ b/lenses/cobblersettings.aug @@ -2,7 +2,7 @@ Parse the /etc/cobbler/settings file which is in YAML 1.0 format. - The lens can handle the following contructs + The lens can handle the following constructs * key: value * key: "value" * key: 'value' @@ -23,7 +23,7 @@ module CobblerSettings = autoload xfm let kw = /[a-zA-Z0-9_]+/ - (* TODO Would be better if this stripped off the "" and '' chracters *) + (* TODO Would be better if this stripped off the "" and '' characters *) let kv = /([^]['", \t\n#:@-]+|"[^"\n]*"|'[^'\n]*')/ let lbr = del /\[/ "[" diff --git a/lenses/cron_user.aug b/lenses/cron_user.aug index eec7f1b..afbfc88 100644 --- a/lenses/cron_user.aug +++ b/lenses/cron_user.aug @@ -6,7 +6,7 @@ Author: David Lutterkort About: Reference This lens parses the user crontab files in /var/spool/cron. It produces - almost the same tree as teh Cron.lns, except that it never contains a user + almost the same tree as the Cron.lns, except that it never contains a user field. About: License diff --git a/lenses/dhclient.aug b/lenses/dhclient.aug index 722e7f9..33e68d4 100644 --- a/lenses/dhclient.aug +++ b/lenses/dhclient.aug @@ -6,7 +6,7 @@ that statements end with a new line, while the reference syntax allows new statements to be started right after the trailing ";" of the previous statement. This should not be a problem in real-life - configuration files as statements get usually splitted across several + configuration files as statements get usually split across several lines, rather than merged in a single one. *) diff --git a/lenses/dpkg.aug b/lenses/dpkg.aug index 41f252a..15cbe04 100644 --- a/lenses/dpkg.aug +++ b/lenses/dpkg.aug @@ -5,7 +5,7 @@ Module: Dpkg Author: Robin Lee Powell About: License - This file, and the attendant test_dpgk.aug, are explicitely + This file, and the attendant test_dpgk.aug, are explicitly placed in the public domain. About: Description @@ -77,7 +77,7 @@ module Dpkg = by EOL. The actual file specification doesn't require EOL, but the - likelyhood of the file not having one is pretty slim, and + likelihood of the file not having one is pretty slim, and this way things we add have EOL. *) diff --git a/lenses/grubenv.aug b/lenses/grubenv.aug new file mode 100644 index 0000000..5a3878a --- /dev/null +++ b/lenses/grubenv.aug @@ -0,0 +1,19 @@ +(* Parsing /boot/grub/grubenv *) + +module GrubEnv = + autoload xfm + + let eol = Util.del_str "\n" + + let comment = Util.comment + let eq = Util.del_str "=" + let value = /[^\\\n]*(\\\\(\\\\|\n)[^\\\n]*)*/ + + let word = /[A-Za-z_][A-Za-z0-9_]*/ + let record = [ seq "target" . + [ label "name" . store word ] . eq . + [ label "value" . store value ] . eol ] + + let lns = ( comment | record ) * + + let xfm = transform lns (incl "/boot/grub/grubenv" . incl "/boot/grub2/grubenv") diff --git a/lenses/httpd.aug b/lenses/httpd.aug index 61141e5..ff93a61 100644 --- a/lenses/httpd.aug +++ b/lenses/httpd.aug @@ -200,6 +200,7 @@ let filter = (incl "/etc/apache2/apache2.conf") . (incl "/etc/httpd/conf.d/*.conf") . (incl "/etc/httpd/httpd.conf") . (incl "/etc/httpd/conf/httpd.conf") . + (incl "/etc/httpd/conf.modules.d/*.conf") . Util.stdexcl let xfm = transform lns filter diff --git a/lenses/mdadm_conf.aug b/lenses/mdadm_conf.aug index d831f1a..3bf5887 100644 --- a/lenses/mdadm_conf.aug +++ b/lenses/mdadm_conf.aug @@ -149,7 +149,7 @@ let array = [ del array_re "ARRAY" . label "array" . arr_options . eol ] let mailaddr_re = /mai(l(a(d(dr?)?)?)?)?/i -(* We intentially allow multiple mailaddr values here, even though this is +(* We intentionally allow multiple mailaddr values here, even though this is invalid and would produce a warning. This is better than not parsing the file. *) let mailaddr = simplevalue mailaddr_re "mailaddr" "MAILADDR" diff --git a/lenses/nsswitch.aug b/lenses/nsswitch.aug index 2d65345..5f9e717 100644 --- a/lenses/nsswitch.aug +++ b/lenses/nsswitch.aug @@ -69,7 +69,7 @@ let database = . (Build.opt_list (service|reaction) Sep.space) - . Util.eol ] + . Util.comment_or_eol ] (* View: lns *) let lns = ( empty | comment | database )* diff --git a/lenses/ntp.aug b/lenses/ntp.aug index c11d5c0..5a52119 100644 --- a/lenses/ntp.aug +++ b/lenses/ntp.aug @@ -61,7 +61,8 @@ module Ntp = kv "broadcastdelay" Rx.decimal | flags | simple_setting /driftfile|leapfile|logfile|includefile/ - | simple_setting "statsdir" + | simple_setting "statsdir" + | simple_setting "ntpsigndsocket" (* Misc commands, see miscopt.html in ntp docs *) diff --git a/lenses/properties.aug b/lenses/properties.aug index 82828a5..abc04b2 100644 --- a/lenses/properties.aug +++ b/lenses/properties.aug @@ -12,7 +12,7 @@ module Properties = (* Define some basic primitives *) - let empty = Util.empty + let empty = Util.empty_generic_dos /[ \t]*[#!]?[ \t]*/ let eol = Util.doseol let hard_eol = del /\r?\n/ "\n" let sepch = del /([ \t]*(=|:)|[ \t])/ "=" diff --git a/lenses/quote.aug b/lenses/quote.aug index bed9c87..32e56ac 100644 --- a/lenses/quote.aug +++ b/lenses/quote.aug @@ -173,7 +173,7 @@ let double = let double_opt_re = /[^\n\t "]([^\n"]*[^\n\t "])?/ (* View: double_opt - An optionaly double-quoted value + An optionally double-quoted value Double quotes are not allowed in value Value cannot begin or end with spaces *) let double_opt = @@ -192,7 +192,7 @@ let single = let single_opt_re = /[^\n\t ']([^\n']*[^\n\t '])?/ (* View: single_opt - An optionaly single-quoted value + An optionally single-quoted value Single quotes are not allowed in value Value cannot begin or end with spaces *) let single_opt = @@ -211,7 +211,7 @@ let any = let any_opt_re = /[^\n\t "']([^\n"']*[^\n\t "'])?/ (* View: any_opt - An optionaly quoted value + An optionally quoted value Double or single quotes are not allowed in value Value cannot begin or end with spaces *) let any_opt = diff --git a/lenses/rancid.aug b/lenses/rancid.aug new file mode 100644 index 0000000..a582b32 --- /dev/null +++ b/lenses/rancid.aug @@ -0,0 +1,34 @@ +(* +Module: Rancid + Parses RANCiD router database + +Author: Matt Dainty + +About: Reference + - man 5 router.db + +Each line represents a record consisting of a number of ';'-separated fields +the first of which is the IP/Hostname of the device, followed by the type, its +state and optionally a comment. + +*) + +module Rancid = + autoload xfm + + let sep = Util.del_str ";" + let field = /[^;#\n]+/ + let comment = [ label "comment" . store /[^;#\n]*/ ] + let eol = Util.del_str "\n" + let record = [ label "device" . store field . sep . [ label "type" . store field ] . sep . [ label "state" . store field ] . ( sep . comment )? . eol ] + + let lns = ( Util.empty | Util.comment_generic /#[ \t]*/ "# " | record )* + + let filter = incl "/var/rancid/*/router.db" + . Util.stdexcl + + let xfm = transform lns filter + +(* Local Variables: *) +(* mode: caml *) +(* End: *) diff --git a/lenses/redis.aug b/lenses/redis.aug index 3ea3551..28e99f3 100644 --- a/lenses/redis.aug +++ b/lenses/redis.aug @@ -99,7 +99,7 @@ let soft_limit = [ label "soft_limit" . Quote.do_quote_opt_nil (store Rx.word) ] let soft_seconds = [ label "soft_seconds" . Quote.do_quote_opt_nil (store Rx.integer) ] (* View: client_output_buffer_limit_entry Entries identified by the "client-output-buffer-limit" keyword can be found -more than once. They have four mandatory paramters, of which the first is a +more than once. They have four mandatory parameters, of which the first is a string, the last one is an integer and the others are either integers or words, although redis is very liberal and takes "4242yadayadabytes" as a valid limit. The same rules as standard_entry apply for quoting, comments and whitespaces. diff --git a/lenses/resolv.aug b/lenses/resolv.aug index 208a249..ea98071 100644 --- a/lenses/resolv.aug +++ b/lenses/resolv.aug @@ -30,7 +30,7 @@ let comment = Util.comment_generic /[ \t]*[;#][ \t]*/ "# " let comment_eol = Util.comment_generic /[ \t]*[;#][ \t]*/ " # " (* View: empty *) -let empty = Util.empty +let empty = Util.empty_generic_dos /[ \t]*[#;]?[ \t]*/ (************************************************************************ @@ -41,8 +41,8 @@ let empty = Util.empty A network mask for IP addresses *) let netmask = [ label "netmask" . Util.del_str "/" . store Rx.ip ] -(* View: ipaddr -An IP address or range with an optional mask *) +(* View: ipaddr +An IP address or range with an optional mask *) let ipaddr = [label "ipaddr" . store Rx.ip . netmask?] @@ -58,7 +58,7 @@ let domain = Build.key_value_line_comment (* View: search *) let search = Build.key_value_line_comment "search" Sep.space - (Build.opt_list + (Build.opt_list [label "domain" . store Rx.word] Sep.space) comment_eol @@ -68,7 +68,7 @@ let sortlist = Build.key_value_line_comment "sortlist" Sep.space (Build.opt_list ipaddr - Sep.space) + Sep.space) comment_eol (* View: lookup *) @@ -97,14 +97,14 @@ let family = (* View: ip6_dotint ip6-dotint option, which supports negation *) -let ip6_dotint = +let ip6_dotint = let negate = [ del "no-" "no-" . label "negate" ] in [ negate? . key "ip6-dotint" ] -(* View: options +(* View: options Options values *) let options = - let options_entry = Build.key_value ("ndots"|"timeout"|"attempts") + let options_entry = Build.key_value ("ndots"|"timeout"|"attempts") (Util.del_str ":") (store Rx.integer) | Build.flag ("debug"|"rotate"|"no-check-names" |"inet6"|"ip6-bytestring"|"edns0" @@ -134,4 +134,3 @@ let lns = ( empty | comment | entry )* let filter = (incl "/etc/resolv.conf") let xfm = transform lns filter - diff --git a/lenses/shellvars_list.aug b/lenses/shellvars_list.aug index f2c0c19..105939c 100644 --- a/lenses/shellvars_list.aug +++ b/lenses/shellvars_list.aug @@ -32,7 +32,7 @@ module Shellvars_list = let squote_arr = [ label "quote" . store /'/ ] . (list sqword space_or_nl)? . del /'/ "'" - (* similarly handle double qouted lists *) + (* similarly handle double quoted lists *) let dquote_arr = [ label "quote" . store /"/ ] . (list dqword space_or_cl)? . del /"/ "\"" diff --git a/lenses/sudoers.aug b/lenses/sudoers.aug index 534d6ae..11920d7 100644 --- a/lenses/sudoers.aug +++ b/lenses/sudoers.aug @@ -29,7 +29,7 @@ About: Lens Usage * Set first Defaults to apply to the "LOCALNET" network alias > set /files/etc/sudoers/Defaults[1]/type "@LOCALNET" - * List all user specifications applying explicitely to the "admin" Unix group + * List all user specifications applying explicitly to the "admin" Unix group > match /files/etc/sudoers/spec/user "%admin" * Remove the full 3rd user specification > rm /files/etc/sudoers/spec[3] diff --git a/lenses/syslog.aug b/lenses/syslog.aug index d961281..5d9023b 100644 --- a/lenses/syslog.aug +++ b/lenses/syslog.aug @@ -101,7 +101,7 @@ module Syslog = let word = /[A-Za-z0-9][A-Za-z0-9_.-]*/ (* Variable: comparison - a comparison is an optional ! with optionaly some of [<=>] + a comparison is an optional ! with optionally some of [<=>] *) let comparison = /(!|[<=>]+|![<=>]+)/ diff --git a/lenses/systemd.aug b/lenses/systemd.aug index ade70d5..f7858e9 100644 --- a/lenses/systemd.aug +++ b/lenses/systemd.aug @@ -170,6 +170,7 @@ let filter = incl "/lib/systemd/system/*" . incl "/lib/systemd/system/*/*" . incl "/etc/systemd/system/*" . incl "/etc/systemd/system/*/*" + . incl "/etc/systemd/logind.conf" . incl "/etc/sysconfig/*.systemd" . Util.stdexcl diff --git a/lenses/tests/test_afs_cellalias.aug b/lenses/tests/test_afs_cellalias.aug index eee3d43..891a346 100644 --- a/lenses/tests/test_afs_cellalias.aug +++ b/lenses/tests/test_afs_cellalias.aug @@ -12,7 +12,7 @@ let conf = "# Cell Aliases are meant to act like symlinks like '/afs/openafs.org # These aliases are set with 'fs newalias', or read from # /usr/vice/etc/CellAlias # -# Formating for /usr/vice/etc/CellAlias is in the form +# Formatting for /usr/vice/etc/CellAlias is in the form # # an example would be # fnal.gov/common/usr usr @@ -28,7 +28,7 @@ test AFS_cellalias.lns get conf = { "#comment" = "Cell Aliases are meant to act { "#comment" = "These aliases are set with 'fs newalias', or read from" } { "#comment" = "/usr/vice/etc/CellAlias" } { } - { "#comment" = "Formating for /usr/vice/etc/CellAlias is in the form" } + { "#comment" = "Formatting for /usr/vice/etc/CellAlias is in the form" } { "#comment" = " " } { "#comment" = "an example would be" } { "#comment" = "fnal.gov/common/usr usr" } diff --git a/lenses/tests/test_grubenv.aug b/lenses/tests/test_grubenv.aug new file mode 100644 index 0000000..2976c16 --- /dev/null +++ b/lenses/tests/test_grubenv.aug @@ -0,0 +1,36 @@ +module Test_grubenv = + + let conf = "# GRUB Environment Block +serial=1 +serial_speed=115200 +dummy1=abc\\\\xyz +dummy2=abc\\ +xyz +dummy3=abc\\\\uvw\\ +xyz +######################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################## +" + + test GrubEnv.lns get conf = + { "#comment" = "GRUB Environment Block" } + { "1" + { "name" = "serial" } + { "value" = "1" } + } + { "2" + { "name" = "serial_speed" } + { "value" = "115200" } + } + { "3" + { "name" = "dummy1" } + { "value" = "abc\\\\xyz" } + } + { "4" + { "name" = "dummy2" } + { "value" = "abc\\\nxyz" } + } + { "5" + { "name" = "dummy3" } + { "value" = "abc\\\\uvw\\\nxyz" } + } + { "#comment" = "#######################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################" } diff --git a/lenses/tests/test_nsswitch.aug b/lenses/tests/test_nsswitch.aug index c9c5747..b467cf7 100644 --- a/lenses/tests/test_nsswitch.aug +++ b/lenses/tests/test_nsswitch.aug @@ -8,6 +8,7 @@ networks: nis [!UNAVAIL=return success=continue] files protocols: db files netgroup: nis bootparams: nisplus [NOTFOUND=return] files +aliases: files # uses by mail sudoers: files ldap " @@ -44,6 +45,9 @@ test Nsswitch.lns get conf = { "status" = "NOTFOUND" { "action" = "return" } } } { "service" = "files" } } + { "database" = "aliases" + { "service" = "files" } + { "#comment" = "uses by mail" } } { "database" = "sudoers" { "service" = "files" } { "service" = "ldap" } } diff --git a/lenses/tests/test_ntp.aug b/lenses/tests/test_ntp.aug index f6aaa2d..2eea1d8 100644 --- a/lenses/tests/test_ntp.aug +++ b/lenses/tests/test_ntp.aug @@ -19,6 +19,7 @@ restrict 127.0.0.1 logfile /var/log/ntpd statsdir /var/log/ntpstats/ +ntpsigndsocket /var/lib/samba/ntp_signd statistics loopstats peerstats clockstats filegen loopstats file loopstats type day enable link @@ -53,6 +54,7 @@ interface listen 127.0.0.1 {} { "logfile" = "/var/log/ntpd" } { "statsdir" = "/var/log/ntpstats/" } + { "ntpsigndsocket" = "/var/lib/samba/ntp_signd" } {} { "statistics" { "loopstats" } diff --git a/lenses/tests/test_opendkim.aug b/lenses/tests/test_opendkim.aug index f0f81af..d164665 100644 --- a/lenses/tests/test_opendkim.aug +++ b/lenses/tests/test_opendkim.aug @@ -95,7 +95,7 @@ UMask 002 # Always oversign From (sign using actual From and a null From to prevent # malicious signatures header fields (From and/or others) between the signer -# and the verifier. From is oversigned by default in the Debian pacakge +# and the verifier. From is oversigned by default in the Debian package # because it is often the identity key used by reputation systems and thus # somewhat security sensitive. OversignHeaders From @@ -130,7 +130,7 @@ OversignHeaders From { } { "#comment" = "Always oversign From (sign using actual From and a null From to prevent" } { "#comment" = "malicious signatures header fields (From and/or others) between the signer" } - { "#comment" = "and the verifier. From is oversigned by default in the Debian pacakge" } + { "#comment" = "and the verifier. From is oversigned by default in the Debian package" } { "#comment" = "because it is often the identity key used by reputation systems and thus" } { "#comment" = "somewhat security sensitive." } { "OversignHeaders" = "From" } diff --git a/lenses/tests/test_properties.aug b/lenses/tests/test_properties.aug index 556f12e..d6e7323 100644 --- a/lenses/tests/test_properties.aug +++ b/lenses/tests/test_properties.aug @@ -165,3 +165,5 @@ test lns get "# comment\r\na.b=val\r\nx=\r\n" = { "#comment" = "comment" } { "a.b" = "val" } { "x" } + +test lns get "# \r\n! \r\n" = { } { } diff --git a/lenses/tests/test_rancid.aug b/lenses/tests/test_rancid.aug new file mode 100644 index 0000000..884c974 --- /dev/null +++ b/lenses/tests/test_rancid.aug @@ -0,0 +1,30 @@ +module Test_rancid = + +(* Examples from router.db(5) *) +let rancid = "dial1.paris;cisco;up +core1.paris;cisco;down;in testing until 5/5/2001. +core2.paris;cisco;ticketed;Ticket 6054234, 5/3/2001 +border1.paris;juniper;up; +" + +test Rancid.lns get rancid = + { "device" = "dial1.paris" + { "type" = "cisco" } + { "state" = "up" } + } + { "device" = "core1.paris" + { "type" = "cisco" } + { "state" = "down" } + { "comment" = "in testing until 5/5/2001." } + } + { "device" = "core2.paris" + { "type" = "cisco" } + { "state" = "ticketed" } + { "comment" = "Ticket 6054234, 5/3/2001" } + } + { "device" = "border1.paris" + { "type" = "juniper" } + { "state" = "up" } + { "comment" = "" } + } + diff --git a/lenses/tests/test_redis.aug b/lenses/tests/test_redis.aug index ff0e4ed..12cc25c 100644 --- a/lenses/tests/test_redis.aug +++ b/lenses/tests/test_redis.aug @@ -76,7 +76,7 @@ test Redis.lns get extra_whitespace_comment = { "#comment" = "another comment" } let redis_conf = "# Redis configuration file example -# Note on units: when memory size is needed, it is possible to specifiy +# Note on units: when memory size is needed, it is possible to specify # it in the usual form of 1k 5GB 4M and so forth: # # 1k => 1000 bytes @@ -123,7 +123,7 @@ include /path/to/other.conf test Redis.lns get redis_conf = { "#comment" = "Redis configuration file example" } { } - { "#comment" = "Note on units: when memory size is needed, it is possible to specifiy" } + { "#comment" = "Note on units: when memory size is needed, it is possible to specify" } { "#comment" = "it in the usual form of 1k 5GB 4M and so forth:" } { } { "#comment" = "1k => 1000 bytes" } diff --git a/lenses/tests/test_resolv.aug b/lenses/tests/test_resolv.aug index 7eed96b..1f81634 100644 --- a/lenses/tests/test_resolv.aug +++ b/lenses/tests/test_resolv.aug @@ -56,4 +56,4 @@ test Resolv.ip6_dotint put "ip6-dotint" after set "/ip6-dotint/negate" "" = "no-ip6-dotint" - +test Resolv.lns get "; \r\n; \t \n" = { } { } diff --git a/lenses/tests/test_shellvars_list.aug b/lenses/tests/test_shellvars_list.aug index 36fecc1..3fcb969 100644 --- a/lenses/tests/test_shellvars_list.aug +++ b/lenses/tests/test_shellvars_list.aug @@ -75,7 +75,7 @@ FAILSAVE_APPEND=\"console=ttyS0\" { "value" = "test\"1" } { "value" = "test2" } } - (* emtpy list with quotes *) + (* empty list with quotes *) test Shellvars_list.lns get "VAR=''\n" = { "VAR" { "quote" = "'" } } diff --git a/lenses/tests/test_yaml.aug b/lenses/tests/test_yaml.aug index 6353a0a..34eef08 100644 --- a/lenses/tests/test_yaml.aug +++ b/lenses/tests/test_yaml.aug @@ -7,6 +7,25 @@ test YAML.lns get "host1: { "<<" = "production" } } +(* top level sequence *) +test YAML.lns get " +- foo: 1 + bar: 2 + +- baz: 3 + gee: 4 +" = +{ } +{ "@sequence" + { "foo" = "1" } + { "bar" = "2" } +} +{ } +{ "@sequence" + { "baz" = "3" } + { "gee" = "4" } +} + test YAML.lns get " defaults: &defaults repo1: master diff --git a/lenses/yaml.aug b/lenses/yaml.aug index 8591a7e..e54b110 100644 --- a/lenses/yaml.aug +++ b/lenses/yaml.aug @@ -15,6 +15,7 @@ Author: Dimitar Dimitrov module YAML = (* Group: helpers *) +let dash = Util.del_str "-" let colon = Sep.colon let space = Sep.space let val = store Rx.word @@ -63,6 +64,9 @@ let entry = [ key Rx.word . colon . (space . anchor)? . eol . ((inherit . (repo+)?) | repo+) ] +(* View: top level sequence *) +let sequence = [ label "@sequence" . counter "sequence" . dash . repo+ ] + (* View: header *) let header = [ label "@yaml" . Util.del_str "---" . (Sep.space . store Rx.space_in)? . eol ] @@ -71,4 +75,4 @@ let header = [ label "@yaml" . Util.del_str "---" View: lns The yaml lens *) -let lns = ((empty|comment)* . header)? . (entry | comment | empty)* +let lns = ((empty|comment)* . header)? . (sequence | entry | comment | empty)* diff --git a/man/Makefile.am b/man/Makefile.am index b0efbb8..d0c56e6 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -1,7 +1,7 @@ EXTRA_DIST=$(wildcard *.pod) $(wildcard *.[0-9]) -man1_MANS=augtool.1 augparse.1 +man1_MANS=augtool.1 augparse.1 augmatch.1 %.1: %.pod pod2man -c "Augeas" -r "Augeas $(VERSION)" $< > $@ diff --git a/man/augmatch.pod b/man/augmatch.pod new file mode 100644 index 0000000..94ad944 --- /dev/null +++ b/man/augmatch.pod @@ -0,0 +1,119 @@ +=head1 NAME + +augmatch - inspect and match contents of configuration files + +=head1 SYNOPSIS + +augmatch [OPTIONS] FILE + +=head1 DESCRIPTION + +B prints the tree that Augeas generates by parsing a +configuration file, or only those parts of the tree that match a certain +path expression. Parsing is controlled by lenses, many of which ship with +Augeas. B to select the correct lens for a given file +automatically unless one is specified with the B<--lens> option. + +=head1 OPTIONS + +=over 4 + +=item B<-a>, B<--all> + +Print all tree nodes, even ones without an associated value. Without this +flag, augmatch omits these nodes from the output as they are usually +uninteresting. + +=item B<-e>, B<--exact> + +Only print the parts of the tree that exactly match the expression provided +with B<--match> and not any of the descendants of matching nodes. + +=item B<-I>, B<--include>=I + +Add DIR to the module loadpath. Can be given multiple times. The +directories set here are searched before any directories specified in the +AUGEAS_LENS_LIB environment variable, and before the default directories +F and F. + +=item B<-l>, B<--lens>=I + +Use LENS for the given file; without this option, B tries to +guess the lens for the file based on the file's name and path which only +works for files in standard locations. + +=item B<-L>, B<--print-lens> + +Print the name of the lens that will be used with the given file and exit. + +=item B<-m>, B<--match>=I + +Only print the parts of the tree that match the path expression EXPR. All +nodes that match EXPR and their descendants will be printed. Use L<--exact> +to print only matching nodes but no descendants. + +=item B<-r>, B<--root>=I + +Use directory ROOT as the root of the filesystem. Takes precedence over a +root set with the AUGEAS_ROOT environment variable. + +=item B<-S>, B<--nostdinc> + +Do not search any of the default directories for lenses. When this option +is set, only directories specified explicitly with B<-I> or specified in +B will be searched for modules. + +=item B<-o>, B<--only-value> + +Print only the value and not the label or the path of nodes. + +=back + +=head1 ENVIRONMENT VARIABLES + +=over 4 + +=item B + +The file system root, defaults to '/'. Can be overridden with +the B<-r> command line option + +=item B + +Colon separated list of directories with lenses. Directories specified here +are searched after any directories set with the B<-I> command line option, +but before the default directories F and +F + +=back + +=head1 EXAMPLES + + # print the tree for /etc/exports + augmatch /etc/exports + + # show only the entry for a specific mount + augmatch -m 'dir["/home"]' /etc/exports + + # show all the clients to which we are exporting /home + augmatch -eom 'dir["/home"]/client' /etc/exports + +=head1 FILES + +Lenses and schema definitions in F and +F + +=head1 AUTHOR + +David Lutterkort + +=head1 COPYRIGHT AND LICENSE + +Copyright 2007-2018 David Lutterkort + +Augeas (and augmatch) are distributed under the GNU Lesser General Public +License (LGPL) + +=head1 SEE ALSO + +B project homepage L diff --git a/man/augtool.1 b/man/augtool.1 index 7f2a3d7..c58cbc1 100644 --- a/man/augtool.1 +++ b/man/augtool.1 @@ -184,7 +184,7 @@ Add a file transform; uses the 'transform' command syntax, e.g. \f(CW\*(C`\-t \*(AqFstab incl /etc/fstab.bak\*(Aq\*(C'\fR. .IP "\fB\-l\fR, \fB\-\-load\-file\fR=\fI\s-1FILE\s0\fR" 4 .IX Item "-l, --load-file=FILE" -Load an invididual \s-1FILE\s0 into the tree. The lens to use is determined +Load an individual \s-1FILE\s0 into the tree. The lens to use is determined automatically (based on autoload information in the lenses) and will be the same that is used for this file when the entire tree is loaded. The option can be specified multiple times to load several files, e.g. \f(CW\*(C`\-l /etc/fstab diff --git a/man/augtool.pod b/man/augtool.pod index a167b6f..4afbd27 100644 --- a/man/augtool.pod +++ b/man/augtool.pod @@ -56,7 +56,7 @@ e.g. C<-t 'Fstab incl /etc/fstab.bak'>. =item B<-l>, B<--load-file>=I -Load an invididual FILE into the tree. The lens to use is determined +Load an individual FILE into the tree. The lens to use is determined automatically (based on autoload information in the lenses) and will be the same that is used for this file when the entire tree is loaded. The option can be specified multiple times to load several files, e.g. C<-l /etc/fstab diff --git a/src/Makefile.am b/src/Makefile.am index fe5f593..d14314d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,7 +14,7 @@ DISTCLEANFILES = datadir.h lib_LTLIBRARIES = libfa.la libaugeas.la noinst_LTLIBRARIES = liblexer.la -bin_PROGRAMS = augtool augparse +bin_PROGRAMS = augtool augparse augmatch include_HEADERS = augeas.h fa.h @@ -43,6 +43,9 @@ augtool_LDADD = libaugeas.la $(READLINE_LIBS) $(LIBXML_LIBS) $(GNULIB) augparse_SOURCES = augparse.c augparse_LDADD = libaugeas.la $(LIBXML_LIBS) $(GNULIB) +augmatch_SOURCES = augmatch.c +augmatch_LDADD = libaugeas.la $(LIBXML_LIBS) $(GNULIB) + libfa_la_SOURCES = fa.c fa.h hash.c hash.h memory.c memory.h ref.h ref.c libfa_la_LIBADD = $(LIB_SELINUX) $(GNULIB) libfa_la_LDFLAGS = $(FA_VERSION_SCRIPT) -version-info $(LIBFA_VERSION_INFO) diff --git a/src/augeas.c b/src/augeas.c index 597feff..8ec264c 100644 --- a/src/augeas.c +++ b/src/augeas.c @@ -2047,6 +2047,91 @@ int aug_ns_attr(const augeas* aug, const char *var, int i, 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 */ diff --git a/src/augeas.h b/src/augeas.h index 9f7b037..d6a1a70 100644 --- a/src/augeas.h +++ b/src/augeas.h @@ -478,7 +478,7 @@ void aug_close(augeas *aug); // more wordy notation /descendant::* /* - * Function: aug_ns_get + * Function: aug_ns_attr * * Look up the ith node in the variable VAR and retrieve information about * it. Set *VALUE to the value of the node, *LABEL to its label, and @@ -486,15 +486,20 @@ void aug_close(augeas *aug); * node does not belong to a file. It is permissible to pass NULL for any * of these variables to indicate that the caller is not interested in that * attribute. - + * * It is assumed that VAR was defined with a path expression evaluating to * a nodeset, like '/files/etc/hosts/descendant::*'. This function is - * equivalent to, but faster than, aug_get(aug, "$VAR[I]", value), - * respectively the corresponding calls to aug_label and aug_source. + * equivalent to, but faster than, aug_get(aug, "$VAR[I+1]", value), + * respectively the corresponding calls to aug_label and aug_source. Note + * that the index is 0-based, not 1-based. * * If VAR does not exist, or is not a nodeset, or if it has fewer than I * nodes, this call fails. * + * The caller is responsible for freeing *FILE_PATH, but must not free + * *VALUE or *LABEL. Those pointers are only valid up to the next call to a + * function in this API that might modify the tree. + * * Returns: * 1 on success (for consistency with aug_get), a negative value on failure */ @@ -502,6 +507,66 @@ int aug_ns_attr(const augeas* aug, const char *var, int i, const char **value, const char **label, char **file_path); /* + * Function: aug_ns_label + * + * Look up the LABEL and its INDEX amongst its siblings for the ith node in + * variable VAR. (See aug_ns_attr for details of what is expected of VAR) + * + * Either of LABEL and INDEX may be NULL. The *INDEX will be set to the + * number of siblings + 1 of the node $VAR[I+1] that precede it and have + * the same label if there are at least two siblings with that label. If + * the node $VAR[I+1] does not have any siblings with the same label as + * itself, *INDEX will be set to 0. + * + * The caller must not free *LABEL. The pointer is only valid up to the + * next call to a function in this API that might modify the tree. + * + * Returns: + * 1 on success (for consistency with aug_get), a negative value on failure + */ +int aug_ns_label(const augeas *aug, const char *var, int i, + const char **label, int *index); + +/* + * Function: aug_ns_value + * + * Look up the VALUE of the ith node in variable VAR. (See aug_ns_attr for + * details of what is expected of VAR) + * + * The caller must not free *VALUE. The pointer is only valid up to the + * next call to a function in this API that might modify the tree. + * + * Returns: + * 1 on success (for consistency with aug_get), a negative value on failure + */ +int aug_ns_value(const augeas *aug, const char *var, int i, + const char **value); + +/* + * Function: aug_ns_count + * + * Return the number of nodes in variable VAR. (See aug_ns_attr for details + * of what is expected of VAR) + * + * Returns: the number of nodes in VAR, or a negative value on failure + */ +int aug_ns_count(const augeas *aug, const char *var); + +/* + * Function: aug_ns_count + * + * Put the fully qualified path to the ith node in VAR into *PATH. (See + * aug_ns_attr for details of what is expected of VAR) + * + * The caller is responsible for freeing *PATH, which is allocated by this + * function. + * + * Returns: 1 on success (for consistency with aug_get), a negative value + * on failure + */ +int aug_ns_path(const augeas *aug, const char *var, int i, char **path); + +/* * Error reporting */ @@ -518,7 +583,7 @@ typedef enum { AUG_ENOSPAN, /* No span for this node */ AUG_EMVDESC, /* Cannot move node into its descendant */ AUG_ECMDRUN, /* Failed to execute command */ - AUG_EBADARG, /* Invalid argument in funcion call */ + AUG_EBADARG, /* Invalid argument in function call */ AUG_ELABEL, /* Invalid label */ AUG_ECPDESC /* Cannot copy node into its descendant */ } aug_errcode_t; diff --git a/src/augeas_sym.version b/src/augeas_sym.version index bce6239..219f4c1 100644 --- a/src/augeas_sym.version +++ b/src/augeas_sym.version @@ -85,3 +85,11 @@ AUGEAS_0.23.0 { global: aug_ns_attr; } AUGEAS_0.22.0; + +AUGEAS_0.24.0 { + global: + aug_ns_label; + aug_ns_value; + aug_ns_count; + aug_ns_path; +} AUGEAS_0.23.0; diff --git a/src/augmatch.c b/src/augmatch.c new file mode 100644 index 0000000..20045c5 --- /dev/null +++ b/src/augmatch.c @@ -0,0 +1,433 @@ +/* + * augrp.c: utility for printing and reading files as parsed by Augeas + * + * Copyright (C) 2007-2016 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 + */ + +#include +#include +#include +#include +#include + +#include "memory.h" +#include "augeas.h" +#include + +#define cleanup(_x) __attribute__((__cleanup__(_x))) + +const char *progname; +bool print_all = false; +bool print_only_values = false; +bool print_exact = false; + +static void freep(void *p) { + free(*(void **)p); +} + +static void aug_closep(struct augeas **p) { + aug_close(*p); +} + +__attribute__((noreturn)) +static void usage(void) { + fprintf(stderr, "Usage: %s [OPTIONS] FILE\n", progname); + fprintf(stderr, +"Print the contents of a file as parsed by augeas.\n\n" +"Options:\n\n" +" -l, --lens LENS use LENS to transform the file\n" +" -L, --print-lens print the lens that will be used for a file an exit\n" +" -a, --all print all nodes, even ones without a value\n" +" -m, --match EXPR start printing where nodes match EXPR\n" +" -e, --exact print only exact matches instead of the entire tree\n" +" starting at a match\n" +" -o, --only-value print only the values of tree nodes, but no path\n" +" -r, --root ROOT use ROOT as the root of the filesystem\n" +" -I, --include DIR search DIR for modules; can be given mutiple times\n" +" -S, --nostdinc do not search the builtin default directories\n" +" for modules\n\n" +"Examples:\n\n" +" Print how augeas sees /etc/exports:\n" +" augmatch /etc/exports\n\n" +" Show only the entry for a specific mount:\n" +" augmatch -m 'dir[\"/home\"]' /etc/exports\n\n" +" Show all the clients to which we are exporting /home:\n" +" augmatch -eom 'dir[\"/home\"]/client' /etc/exports\n\n"); + exit(EXIT_SUCCESS); +} + +/* Exit with a failure if COND is true. Use FORMAT and the remaining + * arguments like printf to print an error message on stderr before + * exiting */ +static void die(bool cond, const char *format, ...) { + if (cond) { + fputs("error: ", stderr); + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + exit(EXIT_FAILURE); + } +} + +static void oom_when(bool cond) { + die(cond, "out of memory.\n"); +} + +/* Format a string with vasprintf and return it. The caller is responsible + * for freeing the result. */ +static char *format(const char *format, ...) { + va_list args; + char *result; + int r; + + va_start(args, format); + r = vasprintf(&result, format, args); + va_end(args); + oom_when(r < 0); + return result; +} + +/* Check for an Augeas error. If there is one, print it and all its detail + * and exit with failure. If there is no error, do nothing */ +static void check_error(struct augeas *aug) { + die(aug == NULL, "could not initialize augeas\n"); + oom_when(aug_error(aug) == AUG_ENOMEM); + if (aug_error(aug) != AUG_NOERROR) { + fprintf(stderr, "error: %s\n", aug_error_message(aug)); + const char *msg = aug_error_minor_message(aug); + if (msg != NULL) { + fprintf(stderr, "%s\n", msg); + } + msg = aug_error_details(aug); + if (msg != NULL) { + fprintf(stderr, "%s\n", msg); + } + exit(EXIT_FAILURE); + } +} + +/* Check for an error trying to load FILE (e.g., a parse error) If there + * was one, print details and exit with failure. If there was none, do + * nothing. */ +static void check_load_error(struct augeas *aug, const char *file) { + char *info = format("/augeas/files%s", file); + const char *msg, *line, *col; + + aug_defvar(aug, "info", info); + die(aug_ns_count(aug, "info") == 0, "file %s does not exist\n", file); + + aug_defvar(aug, "error", "$info/error"); + if (aug_ns_count(aug, "error") == 0) + return; + + aug_get(aug, "$error", &msg); + aug_get(aug, "$error/line", &line); + aug_get(aug, "$error/char", &col); + + if (streqv(msg, "parse_failed")) { + msg = "parsing failed"; + } else if (streqv(msg, "read_failed")) { + aug_get(aug, "$error/message", &msg); + } + + if ((line != NULL) && (col != NULL)) { + fprintf(stderr, "error reading %s: %s on line %s, column %s\n", + file, msg, line, col); + } else { + fprintf(stderr, "error reading %s: %s\n", file, msg); + } + exit(EXIT_FAILURE); +} + +/* We keep track of where we are in the tree when we are printing it by + * assigning to augeas variables and using one struct node for each level + * in the tree. To keep things simple, we just preallocate a lot of them + * (up to max_nodes many). If we ever have a tree deeper than this, we are + * in trouble and will simply abort the program. */ +static const size_t max_nodes = 256; + +struct node { + char *var; /* The variable where we store the nodes for this level */ + const char *label; /* The label, index, and value of the current node */ + int index; /* at the level that this struct node is for */ + const char *value; +}; + +/* Print information about NODES[LEVEL] by printing the path to it going + * from NODES[0] to NODES[LEVEL]. PREFIX is the path from the start of the + * file to the current match (if the user specified --match) or the empty + * string (if we are printing the entire file. */ +static void print_one(int level, const char *prefix, struct node *nodes) { + if (nodes[level].value == NULL && ! print_all) + return; + + if (print_only_values && nodes[level].value != NULL) { + printf("%s\n", nodes[level].value); + return; + } + + if (*prefix) { + if (level > 0) + printf("%s/", prefix); + else + printf("%s", prefix); + } + + for (int i=1; i <= level; i++) { + if (nodes[i].index > 0) { + printf("%s[%d]", nodes[i].label, nodes[i].index); + } else { + printf("%s", nodes[i].label); + } + if (i < level) { + printf("/"); + } + } + + if (nodes[level].value) { + printf(" = %s\n", nodes[level].value); + } else { + printf("\n"); + } +} + +/* Recursively print the tree starting at NODES[LEVEL] */ +static void print_tree(struct augeas *aug, int level, + const char *prefix, struct node *nodes) { + die(level + 1 >= max_nodes, + "tree has more than %d levels, which is more than we can handle\n", + max_nodes); + + struct node *cur = nodes + level; + struct node *next = cur + 1; + + int count = aug_ns_count(aug, cur->var); + for (int i=0; i < count; i++) { + cleanup(freep) char *pattern = NULL; + + aug_ns_label(aug, cur->var, i, &(cur->label), &(cur->index)); + aug_ns_value(aug, cur->var, i, &(cur->value)); + print_one(level, prefix, nodes); + + if (! print_exact) { + pattern = format("$%s[%d]/*", cur->var, i+1); + aug_defvar(aug, next->var, pattern); + check_error(aug); + print_tree(aug, level+1, prefix, nodes); + } + } +} + +/* Print the tree for file PATH (which must already start with /files), but + * only the nodes matching MATCH */ +static void print(struct augeas *aug, const char *path, const char *match) { + static const char *const match_var = "match"; + + cleanup(freep) struct node *nodes = NULL; + + nodes = calloc(max_nodes, sizeof(struct node)); + oom_when(nodes == NULL); + + aug_set(aug, "/augeas/context", path); + + for (int i=0; i < max_nodes; i++) { + nodes[i].var = format("var%d", i); + } + + /* Set $match to the nodes matching the user's match expression */ + aug_defvar(aug, match_var, match); + check_error(aug); + + /* Go through the matches in MATCH_VAR one by one. We need to do it + * this way, since the prefix we need to print for each entry in + * MATCH_VAR is different for each entry. */ + int count = aug_ns_count(aug, match_var); + for (int i=0; i < count; i++) { + cleanup(freep) char *prefix = NULL; + aug_ns_path(aug, match_var, i, &prefix); + aug_defvar(aug, nodes[0].var, prefix); + print_tree(aug, 0, prefix + strlen(path) + 1, nodes); + } +} + +/* Look at the filename and try to guess based on the extension. The + * builtin filters for lenses do not do that, as that would force augtool + * to scan everything on start + */ +static char *guess_lens_name(const char *file) { + const char *ext = strrchr(file, '.'); + + if (ext == NULL) + return NULL; + + if (streqv(ext, ".json")) { + return strdup("Json.lns"); + } else if (streqv(ext, ".xml")) { + return strdup("Xml.lns"); + } + + return NULL; +} + +int main(int argc, char **argv) { + int opt; + cleanup(aug_closep) struct augeas *aug; + cleanup(freep) char *loadpath = NULL; + size_t loadpath_len = 0; + cleanup(freep) char *root = NULL; + cleanup(freep) char *lens = NULL; + cleanup(freep) char *matches = NULL; + size_t matches_len = 0; + const char *match = "*"; + bool print_lens = false; + + struct option options[] = { + { "help", 0, 0, 'h' }, + { "include", 1, 0, 'I' }, + { "lens", 1, 0, 'l' }, + { "all", 0, 0, 'a' }, + { "index", 0, 0, 'i' }, + { "match", 1, 0, 'm' }, + { "only-value", 0, 0, 'o' }, + { "nostdinc", 0, 0, 'S' }, + { "root", 1, 0, 'r' }, + { "print-lens", 0, 0, 'L' }, + { "exact", 0, 0, 'e' }, + { 0, 0, 0, 0} + }; + unsigned int flags = AUG_NO_LOAD|AUG_NO_ERR_CLOSE; + progname = basename(argv[0]); + + setlocale(LC_ALL, ""); + while ((opt = getopt_long(argc, argv, "ahI:l:m:oSr:eL", options, NULL)) != -1) { + switch(opt) { + case 'I': + argz_add(&loadpath, &loadpath_len, optarg); + break; + case 'l': + lens = strdup(optarg); + break; + case 'L': + print_lens = true; + break; + case 'h': + usage(); + break; + case 'a': + print_all = true; + break; + case 'm': + // If optarg is a numeric string like '1', it is not a legal + // part of a path by itself, and so we need to prefix it with + // an explicit axis + die(optarg[0] == '/', + "matches can only be relative paths, not %s\n", optarg); + argz_add(&matches, &matches_len, format("child::%s", optarg)); + break; + case 'o': + print_only_values = true; + break; + case 'r': + root = strdup(optarg); + break; + case 'S': + flags |= AUG_NO_STDINC; + break; + case 'e': + print_exact = true; + break; + default: + fprintf(stderr, "Try '%s --help' for more information.\n", + progname); + exit(EXIT_FAILURE); + break; + } + } + + if (optind >= argc) { + fprintf(stderr, "Expected an input file\n"); + fprintf(stderr, "Try '%s --help' for more information.\n", + progname); + exit(EXIT_FAILURE); + } + + const char *file = argv[optind]; + + argz_stringify(loadpath, loadpath_len, ':'); + + if (lens == NULL) { + lens = guess_lens_name(file); + } + + if (lens != NULL) { + /* We know which lens we want, we do not need to load all of them */ + flags |= AUG_NO_MODL_AUTOLOAD; + } + + aug = aug_init(root, loadpath, flags|AUG_NO_ERR_CLOSE); + check_error(aug); + + if (lens == NULL) { + aug_load_file(aug, file); + } else { + aug_transform(aug, lens, file, false); + aug_load(aug); + } + check_error(aug); + + /* The user just wants the lens name */ + if (print_lens) { + char *info = format("/augeas/files%s", file); + const char *lens_name; + aug_defvar(aug, "info", info); + die(aug_ns_count(aug, "info") == 0, + "file %s does not exist\n", file); + aug_get(aug, "$info/lens", &lens_name); + /* We are being extra careful here - the check_error above would + have already aborted the program if we could not determine a + lens; dieing here indicates some sort of bug */ + die(lens_name == NULL, "could not find lens for %s\n", + file); + if (lens_name[0] == '@') + lens_name += 1; + printf("%s\n", lens_name); + exit(EXIT_SUCCESS); + } + + check_load_error(aug, file); + + char *path = format("/files%s", file); + + if (matches_len > 0) { + argz_stringify(matches, matches_len, '|'); + match = matches; + } + + print(aug, path, match); +} + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/src/augparse.c b/src/augparse.c index e2b195f..96a686c 100644 --- a/src/augparse.c +++ b/src/augparse.c @@ -37,7 +37,7 @@ static void usage(void) { fprintf(stderr, "Usage: %s [OPTIONS] MODULE\n", progname); fprintf(stderr, "Evaluate MODULE. Generally, MODULE should contain unit tests.\n"); fprintf(stderr, "\nOptions:\n\n"); - fprintf(stderr, " -I, --include DIR search DIR for modules; can be given mutiple times\n"); + fprintf(stderr, " -I, --include DIR search DIR for modules; can be given multiple times\n"); fprintf(stderr, " -t, --trace trace module loading\n"); fprintf(stderr, " --nostdinc do not search the builtin default directories for modules\n"); fprintf(stderr, " --notypecheck do not typecheck lenses\n"); diff --git a/src/augrun.c b/src/augrun.c index 75167a9..07f865a 100644 --- a/src/augrun.c +++ b/src/augrun.c @@ -476,6 +476,40 @@ static const struct command_def cmd_match_def = { "VALUE are printed" }; +static void cmd_count(struct command *cmd) { + int cnt = 0; + const char *pattern = arg_value(cmd, "path"); + + cnt = aug_match(cmd->aug, pattern, NULL); + ERR_BAIL(cmd); + ERR_THROW(cnt < 0, cmd->aug, AUG_ECMDRUN, + " (error matching %s)\n", pattern); + if (cnt == 0) { + fprintf(cmd->out, " no matches\n"); + } else if (cnt == 1) { + fprintf(cmd->out, " 1 match\n"); + } else { + fprintf(cmd->out, " %d matches\n", cnt); + } + + error: + return; +} + +static const struct command_opt_def cmd_count_opts[] = { + { .type = CMD_PATH, .name = "path", .optional = false, + .help = "the path expression to match" }, + CMD_OPT_DEF_LAST +}; + +static const struct command_def cmd_count_def = { + .name = "count", + .opts = cmd_count_opts, + .handler = cmd_count, + .synopsis = "print the number of matches for a path expression", + .help = "Print how many paths match the the path expression PATH" +}; + static void cmd_rm(struct command *cmd) { int cnt; const char *path = arg_value(cmd, "path"); @@ -1464,6 +1498,7 @@ static const struct command_grp_def cmd_grp_read_def = { &cmd_label_def, &cmd_ls_def, &cmd_match_def, + &cmd_count_def, &cmd_print_def, &cmd_span_def, &cmd_def_last diff --git a/src/augtool.c b/src/augtool.c index 17300c4..ff097bd 100644 --- a/src/augtool.c +++ b/src/augtool.c @@ -183,7 +183,7 @@ static char *readline_command_generator(const char *text, int state) { "mv", "cp", "rename", "print", "dump-xml", "rm", "save", "set", "setm", "clearm", "span", "store", "retrieve", "transform", "load-file", "help", "touch", "insert", "move", "copy", "errors", "source", "context", - "info", + "info", "count", NULL }; static int current = 0; diff --git a/src/builtin.c b/src/builtin.c index bdf5dc8..732ee10 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -92,7 +92,7 @@ static struct value *lns_square(struct info *info, struct value *l1, assert(l1->tag == V_LENS); assert(l2->tag == V_LENS); assert(l3->tag == V_LENS); - int check = info->error->aug->flags & AUG_TYPE_CHECK; + int check = typecheck_p(info); return lns_make_square(ref(info), ref(l1->lens), ref(l2->lens), ref(l3->lens), check); } @@ -187,7 +187,7 @@ static struct value *lens_get(struct info *info, struct value *l, struct value *v; const char *text = str->string->str; - struct tree *tree = lns_get(info, l->lens, text, &err); + struct tree *tree = lns_get(info, l->lens, text, 0, &err); if (err == NULL && ! HAS_ERR(info)) { v = make_value(V_TREE, ref(info)); v->origin = make_tree_origin(tree); @@ -222,7 +222,7 @@ static struct value *lens_put(struct info *info, struct value *l, init_memstream(&ms); lns_put(info, ms.stream, l->lens, tree->origin->children, - str->string->str, &err); + str->string->str, 0, &err); close_memstream(&ms); if (err == NULL && ! HAS_ERR(info)) { @@ -467,7 +467,7 @@ static struct value *lns_check_rec_glue(struct info *info, struct value *l, struct value *r) { assert(l->tag == V_LENS); assert(r->tag == V_LENS); - int check = info->error->aug->flags & AUG_TYPE_CHECK; + int check = typecheck_p(info); return lns_check_rec(info, l->lens, r->lens, check); } diff --git a/src/fa.c b/src/fa.c index b885b58..412d139 100644 --- a/src/fa.c +++ b/src/fa.c @@ -1672,7 +1672,7 @@ static int minimize_hopcroft(struct fa *fa) { struct state *qs = partn->states[q]; int qnum = state_set_index(states, qs); if (qs == fa->initial) - s->live = 1; /* Abuse live to flag the new intial state */ + s->live = 1; /* Abuse live to flag the new initial state */ nsnum[n] = qnum; /* select representative */ nsind[qnum] = n; /* and point from partition to new state */ } @@ -2958,6 +2958,12 @@ int fa_ambig_example(struct fa *fa1, struct fa *fa2, goto error; if (concat_in_place(b1, &ms) < 0) goto error; + if (fa_is_basic(b1, FA_EMPTY)) { + /* We are done - amb which we take an example from below + * will be empty, and there can therefore not be an ambiguity */ + ret = 0; + goto done; + } /* Compute b2 = ss . ((sp . a2f) & a2t) */ if (concat_in_place(sp, &a2f) < 0) @@ -4513,6 +4519,105 @@ void fa_dot(FILE *out, struct fa *fa) { fprintf(out, "}\n"); } +int fa_json(FILE *out, struct fa *fa) { + hash_val_t *list_hashes = NULL; + int list_size = 100; + int num_states = 0; + int it; + char first = true; + int result = -1; + + fprintf(out,"{\n\t\"final\": ["); + + F(ALLOC_N(list_hashes, list_size)); + + list_for_each(s, fa->initial) { + if (num_states == list_size - 1){ + list_size += list_size; + F(REALLOC_N(list_hashes, list_size)); + } + // Store hash value + list_hashes[num_states] = s->hash; + // We use the hashes to map states to Z_{num_states} + s->hash = num_states++; + if (s->accept) { + if (first) { + fprintf(out,"%ld", s->hash); + first = false; + } else { + fprintf(out, ", %ld", s->hash); + } + } + } + + fprintf(out, "],\n\t\"deterministic\": %d,\n\t\"transitions\": [\n", + fa->deterministic ? 1 : 0); + + first = true; + list_for_each(s, fa->initial) { + for_each_trans(t, s) { + if (!first) + fprintf(out, ",\n"); + first = false; + fprintf(out, "\t\t{ \"from\": %ld, \"to\": %ld, \"on\": \"", + s->hash, t->to->hash); + print_char(out, t->min); + if (t->min != t->max) { + fputc('-', out); + print_char(out, t->max); + } + fprintf(out, "\" }"); + } + } + + fprintf(out,"\n\t]\n}"); + result = 0; + +error: + // Restoring hash values to leave the FA structure untouched. That is + // only needed if we actually copied hashes, indicated by num_states + // being non-zero + if (num_states > 0) { + it = 0; + list_for_each(s, fa->initial) { + s->hash = list_hashes[it++]; + } + } + free(list_hashes); + return result; +} + +bool fa_is_deterministic(struct fa *fa) { + return fa->deterministic; +} + +struct state *fa_state_initial(struct fa *fa) { + return fa->initial; +} + +bool fa_state_is_accepting(struct state *st) { + return st->accept; +} + +struct state* fa_state_next(struct state *st) { + return st->next; +} + +size_t fa_state_num_trans(struct state *st) { + return st->tused; +} + +int fa_state_trans(struct state *st, size_t i, + struct state **to, unsigned char *min, unsigned char *max) { + if (st->tused <= i) + return -1; + + (*to) = st->trans[i].to; + (*min) = st->trans[i].min; + (*max) = st->trans[i].max; + return 0; +} + /* * Local variables: * indent-tabs-mode: nil diff --git a/src/fa.h b/src/fa.h index 9ea2533..1fd754a 100644 --- a/src/fa.h +++ b/src/fa.h @@ -25,10 +25,25 @@ #include #include +#include +#include /* The type for a finite automaton. */ struct fa; +/* The type of a state of a finite automaton. The fa_state functions return + * pointers to this struct. Those pointers are only valid as long as the + * only fa_* functions that are called are fa_state_* functions. For + * example, the following code will almost certainly result in a crash (or + * worse): + * + * struct state *s = fa_state_initial(fa); + * fa_minimize(fa); + * // Crashes as S will likely have been freed + * s = fa_state_next(s) + */ +struct state; + /* Denote some basic automata, used by fa_is_basic and fa_make_basic */ enum fa_basic { FA_EMPTY, /* Accepts the empty language, i.e. no strings */ @@ -156,7 +171,7 @@ struct fa *fa_overlap(struct fa *fa1, struct fa *fa2); /* Produce an example for the language of FA. The example is not * necessarily the shortest possible. The implementation works very hard to - * have printable characters (preferrably alphanumeric) in the example, and + * have printable characters (preferably alphanumeric) in the example, and * to avoid just an empty word. * * *EXAMPLE will be the example, which may be NULL. If it is non-NULL, @@ -272,6 +287,39 @@ int fa_expand_nocase(const char *regexp, size_t regexp_len, */ int fa_enumerate(struct fa *fa, int limit, char ***words); +/* Print FA to OUT as a JSON file. State 0 is always the initial one. + * Returns 0 on success, and -1 on failure. + */ +int fa_json(FILE *out, struct fa *fa); + +/* Returns true if the FA is deterministic and 0 otherwise */ +bool fa_is_deterministic(struct fa *fa); + +/* Return the initial state */ +struct state *fa_state_initial(struct fa *fa); + +/* Return true if this is an accepting state */ +bool fa_state_is_accepting(struct state *st); + +/* Return the next state; return NULL if there are no more states */ +struct state* fa_state_next(struct state *st); + +/* Return the number of transitions for a state */ +size_t fa_state_num_trans(struct state *st); + +/* Produce details about the i-th transition. + * + * On success, *to points to the destination state of the transition and + * the interval [min-max] is the label of the transition. + * + * On failure, *to, min and max are not modified. + * + * Return 0 on success and -1 when I is larger than the number of + * transitions of ST. + */ +int fa_state_trans(struct state *st, size_t i, + struct state **to, unsigned char *min, unsigned char *max); + #endif diff --git a/src/fa_sym.version b/src/fa_sym.version index 8d04cbf..60d97ad 100644 --- a/src/fa_sym.version +++ b/src/fa_sym.version @@ -33,3 +33,13 @@ FA_1.2.0 { FA_1.4.0 { fa_enumerate; } FA_1.2.0; + +FA_1.5.0 { + fa_json; + fa_state_initial; + fa_state_is_accepting; + fa_state_next; + fa_state_num_trans; + fa_state_trans; + fa_is_deterministic; +} FA_1.4.0; diff --git a/src/get.c b/src/get.c index cbd27d6..ce0864b 100644 --- a/src/get.c +++ b/src/get.c @@ -51,6 +51,7 @@ struct state { char *key; char *value; /* GET_STORE leaves a value here */ struct lns_error *error; + int enable_span; /* We use the registers from a regular expression match to keep track * of the substring we are currently looking at. REGS are the registers * from the last regexp match; NREG is the number of the register @@ -899,7 +900,7 @@ static struct tree *get_subtree(struct lens *lens, struct state *state) { state->key = NULL; state->value = NULL; - if (state->info->flags & AUG_ENABLE_SPAN) { + if (state->enable_span) { state->span = make_span(state->info); ERR_NOMEM(state->span == NULL, state->info); } @@ -1158,8 +1159,7 @@ static void visit_terminal(struct lens *lens, size_t start, size_t end, } static bool rec_gen_span(struct rec_state *rec_state) { - return ((rec_state->mode == M_GET) - && (rec_state->state->info->flags & AUG_ENABLE_SPAN)); + return ((rec_state->mode == M_GET) && (rec_state->state->enable_span)); } static void visit_enter(struct lens *lens, @@ -1600,7 +1600,7 @@ static int init_regs(struct state *state, struct lens *lens, uint size) { } struct tree *lns_get(struct info *info, struct lens *lens, const char *text, - struct lns_error **err) { + int enable_span, struct lns_error **err) { struct state state; struct tree *tree = NULL; uint size = strlen(text); @@ -1615,6 +1615,8 @@ struct tree *lns_get(struct info *info, struct lens *lens, const char *text, state.text = text; + state.enable_span = enable_span; + /* We are probably being overly cautious here: if the lens can't process * all of TEXT, we should really fail somewhere in one of the sublenses. * But to be safe, we check that we can process everything anyway, then diff --git a/src/hash.h b/src/hash.h index 90bcf93..4b38dff 100644 --- a/src/hash.h +++ b/src/hash.h @@ -98,7 +98,7 @@ typedef int (*hash_comp_t)(const void *, const void *); * particular, the most significant bits of hash_val_t are most significant to * the hash module. Only as the hash table expands are less significant bits * examined. Thus a function that has good distribution in its upper bits but - * not lower is preferrable to one that has poor distribution in the upper bits + * not lower is preferable to one that has poor distribution in the upper bits * but not the lower ones. */ diff --git a/src/info.c b/src/info.c index 61f28d5..fed1cb1 100644 --- a/src/info.c +++ b/src/info.c @@ -21,10 +21,13 @@ */ #include +#include + #include "info.h" #include "internal.h" #include "memory.h" #include "ref.h" +#include "errcode.h" /* * struct string @@ -111,6 +114,10 @@ void print_info(FILE *out, struct info *info) { } } +bool typecheck_p(const struct info *info) { + return (info->error->aug->flags & AUG_TYPE_CHECK) != 0; +} + void free_info(struct info *info) { if (info == NULL) return; diff --git a/src/info.h b/src/info.h index 5e4e9be..497c7f3 100644 --- a/src/info.h +++ b/src/info.h @@ -52,7 +52,6 @@ struct info { uint16_t last_line; uint16_t last_column; ref_t ref; - int flags; }; struct span { @@ -69,6 +68,10 @@ char *format_info(struct info *info); void print_info(FILE *out, struct info *info); +/* Return true if typechecking is turned on. (This uses the somewhat gross + * fact that we can get at the augeas flags through error->aug) */ +bool typecheck_p(const struct info *info); + /* Do not call directly, use UNREF instead */ void free_info(struct info *info); diff --git a/src/internal.c b/src/internal.c index 21fa08e..ef83b71 100644 --- a/src/internal.c +++ b/src/internal.c @@ -334,14 +334,10 @@ int __aug_close_memstream(struct memstream *ms) { return 0; } -char *path_expand(struct tree *tree, const char *ppath) { +int tree_sibling_index(struct tree *tree) { struct tree *siblings = tree->parent->children; - char *path; - const char *label; - char *escaped = NULL; - - int cnt = 0, ind = 0, r; + int cnt = 0, ind = 0; list_for_each(t, siblings) { if (streqv(t->label, tree->label)) { @@ -351,6 +347,21 @@ char *path_expand(struct tree *tree, const char *ppath) { } } + if (cnt > 1) { + return ind; + } else { + return 0; + } +} + +char *path_expand(struct tree *tree, const char *ppath) { + char *path; + const char *label; + char *escaped = NULL; + int r; + + int ind = tree_sibling_index(tree); + if (ppath == NULL) ppath = ""; @@ -366,7 +377,7 @@ char *path_expand(struct tree *tree, const char *ppath) { if (escaped != NULL) label = escaped; - if (cnt > 1) { + if (ind > 0) { r = asprintf(&path, "%s/%s[%d]", ppath, label, ind); } else { r = asprintf(&path, "%s/%s", ppath, label); diff --git a/src/internal.h b/src/internal.h index 5b9d259..2633a98 100644 --- a/src/internal.h +++ b/src/internal.h @@ -463,6 +463,9 @@ 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); +/* Return the 1-based index of TREE amongst its siblings with the same + * label or 0 if none of TREE's siblings have the same label */ +int tree_sibling_index(struct tree *tree); char *path_expand(struct tree *tree, const char *ppath); char *path_of_tree(struct tree *tree); /* Clear the dirty flag in the whole TREE */ @@ -610,7 +613,7 @@ int pathx_parse(const struct tree *origin, struct error *err_of_pathx(struct pathx *px); struct tree *pathx_first(struct pathx *path); struct tree *pathx_next(struct pathx *path); -/* Return -1 if evalutating PATH runs into trouble, otherwise return the +/* Return -1 if evaluating PATH runs into trouble, otherwise return the * number of nodes matching PATH and set MATCH to the first matching * node */ int pathx_find_one(struct pathx *path, struct tree **match); @@ -626,6 +629,11 @@ int pathx_symtab_assign_tree(struct pathx_symtab **symtab, const char *name, int pathx_symtab_undefine(struct pathx_symtab **symtab, const char *name); void pathx_symtab_remove_descendants(struct pathx_symtab *symtab, const struct tree *tree); + +/* Return the number of nodes in the nodeset NAME. If the variable NAME + * does not exist, or is not a nodeset, return -1 */ +int pathx_symtab_count(const struct pathx_symtab *symtab, const char *name); + /* Return the tree stored in the variable NAME at position I, which is the same as evaluating the path expression '$NAME[I]'. If the variable NAME does not exist, or does not contain a nodeset, or if I is bigger than diff --git a/src/lens.c b/src/lens.c index 778c066..220668c 100644 --- a/src/lens.c +++ b/src/lens.c @@ -562,13 +562,13 @@ static struct regexp *restrict_regexp(struct regexp *r) { goto done; } -struct value *lns_make_prim(enum lens_tag tag, struct info *info, - struct regexp *regexp, struct string *string) { - struct lens *lens = NULL; - struct value *exn = NULL; +static struct value * +typecheck_prim(enum lens_tag tag, struct info *info, + struct regexp *regexp, struct string *string) { struct fa *fa_slash = NULL; struct fa *fa_key = NULL; struct fa *fa_isect = NULL; + struct value *exn = NULL; /* Typecheck */ if (tag == L_KEY) { @@ -583,8 +583,7 @@ struct value *lns_make_prim(enum lens_tag tag, struct info *info, fa_isect = fa_intersect(fa_slash, fa_key); if (! fa_is_basic(fa_isect, FA_EMPTY)) { exn = make_exn_value(info, - "The key regexp /%s/ matches a '/'", - regexp->pattern->str); + "The key regexp /%s/ matches a '/'", regexp->pattern->str); goto error; } fa_free(fa_isect); @@ -594,8 +593,7 @@ struct value *lns_make_prim(enum lens_tag tag, struct info *info, } else if (tag == L_LABEL) { if (strchr(string->str, SEP) != NULL) { exn = make_exn_value(info, - "The label string \"%s\" contains a '/'", - string->str); + "The label string \"%s\" contains a '/'", string->str); goto error; } } else if (tag == L_DEL && string != NULL) { @@ -606,14 +604,31 @@ struct value *lns_make_prim(enum lens_tag tag, struct info *info, char *s = escape(dflt, -1, RX_ESCAPES); char *r = regexp_escape(regexp); exn = make_exn_value(info, - "del: the default value '%s' does not match /%s/", - s, r); + "del: the default value '%s' does not match /%s/", s, r); FREE(s); FREE(r); goto error; } } + error: + fa_free(fa_isect); + fa_free(fa_key); + fa_free(fa_slash); + return exn; +} + +struct value *lns_make_prim(enum lens_tag tag, struct info *info, + struct regexp *regexp, struct string *string) { + struct lens *lens = NULL; + struct value *exn = NULL; + + if (typecheck_p(info)) { + exn = typecheck_prim(tag, info, regexp, string); + if (exn != NULL) + goto error; + } + /* Build the actual lens */ lens = make_lens(tag, info); lens->regexp = regexp; @@ -659,9 +674,6 @@ struct value *lns_make_prim(enum lens_tag tag, struct info *info, return make_lens_value(lens); error: - fa_free(fa_isect); - fa_free(fa_key); - fa_free(fa_slash); return exn; } diff --git a/src/lens.h b/src/lens.h index de250a1..d1a37cd 100644 --- a/src/lens.h +++ b/src/lens.h @@ -191,11 +191,10 @@ void free_lns_error(struct lns_error *err); * message on failure; the constructed tree is always returned. If ERR is * NULL, return the tree on success, and NULL on failure. * - * FLAGS controls what is printed and should be a set of flags from enum - * parse_flags + * ENABLE_SPAN indicates whether span information should be collected or not */ struct tree *lns_get(struct info *info, struct lens *lens, const char *text, - struct lns_error **err); + int enable_span, struct lns_error **err); struct skel *lns_parse(struct lens *lens, const char *text, struct dict **dict, struct lns_error **err); @@ -209,7 +208,7 @@ struct skel *lns_parse(struct lens *lens, const char *text, * to update spans or not. */ void lns_put(struct info *info, FILE *out, struct lens *lens, struct tree *tree, - const char *text, struct lns_error **err); + const char *text, int enable_span, struct lns_error **err); /* Free up temporary data structures, most importantly compiled regular expressions */ diff --git a/src/pathx.c b/src/pathx.c index 8d62e48..48c8b0b 100644 --- a/src/pathx.c +++ b/src/pathx.c @@ -294,6 +294,7 @@ static void func_regexp(struct state *state, int nargs); static void func_regexp_flag(struct state *state, int nargs); static void func_glob(struct state *state, int nargs); static void func_int(struct state *state, int nargs); +static void func_not(struct state *state, int nargs); static const enum type arg_types_nodeset[] = { T_NODESET }; static const enum type arg_types_string[] = { T_STRING }; @@ -334,7 +335,9 @@ static const struct func builtin_funcs[] = { { .name = "int", .arity = 1, .type = T_NUMBER, .arg_types = arg_types_nodeset, .impl = func_int }, { .name = "int", .arity = 1, .type = T_NUMBER, - .arg_types = arg_types_bool, .impl = func_int } + .arg_types = arg_types_bool, .impl = func_int }, + { .name = "not", .arity = 1, .type = T_BOOLEAN, + .arg_types = arg_types_bool, .impl = func_not } }; #define RET_ON_ERROR \ @@ -716,6 +719,16 @@ static void func_int(struct state *state, int nargs) { push_value(vind, state); } +static void func_not(struct state *state, int nargs) { + ensure_arity(1, 1); + RET_ON_ERROR; + + struct value *v = pop_value(state); + if (v->tag == T_BOOLEAN) { + push_boolean_value(! v->boolval, state); + } +} + static struct regexp * nodeset_as_regexp(struct info *info, struct nodeset *ns, int glob, int nocase) { struct regexp *result = NULL; @@ -1140,6 +1153,8 @@ static bool eval_pred(struct expr *expr, struct state *state) { return (state->ctx_pos == v->number); case T_NODESET: return v->nodeset->used > 0; + case T_STRING: + return streqv(state->ctx->value, v->string); default: assert(0); return false; @@ -1362,7 +1377,7 @@ static void eval_filter(struct expr *expr, struct state *state) { } static struct value *lookup_var(const char *ident, - struct pathx_symtab *symtab) { + const struct pathx_symtab *symtab) { list_for_each(tab, symtab) { if (STREQ(ident, tab->name)) return tab->value; @@ -1421,7 +1436,7 @@ static void check_preds(struct pred *pred, struct state *state) { check_expr(e, state); RET_ON_ERROR; if (e->type != T_NODESET && e->type != T_NUMBER && - e->type != T_BOOLEAN) { + e->type != T_BOOLEAN && e->type != T_STRING) { STATE_ERROR(state, PATHX_ETYPE); return; } @@ -3018,6 +3033,16 @@ int pathx_symtab_assign_tree(struct pathx_symtab **symtab, return -1; } +int +pathx_symtab_count(const struct pathx_symtab *symtab, const char *name) { + struct value *v = lookup_var(name, symtab); + + if (v == NULL || v->tag != T_NODESET) + return -1; + + return v->nodeset->used; +} + struct tree * pathx_symtab_get_tree(struct pathx_symtab *symtab, const char *name, int i) { diff --git a/src/put.c b/src/put.c index 8d3ec92..5ada2ce 100644 --- a/src/put.c +++ b/src/put.c @@ -840,7 +840,7 @@ static void create_lens(struct lens *lens, struct state *state) { } void lns_put(struct info *info, FILE *out, struct lens *lens, struct tree *tree, - const char *text, struct lns_error **err) { + const char *text, int enable_span, struct lns_error **err) { struct state state; struct lns_error *err1; @@ -862,7 +862,7 @@ void lns_put(struct info *info, FILE *out, struct lens *lens, struct tree *tree, } state.out = out; state.split = make_split(tree); - state.with_span = info->flags & AUG_ENABLE_SPAN; + state.with_span = enable_span; state.tree = tree; state.info = info; if (state.with_span) { diff --git a/src/syntax.c b/src/syntax.c index 0d8d982..612544c 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -308,7 +308,7 @@ void free_value(struct value *v) { /* * Creation of (some) terms. Others are in parser.y - * Refernce counted arguments are now owned by the returned object, i.e. + * Reference counted arguments are now owned by the returned object, i.e. * the make_* functions do not increment the count. * Returned objects have a referece count of 1. */ @@ -1962,6 +1962,13 @@ static char *module_filename(struct augeas *aug, const char *modname) { char *filename = NULL; char *name = module_basename(modname); + /* Module names that contain slashes can fool us into finding and + * loading a module in another directory, but once loaded we won't find + * it under MODNAME so that we will later try and load it over and + * over */ + if (index(modname, '/') != NULL) + goto error; + while ((dir = argz_next(aug->modpathz, aug->nmodpath, dir)) != NULL) { int len = strlen(name) + strlen(dir) + 2; struct stat st; diff --git a/src/transform.c b/src/transform.c index 03867fc..6a0ce85 100644 --- a/src/transform.c +++ b/src/transform.c @@ -55,7 +55,7 @@ static const int glob_flags = GLOB_NOSORT; * lens/id : unique hexadecimal id of the lens * error : indication of errors during processing FNAME, or NULL * if processing succeeded - * error/pos : position in file where error occured (for get errors) + * error/pos : position in file where error occurred (for get errors) * error/path: path to tree node where error occurred (for put errors) * error/message : human-readable error message */ @@ -569,7 +569,6 @@ make_lns_info(struct augeas *aug, const char *filename, info->last_column = text_len; } - info->flags = aug->flags; info->error = aug->error; return info; @@ -606,7 +605,7 @@ static void lens_get(struct augeas *aug, ERR_NOMEM(span == NULL, info); } - tree = lns_get(info, lens, text, err); + tree = lns_get(info, lens, text, aug->flags & AUG_ENABLE_SPAN, err); if (*err == NULL) { // Successful get @@ -800,6 +799,9 @@ int transform_validate(struct augeas *aug, struct tree *xfm) { return 0; error: xfm_error(xfm, aug->error->details); + /* We recorded this error in the tree, clear it so that future + * operations report this exact same error (against the wrong lens) */ + reset_error(aug->error); return -1; } @@ -1112,7 +1114,8 @@ static void lens_put(struct augeas *aug, const char *filename, tree->span->span_start = ftell(out); } - lns_put(info, out, lens, tree->children, text, err); + lns_put(info, out, lens, tree->children, text, + aug->flags & AUG_ENABLE_SPAN, err); if (with_span) { tree->span->span_end = ftell(out); diff --git a/tests/Makefile.am b/tests/Makefile.am index 6c52b24..8e035e9 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -76,6 +76,7 @@ lens_tests = \ lens-gdm.sh \ lens-getcap.sh \ lens-group.sh \ + lens-grubenv.sh \ lens-gshadow.sh \ lens-gtkbookmarks.sh \ lens-json.sh \ @@ -173,6 +174,7 @@ lens_tests = \ lens-quote.sh \ lens-rabbitmq.sh \ lens-radicale.sh \ + lens-rancid.sh \ lens-redis.sh \ lens-reprepro_uploaders.sh \ lens-resolv.sh \ @@ -257,6 +259,7 @@ check-lens-tests: DISTCLEANFILES = $(lens_tests) $(lens_tests): lens-test-1 + rm -f $@ $(LN_S) $< $@ check_SCRIPTS = \ diff --git a/tests/fatest.c b/tests/fatest.c index 69152c2..0c9ca76 100644 --- a/tests/fatest.c +++ b/tests/fatest.c @@ -661,7 +661,8 @@ static void testEnumerate(CuTest *tc) { } if (!found) { char *msg; - asprintf(&msg, "Generated word %s not expected", words[i]); + /* Ignore return of asprintf intentionally */ + r = asprintf(&msg, "Generated word %s not expected", words[i]); CuFail(tc, msg); } } diff --git a/tests/root/etc/apt/apt.conf.d/01autoremove b/tests/root/etc/apt/apt.conf.d/01autoremove new file mode 100644 index 0000000..fc02350 --- /dev/null +++ b/tests/root/etc/apt/apt.conf.d/01autoremove @@ -0,0 +1,40 @@ +APT +{ + NeverAutoRemove + { + "^firmware-linux.*"; + "^linux-firmware$"; + }; + + VersionedKernelPackages + { + # linux kernels + "linux-image"; + "linux-headers"; + "linux-image-extra"; + "linux-signed-image"; + # kfreebsd kernels + "kfreebsd-image"; + "kfreebsd-headers"; + # hurd kernels + "gnumach-image"; + # (out-of-tree) modules + ".*-modules"; + ".*-kernel"; + "linux-backports-modules-.*"; + # tools + "linux-tools"; + }; + + Never-MarkAuto-Sections + { + "metapackages"; + "restricted/metapackages"; + "universe/metapackages"; + "multiverse/metapackages"; + "oldlibs"; + "restricted/oldlibs"; + "universe/oldlibs"; + "multiverse/oldlibs"; + }; +}; diff --git a/tests/root/etc/apt/apt.conf.d/01autoremove-kernels b/tests/root/etc/apt/apt.conf.d/01autoremove-kernels new file mode 100644 index 0000000..4c86c0a --- /dev/null +++ b/tests/root/etc/apt/apt.conf.d/01autoremove-kernels @@ -0,0 +1,15 @@ +// DO NOT EDIT! File autogenerated by /etc/kernel/postinst.d/apt-auto-removal +APT::NeverAutoRemove +{ + "^linux-image-3\.16\.0-4-amd64$"; + "^linux-headers-3\.16\.0-4-amd64$"; + "^linux-image-extra-3\.16\.0-4-amd64$"; + "^linux-signed-image-3\.16\.0-4-amd64$"; + "^kfreebsd-image-3\.16\.0-4-amd64$"; + "^kfreebsd-headers-3\.16\.0-4-amd64$"; + "^gnumach-image-3\.16\.0-4-amd64$"; + "^.*-modules-3\.16\.0-4-amd64$"; + "^.*-kernel-3\.16\.0-4-amd64$"; + "^linux-backports-modules-.*-3\.16\.0-4-amd64$"; + "^linux-tools-3\.16\.0-4-amd64$"; +}; diff --git a/tests/root/etc/apt/apt.conf.d/50unattended-upgrades b/tests/root/etc/apt/apt.conf.d/50unattended-upgrades new file mode 100644 index 0000000..3961cd8 --- /dev/null +++ b/tests/root/etc/apt/apt.conf.d/50unattended-upgrades @@ -0,0 +1,92 @@ +// Unattended-Upgrade::Origins-Pattern controls which packages are +// upgraded. +// +// Lines below have the format format is "keyword=value,...". A +// package will be upgraded only if the values in its metadata match +// all the supplied keywords in a line. (In other words, omitted +// keywords are wild cards.) The keywords originate from the Release +// file, but several aliases are accepted. The accepted keywords are: +// a,archive,suite (eg, "stable") +// c,component (eg, "main", "crontrib", "non-free") +// l,label (eg, "Debian", "Debian-Security") +// o,origin (eg, "Debian", "Unofficial Multimedia Packages") +// n,codename (eg, "jessie", "jessie-updates") +// site (eg, "http.debian.net") +// The available values on the system are printed by the command +// "apt-cache policy", and can be debugged by running +// "unattended-upgrades -d" and looking at the log file. +// +// Within lines unattended-upgrades allows 2 macros whose values are +// derived from /etc/debian_version: +// ${distro_id} Installed origin. +// ${distro_codename} Installed codename (eg, "jessie") +Unattended-Upgrade::Origins-Pattern { + // Codename based matching: + // This will follow the migration of a release through different + // archives (e.g. from testing to stable and later oldstable). +// "o=Debian,n=jessie"; +// "o=Debian,n=jessie-updates"; +// "o=Debian,n=jessie-proposed-updates"; +// "o=Debian,n=jessie,l=Debian-Security"; + + // Archive or Suite based matching: + // Note that this will silently match a different release after + // migration to the specified archive (e.g. testing becomes the + // new stable). +// "o=Debian,a=stable"; +// "o=Debian,a=stable-updates"; +// "o=Debian,a=proposed-updates"; + "origin=Debian,codename=${distro_codename},label=Debian-Security"; +}; + +// List of packages to not update (regexp are supported) +Unattended-Upgrade::Package-Blacklist { +// "vim"; +// "libc6"; +// "libc6-dev"; +// "libc6-i686"; +}; + +// This option allows you to control if on a unclean dpkg exit +// unattended-upgrades will automatically run +// dpkg --force-confold --configure -a +// The default is true, to ensure updates keep getting installed +//Unattended-Upgrade::AutoFixInterruptedDpkg "false"; + +// Split the upgrade into the smallest possible chunks so that +// they can be interrupted with SIGUSR1. This makes the upgrade +// a bit slower but it has the benefit that shutdown while a upgrade +// is running is possible (with a small delay) +//Unattended-Upgrade::MinimalSteps "true"; + +// Install all unattended-upgrades when the machine is shuting down +// instead of doing it in the background while the machine is running +// This will (obviously) make shutdown slower +//Unattended-Upgrade::InstallOnShutdown "true"; + +// Send email to this address for problems or packages upgrades +// If empty or unset then no email is sent, make sure that you +// have a working mail setup on your system. A package that provides +// 'mailx' must be installed. E.g. "user@example.com" +//Unattended-Upgrade::Mail "root"; + +// Set this value to "true" to get emails only on errors. Default +// is to always send a mail if Unattended-Upgrade::Mail is set +//Unattended-Upgrade::MailOnlyOnError "true"; + +// Do automatic removal of new unused dependencies after the upgrade +// (equivalent to apt-get autoremove) +//Unattended-Upgrade::Remove-Unused-Dependencies "false"; + +// Automatically reboot *WITHOUT CONFIRMATION* if +// the file /var/run/reboot-required is found after the upgrade +//Unattended-Upgrade::Automatic-Reboot "false"; + +// If automatic reboot is enabled and needed, reboot at the specific +// time instead of immediately +// Default: "now" +//Unattended-Upgrade::Automatic-Reboot-Time "02:00"; + +// Use apt bandwidth limit feature, this example limits the download +// speed to 70kb/sec +//Acquire::http::Dl-Limit "70"; diff --git a/tests/root/etc/apt/apt.conf.d/70debconf b/tests/root/etc/apt/apt.conf.d/70debconf new file mode 100644 index 0000000..0c8b4ca --- /dev/null +++ b/tests/root/etc/apt/apt.conf.d/70debconf @@ -0,0 +1,3 @@ +// Pre-configure all packages with debconf before they are installed. +// If you don't like it, comment it out. +DPkg::Pre-Install-Pkgs {"/usr/sbin/dpkg-preconfigure --apt || true";}; diff --git a/tests/root/etc/apt/apt.conf.d/90cloud-init-pipelining b/tests/root/etc/apt/apt.conf.d/90cloud-init-pipelining new file mode 100644 index 0000000..bbea353 --- /dev/null +++ b/tests/root/etc/apt/apt.conf.d/90cloud-init-pipelining @@ -0,0 +1,2 @@ +//Written by cloud-init per 'apt_pipelining' +Acquire::http::Pipeline-Depth "0"; diff --git a/tests/root/etc/httpd/conf.modules.d/00-base.conf b/tests/root/etc/httpd/conf.modules.d/00-base.conf new file mode 100644 index 0000000..ec9acf1 --- /dev/null +++ b/tests/root/etc/httpd/conf.modules.d/00-base.conf @@ -0,0 +1,67 @@ +# +# This file loads most of the modules included with the Apache HTTP +# Server itself. +# + +LoadModule access_compat_module modules/mod_access_compat.so +LoadModule actions_module modules/mod_actions.so +LoadModule alias_module modules/mod_alias.so +LoadModule allowmethods_module modules/mod_allowmethods.so +LoadModule auth_basic_module modules/mod_auth_basic.so +LoadModule auth_digest_module modules/mod_auth_digest.so +LoadModule authn_anon_module modules/mod_authn_anon.so +LoadModule authn_core_module modules/mod_authn_core.so +LoadModule authn_dbd_module modules/mod_authn_dbd.so +LoadModule authn_dbm_module modules/mod_authn_dbm.so +LoadModule authn_file_module modules/mod_authn_file.so +LoadModule authn_socache_module modules/mod_authn_socache.so +LoadModule authz_core_module modules/mod_authz_core.so +LoadModule authz_dbd_module modules/mod_authz_dbd.so +LoadModule authz_dbm_module modules/mod_authz_dbm.so +LoadModule authz_groupfile_module modules/mod_authz_groupfile.so +LoadModule authz_host_module modules/mod_authz_host.so +LoadModule authz_owner_module modules/mod_authz_owner.so +LoadModule authz_user_module modules/mod_authz_user.so +LoadModule autoindex_module modules/mod_autoindex.so +LoadModule cache_module modules/mod_cache.so +LoadModule cache_disk_module modules/mod_cache_disk.so +LoadModule cache_socache_module modules/mod_cache_socache.so +LoadModule data_module modules/mod_data.so +LoadModule dbd_module modules/mod_dbd.so +LoadModule deflate_module modules/mod_deflate.so +LoadModule dir_module modules/mod_dir.so +LoadModule dumpio_module modules/mod_dumpio.so +LoadModule echo_module modules/mod_echo.so +LoadModule env_module modules/mod_env.so +LoadModule expires_module modules/mod_expires.so +LoadModule ext_filter_module modules/mod_ext_filter.so +LoadModule filter_module modules/mod_filter.so +LoadModule headers_module modules/mod_headers.so +LoadModule include_module modules/mod_include.so +LoadModule info_module modules/mod_info.so +LoadModule log_config_module modules/mod_log_config.so +LoadModule logio_module modules/mod_logio.so +LoadModule macro_module modules/mod_macro.so +LoadModule mime_magic_module modules/mod_mime_magic.so +LoadModule mime_module modules/mod_mime.so +LoadModule negotiation_module modules/mod_negotiation.so +LoadModule remoteip_module modules/mod_remoteip.so +LoadModule reqtimeout_module modules/mod_reqtimeout.so +LoadModule request_module modules/mod_request.so +LoadModule rewrite_module modules/mod_rewrite.so +LoadModule setenvif_module modules/mod_setenvif.so +LoadModule slotmem_plain_module modules/mod_slotmem_plain.so +LoadModule slotmem_shm_module modules/mod_slotmem_shm.so +LoadModule socache_dbm_module modules/mod_socache_dbm.so +LoadModule socache_memcache_module modules/mod_socache_memcache.so +LoadModule socache_shmcb_module modules/mod_socache_shmcb.so +LoadModule status_module modules/mod_status.so +LoadModule substitute_module modules/mod_substitute.so +LoadModule suexec_module modules/mod_suexec.so +LoadModule unique_id_module modules/mod_unique_id.so +LoadModule unixd_module modules/mod_unixd.so +LoadModule userdir_module modules/mod_userdir.so +LoadModule version_module modules/mod_version.so +LoadModule vhost_alias_module modules/mod_vhost_alias.so +LoadModule watchdog_module modules/mod_watchdog.so + diff --git a/tests/root/etc/httpd/conf.modules.d/00-dav.conf b/tests/root/etc/httpd/conf.modules.d/00-dav.conf new file mode 100644 index 0000000..e6af8de --- /dev/null +++ b/tests/root/etc/httpd/conf.modules.d/00-dav.conf @@ -0,0 +1,3 @@ +LoadModule dav_module modules/mod_dav.so +LoadModule dav_fs_module modules/mod_dav_fs.so +LoadModule dav_lock_module modules/mod_dav_lock.so diff --git a/tests/root/etc/httpd/conf.modules.d/00-lua.conf b/tests/root/etc/httpd/conf.modules.d/00-lua.conf new file mode 100644 index 0000000..9e0d0db --- /dev/null +++ b/tests/root/etc/httpd/conf.modules.d/00-lua.conf @@ -0,0 +1 @@ +LoadModule lua_module modules/mod_lua.so diff --git a/tests/root/etc/httpd/conf.modules.d/00-mpm.conf b/tests/root/etc/httpd/conf.modules.d/00-mpm.conf new file mode 100644 index 0000000..dcfd4d3 --- /dev/null +++ b/tests/root/etc/httpd/conf.modules.d/00-mpm.conf @@ -0,0 +1,23 @@ +# Select the MPM module which should be used by uncommenting exactly +# one of the following LoadModule lines. See the httpd.service(8) man +# page for more information on changing the MPM. + +# prefork MPM: Implements a non-threaded, pre-forking web server +# See: http://httpd.apache.org/docs/2.4/mod/prefork.html +# +# NOTE: If enabling prefork, the httpd_graceful_shutdown SELinux +# boolean should be enabled, to allow graceful stop/shutdown. +# +#LoadModule mpm_prefork_module modules/mod_mpm_prefork.so + +# worker MPM: Multi-Processing Module implementing a hybrid +# multi-threaded multi-process web server +# See: http://httpd.apache.org/docs/2.4/mod/worker.html +# +#LoadModule mpm_worker_module modules/mod_mpm_worker.so + +# event MPM: A variant of the worker MPM with the goal of consuming +# threads only for connections with active processing +# See: http://httpd.apache.org/docs/2.4/mod/event.html +# +LoadModule mpm_event_module modules/mod_mpm_event.so diff --git a/tests/root/etc/httpd/conf.modules.d/00-optional.conf b/tests/root/etc/httpd/conf.modules.d/00-optional.conf new file mode 100644 index 0000000..ef584ec --- /dev/null +++ b/tests/root/etc/httpd/conf.modules.d/00-optional.conf @@ -0,0 +1,18 @@ +# +# This file lists modules included with the Apache HTTP Server +# which are not enabled by default. +# + +#LoadModule asis_module modules/mod_asis.so +#LoadModule buffer_module modules/mod_buffer.so +#LoadModule heartbeat_module modules/mod_heartbeat.so +#LoadModule heartmonitor_module modules/mod_heartmonitor.so +#LoadModule usertrack_module modules/mod_usertrack.so +#LoadModule dialup_module modules/mod_dialup.so +#LoadModule charset_lite_module modules/mod_charset_lite.so +#LoadModule log_debug_module modules/mod_log_debug.so +#LoadModule log_forensic_module modules/mod_log_forensic.so +#LoadModule ratelimit_module modules/mod_ratelimit.so +#LoadModule reflector_module modules/mod_reflector.so +#LoadModule sed_module modules/mod_sed.so +#LoadModule speling_module modules/mod_speling.so diff --git a/tests/root/etc/httpd/conf.modules.d/00-proxy.conf b/tests/root/etc/httpd/conf.modules.d/00-proxy.conf new file mode 100644 index 0000000..448eb63 --- /dev/null +++ b/tests/root/etc/httpd/conf.modules.d/00-proxy.conf @@ -0,0 +1,17 @@ +# This file configures all the proxy modules: +LoadModule proxy_module modules/mod_proxy.so +LoadModule lbmethod_bybusyness_module modules/mod_lbmethod_bybusyness.so +LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so +LoadModule lbmethod_bytraffic_module modules/mod_lbmethod_bytraffic.so +LoadModule lbmethod_heartbeat_module modules/mod_lbmethod_heartbeat.so +LoadModule proxy_ajp_module modules/mod_proxy_ajp.so +LoadModule proxy_balancer_module modules/mod_proxy_balancer.so +LoadModule proxy_connect_module modules/mod_proxy_connect.so +LoadModule proxy_express_module modules/mod_proxy_express.so +LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so +LoadModule proxy_fdpass_module modules/mod_proxy_fdpass.so +LoadModule proxy_ftp_module modules/mod_proxy_ftp.so +LoadModule proxy_http_module modules/mod_proxy_http.so +LoadModule proxy_hcheck_module modules/mod_proxy_hcheck.so +LoadModule proxy_scgi_module modules/mod_proxy_scgi.so +LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so diff --git a/tests/root/etc/httpd/conf.modules.d/00-systemd.conf b/tests/root/etc/httpd/conf.modules.d/00-systemd.conf new file mode 100644 index 0000000..b208c97 --- /dev/null +++ b/tests/root/etc/httpd/conf.modules.d/00-systemd.conf @@ -0,0 +1,2 @@ +# This file configures systemd module: +LoadModule systemd_module modules/mod_systemd.so diff --git a/tests/root/etc/httpd/conf.modules.d/01-cgi.conf b/tests/root/etc/httpd/conf.modules.d/01-cgi.conf new file mode 100644 index 0000000..5b8b936 --- /dev/null +++ b/tests/root/etc/httpd/conf.modules.d/01-cgi.conf @@ -0,0 +1,14 @@ +# This configuration file loads a CGI module appropriate to the MPM +# which has been configured in 00-mpm.conf. mod_cgid should be used +# with a threaded MPM; mod_cgi with the prefork MPM. + + + LoadModule cgid_module modules/mod_cgid.so + + + LoadModule cgid_module modules/mod_cgid.so + + + LoadModule cgi_module modules/mod_cgi.so + + diff --git a/tests/root/etc/httpd/conf.modules.d/10-h2.conf b/tests/root/etc/httpd/conf.modules.d/10-h2.conf new file mode 100644 index 0000000..12c28aa --- /dev/null +++ b/tests/root/etc/httpd/conf.modules.d/10-h2.conf @@ -0,0 +1 @@ +LoadModule http2_module modules/mod_http2.so diff --git a/tests/root/etc/httpd/conf.modules.d/10-mod_dnssd.conf b/tests/root/etc/httpd/conf.modules.d/10-mod_dnssd.conf new file mode 100644 index 0000000..9a9d48d --- /dev/null +++ b/tests/root/etc/httpd/conf.modules.d/10-mod_dnssd.conf @@ -0,0 +1 @@ +LoadModule dnssd_module modules/mod_dnssd.so diff --git a/tests/root/etc/httpd/conf.modules.d/10-proxy_h2.conf b/tests/root/etc/httpd/conf.modules.d/10-proxy_h2.conf new file mode 100644 index 0000000..61dc6d0 --- /dev/null +++ b/tests/root/etc/httpd/conf.modules.d/10-proxy_h2.conf @@ -0,0 +1 @@ +LoadModule proxy_http2_module modules/mod_proxy_http2.so diff --git a/tests/root/etc/httpd/conf.modules.d/README b/tests/root/etc/httpd/conf.modules.d/README new file mode 100644 index 0000000..d33d1d4 --- /dev/null +++ b/tests/root/etc/httpd/conf.modules.d/README @@ -0,0 +1,9 @@ + +This directory holds configuration files for the Apache HTTP Server; +any files in this directory which have the ".conf" extension will be +processed as httpd configuration files. This directory contains +configuration fragments necessary only to load modules. +Administrators should use the directory "/etc/httpd/conf.d" to modify +the configuration of httpd, or any modules. + +Files are processed in alphanumeric order. diff --git a/tests/root/etc/resolv.conf b/tests/root/etc/resolv.conf new file mode 100644 index 0000000..b0fc624 --- /dev/null +++ b/tests/root/etc/resolv.conf @@ -0,0 +1,6 @@ +; Created by cloud-init on instance boot automatically, do not edit. +; +search awsqualif.net aws.eu-west-1.censured_here +nameserver 192.168.0.1 +nameserver 192.168.0.2 +options timeout:2 rotate diff --git a/tests/root/etc/samba/smb.conf b/tests/root/etc/samba/smb.conf index ec53c0a..d8f5237 100644 --- a/tests/root/etc/samba/smb.conf +++ b/tests/root/etc/samba/smb.conf @@ -69,7 +69,7 @@ # you want to listen on (never omit localhost) # # Hosts Allow/Hosts Deny lets you restrict who can connect, and you can -# specifiy it as a per share option as well +# specify it as a per share option as well # workgroup = MYGROUP server string = Samba Server Version %v @@ -160,7 +160,7 @@ # the login script name depends on the unix user used ; logon script = %u.bat ; logon path = \\%L\Profiles\%u - # disables profiles support by specifing an empty path + # disables profiles support by specifying an empty path ; logon path = ; add user script = /usr/sbin/useradd "%u" -n -g users diff --git a/tests/root/etc/squid/squid.conf b/tests/root/etc/squid/squid.conf index 208625c..92d4871 100644 --- a/tests/root/etc/squid/squid.conf +++ b/tests/root/etc/squid/squid.conf @@ -100,7 +100,7 @@ # The default of 0 is used for helpers who only supports # one request at a time. Setting this changes the protocol used to # include a channel number first on the request/response line, allowing -# multiple requests to be sent to the same helper in parallell without +# multiple requests to be sent to the same helper in parallel without # wating for the response. # Must not be set unless it's known the helper supports this. # auth_param basic concurrency 0 @@ -2354,7 +2354,7 @@ refresh_pattern . 0 20% 4320 # downloads. # # When the user aborts a request, Squid will check the -# quick_abort values to the amount of data transfered until +# quick_abort values to the amount of data transferred until # then. # # If the transfer has less than 'quick_abort_min' KB remaining, @@ -2393,7 +2393,7 @@ refresh_pattern . 0 20% 4320 # negative caching of DNS lookups. # # WARNING: This setting VIOLATES RFC 2616 when non-zero. -# If you have problems with eror pages caching, set to '0 seconds' +# If you have problems with error pages caching, set to '0 seconds' # #Default: # negative_ttl 5 minutes @@ -3842,7 +3842,7 @@ icp_port 3130 # # The squid developers are interested in making squid available in # a wide variety of languages. If you are making translations for a -# langauge that Squid does not currently provide please consider +# language that Squid does not currently provide please consider # contributing your translation back to the project. # see http://wiki.squid-cache.org/Translations # diff --git a/tests/root/etc/sysconfig/atd b/tests/root/etc/sysconfig/atd index 056ce81..db44f79 100644 --- a/tests/root/etc/sysconfig/atd +++ b/tests/root/etc/sysconfig/atd @@ -3,7 +3,7 @@ # -l Specifies a limiting load factor, over which batch jobs should not be run, instead of the compile-time # choice of 0.8. For an SMP system with n CPUs, you will probably want to set this higher than n-1. # -# -b Specifiy the minimum interval in seconds between the start of two batch jobs (60 default). +# -b Specify the minimum interval in seconds between the start of two batch jobs (60 default). #example: #OPTS="-l 4 -b 120" diff --git a/tests/root/etc/sysconfig/autofs b/tests/root/etc/sysconfig/autofs index 546fcd2..2130bba 100644 --- a/tests/root/etc/sysconfig/autofs +++ b/tests/root/etc/sysconfig/autofs @@ -30,7 +30,7 @@ BROWSE_MODE="yes" # # Define server URIs # -# LDAP_URI - space seperated list of server uris of the form +# LDAP_URI - space separated list of server uris of the form # ://[/] where can be ldap # or ldaps. The option can be given multiple times. # Map entries that include a server name override diff --git a/tests/root/etc/sysconfig/hsqldb b/tests/root/etc/sysconfig/hsqldb index 5e1d587..abb7d14 100644 --- a/tests/root/etc/sysconfig/hsqldb +++ b/tests/root/etc/sysconfig/hsqldb @@ -119,7 +119,7 @@ AUTH_FILE=${HSQLDB_HOME}/sqltool.rc # HSQLDB) are as follows: # If [it] is not set, then static methods of all available Java classes # can be accessed as functions in HSQLDB. If the property is set, then -# only the list of semicolon seperated method names becomes accessible. +# only the list of semicolon separated method names becomes accessible. # An empty property value means no class is accessible. # Regardless of the value of hsqldb.method_class_names, methods in # org.hsqldb.Library will be accessible. diff --git a/tests/root/etc/sysconfig/nfs b/tests/root/etc/sysconfig/nfs index 5e99161..1325c06 100644 --- a/tests/root/etc/sysconfig/nfs +++ b/tests/root/etc/sysconfig/nfs @@ -18,7 +18,7 @@ MOUNTD_PORT=892 #RQUOTAD="/usr/sbin/rpc.rquotad" # Port rquotad should listen on. #RQUOTAD_PORT=875 -# Optinal options passed to rquotad +# Optional options passed to rquotad #RPCRQUOTADOPTS="" # # diff --git a/tests/root/etc/sysconfig/readonly-root b/tests/root/etc/sysconfig/readonly-root index dbf1829..a19f7ec 100644 --- a/tests/root/etc/sysconfig/readonly-root +++ b/tests/root/etc/sysconfig/readonly-root @@ -13,5 +13,5 @@ RW_OPTIONS= STATE_LABEL=stateless-state # Where to mount to the persistent data STATE_MOUNT=/var/lib/stateless/state -# Options to use for peristent mount +# Options to use for persistent mount STATE_OPTIONS= diff --git a/tests/root/etc/sysconfig/rsyslog b/tests/root/etc/sysconfig/rsyslog index 7fa4b78..6f6c147 100644 --- a/tests/root/etc/sysconfig/rsyslog +++ b/tests/root/etc/sysconfig/rsyslog @@ -1,7 +1,7 @@ # Options to syslogd # -m 0 disables 'MARK' messages. # -r enables logging from remote machines -# -x disables DNS lookups on messages recieved with -r +# -x disables DNS lookups on messages received with -r # See syslogd(8) for more details SYSLOGD_OPTIONS="-m 0" # Options to klogd diff --git a/tests/run.tests b/tests/run.tests index 97d81ec..7a9a2d9 100644 --- a/tests/run.tests +++ b/tests/run.tests @@ -16,7 +16,7 @@ # of aug_srun # ERRCODE - one of the error codes defined in enum errcode_t in augeas.h # without the AUG_ prefix, i.e. NOERROR, EMMATCH etc. If ERRCODE -# is ommitted, it defaults to NOERROR +# is omitted, it defaults to NOERROR # MODULE - the name of a module that should be loaded before the test # COMMANDS - the commands to hand to aug_srun; can be multiple lines, # which are passed as one string. diff --git a/tests/test-api.c b/tests/test-api.c index 8590311..fda8ab6 100644 --- a/tests/test-api.c +++ b/tests/test-api.c @@ -705,7 +705,7 @@ static void testLoadFile(CuTest *tc) { CuAssertIntEquals(tc, AUG_ENOLENS, aug_error(aug)); /* augeas should return without an error when trying to load a - nonexistant file that would be handled by a lens */ + nonexistent file that would be handled by a lens */ r = aug_load_file(aug, "/etc/mtab"); CuAssertRetSuccess(tc, r); r = aug_match(aug, "/files/etc/mtab", NULL); @@ -735,6 +735,40 @@ static void testLoadBadPath(CuTest *tc) { aug_close(aug); } +/* If a lens is set to a partial path which happens to actually resolve to + a file when appended to the loadpath, we went into an infinite loop of + loading a module, but then not realizing that it had actually been + loaded, reloading it over and over again. + See https://github.com/hercules-team/augeas/issues/522 +*/ +static void testLoadBadLens(CuTest *tc) { + struct augeas *aug; + int r; + char *lp; + + // This setup depends on the fact that + // loadpath == abs_top_srcdir + "/lenses" + r = asprintf(&lp, "%s:%s", loadpath, abs_top_srcdir); + CuAssert(tc, "failed to allocate loadpath", (r >= 0)); + + aug = aug_init(root, lp, AUG_NO_STDINC|AUG_NO_LOAD); + CuAssertPtrNotNull(tc, aug); + CuAssertIntEquals(tc, AUG_NOERROR, aug_error(aug)); + + r = aug_set(aug, "/augeas/load/Fstab/lens", "lenses/Fstab.lns"); + CuAssertRetSuccess(tc, r); + + r = aug_load(aug); + CuAssertRetSuccess(tc, r); + + // We used to record the error to load the lens above against every + // lens that we tried to load after it. + r = aug_match(aug, "/augeas//error", NULL); + CuAssertIntEquals(tc, 1, r); + + free(lp); +} + /* Test the aug_ns_* functions */ static void testAugNs(CuTest *tc) { struct augeas *aug; @@ -804,6 +838,7 @@ int main(void) { SUITE_ADD_TEST(suite, testRm); SUITE_ADD_TEST(suite, testLoadFile); SUITE_ADD_TEST(suite, testLoadBadPath); + SUITE_ADD_TEST(suite, testLoadBadLens); SUITE_ADD_TEST(suite, testAugNs); abs_top_srcdir = getenv("abs_top_srcdir"); diff --git a/tests/test-save-empty.sh b/tests/test-save-empty.sh index 3ca8ddd..17650f7 100755 --- a/tests/test-save-empty.sh +++ b/tests/test-save-empty.sh @@ -1,6 +1,6 @@ #!/bin/sh -# Test that we report an error when writing to nonexistant dirs +# Test that we report an error when writing to nonexistent dirs # but that we do create new files correctly save_hosts() { -- 2.7.4