Imported Upstream version 1.11.0 upstream/1.11.0
authorDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 3 Jul 2019 00:21:19 +0000 (09:21 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 3 Jul 2019 00:21:19 +0000 (09:21 +0900)
51 files changed:
.travis.yml
NEWS
configure.ac
doc/naturaldocs/conf/c_api/Topics.txt
doc/naturaldocs/conf/lenses/Languages.txt
doc/naturaldocs/conf/lenses/Menu.txt
doc/naturaldocs/conf/lenses/Topics.txt
lenses/chrony.aug
lenses/dhclient.aug
lenses/fstab.aug
lenses/grub.aug
lenses/httpd.aug
lenses/json.aug
lenses/multipath.aug
lenses/nginx.aug
lenses/redis.aug
lenses/rsyslog.aug
lenses/strongswan.aug [new file with mode: 0644]
lenses/systemd.aug
lenses/tests/test_chrony.aug
lenses/tests/test_dhclient.aug
lenses/tests/test_fstab.aug
lenses/tests/test_grub.aug
lenses/tests/test_httpd.aug
lenses/tests/test_json.aug
lenses/tests/test_multipath.aug
lenses/tests/test_nginx.aug
lenses/tests/test_redis.aug
lenses/tests/test_rsyslog.aug
lenses/tests/test_strongswan.aug [new file with mode: 0644]
man/augmatch.pod
src/augeas.c
src/augeas.h
src/augmatch.c
src/augparse.c
src/augtool.c
src/builtin.c
src/fa.c
src/get.c
src/internal.h
src/jmt.c
src/lens.c
src/pathx.c
src/put.c
src/regexp.c
src/syntax.c
src/syntax.h
src/transform.c
tests/Makefile.am
tests/test-api.c
tests/test-augmatch.sh [new file with mode: 0755]

index 3df9693..8be1d2a 100644 (file)
@@ -14,4 +14,4 @@ notifications:
   irc:
     channels:
       - "irc.freenode.org#augeas"
-script: ./configure && make && make check && ./src/try valgrind
+script: ./configure && make && make check VERBOSE=1 && ./src/try valgrind
diff --git a/NEWS b/NEWS
index a03936b..1f42452 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,38 @@
+1.11.0 - 2018-08-24
+  - General changes/additions
+    * augmatch: add a --quiet option; make the exit status useful to tell
+                whether there was a match or not
+    * Drastically reduce the amount of memory needed to evaluate complex
+      path expressions against large files (Issue #569)
+    * Fix a segfault on OSX when 'augmatch' is run without any
+      arguments (Issue #556)
+  - API changes
+    * aug_source did not in fact return the source; and always returned
+      NULL for that. That has been fixed.
+  - Lens changes/additions
+    * Chrony: add new options supported in chrony 3.2 and 3.3 (Miroslav Lichvar)
+    * Dhclient: fix parsing of append/prepend and similar directives
+                (John Morrissey)
+    * Fstab: allow leading whitespace in mount entry lines
+             (Pino Toscano) (Issue #544)
+    * Grub: tolerate some invalid entries. Those invalid entries
+            get mapped to '#error' nodes
+    * Httpd: accept comments with whitespace right after a tag
+             opening a section (Issue #577)
+    * Json: allow escaped slashes in strings (Issue #557)
+    * Multipath: accept regular expressions for devnode, wwid, and property
+      in blacklist and blacklist_exceptions sections (Issue #564)
+    * Nginx: parse /etc/nginx/sites-enabled (plumbeo)
+             allow semicolons inside double quoted strings in
+             simple directives, and allow simple directives without
+             an argument (Issue #566)
+    * Redis: accept the 'bind' statement with multiple IP addresses
+             (yannh) (Issue #194)
+    * Rsyslog: support include() directive introduced in rsyslog 8.33
+    * Strongswan: new lens (Kaarle Ritvanen)
+    * Systemd: do not try to treat *.d or *.wants directories as
+               configuration files (Issue #548)
+
 1.10.1 - 2018-01-29
   - API changes
     * Fix a symbol versioning mistake in libfa that unnecessarily broke ABI
index 41c8eb1..11d7ade 100644 (file)
@@ -1,4 +1,4 @@
-AC_INIT(augeas, 1.10.1)
+AC_INIT(augeas, 1.11.0)
 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], [24:0:24])
-AC_SUBST([LIBFA_VERSION_INFO], [6:1:5])
+AC_SUBST([LIBAUGEAS_VERSION_INFO], [24:1:24])
+AC_SUBST([LIBFA_VERSION_INFO], [6:2:5])
 
 AC_GNU_SOURCE
 
index 3a45bb3..905270f 100644 (file)
@@ -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 characters: - / . '
+#    spaces, and these charaters: - / . '
 #
 # Plural: [name]
 #    Sets the plural name of the topic type, if different.
index 32fbe4e..47a9622 100644 (file)
@@ -117,7 +117,7 @@ Language: Augeas
 
    Extension: aug
    Block Comment: (* *)
+   Augeas Test Prototype Enders: \n let test module filter
    Augeas Lens Prototype Enders: \n let test module filter
    Augeas Variable Prototype Enders: \n let test module filter
-   Augeas Test Prototype Enders: \n let test module filter
    Perl Package: NaturalDocs::Languages::Augeas
index a41b530..190907f 100644 (file)
@@ -204,6 +204,11 @@ Group: Specific Modules  {
    File: Yaml  (yaml.aug)
    File: Cron_User  (cron_user.aug)
    File: Oz  (oz.aug)
+   File: Getcap  (getcap.aug)
+   File: Rancid  (rancid.aug)
+   File: Rtadvd  (rtadvd.aug)
+   File: Strongswan  (strongswan.aug)
+   File: Termcap  (termcap.aug)
    }  # Group: Specific Modules
 
 Group: Generic Modules  {
index c0657ec..7054ff0 100644 (file)
@@ -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 characters: - / . '
+#    spaces, and these charaters: - / . '
 #
 # Plural: [name]
 #    Sets the plural name of the topic type, if different.
index 2792ced..9ce177b 100644 (file)
@@ -78,11 +78,13 @@ module Chrony =
     (* Variable: cmd_options
          Server/Peer/Pool options with values
     *)
-    let cmd_options = "key"
+    let cmd_options = "asymmetry"
+                    | "key"
                     | /maxdelay((dev)?ratio)?/
                     | /(min|max)poll/
                     | /(min|max)samples/
                     | "maxsources"
+                    | "mindelay"
                     | "offset"
                     | "polltarget"
                     | "port"
@@ -93,7 +95,7 @@ module Chrony =
          Server/Peer/Pool options without values
     *)
     let cmd_flags = "auto_offline"|"iburst"|"noselect"|"offline"|"prefer"
-                  |"require"|"trust"|"xleave"
+                  |"require"|"trust"|"xleave"|"burst"
 
     (* Variable: ntp_source
          Server/Peer/Pool key names
@@ -109,6 +111,7 @@ module Chrony =
          HW timestamping options with values
     *)
     let hwtimestamp_options = "minpoll"|"precision"|"rxcomp"|"txcomp"
+                            |"rxfilter"
 
     (* Variable: hwtimestamp_flags
          HW timestamping options without values
@@ -135,12 +138,12 @@ module Chrony =
     *)
     let refclock_options = "refid"|"lock"|"poll"|"dpoll"|"filter"|"rate"
                             |"minsamples"|"maxsamples"|"offset"|"delay"
-                            |"precision"|"maxdispersion"
+                            |"precision"|"maxdispersion"|"stratum"|"width"
 
     (* Variable: refclock_flags
          refclock options without values
     *)
-    let refclock_flags = "noselect"|"prefer"|"require"|"trust"
+    let refclock_flags = "noselect"|"pps"|"prefer"|"require"|"tai"|"trust"
 
     (* Variable: flags
          Options without values
@@ -265,7 +268,8 @@ module Chrony =
     let hwtimestamp = [ Util.indent . key "hwtimestamp"
                       . space . [ label "interface" . store no_space ]
                       . ( space . ( [ key hwtimestamp_flags ]
-                         | [ key hwtimestamp_options . space . store number ] )
+                         | [ key hwtimestamp_options . space
+                             . store no_space ] )
                         )*
                       . eol ]
     (* View: istepslew
index 33e68d4..19e924c 100644 (file)
@@ -93,19 +93,23 @@ let stmt_array        = [ key stmt_array_re
 let stmt_hash_re      = "send"
                       | "option"
 
-let stmt_hash         = [ key stmt_hash_re
-                        . sep_spc
-                        . ( [ key word . sep_spc . sto_to_spc_noeval ]
+let stmt_args         = ( [ key word . sep_spc . sto_to_spc_noeval ]
                           | [ key word . sep_spc . (rfc_code|eval) ] )
                         . sep_scl
-                        . comment_or_eol ]
+                        . comment_or_eol
+
+let stmt_hash         = [ key stmt_hash_re
+                        . sep_spc
+                        . stmt_args ]
 
 let stmt_opt_mod_re   = "append"
                       | "prepend"
                       | "default"
                       | "supersede"
 
-let stmt_opt_mod      = [ key stmt_opt_mod_re . sep_spc . stmt_hash ]
+let stmt_opt_mod      = [ key stmt_opt_mod_re
+                        . sep_spc
+                        . stmt_args ]
 
 (************************************************************************
  *                         BLOCK STATEMENTS
index 7087c34..2e54955 100644 (file)
@@ -23,6 +23,7 @@ module Fstab =
          Build.opt_list lns comma
 
   let record = [ seq "mntent" .
+                   Util.indent .
                    [ label "spec" . store spec ] . sep_tab .
                    [ label "file" . store file ] . sep_tab .
                    comma_sep_list "vfstype" .
index 06f9e79..24ad39b 100644 (file)
@@ -29,9 +29,6 @@ module Grub =
     (* View: eol *)
     let eol = Util.eol
 
-    (* View: del_to_eol *)
-    let del_to_eol = del /[^ \t\n]*/ ""
-
     (* View: spc *)
     let spc = Util.del_ws_spc
 
@@ -92,7 +89,22 @@ module Grub =
       eol ]
 
     (* View: kw_pres *)
-    let kw_pres (kw:string) = [ opt_ws . key kw . del_to_eol . eol ]
+    let kw_pres (kw:string) = [ opt_ws . key kw . eol ]
+
+    (* View: error
+     *   Parse a line that looks almost like a valid setting, but isn't,
+     *   into an '#error' node. Any line that starts with letters, but not
+     *   anything matching kw, is considered an error line.
+     *
+     *   Parameters:
+     *     kw:regexp - the valid keywords that are _not_ considered an
+     *                 error
+     *)
+    let error (kw:regexp) =
+      let not_kw = /[a-zA-Z]+/ - kw in
+      [ label "#error" . Util.del_opt_ws "\t"
+        . store (not_kw . /([^a-zA-Z\n].*[^ \t\n])?/) . eol ]
+
 
 (************************************************************************
  * Group:                 BOOT ENTRIES
@@ -138,8 +150,8 @@ module Grub =
         spc . [ label "from" . store Rx.no_spaces ] )? .
       eol ]
 
-    (* View: menu_setting *)
-    let menu_setting = kw_menu_arg "default"
+    (* View: menu_entry *)
+    let menu_entry = kw_menu_arg "default"
                      | kw_menu_arg "fallback"
                      | kw_pres "hiddenmenu"
                      | kw_menu_arg "timeout"
@@ -156,6 +168,21 @@ module Grub =
                      | device
                      | setkey
 
+    (* View: menu_error
+     *   Accept lines not matching menu_entry and stuff them into
+     *   '#error' nodes
+     *)
+    let menu_error =
+      let kw = /default|fallback|hiddenmenu|timeout|splashimage|gfxmenu/
+             |/foreground|background|verbose|boot|password|title/
+             |/serial|setkey|terminal|color|device/ in
+      error kw
+
+    (* View: menu_setting
+     *   a valid menu setting or a line that looks like one but is an #error
+     *)
+    let menu_setting = menu_entry | menu_error
+
     (* View: title *)
     let title = del /title[ \t=]+/ "title " . value_to_eol . eol
 
@@ -206,9 +233,9 @@ module Grub =
     let configfile =
       [ command "configfile" "\t" . spc . store Rx.no_spaces . eol ]
 
-    (* View: boot_setting
+    (* View: boot_entry
         <boot> entries *)
-    let boot_setting =
+    let boot_entry =
           let boot_arg_re = "root" | "initrd" | "rootnoverify" | "uuid"
                           | "findroot" | "bootfs" (* Solaris extensions *)
        in kw_boot_arg boot_arg_re
@@ -223,6 +250,21 @@ module Grub =
         | kw_pres "makeactive"
         | password_arg
 
+    (* View: boot_error
+     *   Accept lines not matching boot_entry and stuff them into
+     *   '#error' nodes
+     *)
+    let boot_error =
+      let kw = /lock|uuid|password|root|initrd|rootnoverify|findroot|bootfs/
+             |/configfile|chainloader|title|boot|quiet|kernel|module/
+             |/makeactive|savedefault|map/ in
+      error kw
+
+    (* View: boot_setting
+     *   a valid boot setting or a line that looks like one but is an #error
+     *)
+    let boot_setting = boot_entry | boot_error
+
     (* View: boot *)
     let boot =
       let line = ((boot_setting|comment)* . boot_setting)? in
index ff93a61..5600088 100644 (file)
@@ -171,7 +171,7 @@ let arg_sec = [ label "arg" . store (char_arg_sec+|comp|dquot|squot) ]
 
 let section (body:lens) =
     (* opt_eol includes empty lines *)
-    let opt_eol = del /([ \t]*#?\r?\n)*/ "\n" in
+    let opt_eol = del /([ \t]*#?[ \t]*\r?\n)*/ "\n" in
     let inner = (sep_spc . argv arg_sec)? . sep_osp .
              dels ">" . opt_eol . ((body|comment) . (body|empty|comment)*)? .
              indent . dels "</" in
index f47f9b0..c17f514 100644 (file)
@@ -23,7 +23,10 @@ let rbrace = Util.del_str "}"
 let lbrack = Util.del_str "[" . comments
 let rbrack = Util.del_str "]"
 
-let str_store = Quote.dquote . store /([^\\\\"]|\\\\("|n|r|t|\\\\))*/ . Quote.dquote  (* " Emacs, relax *)
+(* This follows the definition of 'string' at https://www.json.org/
+   It's a little wider than what's allowed there as it would accept
+   nonsensical \u escapes *)
+let str_store = Quote.dquote . store /([^\\"]|\\\\["\/bfnrtu\\])*/ . Quote.dquote
 
 let number = [ label "number" . store /-?[0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?/
              . comments ]
index ba0e8ce..03dacb5 100644 (file)
@@ -33,8 +33,6 @@ let qstr (k:regexp) =
   [ ikey k . ws . bare . eol ]
  |[ ikey k . ws . quoted . eol ]
 
-let wwid = kv "wwid" (Rx.word|"*")
-
 (* Settings that can be changed in various places *)
 let common_setting =
    qstr "path_selector"
@@ -109,15 +107,14 @@ let defaults =
 (* The blacklist and blacklist_exceptions sections *)
 let blacklist =
   let setting =
-    wwid
-   |qstr "devnode"
+    qstr /devnode|wwid|property/
    |device in
   section /blacklist(_exceptions)?/ setting
 
 (* A multipath subsection *)
 let multipath =
   let setting =
-    wwid
+    kv "wwid" (Rx.word|"*")
    |qstr "alias"
    |common_setting in
   section "multipath" setting
index 650fb01..9c485c1 100644 (file)
@@ -47,8 +47,11 @@ let block_re_all = block_re | "if" | "location" | "geo" | "map"
 let simple =
      let kw = word - block_re_all
   in let mask = [ label "mask" . Util.del_str "/" . store Rx.integer ]
-  in let sto = store /[^ \t\n;][^;]*/ . Sep.semicolon
-  in [ Util.indent . key kw . mask? . Sep.space . sto . (Util.eol|Util.comment_eol) ]
+  in let sto = store /[^ \t\n;#]([^";#]|"[^"]*\")*/
+  in [ Util.indent .
+       key kw . mask? .
+       (Sep.space . sto)? . Sep.semicolon .
+       (Util.eol|Util.comment_eol) ]
 
 (* View: server
      A simple server entry *)
@@ -120,9 +123,11 @@ let lns = ( Util.comment | Util.empty | directive )*
 let filter = incl "/etc/nginx/nginx.conf"
            . incl "/etc/nginx/conf.d/*.conf"
            . incl "/etc/nginx/sites-available/*"
+           . incl "/etc/nginx/sites-enabled/*"
            . incl "/usr/portage/www-servers/nginx/files/nginx.conf"
            . incl "/usr/local/etc/nginx/nginx.conf"
            . incl "/usr/local/etc/nginx/conf.d/*.conf"
            . incl "/usr/local/etc/nginx/sites-available/*"
+           . incl "/usr/local/etc/nginx/sites-enabled/*"
 
 let xfm = transform lns filter
index 28e99f3..70e18cc 100644 (file)
@@ -54,8 +54,8 @@ redis-server.
 *)
 let standard_entry =
      let reserved_k = "save" | "rename-command" | "slaveof"
-                    | "client-output-buffer-limit"
-  in let entry_noempty = [ indent . key k . del_ws_spc
+                    | "bind" | "client-output-buffer-limit"
+  in let entry_noempty = [ indent . key (k - reserved_k) . del_ws_spc
                          . Quote.do_quote_opt_nil (store v) . eol ]
   in let entry_empty = [ indent . key (k - reserved_k) . del_ws_spc
                          . dquote . store "" . dquote . eol ]
@@ -82,6 +82,15 @@ whitespaces.
 *)
 let slaveof_entry = [ indent . key slaveof . del_ws_spc . ip . del_ws_spc . port . eol ]
 
+(* View: bind_entry
+The "bind" entry can be passed one or several ip addresses. A bind
+statement "bind ip1 ip2 .. ipn" results in a tree
+{ "bind" { "ip" = ip1 } { "ip" = ip2 } ... { "ip" = ipn } }
+*)
+let bind_entry =
+  let ip = del_ws_spc . Quote.do_quote_opt_nil (store Rx.ip) in
+  indent . [ key "bind" . [ label "ip" . ip ]+ ] . eol
+
 let renamecmd = /rename-command/
 let from = [ label "from" . Quote.do_quote_opt_nil (store Rx.word) ]
 let to = [ label "to" . Quote.do_quote_opt_nil (store Rx.word) ]
@@ -112,6 +121,7 @@ let entry = standard_entry
           | save_entry
          | renamecmd_entry
          | slaveof_entry
+         | bind_entry
          | client_output_buffer_limit_entry
 
 (* View: lns
index 6648f3d..35f19a5 100644 (file)
@@ -33,7 +33,7 @@ let config_object_param = [ key /[A-Za-z.]+/ . Sep.equal . Quote.dquote
 let config_sep = del /[ \t]+|[ \t]*#.*\n[ \t]*/ " "
 
 let config_object =
-  [ key /action|global|input|module|parser|timezone/ .
+  [ key /action|global|input|module|parser|timezone|include/ .
     Sep.lbracket .
     config_object_param . ( config_sep . config_object_param )* .
     Sep.rbracket . Util.comment_or_eol ]
diff --git a/lenses/strongswan.aug b/lenses/strongswan.aug
new file mode 100644 (file)
index 0000000..69d112b
--- /dev/null
@@ -0,0 +1,41 @@
+(*
+Module: Strongswan
+  Lens for parsing strongSwan configuration files
+
+Authors:
+  Kaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>
+
+About: Reference
+  strongswan.conf(5)
+
+About: License
+  This file is licensed under the LGPL v2+
+*)
+
+module Strongswan =
+
+autoload xfm
+
+let ws = del /[\n\t ]*(#[\t ]*\n[\n\t ]*)*/
+
+let rec conf =
+          let name (sep:string) =
+               key (/[^\/.\{\}#\n\t ]+/ - /include/) . Util.del_ws_spc .
+               Util.del_str sep
+       in let val = store /[^\n\t ].*/ . Util.del_str "\n" . ws ""
+       in let sval = Util.del_ws_spc . val
+in (
+       [ Util.del_str "#" . label "#comment" . Util.del_opt_ws " " . val ] |
+       [ key "include" . sval ] |
+       [ name "=" . sval ] |
+       [ name "{" . ws "\n" . conf . Util.del_str "}" . ws "\n" ]
+)*
+
+let lns = ws "" . conf
+
+let xfm = transform lns (
+       incl "/etc/strongswan.d/*.conf" .
+       incl "/etc/strongswan.d/**/*.conf" .
+       incl "/etc/swanctl/conf.d/*.conf" .
+       incl "/etc/swanctl/swanctl.conf"
+)
index f7858e9..b868e86 100644 (file)
@@ -172,6 +172,10 @@ let filter = incl "/lib/systemd/system/*"
            . incl "/etc/systemd/system/*/*"
            . incl "/etc/systemd/logind.conf"
            . incl "/etc/sysconfig/*.systemd"
+           . excl "/lib/systemd/system/*.d"
+           . excl "/etc/systemd/system/*.d"
+           . excl "/lib/systemd/system/*.wants"
+           . excl "/etc/systemd/system/*.wants"
            . Util.stdexcl
 
 let xfm = transform lns filter
index ba201c2..9011caf 100644 (file)
@@ -22,6 +22,7 @@ server ntp5.example.com maxdelay 2 offline
 server ntp6.example.com maxdelay 2 iburst presend 2 xleave offset 1e-4
 server ntp7.example.com iburst presend 2 offline prefer trust require
 server ntp8.example.com minsamples 8 maxsamples 16 version 3
+server ntp9.example.com burst mindelay 0.1 asymmetry 0.5
 peer ntpc1.example.com
 pool pool1.example.com iburst maxsources 3
 allow
@@ -53,12 +54,13 @@ mailonchange root@localhost 0.5
 maxchange 1000 1 2
 maxdistance 1.0
 maxdrift 100
-hwtimestamp eth0 minpoll -2 txcomp 300e-9 rxcomp 645e-9 nocrossts
+hwtimestamp eth0 minpoll -2 txcomp 300e-9 rxcomp 645e-9 nocrossts rxfilter all
 initstepslew 30 foo.bar.com
 initstepslew 30 foo.bar.com baz.quz.com
 ratelimit interval 4 burst 16 leak 2
 cmdratelimit
-refclock SHM 0 refid SHM0 delay 0.1 offset 0.2 noselect
+refclock SHM 0 refid SHM0 delay 0.1 offset 0.2 noselect tai stratum 3
+refclock SOCK /var/run/chrony-GPS.sock pps width 0.1
 refclock PPS /dev/pps0 dpoll 2 poll 3 lock SHM0 rate 5 minsamples 8
 smoothtime 400 0.001 leaponly
 tempcomp /sys/class/hwmon/hwmon0/temp2_input 30 26000 0.0 0.000183 0.0
@@ -111,6 +113,11 @@ ntpsigndsocket /var/lib/samba/ntp_signd
     { "maxsamples" = "16" }
     { "version" = "3" }
   }
+  { "server" = "ntp9.example.com"
+    { "burst" }
+    { "mindelay" = "0.1" }
+    { "asymmetry" = "0.5" }
+  }
   { "peer" = "ntpc1.example.com" }
   { "pool" = "pool1.example.com"
     { "iburst" }
@@ -188,6 +195,7 @@ ntpsigndsocket /var/lib/samba/ntp_signd
     { "txcomp" = "300e-9" }
     { "rxcomp" = "645e-9" }
     { "nocrossts" }
+    { "rxfilter" = "all" }
   }
   { "initstepslew"
     { "threshold" = "30" }
@@ -211,6 +219,14 @@ ntpsigndsocket /var/lib/samba/ntp_signd
     { "delay" = "0.1" }
     { "offset" = "0.2" }
     { "noselect" }
+    { "tai" }
+    { "stratum" = "3" }
+  }
+  { "refclock"
+    { "driver" = "SOCK" }
+    { "parameter" = "/var/run/chrony-GPS.sock" }
+    { "pps" }
+    { "width" = "0.1" }
   }
   { "refclock"
     { "driver" = "PPS" }
index 6bc1d75..3db8ac9 100644 (file)
@@ -133,15 +133,12 @@ lease {
                { "second"  = "01" } } }
 
 
-test Dhclient.lns get "append option domain-name-servers 127.0.0.1;\n" =
+test Dhclient.lns get "append domain-name-servers 127.0.0.1;\n" =
     { "append"
-        { "option"
-            { "domain-name-servers" = "127.0.0.1" }
-        }
-    }
+        { "domain-name-servers" = "127.0.0.1" } }
 
-test Dhclient.lns put "" after set "/prepend/option/domain-name-servers" "127.0.0.1" = 
-    "prepend option domain-name-servers 127.0.0.1;\n"
+test Dhclient.lns put "" after set "/prepend/domain-name-servers" "127.0.0.1" =
+    "prepend domain-name-servers 127.0.0.1;\n"
 
 (* When = is used before the value, it's an evaluated string, see dhcp-eval *)
 test Dhclient.lns get "send dhcp-client-identifier = hardware;\n" =
index fa044ae..438f619 100644 (file)
@@ -11,6 +11,8 @@ module Test_fstab =
         { "dump" = "1" }
         { "passno" = "1" } }
 
+  let leading_ws = "   /dev/vg00/lv00\t /\t ext3\t    defaults        1 1\n"
+
   let trailing_ws = "/dev/vg00/lv00\t /\t ext3\t    defaults        1 1  \t\n"
 
   let gen_no_passno(passno:string) =
@@ -60,6 +62,8 @@ module Test_fstab =
 
   test Fstab.lns get simple = simple_tree
 
+  test Fstab.lns get leading_ws = simple_tree
+
   test Fstab.lns get trailing_ws = simple_tree
 
   test Fstab.lns get no_passno = no_passno_tree
index 8a0d9f4..7565720 100644 (file)
@@ -257,3 +257,28 @@ password --encrypted ^9^32kwzzX./3WISQ0C /boot/grub/custom.lst
     { "password" = "secret"
       { "md5" }
     } }
+
+  (* Test parsing of invalid entries via menu_error *)
+  test Grub.lns get "default=0\ncrud=no\n" =
+  { "default" = "0" }
+  { "#error" = "crud=no" }
+
+  (* We handle some pretty bizarre bad syntax *)
+  test Grub.lns get "default=0
+crud no
+valid:nope
+nonsense  =   yes
+bad arg1 arg2 arg3=v\n" =
+  { "default" = "0" }
+  { "#error" = "crud no" }
+  { "#error" = "valid:nope" }
+  { "#error" = "nonsense  =   yes" }
+  { "#error" = "bad arg1 arg2 arg3=v" }
+
+  (* Test parsing of invalid entries via boot_error *)
+  test Grub.lns get "title test
+    root (hd0,0)
+    crud foo\n" =
+  { "title" = "test"
+    { "root" = "(hd0,0)" }
+    { "#error" = "crud foo" } }
index 1d3737b..9887f7a 100644 (file)
@@ -627,3 +627,22 @@ test Httpd.directive get
     { "arg" = "X-Forwarded-Proto" }
     { "arg" = "https" }
     { "arg" = "expr=(%{HTTP:CF-Visitor}='{\"scheme\":\"https\"}')" } }
+
+(* Issue #577: we make the newline starting a section optional, including
+   an empty comment at the end of the line. This used to miss empty comments
+   with whitespace *)
+test Httpd.lns get "<If cond>#\n</If>\n" = { "If" { "arg" = "cond" } }
+
+test Httpd.lns get "<If cond># \n</If>\n" = { "If" { "arg" = "cond" } }
+
+test Httpd.lns get "<If cond>\n# \n</If>\n" =  { "If" { "arg" = "cond" } }
+
+test Httpd.lns get "<If cond># text\n</If>\n" =
+  { "If"
+    { "arg" = "cond" }
+    { "#comment" = "text" } }
+
+test Httpd.lns get "<If cond>\n\t# text\n</If>\n" =
+  { "If"
+    { "arg" = "cond" }
+    { "#comment" = "text" } }
index cb61cef..1ad0624 100644 (file)
@@ -494,3 +494,19 @@ test lns get "{ \"filesystem\": \"ext3\\\" \\\\ \t \r\n SEC_TYPE=\\\"ext2\" }\n"
     { "entry" = "filesystem"
       { "string" = "ext3\\\" \\\\ \t \r\n SEC_TYPE=\\\"ext2" } }
     {  } }
+
+test Json.str get "\"\\\"\"" = { "string" = "\\\"" }
+
+test Json.str get "\"\\\"" = *
+
+test Json.str get "\"\"\"" = *
+
+test Json.str get "\"\\u1234\"" = { "string" = "\u1234" }
+
+(* Allow spurious backslashes; Issue #557 *)
+test Json.str get "\"\\/\"" = { "string" = "\\/" }
+
+test lns get "{ \"download-dir\": \"\\/var\\/tmp\\/\" }" =
+  { "dict"
+    { "entry" = "download-dir"
+      { "string" = "\/var\/tmp\/" } } }
index 02c95eb..6e877b0 100644 (file)
@@ -192,4 +192,28 @@ test Multipath.lns get conf =
       { "path_grouping_policy" = "multibus" }
       { "polling_interval" = "9" }
       { "delay_watch_checks" = "10" }
-      { "delay_wait_checks" = "10" } } }
\ No newline at end of file
+      { "delay_wait_checks" = "10" } } }
+
+test Multipath.lns get "blacklist {
+        devnode \".*\"
+        wwid \"([a-z]+|ab?c).*\"
+}\n" =
+  { "blacklist"
+    { "devnode" = ".*" }
+    { "wwid" = "([a-z]+|ab?c).*" } }
+
+test Multipath.lns get "blacklist {\n property \"[a-z]+\"\n}\n" =
+  { "blacklist"
+    { "property" = "[a-z]+" } }
+
+(* Check that '""' inside a string works *)
+test Multipath.lns get "blacklist {
+  device {
+    vendor SomeCorp
+    product \"2.5\"\" SSD\"
+  }
+}\n" =
+  { "blacklist"
+    { "device"
+      { "vendor" = "SomeCorp" }
+      { "product" = "2.5\"\" SSD" } } }
index f70ff52..d17b70a 100644 (file)
@@ -275,3 +275,16 @@ test lns get "http {
       { "::1" = "2" }
       { "2001:0db8::" = "1"
         { "mask" = "32" } } } }
+
+test lns get "add_header X-XSS-Protection \"1; mode=block\" always;\n" =
+  { "add_header" = "X-XSS-Protection \"1; mode=block\" always" }
+
+test lns get "location /foo {
+  root /var/www/html;
+  internal;  # only valid in location blocks
+}\n" =
+  { "location"
+    { "#uri" = "/foo" }
+    { "root" = "/var/www/html" }
+    { "internal"
+      { "#comment" = "only valid in location blocks" } } }
index 12cc25c..dbe854c 100644 (file)
@@ -150,7 +150,7 @@ test Redis.lns get redis_conf =
   { "#comment" = "If you want you can bind a single interface, if the bind option is not" }
   { "#comment" = "specified all the interfaces will listen for incoming connections." }
   { }
-  { "bind" = "127.0.0.1" }
+  { "bind" { "ip" = "127.0.0.1" } }
   { }
   { "#comment" = "Note: you can disable saving at all commenting all the \"save\" lines." }
   { }
@@ -179,3 +179,17 @@ test Redis.lns get redis_conf =
      Empty value (GH issue #115) *)
 test Redis.lns get "notify-keyspace-events \"\"\n" =
   { "notify-keyspace-events" = "" }
+
+(* Test: Redis.lns
+     Multiple bind IP addresses (GH issue #194) *)
+test Redis.lns get "bind 127.0.0.1 \"::1\" 192.168.1.1\n" =
+  { "bind"
+      { "ip" = "127.0.0.1" }
+      { "ip" = "::1" }
+      { "ip" = "192.168.1.1" } }
+
+test Redis.lns get "bind 127.0.0.1\n bind 192.168.1.1\n" =
+  { "bind"
+    { "ip" = "127.0.0.1" } }
+  { "bind"
+    { "ip" = "192.168.1.1" } }
index b71d32c..5386f83 100644 (file)
@@ -199,3 +199,9 @@ test Rsyslog.lns get "module(load=\"imuxsock\"        # provides support for local s
     { "SysSock.Use" = "off" }
     { "#comment" = "Turn off message reception via local log socket;" } }
   { "#comment" = "local messages are retrieved through imjournal now." }
+
+(* Added in rsyslog 8.33 *)
+test Rsyslog.lns get "include(file=\"/etc/rsyslog.d/*.conf\" mode=\"optional\")\n" =
+  { "include"
+    { "file" = "/etc/rsyslog.d/*.conf" }
+    { "mode" = "optional" } }
diff --git a/lenses/tests/test_strongswan.aug b/lenses/tests/test_strongswan.aug
new file mode 100644 (file)
index 0000000..55fe905
--- /dev/null
@@ -0,0 +1,65 @@
+(*
+       Some of the configuration snippets have been copied from the strongSwan
+       source tree.
+*)
+
+module Test_Strongswan =
+
+(* conf/strongswan.conf *)
+let default = "
+# strongswan.conf - strongSwan configuration file
+#
+# Refer to the strongswan.conf(5) manpage for details
+#
+# Configuration changes should be made in the included files
+
+charon {
+       load_modular = yes
+       plugins {
+               include strongswan.d/charon/*.conf
+       }
+}
+
+include strongswan.d/*.conf
+"
+
+test Strongswan.lns get default =
+  { "#comment" = "strongswan.conf - strongSwan configuration file" }
+  { "#comment" = "Refer to the strongswan.conf(5) manpage for details" }
+  { "#comment" = "Configuration changes should be made in the included files" }
+  { "charon"
+    { "load_modular" = "yes" }
+    { "plugins" { "include" = "strongswan.d/charon/*.conf" } }
+  }
+  { "include" = "strongswan.d/*.conf" }
+
+(* conf/strongswan.conf.5.head.in *)
+let man_example = "
+       a = b
+       section-one {
+               somevalue = asdf
+               subsection {
+                       othervalue = xxx
+               }
+               # yei, a comment
+               yetanother = zz
+       }
+       section-two {
+               x = 12
+       }
+"
+
+test Strongswan.lns get man_example =
+  { "a" = "b" }
+  { "section-one"
+    { "somevalue" = "asdf" }
+    { "subsection" { "othervalue" = "xxx" } }
+    { "#comment" = "yei, a comment" }
+    { "yetanother" = "zz" }
+  }
+  { "section-two" { "x" = "12" } }
+
+test Strongswan.lns get "foo { bar = baz\n } quux {}\t#quuux\n" =
+  { "foo" { "bar" = "baz" } }
+  { "quux" }
+  { "#comment" = "quuux" }
index 94ad944..076bd14 100644 (file)
@@ -67,6 +67,10 @@ B<AUGEAS_LENS_LIB> will be searched for modules.
 
 Print only the value and not the label or the path of nodes.
 
+=item B<-q>, B<--quiet>
+
+Do not print anything. Exit with zero status if a match was found
+
 =back
 
 =head1 ENVIRONMENT VARIABLES
@@ -98,6 +102,11 @@ F</usr/share/augeas/lenses/dist>
   # show all the clients to which we are exporting /home
   augmatch -eom 'dir["/home"]/client' /etc/exports
 
+=head1 EXIT STATUS
+
+The exit status is 0 when there was at least one match, 1 if there was no
+match, and 2 if an error occurred.
+
 =head1 FILES
 
 Lenses and schema definitions in F</usr/share/augeas/lenses> and
index 8ec264c..df760f5 100644 (file)
@@ -1958,7 +1958,7 @@ int aug_source(const augeas *aug, const char *path, char **file_path) {
     ERR_THROW(r == 0, aug, AUG_ENOMATCH, "There is no node matching %s",
               path);
 
-    tree_source(aug, match);
+    *file_path = tree_source(aug, match);
     ERR_BAIL(aug);
 
     result = 0;
index d6a1a70..a877079 100644 (file)
@@ -403,6 +403,8 @@ int aug_print(const augeas *aug, FILE *out, const char *path);
  * contain the path to the toplevel node of that file underneath /files. If
  * it does not, *FILE_PATH will be NULL.
  *
+ * The caller is responsible for freeing *FILE_PATH
+ *
  * Returns:
  * 0 on success, or a negative value on failure. It is an error if PATH
  * matches more than one node.
index 20045c5..67c9d2a 100644 (file)
 #include <getopt.h>
 #include <stdbool.h>
 #include <ctype.h>
+#include <libgen.h>
 
 #include "memory.h"
 #include "augeas.h"
 #include <locale.h>
 
+#define EXIT_TROUBLE 2
+
 #define cleanup(_x) __attribute__((__cleanup__(_x)))
 
 const char *progname;
@@ -58,6 +61,8 @@ static void usage(void) {
 "  -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"
+"  -q, --quiet        do not print anything. Exit with zero status if a\n"
+"                     match was found\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"
@@ -82,7 +87,7 @@ static void die(bool cond, const char *format, ...) {
         va_start(args, format);
         vfprintf(stderr, format, args);
         va_end(args);
-        exit(EXIT_FAILURE);
+        exit(EXIT_TROUBLE);
     }
 }
 
@@ -119,7 +124,7 @@ static void check_error(struct augeas *aug) {
         if (msg != NULL) {
             fprintf(stderr, "%s\n", msg);
         }
-        exit(EXIT_FAILURE);
+        exit(EXIT_TROUBLE);
     }
 }
 
@@ -131,6 +136,7 @@ static void check_load_error(struct augeas *aug, const char *file) {
     const char *msg, *line, *col;
 
     aug_defvar(aug, "info", info);
+    free(info);
     die(aug_ns_count(aug, "info") == 0, "file %s does not exist\n", file);
 
     aug_defvar(aug, "error", "$info/error");
@@ -153,7 +159,7 @@ static void check_load_error(struct augeas *aug, const char *file) {
     } else {
         fprintf(stderr, "error reading %s: %s\n", file, msg);
     }
-    exit(EXIT_FAILURE);
+    exit(EXIT_TROUBLE);
 }
 
 /* We keep track of where we are in the tree when we are printing it by
@@ -236,17 +242,19 @@ static void print_tree(struct augeas *aug, int level,
 }
 
 /* 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) {
+ * only the nodes matching MATCH.
+ *
+ * Return EXIT_SUCCESS if there was at least one match, and EXIT_FAILURE
+ * if there was none.
+ */
+static int print(struct augeas *aug, const char *path, const char *match) {
     static const char *const match_var = "match";
 
-    cleanup(freep) struct node *nodes = NULL;
+    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);
     }
@@ -265,6 +273,12 @@ static void print(struct augeas *aug, const char *path, const char *match) {
         aug_defvar(aug, nodes[0].var, prefix);
         print_tree(aug, 0, prefix + strlen(path) + 1, nodes);
     }
+    for (int i=0; i < max_nodes; i++) {
+        free(nodes[i].var);
+    }
+    free(nodes);
+
+    return (count == 0) ? EXIT_FAILURE : EXIT_SUCCESS;
 }
 
 /* Look at the filename and try to guess based on the extension. The
@@ -297,6 +311,8 @@ int main(int argc, char **argv) {
     size_t matches_len = 0;
     const char *match = "*";
     bool print_lens = false;
+    bool quiet = false;
+    int result = EXIT_SUCCESS;
 
     struct option options[] = {
         { "help",       0, 0, 'h' },
@@ -310,13 +326,14 @@ int main(int argc, char **argv) {
         { "root",       1, 0, 'r' },
         { "print-lens", 0, 0, 'L' },
         { "exact",      0, 0, 'e' },
+        { "quiet",      0, 0, 'q' },
         { 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) {
+    while ((opt = getopt_long(argc, argv, "ahI:l:m:oSr:eLq", options, NULL)) != -1) {
         switch(opt) {
         case 'I':
             argz_add(&loadpath, &loadpath_len, optarg);
@@ -353,10 +370,13 @@ int main(int argc, char **argv) {
         case 'e':
             print_exact = true;
             break;
+        case 'q':
+            quiet = true;
+            break;
         default:
             fprintf(stderr, "Try '%s --help' for more information.\n",
                     progname);
-            exit(EXIT_FAILURE);
+            exit(EXIT_TROUBLE);
             break;
         }
     }
@@ -365,7 +385,7 @@ int main(int argc, char **argv) {
         fprintf(stderr, "Expected an input file\n");
         fprintf(stderr, "Try '%s --help' for more information.\n",
                 progname);
-        exit(EXIT_FAILURE);
+        exit(EXIT_TROUBLE);
     }
 
     const char *file = argv[optind];
@@ -414,13 +434,24 @@ int main(int argc, char **argv) {
     check_load_error(aug, file);
 
     char *path = format("/files%s", file);
+    aug_set(aug, "/augeas/context", path);
+
 
     if (matches_len > 0) {
         argz_stringify(matches, matches_len, '|');
         match = matches;
     }
 
-    print(aug, path, match);
+    if (quiet) {
+        int n = aug_match(aug, match, NULL);
+        check_error(aug);
+        result = (n == 0) ? EXIT_FAILURE : EXIT_SUCCESS;
+    } else {
+        result = print(aug, path, match);
+    }
+    free(path);
+
+    return result;
 }
 
 /*
index 96a686c..ad2342a 100644 (file)
@@ -140,6 +140,7 @@ int main(int argc, char **argv) {
         if (s != NULL) {
             fprintf(stderr, "%s\n", s);
         }
+        aug_close(aug);
         exit(EXIT_FAILURE);
     }
 
index ff097bd..b05fbfc 100644 (file)
@@ -112,8 +112,7 @@ static char *readline_path_generator(const char *text, int state) {
             if ((path = strdup("*")) == NULL)
                 return NULL;
         } else {
-            CALLOC(path, end - text + 2);
-            if (path == NULL)
+            if (ALLOC_N(path, end - text + 2) < 0)
                 return NULL;
             strncpy(path, text, end - text);
             strcat(path, "*");
@@ -153,15 +152,13 @@ static char *readline_path_generator(const char *text, int state) {
 
             /* strip off context if the user didn't give it */
             if (ctx != NULL) {
-                char *c = realloc(child, strlen(child)-strlen(ctx)+1);
-                if (c == NULL) {
-                    free(child);
-                    return NULL;
-                }
                 int ctxidx = strlen(ctx);
                 if (child[ctxidx] == SEP)
                     ctxidx++;
-                strcpy(c, &child[ctxidx]);
+                char *c = strdup(&child[ctxidx]);
+                free(child);
+                if (c == NULL)
+                    return NULL;
                 child = c;
             }
 
index 732ee10..7cf4fa0 100644 (file)
  */
 
 /* V_REGEXP -> V_STRING -> V_LENS */
-static struct value *lns_del(struct info *info,
-                             struct value *rxp, struct value *dflt) {
+static struct value *lns_del(struct info *info, struct value **argv) {
+    struct value *rxp = argv[0];
+    struct value *dflt = argv[1];
+
     assert(rxp->tag == V_REGEXP);
     assert(dflt->tag == V_STRING);
     return lns_make_prim(L_DEL, ref(info),
@@ -51,44 +53,59 @@ static struct value *lns_del(struct info *info,
 }
 
 /* V_REGEXP -> V_LENS */
-static struct value *lns_store(struct info *info, struct value *rxp) {
+static struct value *lns_store(struct info *info, struct value **argv) {
+    struct value *rxp = argv[0];
+
     assert(rxp->tag == V_REGEXP);
     return lns_make_prim(L_STORE, ref(info), ref(rxp->regexp), NULL);
 }
 
 /* V_STRING -> V_LENS */
-static struct value *lns_value(struct info *info, struct value *str) {
+static struct value *lns_value(struct info *info, struct value **argv) {
+    struct value *str = argv[0];
+
     assert(str->tag == V_STRING);
     return lns_make_prim(L_VALUE, ref(info), NULL, ref(str->string));
 }
 
 /* V_REGEXP -> V_LENS */
-static struct value *lns_key(struct info *info, struct value *rxp) {
+static struct value *lns_key(struct info *info, struct value **argv) {
+    struct value *rxp = argv[0];
+
     assert(rxp->tag == V_REGEXP);
     return lns_make_prim(L_KEY, ref(info), ref(rxp->regexp), NULL);
 }
 
 /* V_STRING -> V_LENS */
-static struct value *lns_label(struct info *info, struct value *str) {
+static struct value *lns_label(struct info *info, struct value **argv) {
+    struct value *str = argv[0];
+
     assert(str->tag == V_STRING);
     return lns_make_prim(L_LABEL, ref(info), NULL, ref(str->string));
 }
 
 /* V_STRING -> V_LENS */
-static struct value *lns_seq(struct info *info, struct value *str) {
+static struct value *lns_seq(struct info *info, struct value **argv) {
+    struct value *str = argv[0];
+
     assert(str->tag == V_STRING);
     return lns_make_prim(L_SEQ, ref(info), NULL, ref(str->string));
 }
 
 /* V_STRING -> V_LENS */
-static struct value *lns_counter(struct info *info, struct value *str) {
+static struct value *lns_counter(struct info *info, struct value **argv) {
+    struct value *str = argv[0];
+
     assert(str->tag == V_STRING);
     return lns_make_prim(L_COUNTER, ref(info), NULL, ref(str->string));
 }
 
 /* V_LENS -> V_LENS -> V_LENS -> V_LENS */
-static struct value *lns_square(struct info *info, struct value *l1,
-                                struct value *l2, struct value *l3) {
+static struct value *lns_square(struct info *info, struct value **argv) {
+    struct value *l1 = argv[0];
+    struct value *l2 = argv[1];
+    struct value *l3 = argv[2];
+
     assert(l1->tag == V_LENS);
     assert(l2->tag == V_LENS);
     assert(l3->tag == V_LENS);
@@ -179,8 +196,10 @@ static struct value *pathx_parse_glue(struct info *info, struct value *tree,
 }
 
 /* V_LENS -> V_STRING -> V_TREE */
-static struct value *lens_get(struct info *info, struct value *l,
-                              struct value *str) {
+static struct value *lens_get(struct info *info, struct value **argv) {
+    struct value *l = argv[0];
+    struct value *str = argv[1];
+
     assert(l->tag == V_LENS);
     assert(str->tag == V_STRING);
     struct lns_error *err;
@@ -210,8 +229,11 @@ static struct value *lens_get(struct info *info, struct value *l,
 
 
 /* V_LENS -> V_TREE -> V_STRING -> V_STRING */
-static struct value *lens_put(struct info *info, struct value *l,
-                              struct value *tree, struct value *str) {
+static struct value *lens_put(struct info *info, struct value **argv) {
+    struct value *l    = argv[0];
+    struct value *tree = argv[1];
+    struct value *str  = argv[2];
+
     assert(l->tag == V_LENS);
     assert(tree->tag == V_TREE);
     assert(str->tag == V_STRING);
@@ -237,11 +259,14 @@ static struct value *lens_put(struct info *info, struct value *l,
 }
 
 /* V_STRING -> V_STRING -> V_TREE -> V_TREE */
-static struct value *tree_set_glue(struct info *info, struct value *path,
-                                   struct value *val, struct value *tree) {
+static struct value *tree_set_glue(struct info *info, struct value **argv) {
     // FIXME: This only works if TREE is not referenced more than once;
     // otherwise we'll have some pretty weird semantics, and would really
     // need to copy TREE first
+    struct value *path = argv[0];
+    struct value *val  = argv[1];
+    struct value *tree = argv[2];
+
     assert(path->tag == V_STRING);
     assert(val->tag == V_STRING);
     assert(tree->tag == V_TREE);
@@ -277,11 +302,13 @@ static struct value *tree_set_glue(struct info *info, struct value *path,
 }
 
 /* V_STRING -> V_TREE -> V_TREE */
-static struct value *tree_clear_glue(struct info *info, struct value *path,
-                                     struct value *tree) {
+static struct value *tree_clear_glue(struct info *info, struct value **argv) {
     // FIXME: This only works if TREE is not referenced more than once;
     // otherwise we'll have some pretty weird semantics, and would really
     // need to copy TREE first
+    struct value *path = argv[0];
+    struct value *tree = argv[1];
+
     assert(path->tag == V_STRING);
     assert(tree->tag == V_TREE);
 
@@ -349,25 +376,32 @@ static struct value *tree_insert_glue(struct info *info, struct value *label,
 
 /* Insert after */
 /* V_STRING -> V_STRING -> V_TREE -> V_TREE */
-static struct value *tree_insa_glue(struct info *info, struct value *label,
-                                    struct value *path, struct value *tree) {
+static struct value *tree_insa_glue(struct info *info, struct value **argv) {
+    struct value *label = argv[0];
+    struct value *path  = argv[1];
+    struct value *tree  = argv[2];
+
     return tree_insert_glue(info, label, path, tree, 0);
 }
 
 /* Insert before */
 /* V_STRING -> V_STRING -> V_TREE -> V_TREE */
-static struct value *tree_insb_glue(struct info *info, struct value *label,
-                                    struct value *path, struct value *tree) {
+static struct value *tree_insb_glue(struct info *info, struct value **argv) {
+    struct value *label = argv[0];
+    struct value *path  = argv[1];
+    struct value *tree  = argv[2];
+
     return tree_insert_glue(info, label, path, tree, 1);
 }
 
 /* V_STRING -> V_TREE -> V_TREE */
-static struct value *tree_rm_glue(struct info *info,
-                                  struct value *path,
-                                  struct value *tree) {
+static struct value *tree_rm_glue(struct info *info, struct value **argv) {
     // FIXME: This only works if TREE is not referenced more than once;
     // otherwise we'll have some pretty weird semantics, and would really
     // need to copy TREE first
+    struct value *path  = argv[0];
+    struct value *tree  = argv[1];
+
     assert(path->tag == V_STRING);
     assert(tree->tag == V_TREE);
 
@@ -390,7 +424,9 @@ static struct value *tree_rm_glue(struct info *info,
 }
 
 /* V_STRING -> V_STRING */
-static struct value *gensym(struct info *info, struct value *prefix) {
+static struct value *gensym(struct info *info, struct value **argv) {
+    struct value *prefix = argv[0];
+
     assert(prefix->tag == V_STRING);
     static unsigned int count = 0;
     struct value *v;
@@ -406,7 +442,9 @@ static struct value *gensym(struct info *info, struct value *prefix) {
 }
 
 /* V_STRING -> V_FILTER */
-static struct value *xform_incl(struct info *info, struct value *s) {
+static struct value *xform_incl(struct info *info, struct value **argv) {
+    struct value *s = argv[0];
+
     assert(s->tag == V_STRING);
     struct value *v = make_value(V_FILTER, ref(info));
     v->filter = make_filter(ref(s->string), 1);
@@ -414,7 +452,9 @@ static struct value *xform_incl(struct info *info, struct value *s) {
 }
 
 /* V_STRING -> V_FILTER */
-static struct value *xform_excl(struct info *info, struct value *s) {
+static struct value *xform_excl(struct info *info, struct value **argv) {
+    struct value *s = argv[0];
+
     assert(s->tag == V_STRING);
     struct value *v = make_value(V_FILTER, ref(info));
     v->filter = make_filter(ref(s->string), 0);
@@ -422,8 +462,10 @@ static struct value *xform_excl(struct info *info, struct value *s) {
 }
 
 /* V_LENS -> V_FILTER -> V_TRANSFORM */
-static struct value *xform_transform(struct info *info, struct value *l,
-                                     struct value *f) {
+static struct value *xform_transform(struct info *info, struct value **argv) {
+    struct value *l = argv[0];
+    struct value *f = argv[1];
+
     assert(l->tag == V_LENS);
     assert(f->tag == V_FILTER);
     if (l->lens->value || l->lens->key) {
@@ -436,14 +478,16 @@ static struct value *xform_transform(struct info *info, struct value *l,
     return v;
 }
 
-static struct value *sys_getenv(struct info *info, struct value *n) {
-    assert(n->tag == V_STRING);
+static struct value *sys_getenv(struct info *info, struct value **argv) {
+    assert(argv[0]->tag == V_STRING);
     struct value *v = make_value(V_STRING, ref(info));
-    v->string = dup_string(getenv(n->string->str));
+    v->string = dup_string(getenv(argv[0]->string->str));
     return v;
 }
 
-static struct value *sys_read_file(struct info *info, struct value *n) {
+static struct value *sys_read_file(struct info *info, struct value **argv) {
+    struct value *n = argv[0];
+
     assert(n->tag == V_STRING);
     char *str = NULL;
 
@@ -464,7 +508,10 @@ static struct value *sys_read_file(struct info *info, struct value *n) {
 
 /* V_LENS -> V_LENS */
 static struct value *lns_check_rec_glue(struct info *info,
-                                        struct value *l, struct value *r) {
+                                        struct value **argv) {
+    struct value *l = argv[0];
+    struct value *r = argv[1];
+
     assert(l->tag == V_LENS);
     assert(r->tag == V_LENS);
     int check = typecheck_p(info);
@@ -477,28 +524,28 @@ static struct value *lns_check_rec_glue(struct info *info,
  */
 
 /* V_STRING -> V_UNIT */
-static struct value *pr_string(struct info *info, struct value *s) {
-    printf("%s", s->string->str);
+static struct value *pr_string(struct info *info, struct value **argv) {
+    printf("%s", argv[0]->string->str);
     return make_unit(ref(info));
 }
 
 /* V_REGEXP -> V_UNIT */
-static struct value *pr_regexp(struct info *info, struct value *r) {
-    print_regexp(stdout, r->regexp);
+static struct value *pr_regexp(struct info *info, struct value **argv) {
+    print_regexp(stdout, argv[0]->regexp);
     return make_unit(ref(info));
 }
 
 /* V_STRING -> V_UNIT */
-static struct value *pr_endline(struct info *info, struct value *s) {
-    printf("%s\n", s->string->str);
+static struct value *pr_endline(struct info *info, struct value **argv) {
+    printf("%s\n", argv[0]->string->str);
     return make_unit(ref(info));
 }
 
 /* V_TREE -> V_TREE */
 static struct value *pr_tree(ATTRIBUTE_UNUSED struct info *info,
-                             struct value *t) {
-    print_tree_braces(stdout, 0, t->origin);
-    return ref(t);
+                             struct value **argv) {
+    print_tree_braces(stdout, 0, argv[0]->origin);
+    return ref(argv[0]);
 }
 
 /*
@@ -515,27 +562,29 @@ static struct value *lns_value_of_type(struct info *info, struct regexp *rx) {
 }
 
 /* V_LENS -> V_REGEXP */
-static struct value *lns_ctype(struct info *info, struct value *l) {
-    return lns_value_of_type(info, l->lens->ctype);
+static struct value *lns_ctype(struct info *info, struct value **argv) {
+    return lns_value_of_type(info, argv[0]->lens->ctype);
 }
 
 /* V_LENS -> V_REGEXP */
-static struct value *lns_atype(struct info *info, struct value *l) {
-    return lns_value_of_type(info, l->lens->atype);
+static struct value *lns_atype(struct info *info, struct value **argv) {
+    return lns_value_of_type(info, argv[0]->lens->atype);
 }
 
 /* V_LENS -> V_REGEXP */
-static struct value *lns_vtype(struct info *info, struct value *l) {
-    return lns_value_of_type(info, l->lens->vtype);
+static struct value *lns_vtype(struct info *info, struct value **argv) {
+    return lns_value_of_type(info, argv[0]->lens->vtype);
 }
 
 /* V_LENS -> V_REGEXP */
-static struct value *lns_ktype(struct info *info, struct value *l) {
-    return lns_value_of_type(info, l->lens->ktype);
+static struct value *lns_ktype(struct info *info, struct value **argv) {
+    return lns_value_of_type(info, argv[0]->lens->ktype);
 }
 
 /* V_LENS -> V_STRING */
-static struct value *lns_fmt_atype(struct info *info, struct value *l) {
+static struct value *lns_fmt_atype(struct info *info, struct value **argv) {
+    struct value *l = argv[0];
+
     struct value *result = NULL;
     char *s = NULL;
     int r;
@@ -549,8 +598,10 @@ static struct value *lns_fmt_atype(struct info *info, struct value *l) {
 }
 
 /* V_REGEXP -> V_STRING -> V_STRING */
-static struct value *rx_match(struct info *info,
-                              struct value *rx, struct value *s) {
+static struct value *rx_match(struct info *info, struct value **argv) {
+    struct value *rx = argv[0];
+    struct value *s  = argv[1];
+
     struct value *result = NULL;
     const char *str = s->string->str;
     struct re_registers regs;
index 412d139..8e1d7d4 100644 (file)
--- a/src/fa.c
+++ b/src/fa.c
@@ -286,6 +286,11 @@ static void fa_dot_debug(struct fa *fa, const char *tag) {
         return;
 
     fp = fopen(fname, "w");
+    if (fp == NULL) {
+        free(fname);
+        return;
+    }
+
     fa_dot(fp, fa);
     fclose(fp);
     free(fname);
index ce0864b..94b9ba2 100644 (file)
--- a/src/get.c
+++ b/src/get.c
@@ -288,7 +288,8 @@ static void get_error(struct state *state, struct lens *lens,
 static struct skel *make_skel(struct lens *lens) {
     struct skel *skel;
     enum lens_tag tag = lens->tag;
-    CALLOC(skel, 1);
+    if (ALLOC(skel) < 0)
+        return NULL;
     skel->tag = tag;
     return skel;
 }
@@ -487,7 +488,8 @@ static struct seq *find_seq(const char *name, struct state *state) {
          seq = seq->next);
 
     if (seq == NULL) {
-        CALLOC(seq, 1);
+        if (ALLOC(seq) < 0)
+            return NULL;
         seq->name = name;
         seq->value = 1;
         list_append(state->seqs, seq);
index 2633a98..482648a 100644 (file)
@@ -268,13 +268,8 @@ static inline int pathendswith(const char *path, const char *basenam) {
    Allocate as needed. Return 0 on success, -1 on failure */
 int pathjoin(char **path, int nseg, ...);
 
-/* Call calloc to allocate an array of N instances of *VAR */
-#define CALLOC(Var,N) do { (Var) = calloc ((N), sizeof (*(Var))); } while (0)
-
 #define MEMZERO(ptr, n) memset((ptr), 0, (n) * sizeof(*(ptr)));
 
-#define MEMCPY(dest, src, n) memcpy((dest), (src), (n) * sizeof(*(src)))
-
 #define MEMMOVE(dest, src, n) memmove((dest), (src), (n) * sizeof(*(src)))
 
 /**
index 79953da..86179ee 100644 (file)
--- a/src/jmt.c
+++ b/src/jmt.c
@@ -1109,16 +1109,20 @@ static ind_t add_lens(struct jmt *jmt, struct lens *lens) {
 
     if (debugging("cf.jmt")) {
         if (sA == NULL) {
+            char *s = format_lens(lens);
             printf("add_lens: ");
             print_regexp(stdout, lens->ctype);
-            printf(" %s\n", format_lens(lens));
+            printf(" %s\n", s);
+            free(s);
         } else {
+            char *s = format_lens(lens);
             printf("add_lens: ");
             flens(stdout, l);
-            printf(" %u %s\n", sA->num, format_lens(lens));
+            printf(" %u %s\n", sA->num, s);
             if (nullable) {
-                printf("add_lens: // %s\n", format_lens(lens));
+                printf("add_lens: // %s\n", s);
             }
+            free(s);
         }
     }
 
index 220668c..7234b8f 100644 (file)
@@ -583,7 +583,7 @@ typecheck_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 '/' which is used to separate nodes.", regexp->pattern->str);
             goto error;
         }
         fa_free(fa_isect);
@@ -670,6 +670,8 @@ struct value *lns_make_prim(enum lens_tag tag, struct info *info,
         lens->vtype = restrict_regexp(lens->regexp);
     } else if (tag == L_VALUE) {
         lens->vtype = make_regexp_literal(info, lens->string->str);
+        if (lens->vtype == NULL)
+            goto error;
     }
 
     return make_lens_value(lens);
index 48c8b0b..35be7a2 100644 (file)
@@ -208,6 +208,10 @@ struct expr {
         struct {                       /* E_APP */
             const struct func *func;
             struct expr       **args;
+            /* If fold is true, replace this function invocation
+             * with its value after the first time we evaluate this
+             * expression */
+            bool              fold;
         };
     };
 };
@@ -282,6 +286,7 @@ struct func {
     const char      *name;
     unsigned int     arity;
     enum type        type;
+    bool             pure;      /* Result only depends on args */
     const enum type *arg_types;
     func_impl_t      impl;
 };
@@ -304,40 +309,40 @@ static const enum type arg_types_nodeset_string[] = { T_NODESET, T_STRING };
 
 static const struct func builtin_funcs[] = {
     { .name = "last", .arity = 0, .type = T_NUMBER, .arg_types = NULL,
-      .impl = func_last },
+      .impl = func_last, .pure = false },
     { .name = "position", .arity = 0, .type = T_NUMBER, .arg_types = NULL,
-      .impl = func_position },
+      .impl = func_position, .pure = false },
     { .name = "label", .arity = 0, .type = T_STRING, .arg_types = NULL,
-      .impl = func_label },
+      .impl = func_label, .pure = false },
     { .name = "count", .arity = 1, .type = T_NUMBER,
       .arg_types = arg_types_nodeset,
-      .impl = func_count },
+      .impl = func_count, .pure = false },
     { .name = "regexp", .arity = 1, .type = T_REGEXP,
       .arg_types = arg_types_string,
-      .impl = func_regexp },
+      .impl = func_regexp, .pure = true },
     { .name = "regexp", .arity = 1, .type = T_REGEXP,
       .arg_types = arg_types_nodeset,
-      .impl = func_regexp },
+      .impl = func_regexp, .pure = true },
     { .name = "regexp", .arity = 2, .type = T_REGEXP,
       .arg_types = arg_types_string_string,
-      .impl = func_regexp_flag },
+      .impl = func_regexp_flag, .pure = true },
     { .name = "regexp", .arity = 2, .type = T_REGEXP,
       .arg_types = arg_types_nodeset_string,
-      .impl = func_regexp_flag },
+      .impl = func_regexp_flag, .pure = true },
     { .name = "glob", .arity = 1, .type = T_REGEXP,
       .arg_types = arg_types_string,
-      .impl = func_glob },
+      .impl = func_glob, .pure = true },
     { .name = "glob", .arity = 1, .type = T_REGEXP,
       .arg_types = arg_types_nodeset,
-      .impl = func_glob },
+      .impl = func_glob, .pure = true },
     { .name = "int", .arity = 1, .type = T_NUMBER,
-      .arg_types = arg_types_string, .impl = func_int },
+      .arg_types = arg_types_string, .impl = func_int, .pure = false },
     { .name = "int", .arity = 1, .type = T_NUMBER,
-      .arg_types = arg_types_nodeset, .impl = func_int },
+      .arg_types = arg_types_nodeset, .impl = func_int, .pure = false },
     { .name = "int", .arity = 1, .type = T_NUMBER,
-      .arg_types = arg_types_bool, .impl = func_int },
+      .arg_types = arg_types_bool, .impl = func_int, .pure = false },
     { .name = "not", .arity = 1, .type = T_BOOLEAN,
-      .arg_types = arg_types_bool, .impl = func_not }
+      .arg_types = arg_types_bool, .impl = func_not, .pure = true }
 };
 
 #define RET_ON_ERROR                                                    \
@@ -1409,6 +1414,16 @@ static void eval_expr(struct expr *expr, struct state *state) {
         break;
     case E_APP:
         eval_app(expr, state);
+        if (expr->fold) {
+            /* Do constant folding: replace the function application with
+             * a reference to the value that resulted from evaluating it */
+            for (int i=0; i < expr->func->arity; i++)
+                free_expr(expr->args[i]);
+            free(expr->args);
+            value_ind_t vind = state->values_used - 1;
+            expr->tag = E_VALUE;
+            expr->value_ind = state->values[vind];
+        }
         break;
     default:
         assert(0);
@@ -1493,6 +1508,18 @@ static void check_app(struct expr *expr, struct state *state) {
     if (f < ARRAY_CARDINALITY(builtin_funcs)) {
         expr->func = builtin_funcs + f;
         expr->type = expr->func->type;
+        expr->fold = expr->func->pure;
+        if (expr->fold) {
+            /* We only do constant folding for invocations of pure functions
+             * whose arguments are literal values. That misses opportunities
+             * for constant folding, e.g., "regexp('foo' + 'bar')" but is
+             * a bit simpler than doing full tracking of constants
+             */
+            for (int i=0; i < expr->func->arity; i++) {
+                if (expr->args[i]->tag != E_VALUE)
+                    expr->fold = false;
+            }
+        }
     } else {
         STATE_ERROR(state, PATHX_ETYPE);
     }
@@ -1971,7 +1998,7 @@ static void parse_location_path(struct state *state) {
             state->pos += 1;
             locpath = parse_relative_location_path(state);
             if (HAS_ERROR(state))
-                return;
+                goto error;
             struct step *step = make_step(DESCENDANT_OR_SELF, state);
             if (HAS_ERROR(state))
                 goto error;
index 5ada2ce..bcc3de2 100644 (file)
--- a/src/put.c
+++ b/src/put.c
@@ -80,7 +80,8 @@ static void put_error(struct state *state, struct lens *lens,
     if (state->error != NULL)
         return;
 
-    CALLOC(state->error, 1);
+    if (ALLOC(state->error) < 0)
+        return;
     state->error->lens = ref(lens);
     state->error->pos  = -1;
     if (strlen(state->path) == 0) {
@@ -174,7 +175,8 @@ static struct split *split_append(struct split **split, struct split *tail,
                                   struct tree *tree, struct tree *follow,
                                   char *enc, size_t start, size_t end) {
     struct split *sp;
-    CALLOC(sp, 1);
+    if (ALLOC(sp) < 0)
+        return NULL;
     sp->tree = tree;
     sp->follow = follow;
     sp->enc = enc;
@@ -217,6 +219,8 @@ static struct split *split_concat(struct state *state, struct lens *lens) {
         for (int i=0; i < lens->nchildren; i++) {
             tail = split_append(&split, tail, NULL, NULL,
                                 outer->enc, 0, 0);
+            if (tail == NULL)
+                goto error;
         }
         return split;
     }
index d460361..ad5ae65 100644 (file)
@@ -234,7 +234,8 @@ struct regexp *make_regexp_literal(struct info *info, const char *text) {
 
     /* Escape special characters in text since it should be taken
        literally */
-    CALLOC(pattern, 2*strlen(text)+1);
+    if (ALLOC_N(pattern, 2*strlen(text)+1) < 0)
+        return NULL;
     p = pattern;
     for (const char *t = text; *t != '\0'; t++) {
         if ((*t == '\\') && t[1]) {
@@ -544,8 +545,10 @@ static int regexp_compile_internal(struct regexp *r, const char **c) {
 
     *c = NULL;
 
-    if (r->re == NULL)
-        CALLOC(r->re, 1);
+    if (r->re == NULL) {
+        if (ALLOC(r->re) < 0)
+            return -1;
+    }
 
     re_syntax_options = syntax;
     if (r->nocase)
index 612544c..894c24a 100644 (file)
@@ -414,7 +414,8 @@ struct value *make_exn_value(struct info *info,
         return NULL;
 
     v = make_value(V_EXN, ref(info));
-    CALLOC(v->exn, 1);
+    if (ALLOC(v->exn) < 0)
+        return info->error->exn;
     v->exn->info = info;
     v->exn->message = message;
 
@@ -954,6 +955,9 @@ static struct value *coerce(struct value *v, struct type *t) {
     if (vt->tag == T_STRING && t->tag == T_REGEXP) {
         struct value *rxp = make_value(V_REGEXP, ref(v->info));
         rxp->regexp = make_regexp_literal(v->info, v->string->str);
+        if (rxp->regexp == NULL) {
+            report_error(v->info->error, AUG_ENOMEM, NULL);
+        };
         unref(v, value);
         unref(vt, type);
         return rxp;
@@ -982,7 +986,8 @@ static struct type *expect_types_arr(struct info *info,
         }
         len += (ntypes - 1) * 4 + 1;
         char *allowed_names;
-        CALLOC(allowed_names, len);
+        if (ALLOC_N(allowed_names, len) < 0)
+            return NULL;
         for (int i=0; i < ntypes; i++) {
             if (i > 0)
                 strcat(allowed_names, (i == ntypes - 1) ? ", or " : ", ");
@@ -1023,42 +1028,16 @@ typedef struct value *(*impl5)(struct info *, struct value *, struct value *,
 
 static struct value *native_call(struct info *info,
                                  struct native *func, struct ctx *ctx) {
-    struct value *argv[func->argc];
+    struct value *argv[func->argc + 1];
     struct binding *b = ctx->local;
-    struct value *result;
 
     for (int i = func->argc - 1; i >= 0; i--) {
         argv[i] = b->value;
         b = b->next;
     }
+    argv[func->argc] = NULL;
 
-    switch(func->argc) {
-    case 0:
-        result = ((impl0) *func->impl)(info);
-        break;
-    case 1:
-        result = ((impl1) *func->impl)(info, argv[0]);
-        break;
-    case 2:
-        result = ((impl2) *func->impl)(info, argv[0], argv[1]);
-        break;
-    case 3:
-        result = ((impl3) *func->impl)(info, argv[0], argv[1], argv[2]);
-        break;
-    case 4:
-        result = ((impl4) *func->impl)(info, argv[0], argv[1], argv[2], argv[3]);
-        break;
-    case 5:
-        result = ((impl5) *func->impl)(info, argv[0], argv[1], argv[2], argv[3],
-                                       argv[4]);
-        break;
-    default:
-        assert(0);
-        abort();
-        break;
-    }
-
-    return result;
+    return func->impl(info, argv);
 }
 
 static void type_error1(struct info *info, const char *msg, struct type *type) {
@@ -1535,7 +1514,8 @@ static struct value *compile_concat(struct term *exp, struct ctx *ctx) {
         const char *s2 = v2->string->str;
         v = make_value(V_STRING, ref(info));
         make_ref(v->string);
-        CALLOC(v->string->str, strlen(s1) + strlen(s2) + 1);
+        if (ALLOC_N(v->string->str, strlen(s1) + strlen(s2) + 1) < 0)
+            goto error;
         char *s = v->string->str;
         strcpy(s, s1);
         strcat(s, s2);
@@ -1574,6 +1554,8 @@ static struct value *compile_concat(struct term *exp, struct ctx *ctx) {
     unref(v1, value);
     unref(v2, value);
     return v;
+ error:
+    return exp->info->error->exn;
 }
 
 static struct value *apply(struct term *app, struct ctx *ctx) {
@@ -1857,7 +1839,7 @@ make_native_info(struct error *error, const char *fname, int line) {
 int define_native_intl(const char *file, int line,
                        struct error *error,
                        struct module *module, const char *name,
-                       int argc, void *impl, ...) {
+                       int argc, func_impl impl, ...) {
     assert(argc > 0);  /* We have no unit type */
     assert(argc <= 5);
     va_list ap;
@@ -2086,6 +2068,7 @@ int interpreter_init(struct augeas *aug) {
 
     for (int i=0; i < globbuf.gl_pathc; i++) {
         char *name, *p, *q;
+        int res;
         p = strrchr(globbuf.gl_pathv[i], SEP);
         if (p == NULL)
             p = globbuf.gl_pathv[i];
@@ -2094,9 +2077,10 @@ int interpreter_init(struct augeas *aug) {
         q = strchr(p, '.');
         name = strndup(p, q - p);
         name[0] = toupper(name[0]);
-        if (load_module(aug, name) == -1)
-            goto error;
+        res = load_module(aug, name);
         free(name);
+        if (res == -1)
+            goto error;
     }
     globfree(&globbuf);
     return 0;
index 12c3bae..30aefe5 100644 (file)
@@ -112,10 +112,17 @@ struct param {
     struct type   *type;
 };
 
+/* The protoype for the implementation of a native/builtin function in the
+ * interpreter.
+ *
+ * The arguments are passed as a NULL-terminated array of values.
+ */
+typedef struct value *(*func_impl)(struct info *, struct value *argv[]);
+
 struct native {
     unsigned int argc;
     struct type *type;
-    struct value *(*impl)(void);
+    func_impl    impl;
 };
 
 /* An exception in the interpreter. Some exceptions are reported directly
@@ -270,7 +277,7 @@ ATTRIBUTE_RETURN_CHECK
 int define_native_intl(const char *fname, int line,
                        struct error *error,
                        struct module *module, const char *name,
-                       int argc, void *impl, ...);
+                       int argc, func_impl impl, ...);
 
 struct module *builtin_init(struct error *);
 
index 6a0ce85..176482b 100644 (file)
@@ -1258,7 +1258,7 @@ int transform_save(struct augeas *aug, struct tree *xfm,
 
         if (fchmod(fileno(fp), 0666 & ~curumsk) < 0) {
             err_status = "create_chmod";
-            return -1;
+            goto done;
         }
     }
 
index 8e035e9..fb20ffe 100644 (file)
@@ -6,7 +6,7 @@ AM_CFLAGS = $(AUGEAS_CFLAGS) $(WARN_CFLAGS) $(GNULIB_CFLAGS) \
 
 VALGRIND=libtool --mode=execute valgrind --quiet --leak-check=full
 valgrind:
-       make check \
+       $(MAKE) $(MAKEFLAGS) check \
          VALGRIND="$(VALGRIND)" \
          AUGPARSE=$(abs_top_builddir)/src/augparse \
          AUGTOOL=$(abs_top_builddir)/src/augtool
@@ -205,6 +205,7 @@ lens_tests =                        \
   lens-sshd.sh                 \
   lens-sssd.sh                 \
   lens-star.sh          \
+  lens-strongswan.sh \
   lens-stunnel.sh                      \
   lens-subversion.sh           \
   lens-sysconfig.sh            \
@@ -272,7 +273,7 @@ check_SCRIPTS = \
   test-save-empty.sh test-bug-1.sh test-idempotent.sh test-preserve.sh \
   test-events-saved.sh test-save-mode.sh test-unlink-error.sh \
   test-augtool-empty-line.sh test-augtool-modify-root.sh \
-  test-span-rec-lens.sh test-nonwritable.sh
+  test-span-rec-lens.sh test-nonwritable.sh test-augmatch.sh
 
 EXTRA_DIST = \
   test-augtool root lens-test-1 \
index fda8ab6..6460d7b 100644 (file)
@@ -816,6 +816,41 @@ static void testAugNs(CuTest *tc) {
     aug_close(aug);
 }
 
+/* Test aug_source */
+static void testAugSource(CuTest *tc) {
+    struct augeas *aug;
+    int r;
+    char *s;
+
+    aug = aug_init(root, loadpath, AUG_NO_STDINC|AUG_NO_LOAD);
+    CuAssertPtrNotNull(tc, aug);
+    CuAssertIntEquals(tc, AUG_NOERROR, aug_error(aug));
+
+    r = aug_load_file(aug, "/etc/hosts");
+    CuAssertIntEquals(tc, 0, r);
+
+    r = aug_source(aug, "/files/etc/hosts/1", &s);
+    CuAssertIntEquals(tc, 0, r);
+    CuAssertStrEquals(tc, "/files/etc/hosts", s);
+    free(s);
+
+    r = aug_source(aug, "/files/etc/fstab", &s);
+    CuAssertIntEquals(tc, -1, r);
+    CuAssertIntEquals(tc, AUG_ENOMATCH, aug_error(aug));
+    CuAssertPtrEquals(tc, NULL, s);
+
+    r = aug_source(aug, "/files[", &s);
+    CuAssertIntEquals(tc, -1, r);
+    CuAssertIntEquals(tc, AUG_EPATHX, aug_error(aug));
+    CuAssertPtrEquals(tc, NULL, s);
+
+    r = aug_source(aug, "/files/etc/hosts/*", &s);
+    CuAssertIntEquals(tc, -1, r);
+    CuAssertIntEquals(tc, AUG_EMMATCH, aug_error(aug));
+    CuAssertPtrEquals(tc, NULL, s);
+
+}
+
 int main(void) {
     char *output = NULL;
     CuSuite* suite = CuSuiteNew();
@@ -840,6 +875,7 @@ int main(void) {
     SUITE_ADD_TEST(suite, testLoadBadPath);
     SUITE_ADD_TEST(suite, testLoadBadLens);
     SUITE_ADD_TEST(suite, testAugNs);
+    SUITE_ADD_TEST(suite, testAugSource);
 
     abs_top_srcdir = getenv("abs_top_srcdir");
     if (abs_top_srcdir == NULL)
diff --git a/tests/test-augmatch.sh b/tests/test-augmatch.sh
new file mode 100755 (executable)
index 0000000..9ef4218
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+# Tests for augmatch
+
+TOPDIR=$(cd $(dirname $0)/.. && pwd)
+[ -n "$abs_top_srcdir" ] || abs_top_srcdir=$TOPDIR
+
+export AUGEAS_LENS_LIB=$abs_top_srcdir/lenses
+export AUGEAS_ROOT=$abs_top_srcdir/tests/root
+
+fail() {
+    echo "failed: $*"
+    exit 1
+}
+
+assert_eq() {
+    if [ "$1" != "$2" ]; then
+        shift 2
+        fail $*
+    fi
+}
+
+# print the tree for /etc/exports
+act=$(augmatch /etc/exports)
+assert_eq 23 $(echo "$act" | wc -l) "t1: expected 23 lines of output"
+
+# show only the entry for a specific mount
+act=$(augmatch -m 'dir["/home"]' /etc/exports)
+assert_eq 9 $(echo "$act" | wc -l) "t2: expected 9 lines of output"
+
+# show all the clients to which we are exporting /home
+act=$(augmatch -eom 'dir["/home"]/client' /etc/exports)
+exp=$(printf "207.46.0.0/16\n192.168.50.2/32\n")
+assert_eq "$exp" "$act" "t3: expected '$exp'"
+
+# report errors with exit code 2
+augmatch -m '**' /etc/exports >/dev/null 2>&1
+ret=$?
+assert_eq 2 $ret "t4: expected exit code 2 but got $ret"
+
+augmatch /etc >/dev/null 2>&1
+ret=$?
+assert_eq 2 $ret "t5: expected exit code 2 but got $ret"
+
+# test --quiet
+act=$(augmatch -q -m "dir['/local']" /etc/exports)
+ret=$?
+assert_eq '' "$act" "t6: expected no output"
+assert_eq 0 $ret "t6: expected exit code 0 but got $ret"
+
+act=$(augmatch -q -m "dir['/not_there']" /etc/exports)
+ret=$?
+assert_eq '' "$act" "t7: expected no output"
+assert_eq 1 $ret "t7: expected exit code 1 but got $ret"