add shorewall aug tests

add shorewall aug tests

module Test_shorewall_rules =
  let basic = "Foo/ACCEPT fw bloc
Bar/DENY bloc fw\n"

  let tabs = "Foo/ACCEPT    fw  bloc
Bar/DENY            bloc    fw\n"

  let three_fields_plus_comment = "ACCEPT   fw:127.0.0.1    bloc
# This should be a comment.
Foo/ACCEPT      bloc fw
"

  let rules_and_comments = "ACCEPT   fw:127.0.0.1   bloc   tcp - 22 127.0.0.1 22/s root/user
# This should be a comment.

Foo/ACCEPT      bloc fw
"

  let rules_comment_section = "# This is a comment.
SECTION NEW
ACCEPT   fw:127.0.0.1   bloc   tcp - 22 127.0.0.1 22/s root/user # This should also be a comment.
# This should be a comment.

Foo/ACCEPT      bloc fw
"

  let hq_server01_test = "SMTP/ACCEPT:info        fw                      net:63.246.22.101,63.246.22.114,63.246.22.117                   tcp     smtp  -  -  25/min\n"

  let put_test_basic = "ACCEPT  net fw  tcp
"

  let put_test_basic_result = "ACCEPT   net fw  tcp
DENY    fw  bloc    tcp
"

  let put_test_comment = "# This is a comment.
ACCEPT  net fw  tcp
"

  let put_test_comment_result = "# This is a comment.
ACCEPT  net fw  tcp
DENY    fw  bloc    tcp
"

  test Shorewall_rules.lns get hq_server01_test =
    { "rule"
      { "action" = "SMTP/ACCEPT:info" }
      { "source" = "fw" }
      { "dest" = "net:63.246.22.101,63.246.22.114,63.246.22.117" }
      { "proto" = "tcp" }
      { "dest_port" = "smtp" }
      { "source_port" = "-" }
      { "dest_original" = "-" }
      { "rate_limit" = "25/min" } }

  test Shorewall_rules.lns get basic =
    { "rule"
        { "action" = "Foo/ACCEPT" }
        { "source" = "fw" }
        { "dest"   = "bloc" } }
    { "rule"
        { "action" = "Bar/DENY" }
        { "source" = "bloc" }
        { "dest"   = "fw" } }

  test Shorewall_rules.lns get tabs =
    { "rule"
        { "action" = "Foo/ACCEPT" }
        { "source" = "fw" }
        { "dest"   = "bloc" } }
    { "rule"
        { "action" = "Bar/DENY" }
        { "source" = "bloc" }
        { "dest"   = "fw" } }

  test Shorewall_rules.lns get three_fields_plus_comment =
    { "rule"
      { "action" = "ACCEPT" }
      { "source" = "fw:127.0.0.1" }
      { "dest"   = "bloc" } }
    { "#comment" = "This should be a comment." }
    { "rule"
      { "action" = "Foo/ACCEPT" }
      { "source" = "bloc" }
      { "dest"   = "fw" } }

  test Shorewall_rules.lns get rules_and_comments =
    { "rule"
        { "action" = "ACCEPT" }
        { "source" = "fw:127.0.0.1" }
        { "dest"   = "bloc" }
        { "proto"  = "tcp" }
        { "dest_port" = "-" }
        { "source_port" = "22" }
        { "dest_original" = "127.0.0.1" }
        { "rate_limit" = "22/s" }
        { "user_group" = "root/user" } }
    { "#comment" = "This should be a comment." }
    {}
    { "rule"
        { "action" = "Foo/ACCEPT" }
        { "source" = "bloc" }
        { "dest"   = "fw" } }

(*  test Shorewall_rules.lns get rules_comment_section =
    { "#comment" = "This is a comment." }
    { "SECTION" = "NEW" }
    { "rule"
        { "action" = "ACCEPT" }
        { "source" = "fw:127.0.0.1" }
        { "dest"   = "bloc" }
        { "proto"  = "tcp" }
        { "dest_port" = "-" }
        { "source_port" = "22" }
        { "dest_original" = "127.0.0.1" }
        { "rate_limit" = "22/s" }
        { "user_group" = "root/user" }
        { "#comment" = "This should also be a comment." } }
    { "#comment" = "This should be a comment." }
    {}
    { "rule"
        { "action" = "Foo/ACCEPT" }
        { "source" = "bloc" }
        { "dest"   = "fw" } }
*)

  test Shorewall_rules.lns put put_test_basic after
    set "/rule[last() + 1]/action" "DENY";
    set "/rule[last()]/source" "fw";
    set "/rule[last()]/dest" "bloc";
    set "/rule[last()]/proto" "tcp"
    = put_test_basic_result

error: pathspec 'files/embedded/share/augeas/lenses/tests/test_shorewall_rules.aug' did not match any file(s) known to git


Shellvars: load /etc/sysconfig/sendmail

Shellvars: load /etc/sysconfig/sendmail

(* Generic lens for shell-script config files like the ones found *)
(* in /etc/sysconfig                                              *)
module Shellvars =
  autoload xfm

  let eol = Util.eol

  let key_re = /[A-Za-z0-9_]+(\[[0-9]+\])?/
  let eq = Util.del_str "="
  let comment = [ del /(#.*)?[ \t]*\n/ "# \n" ]

  let char  = /[^() '"\t\n]|\\\\"/
  let dquot = /"([^"\\\n]|\\\\.)*"/                    (* " Emacs, relax *)
  let squot = /'[^'\n]*'/

  (* Array values of the form '(val1 val2 val3)'. We do not handle empty *)
  (* arrays here because of typechecking headaches. Instead, they are    *)
  (* treated as a simple value                                           *)
  let array =
    let array_value = store (char+ | dquot) in
    del "(" "(" . counter "values" .
      [ seq "values" . array_value ] .
      [ del /[ \t]+/ " " . seq "values" . array_value ] *
      . del ")" ")"

  (* Treat an empty list () as a value '()'; that's not quite correct *)
  (* but fairly close.                                                *)
  let simple_value =
    let empty_array = /\([ \t]*\)/ in
   store (char* | dquot | squot | empty_array)

  let kv = [ key key_re . eq . (simple_value | array) . eol ]

  let source =
    [
      del /\.|source/ "." . label ".source" .
      Util.del_ws_spc . store /[^= \t\n]+/ . eol
    ]

  let lns = (comment | source | kv) *

  let sc_incl (n:string) = (incl ("/etc/sysconfig/" . n))
  let filter_sysconfig =
      sc_incl "atd" .
      sc_incl "authconfig" .
      sc_incl "autofs" .
      sc_incl "clock" .
      sc_incl "cpuspeed" .
      sc_incl "crond" .
      sc_incl "crontab" .
      sc_incl "desktop" .
      sc_incl "firstboot" .
      sc_incl "grub" .
      sc_incl "hsqldb" .
      sc_incl "httpd" .
      sc_incl "i18n" .
      sc_incl "init" .
      sc_incl "iptables-config" .
      sc_incl "irda" .
      sc_incl "irqbalance" .
      sc_incl "kdump" .
      sc_incl "kernel" .
      sc_incl "keyboard" .
      sc_incl "kudzu" .
      sc_incl "libvirtd" .
      sc_incl "lircd" .
      sc_incl "nasd" .
      sc_incl "netconsole" .
      sc_incl "network" .
      sc_incl "nfs" .
      sc_incl "ntpd" .
      sc_incl "prelink" .
      sc_incl "readonly-root" .
      sc_incl "rsyslog" .
      sc_incl "samba" .
      sc_incl "saslauthd" .
      sc_incl "selinux" .
      sc_incl "sendmail" .
      sc_incl "smartmontools" .
      sc_incl "spamassassin" .
      sc_incl "sysstat" .
      sc_incl "system-config-users" .
      sc_incl "vncservers" .
      sc_incl "wpa_supplicant" .
      sc_incl "xend" .
      sc_incl "xendomains"

  let filter_ifcfg   = incl "/etc/sysconfig/network-scripts/ifcfg-*"
  let filter_default = incl "/etc/default/*"
  let filter = filter_sysconfig
             . filter_ifcfg
             . filter_default
             . Util.stdexcl

  let xfm = transform lns filter

(* Local Variables: *)
(* mode: caml       *)
(* End:             *)

(* Generic lens for shell-script config files like the ones found *)
(* in /etc/sysconfig                                              *)
module Shellvars =
  autoload xfm

  let eol = Util.eol

  let key_re = /[A-Za-z0-9_]+(\[[0-9]+\])?/
  let eq = Util.del_str "="
  let comment = [ del /(#.*)?[ \t]*\n/ "# \n" ]

  let char  = /[^() '"\t\n]|\\\\"/
  let dquot = /"([^"\\\n]|\\\\.)*"/                    (* " Emacs, relax *)
  let squot = /'[^'\n]*'/

  (* Array values of the form '(val1 val2 val3)'. We do not handle empty *)
  (* arrays here because of typechecking headaches. Instead, they are    *)
  (* treated as a simple value                                           *)
  let array =
    let array_value = store (char+ | dquot) in
    del "(" "(" . counter "values" .
      [ seq "values" . array_value ] .
      [ del /[ \t]+/ " " . seq "values" . array_value ] *
      . del ")" ")"

  (* Treat an empty list () as a value '()'; that's not quite correct *)
  (* but fairly close.                                                *)
  let simple_value =
    let empty_array = /\([ \t]*\)/ in
   store (char* | dquot | squot | empty_array)

  let kv = [ key key_re . eq . (simple_value | array) . eol ]

  let source =
    [
      del /\.|source/ "." . label ".source" .
      Util.del_ws_spc . store /[^= \t\n]+/ . eol
    ]

  let lns = (comment | source | kv) *

  let sc_incl (n:string) = (incl ("/etc/sysconfig/" . n))
  let filter_sysconfig =
      sc_incl "atd" .
      sc_incl "authconfig" .
      sc_incl "autofs" .
      sc_incl "clock" .
      sc_incl "cpuspeed" .
      sc_incl "crond" .
      sc_incl "crontab" .
      sc_incl "desktop" .
      sc_incl "firstboot" .
      sc_incl "grub" .
      sc_incl "hsqldb" .
      sc_incl "httpd" .
      sc_incl "i18n" .
      sc_incl "init" .
      sc_incl "iptables-config" .
      sc_incl "irda" .
      sc_incl "irqbalance" .
      sc_incl "kdump" .
      sc_incl "kernel" .
      sc_incl "keyboard" .
      sc_incl "kudzu" .
      sc_incl "libvirtd" .
      sc_incl "lircd" .
      sc_incl "nasd" .
      sc_incl "netconsole" .
      sc_incl "network" .
      sc_incl "nfs" .
      sc_incl "ntpd" .
      sc_incl "prelink" .
      sc_incl "readonly-root" .
      sc_incl "rsyslog" .
      sc_incl "samba" .
      sc_incl "saslauthd" .
      sc_incl "selinux" .
      sc_incl "smartmontools" .
      sc_incl "spamassassin" .
      sc_incl "sysstat" .
      sc_incl "system-config-users" .
      sc_incl "vncservers" .
      sc_incl "wpa_supplicant" .
      sc_incl "xend" .
      sc_incl "xendomains"

  let filter_ifcfg   = incl "/etc/sysconfig/network-scripts/ifcfg-*"
  let filter_default = incl "/etc/default/*"
  let filter = filter_sysconfig
             . filter_ifcfg
             . filter_default
             . Util.stdexcl

  let xfm = transform lns filter

(* Local Variables: *)
(* mode: caml       *)
(* End:             *)


bootloader is also present in shellvars_list, exclude from shellvars.

(*
Module: Shellvars
 Generic lens for shell-script config files like the ones found
 in /etc/sysconfig

About: License
   This file is licenced under the LGPLv2+, like the rest of Augeas.

About: Lens Usage
   To be documented
*)

module Shellvars =
  autoload xfm

  let eol = del /[ \t]+|[ \t]*[;\n]/ "\n"
  let semicol_eol = del /[ \t]*[;\n]/ "\n"

  let key_re = /[A-Za-z0-9_]+(\[[0-9]+\])?/ - "unset" - "export"
  let eq = Util.del_str "="

  let comment = Util.comment
  let comment_eol = Util.comment_eol
  let comment_or_eol = comment_eol | semicol_eol

  let empty   = Util.empty
  let xchgs   = Build.xchgs
  let semicol = del /;?/ ""

  let char  = /[^;#() '"\t\n]|\\\\"/
  let dquot = /"([^"\\\n]|\\\\.)*"/                    (* " Emacs, relax *)
  let squot = /'[^'\n]*'/
  (* For some reason, `` conflicts with comment_or_eol *)
  let bquot = /`[^#`\n]*`/
  let dollar_assign = /\$\([^#`\n]*\)/

  let sto_to_semicol = store /[^#; \t\n][^#;\n]+[^#; \t\n]|[^#; \t\n]+/

  (* Array values of the form '(val1 val2 val3)'. We do not handle empty *)
  (* arrays here because of typechecking headaches. Instead, they are    *)
  (* treated as a simple value                                           *)
  let array =
    let array_value = store (char+ | dquot) in
    del /\([ \t]*/ "(" . counter "values" .
      [ seq "values" . array_value ] .
      [ del /[ \t\n]+/ " " . seq "values" . array_value ] *
      . del /[ \t]*\)/ ")"

  (* Treat an empty list () as a value '()'; that's not quite correct *)
  (* but fairly close.                                                *)
  let simple_value =
    let empty_array = /\([ \t]*\)/ in
      store (char* | dquot | squot | bquot | dollar_assign | empty_array)

  let export = [ key "export" . Util.del_ws_spc ]
  let kv = [ Util.indent . export? . key key_re
           . eq . (simple_value | array) . semicol . comment_or_eol ]

  let var_action (name:string) =
    [ Util.indent . xchgs name ("@" . name) . Util.del_ws_spc
    . store key_re . semicol . comment_or_eol ]

  let unset = var_action "unset"
  let bare_export = var_action "export"

  let source =
    [ Util.indent
      . del /\.|source/ "." . label ".source"
      . Util.del_ws_spc . store /[^;=# \t\n]+/ . comment_or_eol ]

  let shell_builtin_cmds = "ulimit"

  let builtin =
    [ Util.indent . label "@builtin"
      . store shell_builtin_cmds
      . Util.del_ws_spc
      . [ label "args" . sto_to_semicol ]
      . comment_or_eol ]

  let keyword (kw:string) = Util.indent . Util.del_str kw
  let keyword_label (kw:string) (lbl:string) = keyword kw . label lbl

  let return =
    [ Util.indent . label "@return"
      . Util.del_str "return"
      . ( Util.del_ws_spc . store Rx.integer )?
      . comment_or_eol ]

(************************************************************************
 * Group:                 CONDITIONALS AND LOOPS
 *************************************************************************)

  let generic_cond_start (start_kw:string) (lbl:string)
                         (then_kw:string) (contents:lens) =
      keyword_label start_kw lbl . Sep.space
      . sto_to_semicol . semicol_eol
      . keyword then_kw . eol
      . contents

  let generic_cond (start_kw:string) (lbl:string)
                       (then_kw:string) (contents:lens) (end_kw:string) =
      [ generic_cond_start start_kw lbl then_kw contents
        . keyword end_kw . comment_or_eol ]

  let cond_if (entry:lens) =
    let elif = [ generic_cond_start "elif" "@elif" "then" entry+ ] in
    let else = [ keyword_label "else" "@else" . eol . entry+ ] in
    generic_cond "if" "@if" "then" (entry+ . elif* . else?) "fi"

  let loop_for (entry:lens) =
    generic_cond "for" "@for" "do" entry+ "done"

  let loop_while (entry:lens) =
    generic_cond "while" "@while" "do" entry+ "done"

  let loop_until (entry:lens) =
    generic_cond "until" "@until" "do" entry+ "done"

  let loop_select (entry:lens) =
    generic_cond "select" "@select" "do" entry+ "done"

  let case (entry:lens) =
    let case_entry = [ label "@case_entry"
                       . Util.indent . store /[^ \t\n\)]+/
                       . Util.del_str ")" . eol
                       . entry+
                       . Util.indent . Util.del_str ";;" . eol ] in
      [ keyword_label "case" "@case" . Sep.space
        . store char+
        . del /[ \t\n]+/ " " . Util.del_str "in" . eol
        . case_entry+
        . keyword "esac" . comment_or_eol ]

  let function (entry:lens) =
    [ Util.indent . label "@function"
      . del /(function[ \t]+)?/ ""
      . store Rx.word . del /[ \t]*\(\)/ "()"
      . eol . Util.del_str "{" . eol
      . entry+
      . Util.indent . Util.del_str "}" . eol ]

  let rec rec_entry =
    let entry = comment | empty | source | kv
              | unset | bare_export | builtin | return | rec_entry in
        cond_if entry
      | loop_for entry
      | loop_select entry
      | loop_while entry
      | loop_until entry
      | case entry
      | function entry

  let lns = (comment | empty | source | kv | unset | bare_export | builtin | return | rec_entry) *

  let sc_incl (n:string) = (incl ("/etc/sysconfig/" . n))
  let sc_excl (n:string) = (excl ("/etc/sysconfig/" . n))

  let filter_sysconfig =
      sc_incl "*" .
      sc_excl "bootloader" .
      sc_excl "hw-uuid" .
      sc_excl "hwconf" .
      sc_excl "ip*tables" .
      sc_excl "kernel" .
      sc_excl "*.pub" .
      sc_excl "sysstat.ioconf" .
      sc_excl "system-config-firewall" .
      sc_excl "system-config-securitylevel" .
      sc_incl "network/config" .
      sc_incl "network/dhcp" .
      sc_incl "network/dhcp6r" .
      sc_incl "network/dhcp6s" .
      sc_incl "network/ifcfg-*" .
      sc_incl "network/if-down.d/*" .
      sc_incl "network/ifroute-*" .
      sc_incl "network/if-up.d/*" .
      sc_incl "network/providers/*" .
      sc_incl "rhn/allowed-actions/*" .
      sc_incl "rhn/allowed-actions/script/*" .
      sc_incl "rhn/rhnsd" .
      sc_incl "SuSEfirewall2.d/cobbler" .
      sc_incl "SuSEfirewall2.d/services/*" .
      sc_excl "SuSEfirewall2.d/services/TEMPLATE"

  let filter_ifcfg   = incl "/etc/sysconfig/network-scripts/ifcfg-*"
                     . incl "/etc/sysconfig/network/ifcfg-*"
  let filter_default = incl "/etc/default/*"
  let filter_misc    = incl "/etc/arno-iptables-firewall/debconf.cfg"
                     . incl "/etc/cron-apt/config"
                     . incl "/etc/environment"
                     . incl "/etc/blkid.conf"
                     . incl "/etc/adduser.conf"
                     . incl "/etc/cowpoke.conf"
                     . incl "/etc/cvs-cron.conf"
                     . incl "/etc/cvs-pserver.conf"
                     . incl "/etc/devscripts.conf"
                     . incl "/etc/lintianrc"
                     . incl "/etc/lsb-release"
                     . incl "/etc/os-release"
                     . incl "/etc/popularity-contest.conf"
                     . incl "/etc/rc.conf"
                     . incl "/etc/selinux/config"
                     . incl "/etc/ucf.conf"

  let filter = filter_sysconfig
             . filter_ifcfg
             . filter_default
             . filter_misc
             . Util.stdexcl

  let xfm = transform lns filter

(* Local Variables: *)
(* mode: caml       *)
(* End:             *)

(*
Module: Shellvars
 Generic lens for shell-script config files like the ones found
 in /etc/sysconfig

About: License
   This file is licenced under the LGPLv2+, like the rest of Augeas.

About: Lens Usage
   To be documented
*)

module Shellvars =
  autoload xfm

  let eol = del /[ \t]+|[ \t]*[;\n]/ "\n"
  let semicol_eol = del /[ \t]*[;\n]/ "\n"

  let key_re = /[A-Za-z0-9_]+(\[[0-9]+\])?/ - "unset" - "export"
  let eq = Util.del_str "="

  let comment = Util.comment
  let comment_eol = Util.comment_eol
  let comment_or_eol = comment_eol | semicol_eol

  let empty   = Util.empty
  let xchgs   = Build.xchgs
  let semicol = del /;?/ ""

  let char  = /[^;#() '"\t\n]|\\\\"/
  let dquot = /"([^"\\\n]|\\\\.)*"/                    (* " Emacs, relax *)
  let squot = /'[^'\n]*'/
  (* For some reason, `` conflicts with comment_or_eol *)
  let bquot = /`[^#`\n]*`/
  let dollar_assign = /\$\([^#`\n]*\)/

  let sto_to_semicol = store /[^#; \t\n][^#;\n]+[^#; \t\n]|[^#; \t\n]+/

  (* Array values of the form '(val1 val2 val3)'. We do not handle empty *)
  (* arrays here because of typechecking headaches. Instead, they are    *)
  (* treated as a simple value                                           *)
  let array =
    let array_value = store (char+ | dquot) in
    del /\([ \t]*/ "(" . counter "values" .
      [ seq "values" . array_value ] .
      [ del /[ \t\n]+/ " " . seq "values" . array_value ] *
      . del /[ \t]*\)/ ")"

  (* Treat an empty list () as a value '()'; that's not quite correct *)
  (* but fairly close.                                                *)
  let simple_value =
    let empty_array = /\([ \t]*\)/ in
      store (char* | dquot | squot | bquot | dollar_assign | empty_array)

  let export = [ key "export" . Util.del_ws_spc ]
  let kv = [ Util.indent . export? . key key_re
           . eq . (simple_value | array) . semicol . comment_or_eol ]

  let var_action (name:string) =
    [ Util.indent . xchgs name ("@" . name) . Util.del_ws_spc
    . store key_re . semicol . comment_or_eol ]

  let unset = var_action "unset"
  let bare_export = var_action "export"

  let source =
    [ Util.indent
      . del /\.|source/ "." . label ".source"
      . Util.del_ws_spc . store /[^;=# \t\n]+/ . comment_or_eol ]

  let shell_builtin_cmds = "ulimit"

  let builtin =
    [ Util.indent . label "@builtin"
      . store shell_builtin_cmds
      . Util.del_ws_spc
      . [ label "args" . sto_to_semicol ]
      . comment_or_eol ]

  let keyword (kw:string) = Util.indent . Util.del_str kw
  let keyword_label (kw:string) (lbl:string) = keyword kw . label lbl

  let return =
    [ Util.indent . label "@return"
      . Util.del_str "return"
      . ( Util.del_ws_spc . store Rx.integer )?
      . comment_or_eol ]

(************************************************************************
 * Group:                 CONDITIONALS AND LOOPS
 *************************************************************************)

  let generic_cond_start (start_kw:string) (lbl:string)
                         (then_kw:string) (contents:lens) =
      keyword_label start_kw lbl . Sep.space
      . sto_to_semicol . semicol_eol
      . keyword then_kw . eol
      . contents

  let generic_cond (start_kw:string) (lbl:string)
                       (then_kw:string) (contents:lens) (end_kw:string) =
      [ generic_cond_start start_kw lbl then_kw contents
        . keyword end_kw . comment_or_eol ]

  let cond_if (entry:lens) =
    let elif = [ generic_cond_start "elif" "@elif" "then" entry+ ] in
    let else = [ keyword_label "else" "@else" . eol . entry+ ] in
    generic_cond "if" "@if" "then" (entry+ . elif* . else?) "fi"

  let loop_for (entry:lens) =
    generic_cond "for" "@for" "do" entry+ "done"

  let loop_while (entry:lens) =
    generic_cond "while" "@while" "do" entry+ "done"

  let loop_until (entry:lens) =
    generic_cond "until" "@until" "do" entry+ "done"

  let loop_select (entry:lens) =
    generic_cond "select" "@select" "do" entry+ "done"

  let case (entry:lens) =
    let case_entry = [ label "@case_entry"
                       . Util.indent . store /[^ \t\n\)]+/
                       . Util.del_str ")" . eol
                       . entry+
                       . Util.indent . Util.del_str ";;" . eol ] in
      [ keyword_label "case" "@case" . Sep.space
        . store char+
        . del /[ \t\n]+/ " " . Util.del_str "in" . eol
        . case_entry+
        . keyword "esac" . comment_or_eol ]

  let function (entry:lens) =
    [ Util.indent . label "@function"
      . del /(function[ \t]+)?/ ""
      . store Rx.word . del /[ \t]*\(\)/ "()"
      . eol . Util.del_str "{" . eol
      . entry+
      . Util.indent . Util.del_str "}" . eol ]

  let rec rec_entry =
    let entry = comment | empty | source | kv
              | unset | bare_export | builtin | return | rec_entry in
        cond_if entry
      | loop_for entry
      | loop_select entry
      | loop_while entry
      | loop_until entry
      | case entry
      | function entry

  let lns = (comment | empty | source | kv | unset | bare_export | builtin | return | rec_entry) *

  let sc_incl (n:string) = (incl ("/etc/sysconfig/" . n))
  let sc_excl (n:string) = (excl ("/etc/sysconfig/" . n))

  let filter_sysconfig =
      sc_incl "*" .
      sc_excl "hw-uuid" .
      sc_excl "hwconf" .
      sc_excl "ip*tables" .
      sc_excl "kernel" .
      sc_excl "*.pub" .
      sc_excl "sysstat.ioconf" .
      sc_excl "system-config-firewall" .
      sc_excl "system-config-securitylevel" .
      sc_incl "network/config" .
      sc_incl "network/dhcp" .
      sc_incl "network/dhcp6r" .
      sc_incl "network/dhcp6s" .
      sc_incl "network/ifcfg-*" .
      sc_incl "network/if-down.d/*" .
      sc_incl "network/ifroute-*" .
      sc_incl "network/if-up.d/*" .
      sc_incl "network/providers/*" .
      sc_incl "rhn/allowed-actions/*" .
      sc_incl "rhn/allowed-actions/script/*" .
      sc_incl "rhn/rhnsd" .
      sc_incl "SuSEfirewall2.d/cobbler" .
      sc_incl "SuSEfirewall2.d/services/*" .
      sc_excl "SuSEfirewall2.d/services/TEMPLATE"

  let filter_ifcfg   = incl "/etc/sysconfig/network-scripts/ifcfg-*"
                     . incl "/etc/sysconfig/network/ifcfg-*"
  let filter_default = incl "/etc/default/*"
  let filter_misc    = incl "/etc/arno-iptables-firewall/debconf.cfg"
                     . incl "/etc/cron-apt/config"
                     . incl "/etc/environment"
                     . incl "/etc/blkid.conf"
                     . incl "/etc/adduser.conf"
                     . incl "/etc/cowpoke.conf"
                     . incl "/etc/cvs-cron.conf"
                     . incl "/etc/cvs-pserver.conf"
                     . incl "/etc/devscripts.conf"
                     . incl "/etc/lintianrc"
                     . incl "/etc/lsb-release"
                     . incl "/etc/os-release"
                     . incl "/etc/popularity-contest.conf"
                     . incl "/etc/rc.conf"
                     . incl "/etc/selinux/config"
                     . incl "/etc/ucf.conf"

  let filter = filter_sysconfig
             . filter_ifcfg
             . filter_default
             . filter_misc
             . Util.stdexcl

  let xfm = transform lns filter

(* Local Variables: *)
(* mode: caml       *)
(* End:             *)


Test that an invalid regexp makes module loading fail

module Fail_regexp_range_end =

  let lns = [ key /[1-0]/ ]

error: pathspec 'tests/modules/fail_regexp_range_end.aug' did not match any file(s) known to git


added augeas lenses for cgroups

added augeas lenses for cgroups

(*
Module: cgconfig
    Parses /etc/cgconfig.conf

Author:
    Ivana Hutarova Varekova <varekova@redhat.com>
    Raphael Pinson          <raphink@gmail.com>

About: Licence
    This file is licensed under the LGPLv2+, like the rest of Augeas.

About: Lens Usage
    Sample usage of this lens in augtool
        * print all mounted cgroups
           print /files/etc/cgconfig.conf/mount

About: Configuration files
    This lens applies to /etc/cgconfig.conf. See <filter>.
 *)

module Cgconfig =
   autoload xfm

   let indent  = Util.indent
   let eol     = Util.eol
   let comment = Util.comment
   let empty   = Util.empty
   let space   = Sep.space

   let id        = /[a-zA-Z0-9_\/.-]+/
   let name      = /[^#= \n\t{}\/]+/
   let cont_name = /(cpuacct|cpu|devices|ns|cpuset|memory|freezer|net_cls)/
   let role_name = /(admin|task)/
   let id_name   = /(uid|gid)/
   let address   = /[^#; \n\t{}]+/

   let lbracket = del /[ \t\n]*\{/ " {"
   let rbracket = del /[ \t]*\}/ "}"
   let eq       = indent . Util.del_str "=" . indent

(******************************************
 * Function to deal with abc=def; entries
 ******************************************)

   let key_value (key_rx:regexp) (val_rx:regexp) =
     [ indent . key key_rx . eq . store val_rx
         . indent . Util.del_str ";" ]

   (* Function to deal with bracketted entries *)
   let brack_entry_base (lnsa:lens) (lnsb:lens) =
     [ indent . lnsa . lbracket . lnsb . rbracket ]

   let brack_entry_key (kw:regexp) (lns:lens) =
     let lnsa = key kw in
     brack_entry_base lnsa lns

   let brack_entry (kw:regexp) (lns:lens) =
     let full_lns = (lns | comment | empty)* in
     brack_entry_key kw full_lns

(******************************************
 * control groups
 ******************************************)

   let permission_setting = key_value id_name address

(* task setting *)
   let t_info =  brack_entry "task" permission_setting

(* admin setting *)
   let a_info =  brack_entry "admin" permission_setting

(* permissions setting *)
   let perm_info =
     let ce = (comment|empty)* in
     let perm_info_lns = ce .
       ((t_info . ce . (a_info . ce)?)
       |(a_info . ce . (t_info . ce)?))? in
     brack_entry_key "perm" perm_info_lns

   let variable_setting = key_value name address

(* controllers setting *)
   let controller_info =
     let lnsa = label "controller" . store cont_name in
     let lnsb = ( variable_setting | comment | empty ) * in
     brack_entry_base lnsa lnsb

(* group { ... } *)
   let group_data  =
     let lnsa = key "group" . space . indent . store id in
     let lnsb = ( perm_info | controller_info | comment | empty )* in
     brack_entry_base lnsa lnsb

(*************************************************
 * mount point
 *************************************************)

(* controller = mount_point; *)
   let mount_point = key_value name address

(* mount { .... } *)
   let mount_data = brack_entry "mount" mount_point

(****************************************************
 * namespace
 ****************************************************)

(* controller = cgroup; *)
   let namespace_instance = key_value name address

(* namespace { .... } *)
   let namespace = brack_entry "namespace" namespace_instance

   let lns =  ( comment | empty | mount_data | group_data | namespace )*

   let xfm = transform lns (incl "/etc/cgconfig.conf")

error: pathspec 'modules/cgroups/files/cgconfig.aug' did not match any file(s) known to git


A test for the del typecheck

A test for the del typecheck

module Fail_del_default_check =

  (* Not valid since the default value "NO" does not match /[a-z]+/ *)
  let lns = del /[a-z]+/ "NO"

error: pathspec 'tests/modules/fail_del_default_check.aug' did not match any file(s) known to git


Process lots of files in /etc/sysconfig

Process lots of files in /etc/sysconfig

Load all those shell-script style config files from /etc/sysconfig using
the generic Shellvars lens.

Ultimately, it would be better if we definedfor each of them which
variables are actually going to be looked at (together with some
restrictions on possible values)

module Sysconfig =
  autoload xfm

  let sc_incl (n:string) = (incl ("/etc/sysconfig/" . n))

  let filter = Util.stdexcl .
      sc_incl "atd" .
      sc_incl "authconfig" .
      sc_incl "autofs" .
      sc_incl "clock" .
      sc_incl "cpuspeed" .
      sc_incl "crond" .
      sc_incl "crontab" .
      sc_incl "desktop" .
      sc_incl "firstboot" .
      sc_incl "grub" .
      sc_incl "hsqldb" .
      sc_incl "httpd" .
      sc_incl "i18n" .
      sc_incl "init" .
      sc_incl "iptables-config" .
      sc_incl "irda" .
      sc_incl "irqbalance" .
      sc_incl "kdump" .
      sc_incl "kernel" .
      sc_incl "keyboard" .
      sc_incl "kudzu" .
      sc_incl "libvirtd" .
      sc_incl "lircd" .
      sc_incl "nasd" .
      sc_incl "netconsole" .
      sc_incl "network" .
      sc_incl "nfs" .
      sc_incl "ntpd" .
      sc_incl "prelink" .
      sc_incl "readonly-root" .
      sc_incl "rsyslog" .
      sc_incl "samba" .
      sc_incl "saslauthd" .
      sc_incl "smartmontools" .
      sc_incl "spamassassin" .
      sc_incl "sysstat" .
      sc_incl "system-config-users" .
      sc_incl "vncservers" .
      sc_incl "wpa_supplicant" .
      sc_incl "xend" .
      sc_incl "xendomains"

  let xfm = transform Shellvars.lns filter

(* Local Variables: *)
(* mode: caml       *)
(* End:             *)

error: pathspec 'lenses/sysconfig.aug' did not match any file(s) known to git


Process virtlockd.conf instead of libvirtd.conf

(* /etc/libvirt/virtlockd.conf *)

module Virtlockd =
   autoload xfm

   let eol   = del /[ \t]*\n/ "\n"
   let value_sep   = del /[ \t]*=[ \t]*/  " = "
   let indent = del /[ \t]*/ ""

   let array_sep  = del /,[ \t\n]*/ ", "
   let array_start = del /\[[ \t\n]*/ "[ "
   let array_end = del /\]/ "]"

   let str_val = del /\"/ "\"" . store /[^\"]*/ . del /\"/ "\""
   let bool_val = store /0|1/
   let int_val = store /[0-9]+/
   let str_array_element = [ seq "el" . str_val ] . del /[ \t\n]*/ ""
   let str_array_val = counter "el" . array_start . ( str_array_element . ( array_sep . str_array_element ) * ) ? . array_end

   let str_entry       (kw:string) = [ key kw . value_sep . str_val ]
   let bool_entry      (kw:string) = [ key kw . value_sep . bool_val ]
   let int_entry      (kw:string) = [ key kw . value_sep . int_val ]
   let str_array_entry (kw:string) = [ key kw . value_sep . str_array_val ]

   (* Config entry grouped by function - same order as example config *)
   let logging_entry = int_entry "log_level"
                     | str_entry "log_filters"
                     | str_entry "log_outputs"
                     | int_entry "log_buffer_size"
                     | int_entry "max_clients"

   (* Each enty in the config is one of the following three ... *)
   let entry = logging_entry
   let comment = [ label "#comment" . del /#[ \t]*/ "# " .  store /([^ \t\n][^\n]*)?/ . del /\n/ "\n" ]
   let empty = [ label "#empty" . eol ]

   let record = indent . entry . eol

   let lns = ( record | comment | empty ) *

   let filter = incl "/etc/libvirt/virtlockd.conf"
              . Util.stdexcl

   let xfm = transform lns filter

(* /etc/libvirt/libvirtd.conf *)

module Libvirtd =
   autoload xfm

   let eol   = del /[ \t]*\n/ "\n"
   let value_sep   = del /[ \t]*=[ \t]*/  " = "
   let indent = del /[ \t]*/ ""

   let array_sep  = del /,[ \t\n]*/ ", "
   let array_start = del /\[[ \t\n]*/ "[ "
   let array_end = del /\]/ "]"

   let str_val = del /\"/ "\"" . store /[^\"]*/ . del /\"/ "\""
   let bool_val = store /0|1/
   let int_val = store /[0-9]+/
   let str_array_element = [ seq "el" . str_val ] . del /[ \t\n]*/ ""
   let str_array_val = counter "el" . array_start . ( str_array_element . ( array_sep . str_array_element ) * ) ? . array_end

   let str_entry       (kw:string) = [ key kw . value_sep . str_val ]
   let bool_entry      (kw:string) = [ key kw . value_sep . bool_val ]
   let int_entry      (kw:string) = [ key kw . value_sep . int_val ]
   let str_array_entry (kw:string) = [ key kw . value_sep . str_array_val ]

   (* Config entry grouped by function - same order as example config *)
   let logging_entry = int_entry "log_level"
                     | str_entry "log_filters"
                     | str_entry "log_outputs"
                     | int_entry "log_buffer_size"
                     | int_entry "max_clients"

   (* Each enty in the config is one of the following three ... *)
   let entry = logging_entry
   let comment = [ label "#comment" . del /#[ \t]*/ "# " .  store /([^ \t\n][^\n]*)?/ . del /\n/ "\n" ]
   let empty = [ label "#empty" . eol ]

   let record = indent . entry . eol

   let lns = ( record | comment | empty ) *

   let filter = incl "/etc/libvirt/virtlockd.conf"
              . Util.stdexcl

   let xfm = transform lns filter


Correctly set the module name, and let it actually test the Lightdm lens.

(*
Module: Test_Lightdm
   Module to test Lightdm module for Augeas

Author: David Salmen <dsalmen@dsalmen.com>

About: License
   This file is licenced under the LGPL v2+, like the rest of Augeas.
*)

module Test_lightdm =

    let conf_lightdm = "
[SeatDefaults]
greeter-session=unity-greeter
user-session=ubuntu
"

   test Lightdm.lns get conf_lightdm =
      {}
      { "SeatDefaults"
         { "greeter-session" = "unity-greeter" }
         { "user-session" = "ubuntu" }
      }

    test Lightdm.lns put conf_lightdm after
       set "SeatDefaults/allow-guest" "false"
    = "
[SeatDefaults]
greeter-session=unity-greeter
user-session=ubuntu
allow-guest=false
"

    test Lightdm.lns put conf_lightdm after
       set "SeatDefaults/allow-guest" "true"
    = "
[SeatDefaults]
greeter-session=unity-greeter
user-session=ubuntu
allow-guest=true
"

    let conf_unity_greeter = "
#
# background = Background file to use, either an image path or a color (e.g. #772953)
# logo = Logo file to use
# theme-name = GTK+ theme to use
# font-name = Font to use
# xft-antialias = Whether to antialias Xft fonts (true or false)
# xft-dpi = Resolution for Xft in dots per inch (e.g. 96)
# xft-hintstyle = What degree of hinting to use (hintnone, hintslight, hintmedium, or hintfull)
# xft-rgba = Type of subpixel antialiasing (none, rgb, bgr, vrgb or vbgr)
#
[greeter]
background=/usr/share/backgrounds/warty-final-ubuntu.png
logo=/usr/share/unity-greeter/logo.png
theme-name=Ambiance
icon-theme-name=ubuntu-mono-dark
font-name=Ubuntu 11
xft-antialias=true
xft-dpi=96
xft-hintstyle=hintslight
xft-rgba=rgb
"

    test Lightdm.lns get conf_unity_greeter =
        {}
        { "#comment" }
        { "#comment" = "background = Background file to use, either an image path or a color (e.g. #772953)" }
        { "#comment" = "logo = Logo file to use" }
        { "#comment" = "theme-name = GTK+ theme to use" }
        { "#comment" = "font-name = Font to use" }
        { "#comment" = "xft-antialias = Whether to antialias Xft fonts (true or false)" }
        { "#comment" = "xft-dpi = Resolution for Xft in dots per inch (e.g. 96)" }
        { "#comment" = "xft-hintstyle = What degree of hinting to use (hintnone, hintslight, hintmedium, or hintfull)" }
        { "#comment" = "xft-rgba = Type of subpixel antialiasing (none, rgb, bgr, vrgb or vbgr)" }
        { "#comment" }
        { "greeter"
            { "background" = "/usr/share/backgrounds/warty-final-ubuntu.png" }
            { "logo" = "/usr/share/unity-greeter/logo.png" }
            { "theme-name" = "Ambiance" }
            { "icon-theme-name" = "ubuntu-mono-dark" }
            { "font-name" = "Ubuntu 11" }
            { "xft-antialias" = "true" }
            { "xft-dpi" = "96" }
            { "xft-hintstyle" = "hintslight" }
            { "xft-rgba" = "rgb" }
        }

    let conf_users = "
#
# User accounts configuration
#
# NOTE: If you have AccountsService installed on your system, then LightDM will
# use this instead and these settings will be ignored
#
# minimum-uid = Minimum UID required to be shown in greeter
# hidden-users = Users that are not shown to the user
# hidden-shells = Shells that indicate a user cannot login
#
[UserAccounts]
minimum-uid=500
hidden-users=nobody nobody4 noaccess
hidden-shells=/bin/false /usr/sbin/nologin
"

    test Lightdm.lns get conf_users =
        {}
        { "#comment" }
        { "#comment" = "User accounts configuration" }
        { "#comment" }
        { "#comment" = "NOTE: If you have AccountsService installed on your system, then LightDM will" }
        { "#comment" = "use this instead and these settings will be ignored" }
        { "#comment" }
        { "#comment" = "minimum-uid = Minimum UID required to be shown in greeter" }
        { "#comment" = "hidden-users = Users that are not shown to the user" }
        { "#comment" = "hidden-shells = Shells that indicate a user cannot login" }
        { "#comment" }
        { "UserAccounts"
            { "minimum-uid" = "500" }
            { "hidden-users" = "nobody nobody4 noaccess" }
            { "hidden-shells" = "/bin/false /usr/sbin/nologin" }
        }

(*
Module: Test_Lightdm module to test Lightdm module for Augeas

Author: David Salmen <dsalmen@dsalmen.com>

About: License
   This file is licenced under the LGPL v2+, like the rest of Augeas.
*)

module Test_lightdm =

    let conf_lightdm = "
[SeatDefaults]
greeter-session=unity-greeter
user-session=ubuntu
"

   test Puppet.lns get conf_lightdm =
      {}
      { "SeatDefaults"
         { "greeter-session" = "unity-greeter" }
         { "user-session" = "ubuntu" }
      }

    test Puppet.lns put conf_lightdm after
       set "SeatDefaults/allow-guest" "false"
    = "
[SeatDefaults]
greeter-session=unity-greeter
user-session=ubuntu
allow-guest=false
"

    test Puppet.lns put conf_lightdm after
       set "SeatDefaults/allow-guest" "true"
    = "
[SeatDefaults]
greeter-session=unity-greeter
user-session=ubuntu
allow-guest=true
"

    let conf_unity_greeter = "
#
# background = Background file to use, either an image path or a color (e.g. #772953)
# logo = Logo file to use
# theme-name = GTK+ theme to use
# font-name = Font to use
# xft-antialias = Whether to antialias Xft fonts (true or false)
# xft-dpi = Resolution for Xft in dots per inch (e.g. 96)
# xft-hintstyle = What degree of hinting to use (hintnone, hintslight, hintmedium, or hintfull)
# xft-rgba = Type of subpixel antialiasing (none, rgb, bgr, vrgb or vbgr)
#
[greeter]
background=/usr/share/backgrounds/warty-final-ubuntu.png
logo=/usr/share/unity-greeter/logo.png
theme-name=Ambiance
icon-theme-name=ubuntu-mono-dark
font-name=Ubuntu 11
xft-antialias=true
xft-dpi=96
xft-hintstyle=hintslight
xft-rgba=rgb
"

    test Puppet.lns get conf_unity_greeter =
        {}
        { "#comment" }
        { "#comment" = "background = Background file to use, either an image path or a color (e.g. #772953)" }
        { "#comment" = "logo = Logo file to use" }
        { "#comment" = "theme-name = GTK+ theme to use" }
        { "#comment" = "font-name = Font to use" }
        { "#comment" = "xft-antialias = Whether to antialias Xft fonts (true or false)" }
        { "#comment" = "xft-dpi = Resolution for Xft in dots per inch (e.g. 96)" }
        { "#comment" = "xft-hintstyle = What degree of hinting to use (hintnone, hintslight, hintmedium, or hintfull)" }
        { "#comment" = "xft-rgba = Type of subpixel antialiasing (none, rgb, bgr, vrgb or vbgr)" }
        { "#comment" }
        { "greeter"
            { "background" = "/usr/share/backgrounds/warty-final-ubuntu.png" }
            { "logo" = "/usr/share/unity-greeter/logo.png" }
            { "theme-name" = "Ambiance" }
            { "icon-theme-name" = "ubuntu-mono-dark" }
            { "font-name" = "Ubuntu 11" }
            { "xft-antialias" = "true" }
            { "xft-dpi" = "96" }
            { "xft-hintstyle" = "hintslight" }
            { "xft-rgba" = "rgb" }
        }

    let conf_users = "
#
# User accounts configuration
#
# NOTE: If you have AccountsService installed on your system, then LightDM will
# use this instead and these settings will be ignored
#
# minimum-uid = Minimum UID required to be shown in greeter
# hidden-users = Users that are not shown to the user
# hidden-shells = Shells that indicate a user cannot login
#
[UserAccounts]
minimum-uid=500
hidden-users=nobody nobody4 noaccess
hidden-shells=/bin/false /usr/sbin/nologin
"

    test Puppet.lns get conf_users =
        {}
        { "#comment" }
        { "#comment" = "User accounts configuration" }
        { "#comment" }
        { "#comment" = "NOTE: If you have AccountsService installed on your system, then LightDM will" }
        { "#comment" = "use this instead and these settings will be ignored" }
        { "#comment" }
        { "#comment" = "minimum-uid = Minimum UID required to be shown in greeter" }
        { "#comment" = "hidden-users = Users that are not shown to the user" }
        { "#comment" = "hidden-shells = Shells that indicate a user cannot login" }
        { "#comment" }
        { "UserAccounts"
            { "minimum-uid" = "500" }
            { "hidden-users" = "nobody nobody4 noaccess" }
            { "hidden-shells" = "/bin/false /usr/sbin/nologin" }
        }


* lenses/tests/test_iptables.aug: test that we accept blank lines

module Test_iptables =

let add_rule = Iptables.table_rule
let ipt_match = Iptables.ipt_match

test add_rule get
"-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT\n" =
  { "append" = "INPUT"
      { "match" = "state" }
      { "state" = "ESTABLISHED,RELATED" }
      { "jump" = "ACCEPT" } }

test add_rule get
"-A INPUT -p icmp -j \tACCEPT \n" =
  { "append" = "INPUT"
      { "protocol" = "icmp" }
      { "jump" = "ACCEPT" } }

test add_rule get
"-A INPUT -i lo -j ACCEPT\n" =
  { "append" = "INPUT"
    { "in-interface" = "lo" }
    { "jump" = "ACCEPT" } }

test ipt_match get " -m tcp -p tcp --dport 53" =
  { "match" = "tcp" } { "protocol" = "tcp" } { "dport" = "53" }

let arule = " -m state --state NEW -m tcp -p tcp --dport 53 -j ACCEPT"

test add_rule get ("--append INPUT" . arule . "\n") =
  { "append" = "INPUT"
      { "match" = "state" }
      { "state" = "NEW" }
      { "match" = "tcp" }
      { "protocol" = "tcp" }
      { "dport" = "53" }
      { "jump" = "ACCEPT" } }

test ipt_match get arule =
  { "match" = "state" } { "state" = "NEW" } { "match" = "tcp" }
  { "protocol" = "tcp" } { "dport" = "53" } { "jump" = "ACCEPT" }

test ipt_match get ("-A INPUT" . arule) = *

test ipt_match get " -p esp -j ACCEPT" =
  { "protocol" = "esp" } { "jump" = "ACCEPT" }

test ipt_match get
  " -m state --state NEW -m udp -p udp --dport 5353 -d 224.0.0.251 -j ACCEPT"
 =
  { "match" = "state" } { "state" = "NEW" } { "match" = "udp" }
  { "protocol" = "udp" } { "dport" = "5353" }
  { "destination" = "224.0.0.251" } { "jump" = "ACCEPT" }

test add_rule get
  "-I FORWARD -m physdev --physdev-is-bridged -j ACCEPT\n" =
  { "insert" = "FORWARD"
      { "match" = "physdev" } { "physdev-is-bridged" } { "jump" = "ACCEPT" } }

test add_rule get
    "-A INPUT -j REJECT --reject-with icmp-host-prohibited\n" =
  { "append" = "INPUT"
      { "jump" = "REJECT" } { "reject-with" = "icmp-host-prohibited" } }

test add_rule get
  "-A RH-Firewall-1-INPUT -p icmp --icmp-type any -j ACCEPT\n" =
  { "append" = "RH-Firewall-1-INPUT"
      { "protocol" = "icmp" }
      { "icmp-type" = "any" }
      { "jump" = "ACCEPT" } }

test Iptables.table get "*filter
:RH-Firewall-1-INPUT - [0:0]
-A FORWARD -j RH-Firewall-1-INPUT
-A RH-Firewall-1-INPUT -i lo -j ACCEPT
COMMIT\n" =
  { "table" = "filter"
      { "chain" = "RH-Firewall-1-INPUT"
          { "policy" = "-" } }
      { "append" = "FORWARD"
          { "jump" = "RH-Firewall-1-INPUT" } }
      { "append" = "RH-Firewall-1-INPUT"
          { "in-interface" = "lo" }
          { "jump" = "ACCEPT" } } }

test Iptables.table get "*filter

:RH-Firewall-1-INPUT - [0:0]

-A FORWARD -j RH-Firewall-1-INPUT

COMMIT\n" =
  { "table" = "filter"
      { }
      { "chain" = "RH-Firewall-1-INPUT"
          { "policy" = "-" } }
      { }
      { "append" = "FORWARD"
          { "jump" = "RH-Firewall-1-INPUT" } }
      { } }

let conf = "# Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:55 2002
*filter
:INPUT DROP [1:229]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

-I FORWARD -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT

# comments and blank lines are allow between rules

-A FORWARD -i eth1 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
--append OUTPUT -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
COMMIT
# Completed on Wed Apr 24 10:19:55 2002
# Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:55 2002
*mangle
:PREROUTING ACCEPT [658:32445]

:INPUT ACCEPT [658:32445]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [891:68234]
:POSTROUTING ACCEPT [891:68234]
COMMIT
# Completed on Wed Apr 24 10:19:55 2002
# Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:55 2002
*nat
:PREROUTING ACCEPT [1:229]
:POSTROUTING ACCEPT [3:450]
# The output chain
:OUTPUT ACCEPT [3:450]
# insert something
--insert POSTROUTING -o eth0 -j SNAT --to-source 195.233.192.1 \t
# and now commit
COMMIT
# Completed on Wed Apr 24 10:19:55 2002\n"

test Iptables.lns get conf =
  { "#comment" =
      "Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:55 2002" }
  { "table" = "filter"
    { "chain" = "INPUT" { "policy" = "DROP" } }
    { "chain" = "FORWARD" { "policy" = "DROP" } }
    { "chain" = "OUTPUT" { "policy" = "DROP" } }
    { "append" = "INPUT"
      { "match" = "state" }
      { "state" = "RELATED,ESTABLISHED" }
      { "jump" = "ACCEPT" } }
    {}
    { "insert" = "FORWARD"
      { "in-interface" = "eth0" }
      { "match" = "state" }
      { "state" = "RELATED,ESTABLISHED" }
      { "jump" = "ACCEPT" } }
    {}
    { "#comment" = "comments and blank lines are allow between rules" }
    {}
    { "append" = "FORWARD"
      { "in-interface" = "eth1" }
      { "match" = "state" }
      { "state" = "NEW,RELATED,ESTABLISHED" }
      { "jump" = "ACCEPT" } }
    { "append" = "OUTPUT"
      { "match" = "state" }
      { "state" = "NEW,RELATED,ESTABLISHED" }
      { "jump" = "ACCEPT" } } }
  { "#comment" = "Completed on Wed Apr 24 10:19:55 2002" }
  { "#comment" =
      "Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:55 2002" }
  { "table" = "mangle"
    { "chain" = "PREROUTING" { "policy" = "ACCEPT" } }
    {}
    { "chain" = "INPUT" { "policy" = "ACCEPT" } }
    { "chain" = "FORWARD" { "policy" = "ACCEPT" } }
    { "chain" = "OUTPUT" { "policy" = "ACCEPT" } }
    { "chain" = "POSTROUTING" { "policy" = "ACCEPT" } } }
  { "#comment" = "Completed on Wed Apr 24 10:19:55 2002" }
  { "#comment" =
      "Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:55 2002" }
  { "table" = "nat"
    { "chain" = "PREROUTING" { "policy" = "ACCEPT" } }
    { "chain" = "POSTROUTING" { "policy" = "ACCEPT" } }
    { "#comment" = "The output chain" }
    { "chain" = "OUTPUT" { "policy" = "ACCEPT" } }
    { "#comment" = "insert something" }
    { "insert" = "POSTROUTING"
      { "out-interface" = "eth0" }
      { "jump" = "SNAT" }
      { "to-source" = "195.233.192.1" } }
    { "#comment" = "and now commit" } }
  { "#comment" = "Completed on Wed Apr 24 10:19:55 2002" }

test ipt_match get " -m comment --comment \"A comment\"" =
  { "match" = "comment" }
  { "comment" = "\"A comment\"" }

(*
 * Test the various schemes for negation that iptables supports
 *
 * Note that the two ways in which a parameter can be negated lead to
 * two different trees that mean the same.
 *)
test add_rule get "-I POSTROUTING ! -d 192.168.122.0/24 -j MASQUERADE\n" =
  { "insert" = "POSTROUTING"
    { "destination" = "192.168.122.0/24"
      { "not" } }
    { "jump" = "MASQUERADE" } }

test add_rule get "-I POSTROUTING -d ! 192.168.122.0/24 -j MASQUERADE\n" =
  { "insert" = "POSTROUTING"
    { "destination" = "! 192.168.122.0/24" }
    { "jump" = "MASQUERADE" } }

test add_rule put "-I POSTROUTING ! -d 192.168.122.0/24 -j MASQUERADE\n"
    after rm "/insert/destination/not" =
  "-I POSTROUTING -d 192.168.122.0/24 -j MASQUERADE\n"

(* I have no idea if iptables will accept double negations, but we
 * allow it syntactically *)
test add_rule put "-I POSTROUTING -d ! 192.168.122.0/24 -j MASQUERADE\n"
    after clear "/insert/destination/not" =
  "-I POSTROUTING ! -d ! 192.168.122.0/24 -j MASQUERADE\n"

test Iptables.chain get ":tcp_packets - [0:0]
" =
    { "chain" = "tcp_packets" { "policy" = "-" } }

(* Bug #157 *)
test ipt_match get " --tcp-flags SYN,RST,ACK,FIN SYN" =
  { "tcp-flags"
    { "mask" = "SYN" }
    { "mask" = "RST" }
    { "mask" = "ACK" }
    { "mask" = "FIN" }
    { "set" = "SYN" } }

(* Bug #224 *)
test ipt_match get " --icmpv6-type neighbor-solicitation" =
  { "icmpv6-type" = "neighbor-solicitation" }

module Test_iptables =

let add_rule = Iptables.table_rule
let ipt_match = Iptables.ipt_match

test add_rule get
"-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT\n" =
  { "append" = "INPUT"
      { "match" = "state" }
      { "state" = "ESTABLISHED,RELATED" }
      { "jump" = "ACCEPT" } }

test add_rule get
"-A INPUT -p icmp -j \tACCEPT \n" =
  { "append" = "INPUT"
      { "protocol" = "icmp" }
      { "jump" = "ACCEPT" } }

test add_rule get
"-A INPUT -i lo -j ACCEPT\n" =
  { "append" = "INPUT"
    { "in-interface" = "lo" }
    { "jump" = "ACCEPT" } }

test ipt_match get " -m tcp -p tcp --dport 53" =
  { "match" = "tcp" } { "protocol" = "tcp" } { "dport" = "53" }

let arule = " -m state --state NEW -m tcp -p tcp --dport 53 -j ACCEPT"

test add_rule get ("--append INPUT" . arule . "\n") =
  { "append" = "INPUT"
      { "match" = "state" }
      { "state" = "NEW" }
      { "match" = "tcp" }
      { "protocol" = "tcp" }
      { "dport" = "53" }
      { "jump" = "ACCEPT" } }

test ipt_match get arule =
  { "match" = "state" } { "state" = "NEW" } { "match" = "tcp" }
  { "protocol" = "tcp" } { "dport" = "53" } { "jump" = "ACCEPT" }

test ipt_match get ("-A INPUT" . arule) = *

test ipt_match get " -p esp -j ACCEPT" =
  { "protocol" = "esp" } { "jump" = "ACCEPT" }

test ipt_match get
  " -m state --state NEW -m udp -p udp --dport 5353 -d 224.0.0.251 -j ACCEPT"
 =
  { "match" = "state" } { "state" = "NEW" } { "match" = "udp" }
  { "protocol" = "udp" } { "dport" = "5353" }
  { "destination" = "224.0.0.251" } { "jump" = "ACCEPT" }

test add_rule get
  "-I FORWARD -m physdev --physdev-is-bridged -j ACCEPT\n" =
  { "insert" = "FORWARD"
      { "match" = "physdev" } { "physdev-is-bridged" } { "jump" = "ACCEPT" } }

test add_rule get
    "-A INPUT -j REJECT --reject-with icmp-host-prohibited\n" =
  { "append" = "INPUT"
      { "jump" = "REJECT" } { "reject-with" = "icmp-host-prohibited" } }

test add_rule get
  "-A RH-Firewall-1-INPUT -p icmp --icmp-type any -j ACCEPT\n" =
  { "append" = "RH-Firewall-1-INPUT"
      { "protocol" = "icmp" }
      { "icmp-type" = "any" }
      { "jump" = "ACCEPT" } }

test Iptables.table get "*filter
:RH-Firewall-1-INPUT - [0:0]
-A FORWARD -j RH-Firewall-1-INPUT
-A RH-Firewall-1-INPUT -i lo -j ACCEPT
COMMIT\n" =
  { "table" = "filter"
      { "chain" = "RH-Firewall-1-INPUT"
          { "policy" = "-" } }
      { "append" = "FORWARD"
          { "jump" = "RH-Firewall-1-INPUT" } }
      { "append" = "RH-Firewall-1-INPUT"
          { "in-interface" = "lo" }
          { "jump" = "ACCEPT" } } }

let conf = "# Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:55 2002
*filter
:INPUT DROP [1:229]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

-I FORWARD -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT

# comments and blank lines are allow between rules

-A FORWARD -i eth1 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
--append OUTPUT -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
COMMIT
# Completed on Wed Apr 24 10:19:55 2002
# Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:55 2002
*mangle
:PREROUTING ACCEPT [658:32445]

:INPUT ACCEPT [658:32445]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [891:68234]
:POSTROUTING ACCEPT [891:68234]
COMMIT
# Completed on Wed Apr 24 10:19:55 2002
# Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:55 2002
*nat
:PREROUTING ACCEPT [1:229]
:POSTROUTING ACCEPT [3:450]
# The output chain
:OUTPUT ACCEPT [3:450]
# insert something
--insert POSTROUTING -o eth0 -j SNAT --to-source 195.233.192.1 \t
# and now commit
COMMIT
# Completed on Wed Apr 24 10:19:55 2002\n"

test Iptables.lns get conf =
  { "#comment" =
      "Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:55 2002" }
  { "table" = "filter"
    { "chain" = "INPUT" { "policy" = "DROP" } }
    { "chain" = "FORWARD" { "policy" = "DROP" } }
    { "chain" = "OUTPUT" { "policy" = "DROP" } }
    { "append" = "INPUT"
      { "match" = "state" }
      { "state" = "RELATED,ESTABLISHED" }
      { "jump" = "ACCEPT" } }
    {}
    { "insert" = "FORWARD"
      { "in-interface" = "eth0" }
      { "match" = "state" }
      { "state" = "RELATED,ESTABLISHED" }
      { "jump" = "ACCEPT" } }
    {}
    { "#comment" = "comments and blank lines are allow between rules" }
    {}
    { "append" = "FORWARD"
      { "in-interface" = "eth1" }
      { "match" = "state" }
      { "state" = "NEW,RELATED,ESTABLISHED" }
      { "jump" = "ACCEPT" } }
    { "append" = "OUTPUT"
      { "match" = "state" }
      { "state" = "NEW,RELATED,ESTABLISHED" }
      { "jump" = "ACCEPT" } } }
  { "#comment" = "Completed on Wed Apr 24 10:19:55 2002" }
  { "#comment" =
      "Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:55 2002" }
  { "table" = "mangle"
    { "chain" = "PREROUTING" { "policy" = "ACCEPT" } }
    {}
    { "chain" = "INPUT" { "policy" = "ACCEPT" } }
    { "chain" = "FORWARD" { "policy" = "ACCEPT" } }
    { "chain" = "OUTPUT" { "policy" = "ACCEPT" } }
    { "chain" = "POSTROUTING" { "policy" = "ACCEPT" } } }
  { "#comment" = "Completed on Wed Apr 24 10:19:55 2002" }
  { "#comment" =
      "Generated by iptables-save v1.2.6a on Wed Apr 24 10:19:55 2002" }
  { "table" = "nat"
    { "chain" = "PREROUTING" { "policy" = "ACCEPT" } }
    { "chain" = "POSTROUTING" { "policy" = "ACCEPT" } }
    { "#comment" = "The output chain" }
    { "chain" = "OUTPUT" { "policy" = "ACCEPT" } }
    { "#comment" = "insert something" }
    { "insert" = "POSTROUTING"
      { "out-interface" = "eth0" }
      { "jump" = "SNAT" }
      { "to-source" = "195.233.192.1" } }
    { "#comment" = "and now commit" } }
  { "#comment" = "Completed on Wed Apr 24 10:19:55 2002" }

test ipt_match get " -m comment --comment \"A comment\"" =
  { "match" = "comment" }
  { "comment" = "\"A comment\"" }

(*
 * Test the various schemes for negation that iptables supports
 *
 * Note that the two ways in which a parameter can be negated lead to
 * two different trees that mean the same.
 *)
test add_rule get "-I POSTROUTING ! -d 192.168.122.0/24 -j MASQUERADE\n" =
  { "insert" = "POSTROUTING"
    { "destination" = "192.168.122.0/24"
      { "not" } }
    { "jump" = "MASQUERADE" } }

test add_rule get "-I POSTROUTING -d ! 192.168.122.0/24 -j MASQUERADE\n" =
  { "insert" = "POSTROUTING"
    { "destination" = "! 192.168.122.0/24" }
    { "jump" = "MASQUERADE" } }

test add_rule put "-I POSTROUTING ! -d 192.168.122.0/24 -j MASQUERADE\n"
    after rm "/insert/destination/not" =
  "-I POSTROUTING -d 192.168.122.0/24 -j MASQUERADE\n"

(* I have no idea if iptables will accept double negations, but we
 * allow it syntactically *)
test add_rule put "-I POSTROUTING -d ! 192.168.122.0/24 -j MASQUERADE\n"
    after clear "/insert/destination/not" =
  "-I POSTROUTING ! -d ! 192.168.122.0/24 -j MASQUERADE\n"

test Iptables.chain get ":tcp_packets - [0:0]
" =
    { "chain" = "tcp_packets" { "policy" = "-" } }

(* Bug #157 *)
test ipt_match get " --tcp-flags SYN,RST,ACK,FIN SYN" =
  { "tcp-flags"
    { "mask" = "SYN" }
    { "mask" = "RST" }
    { "mask" = "ACK" }
    { "mask" = "FIN" }
    { "set" = "SYN" } }

(* Bug #224 *)
test ipt_match get " --icmpv6-type neighbor-solicitation" =
  { "icmpv6-type" = "neighbor-solicitation" }


Limits (filter): no need to exclude anything


Exports: Do not use Hosts lenses

Exports: Do not use Hosts lenses

(* Lens for Linux syntax of NFS exports(5) *)

(*
Module: Exports
    Parses /etc/exports

Author: David Lutterkort <lutter@redhat.com>

About: Description
    /etc/exports contains lines associating a directory with one or
    more hosts, and NFS options for each host.

About: Usage Example

(start code)

    $ augtool
    augtool> ls /files/etc/exports/
    comment[1] = /etc/exports: the access control list for filesystems which may be exported
    comment[2] = to NFS clients.  See exports(5).
    comment[3] = sample /etc/exports file
    dir[1]/ = /
    dir[2]/ = /projects
    dir[3]/ = /usr
    dir[4]/ = /home/joe

    augtool> ls /files/etc/exports/dir[1]
    client[1]/ = master
    client[2]/ = trusty
(end code)

The corresponding line in the file is:

(start code)
	/               master(rw) trusty(rw,no_root_squash)
(end code)

    Digging further:

(start code)
    augtool> ls /files/etc/exports/dir[1]/client[1]
    option = rw

    To add a new entry, you'd do something like this:
(end code)

(start code)
    augtool> set /files/etc/exports/dir[10000] /foo
    augtool> set /files/etc/exports/dir[last()]/client[1] weeble
    augtool> set /files/etc/exports/dir[last()]/client[1]/option[1] ro
    augtool> set /files/etc/exports/dir[last()]/client[1]/option[2] all_squash
    augtool> save
    Saved 1 file(s)
(end code)

    Which creates the line:

(start code)
    /foo weeble(ro,all_squash)
(end code)

About: Limitations
    This lens cannot handle options without a host, as with the last
    example line in "man 5 exports":

	/pub            (ro,insecure,all_squash)

    In this case, though, you can just do:

	/pub            *(ro,insecure,all_squash)

    It also can't handle whitespace before the directory name.
*)

module Exports =
  autoload xfm

  let client_re = /[a-zA-Z0-9.@*?\/:-]+/

  let eol = Util.eol
  let lbracket  = Util.del_str "("
  let rbracket  = Util.del_str ")"
  let sep_com   = Sep.comma
  let sep_spc   = Sep.space

  let option = [ label "option" . store /[^,)]*/ ]

  let client    = [ label "client" . store client_re .
                    ( Build.brackets lbracket rbracket
                         ( Build.opt_list option sep_com ) )? ]

  let entry = [ label "dir" . store /\/[^ \t]*/
                . sep_spc . Build.opt_list client sep_spc . eol ]

  let lns = (Util.empty | Util.comment | entry)*

  let xfm = transform lns (incl "/etc/exports")

(* Lens for Linux syntax of NFS exports(5) *)

(*
Module: Exports
    Parses /etc/exports

Author: David Lutterkort <lutter@redhat.com>

About: Description
    /etc/exports contains lines associating a directory with one or
    more hosts, and NFS options for each host.

About: Usage Example

(start code)

    $ augtool
    augtool> ls /files/etc/exports/
    comment[1] = /etc/exports: the access control list for filesystems which may be exported
    comment[2] = to NFS clients.  See exports(5).
    comment[3] = sample /etc/exports file
    dir[1]/ = /
    dir[2]/ = /projects
    dir[3]/ = /usr
    dir[4]/ = /home/joe

    augtool> ls /files/etc/exports/dir[1]
    client[1]/ = master
    client[2]/ = trusty
(end code)

The corresponding line in the file is:

(start code)
	/               master(rw) trusty(rw,no_root_squash)
(end code)

    Digging further:

(start code)
    augtool> ls /files/etc/exports/dir[1]/client[1]
    option = rw

    To add a new entry, you'd do something like this:
(end code)

(start code)
    augtool> set /files/etc/exports/dir[10000] /foo
    augtool> set /files/etc/exports/dir[last()]/client[1] weeble
    augtool> set /files/etc/exports/dir[last()]/client[1]/option[1] ro
    augtool> set /files/etc/exports/dir[last()]/client[1]/option[2] all_squash
    augtool> save
    Saved 1 file(s)
(end code)

    Which creates the line:

(start code)
    /foo weeble(ro,all_squash)
(end code)

About: Limitations
    This lens cannot handle options without a host, as with the last
    example line in "man 5 exports":

	/pub            (ro,insecure,all_squash)

    In this case, though, you can just do:

	/pub            *(ro,insecure,all_squash)

    It also can't handle whitespace before the directory name.
*)

module Exports =
  autoload xfm

  let client_re = /[a-zA-Z0-9.@*?\/:-]+/

  let eol = Util.eol
  let lbracket  = Util.del_str "("
  let rbracket  = Util.del_str ")"
  let sep_com   = Sep.comma
  let sep_spc   = Sep.space

  let option = [ label "option" . store /[^,)]*/ ]

  let client    = [ label "client" . store client_re .
                    ( Build.brackets lbracket rbracket
                         ( Build.opt_list option sep_com ) )? ]

  let entry = [ label "dir" . store /\/[^ \t]*/
                . sep_spc . Build.opt_list client sep_spc . eol ]

  let lns = (Hosts.empty | Hosts.comment | entry)*

  let xfm = transform lns (incl "/etc/exports")


backport fix ae9dd6, remove after 0.5.1 release

(* Parse vsftpd.conf *)
module Vsftpd =
  autoload xfm

(* The code in parseconf.c does not seem to allow for trailing whitespace *)
(* in the config file                                                     *)
let eol = Util.del_str "\n"
let empty = Util.empty
let comment = Util.comment

let bool_option_re = /anonymous_enable|local_enable|pasv_enable|port_enable|chroot_local_user|write_enable|anon_upload_enable|anon_mkdir_write_enable|anon_other_write_enable|chown_uploads|connect_from_port_20|xferlog_enable|dirmessage_enable|anon_world_readable_only|async_abor_enable|ascii_upload_enable|ascii_download_enable|one_process_model|xferlog_std_format|pasv_promiscuous|deny_email_enable|chroot_list_enable|setproctitle_enable|text_userdb_names|ls_recurse_enable|log_ftp_protocol|guest_enable|userlist_enable|userlist_deny|use_localtime|check_shell|hide_ids|listen|port_promiscuous|passwd_chroot_enable|no_anon_password|tcp_wrappers|use_sendfile|force_dot_files|listen_ipv6|dual_log_enable|syslog_enable|background|virtual_use_local_privs|session_support|download_enable|dirlist_enable|chmod_enable|secure_email_list_enable|run_as_launching_user|no_log_lock|ssl_enable|allow_anon_ssl|force_local_logins_ssl|force_local_data_ssl|ssl_sslv2|ssl_sslv3|ssl_tlsv1|tilde_user_enable|force_anon_logins_ssl|force_anon_data_ssl|mdtm_write|lock_upload_files|pasv_addr_resolve|debug_ssl|require_cert|validate_cert/

let uint_option_re = /accept_timeout|connect_timeout|local_umask|anon_umask|ftp_data_port|idle_session_timeout|data_connection_timeout|pasv_min_port|pasv_max_port|anon_max_rate|local_max_rate|listen_port|max_clients|file_open_mode|max_per_ip|trans_chunk_size|delay_failed_login|delay_successful_login|max_login_fails|chown_upload_mode/

let str_option_re = /secure_chroot_dir|ftp_username|chown_username|xferlog_file|vsftpd_log_file|message_file|nopriv_user|ftpd_banner|banned_email_file|chroot_list_file|pam_service_name|guest_username|userlist_file|anon_root|local_root|banner_file|pasv_address|listen_address|user_config_dir|listen_address6|cmds_allowed|hide_file|deny_file|user_sub_token|email_password_file|rsa_cert_file|dsa_cert_file|ssl_ciphers|rsa_private_key_file|dsa_private_key_file|ca_certs_file/

let bool_value_re = /[yY][eE][sS]|[tT][rR][uU][eE]|1|[nN][oO]|[fF][aA][lL][sS][eE]|0/

let option (k:regexp) (v:regexp) = [ key k . Util.del_str "=" . store v . eol ]

let bool_option = option bool_option_re bool_value_re

let str_option = option str_option_re /[^\n]+/

let uint_option = option uint_option_re /[0-9]+/

let lns = (bool_option|str_option|uint_option|comment|empty)*

let filter = (incl "/etc/vsftpd.conf") . (incl "/etc/vsftpd/vsftpd.conf")

let xfm = transform lns filter

error: pathspec 'files/vsftpd.aug' did not match any file(s) known to git


Inittab: clean up formatting some

Inittab: clean up formatting some

(* Parsing /etc/inittab *)
module Inittab =
   autoload xfm

   let sep = Util.del_str ":"
   let eol = Util.del_str "\n"

   let id = /[^\/#:\n]{1,4}/
   let value = /[^#:\n]*/

   let comment = Util.comment|Util.empty

   let record =
     let field (name:string) = [ label name . store value ] in
       [ key id . sep .
           field "runlevels" . sep .
           field "action" . sep .
           field "process" . eol ]

   let lns = ( comment | record ) *

   let xfm = transform lns (incl "/etc/inittab")

(* Local Variables: *)
(* mode: caml       *)
(* End:             *)

(* Parsing /etc/inittab *)
module Inittab =
   autoload xfm

   let sep = Util.del_str ":"
   let eol = Util.del_str "\n"

   let id = /[^\/#:\n]{1,4}/
   let value = /[^#:\n]*/

   let comment = Util.comment|Util.empty

   let field (name:string) = [ label name . store value ]
   let record = [ key id . sep .
                  field("runlevels") . sep .
                  field("action") . sep .
                  field("process") .
                  eol
                ]

   let lns = ( comment | record ) *

   let xfm = transform lns (incl "/etc/inittab")

(* Local Variables: *)
(* mode: caml       *)
(* End:             *)


Use regexp union instead of lens union in dput.aug

Using a union of regexps is much faster than a union of lenses, since the
regular expressions that are constructed internally are much simpler.

(* Dput module for Augeas                     *)
(* Author: Raphael Pinson <raphink@gmail.com> *)
(*                                            *)

module Dput =
  autoload xfm

    let setting = "allow_non-us_software"
                | "allow_unsigned_uploads"
                | "check_version"
                | "default_host_main"
                | "default_host_non-us"
                | "fqdn"
                | "hash"
                | "incoming"
                | "login"
                | "method"
                | "passive_ftp"
                | "post_upload_command"
                | "pre_upload_command"
                | "progress_indicator"
                | "run_dinstall"
                | "run_lintian"
                | "scp_compress"
		| "ssh_config_options"

    let entry = IniFile.entry setting

    let record = IniFile.record "target" entry

    let lns = IniFile.lns record

    let filter = (incl "/etc/dput.cf")
        . (incl "~/.dput.cf")
        . Util.stdexcl

    let xfm = transform lns filter

(* Dput module for Augeas                     *)
(* Author: Raphael Pinson <raphink@gmail.com> *)
(*                                            *)

module Dput =
  autoload xfm

    let setting = IniFile.entry "allow_non-us_software"
                | IniFile.entry "allow_unsigned_uploads"
                | IniFile.entry "check_version"
                | IniFile.entry "default_host_main"
                | IniFile.entry "default_host_non-us"
                | IniFile.entry "fqdn"
                | IniFile.entry "hash"
                | IniFile.entry "incoming"
                | IniFile.entry "login"
                | IniFile.entry "method"
                | IniFile.entry "passive_ftp"
                | IniFile.entry "post_upload_command"
                | IniFile.entry "pre_upload_command"
                | IniFile.entry "progress_indicator"
                | IniFile.entry "run_dinstall"
                | IniFile.entry "run_lintian"
                | IniFile.entry "scp_compress"
		| IniFile.entry "ssh_config_options"

    let record = IniFile.record "target" setting

    let lns = IniFile.lns record

    let filter = (incl "/etc/dput.cf")
        . (incl "~/.dput.cf")
        . Util.stdexcl

    let xfm = transform lns filter


Remove trailing whitespaces in util.aug

Remove trailing whitespaces in util.aug

(*
Module: Util
  Generic module providing useful primitives

Author: David Lutterkort

About: License
  This file is licensed under the LGPLv2+, like the rest of Augeas.
*)

module Util =

(*
Variable: del_str
  Delete a string and default to it

  Parameters:
     s:string - the string to delete and default to
*)
  let del_str (s:string) = del s s

(*
Variable: del_ws
  Delete mandatory whitespace
*)
  let del_ws = del /[ \t]+/

(*
Variable: del_ws_spc
  Delete mandatory whitespace, default to single space
*)
  let del_ws_spc = del_ws " "

(*
Variable: del_ws_tab
  Delete mandatory whitespace, default to single tab
*)
  let del_ws_tab = del_ws "\t"

(*
Variable: del_opt_ws
  Delete optional whitespace
*)
  let del_opt_ws = del /[ \t]*/

(*
Variable: eol
  Delete end of line, including optional trailing whitespace
*)
  let eol = del /[ \t]*\n/ "\n"

(*
Variable: indent
  Delete indentation, including leading whitespace
*)
  let indent = del /[ \t]*/ ""

(* Group: Comment
     This is a general definition of comment
     It allows indentation for comments, removes the leading and trailing spaces
     of comments and stores them in nodes, except for empty comments which are
     ignored together with empty lines

View: comment_generic
  Map comments and set default comment sign
*)

  let comment_generic (r:regexp) (d:string) =
    [ label "#comment" . del r d
        . store /([^ \t\n].*[^ \t\n]|[^ \t\n])/ . eol ]

(* View: comment
  Map comments into "#comment" nodes
*)
  let comment = comment_generic /[ \t]*#[ \t]*/ "# "

(* View: comment_eol
  Map eol comments into "#comment" nodes
  Add a space before # for end of line comments
*)
  let comment_eol = comment_generic /[ \t]*#[ \t]*/ " # "

(* View: comment_or_eol
    A <comment_eol> or <eol> *)
 let comment_or_eol = comment_eol | (del /[ \t]*#?\n/ "\n")

(* View: comment_multiline
    A C-style multiline comment *)
  let comment_multiline =
     let mline_re = (/[^ \t\n].*[^ \t\n]|[^ \t\n]/ - /.*\*\/.*/) in
     let mline = [ seq "mline"
                 . store mline_re ] in
     [ label "#mcomment" . del /[ \t]*\/\*[ \t\n]*/ "/*\n"
       . counter "mline"
       . (mline . (eol . mline)*)
       . del /[ \t\n]*\*\/[ \t]*\n/ "\n*/\n" ]

(* View: comment_c_style
    A comment line, C-style *)
  let comment_c_style =
    comment_generic /[ \t]*\/\/[ \t]*/ "// "

(* View: empty_generic
  A generic definition of <empty>
  Map empty lines, including empty comments *)
  let empty_generic (r:regexp) =
    [ del r "" . del_str "\n" ]

(* View: empty
  Map empty lines, including empty comments *)
  let empty = empty_generic /[ \t]*#?[ \t]*/

(* View: empty_c_style
  Map empty lines, including C-style empty comment *)
  let empty_c_style =
    empty_generic /[ \t]*((\/\/)|(\/\*[ \t]*\*\/))?[ \t]*/

(* View: Split *)
(* Split (SEP . ELT)* into an array-like tree where each match for ELT *)
(* appears in a separate subtree. The labels for the subtrees are      *)
(* consecutive numbers, starting at 0                                  *)
  let split (elt:lens) (sep:lens) =
    let sym = gensym "split" in
    counter sym . ( [ seq sym . sep . elt ] ) *

(* Group: Exclusions

Variable: stdexcl
  Exclusion for files that are commonly not wanted/needed
*)
  let stdexcl = (excl "*~") .
    (excl "*.rpmnew") .
    (excl "*.rpmsave") .
    (excl "*.dpkg-old") .
    (excl "*.dpkg-new") .
    (excl "*.dpkg-bak") .
    (excl "*.dpkg-dist") .
    (excl "*.augsave") .
    (excl "*.augnew")

(* Local Variables: *)
(* mode: caml       *)
(* End:             *)


Add test for radicale lens

Add test for radicale lens

module Test_radicale =

   let conf = "
[server]

[encoding]

[well-known]

[auth]

[git]

[rights]

[storage]

[logging]

[headers]

"

   test Radicale.lns get conf =
      {}
      { "server"
         {} }
      { "encoding"
         {} }
      { "well-known"
         {} }
      { "auth"
         {} }
      { "git"
         {} }
      { "rights"
         {} }
      { "storage"
         {} }
      { "logging"
         {} }
      { "headers"
         {} }

    test Radicale.lns put conf after
       set "server/hosts" "127.0.0.1:5232, [::1]:5232";
       set "server/base_prefix" "/radicale/";
       set "well-known/caldav" "/radicale/%(user)s/caldav/";
       set "well-known/cardav" "/radicale/%(user)s/carddav/";
       set "auth/type" "remote_user";
       set "rights/type" "owner_only"
    = "
[server]

hosts=127.0.0.1:5232, [::1]:5232
base_prefix=/radicale/
[encoding]

[well-known]

caldav=/radicale/%(user)s/caldav/
cardav=/radicale/%(user)s/carddav/
[auth]

type=remote_user
[git]

[rights]

type=owner_only
[storage]

[logging]

[headers]

"

error: pathspec 'lenses/tests/test_radicale.aug' did not match any file(s) known to git


Use Cron.shellvar in anacron.aug

Use Cron.shellvar in anacron.aug


Xinetd: better handling of whitespace

Xinetd: better handling of whitespace

- allow indented comments
- allow spaces around "}" to close a service

(* Process xinetd config files *)
(* The structure of the lens and allowed attributes are ripped directly   *)
(* from xinetd's parser in xinetd/parse.c in xinetd's source checkout     *)
(* The downside of being so precise here is that if attributes are added  *)
(* they need to be added here, too. Writing a catchall entry, and getting *)
(* to typecheck correctly would be a huge pain.                           *)
(* A really enterprising soul could tighten this down even further by     *)
(* restricting the acceptable values for each attribute.                  *)
module Xinetd =
  autoload xfm

  let comment = [ del /[ \t]*(#.*|[ \t]*)\n/ "#\n" ]

  let name = key /[^ \t\n\/+-=]+/
  let opt_spc = del /[ \t]*/ ""
  let spc = del /[ \t]+/ " "
  let eol = del /[ \t]*\n/ "\n"

  let eq = opt_spc . Util.del_str "="

  let op = opt_spc . ([ label "add" . Util.del_str "+=" ]
                 |[ label "del" . Util.del_str "-=" ]
                 |Util.del_str "=")

  let value = store /[^ \t\n]+/

  let attr_one (n:regexp) =
    [ opt_spc . key n . eq . spc . value . eol ]

  let attr_lst (n:regexp) (op_eq: lens) =
    [ opt_spc . key n . op_eq . [label "value" . spc . value]* . eol ]

  let attr_lst_eq (n:regexp) = attr_lst n eq

  let attr_lst_op (n:regexp) = attr_lst n op

  (* Note: it is much faster to combine, for example, all the attr_one    *)
  (* attributes into one regexp and pass that to a lens instead of        *)
  (* using lens union (attr_one "a" | attr_one "b"|..) because the latter *)
  (* causes the type checker to work _very_ hard.                         *)

  let service_attr =
    attr_one ("socket_type" | "protocol" | "wait" | "user" | "group"
             |"server" | "instances"  | "rpc_version" | "rpc_number"
             | "id" | "port" | "nice" | "banner" | "bind" | "interface"
             | "per_source" | "groups" | "banner_success" | "banner_fail"
             | "disable" | "max_load" | "rlimit_as" | "rlimit_cpu"
             | "rlimit_data" | "rlimit_rss" | "rlimit_stack" | "v6only"
             | "deny_time" | "umask" | "mdns" | "libwrap")
   (* redirect and cps aren't really lists, they take exactly two values *)
   |attr_lst_eq ("server_args" | "log_type" |  "access_times" | "type"
                | "flags" | "redirect" | "cps")
   |attr_lst_op ( "log_on_success" | "log_on_failure"| "only_from"
                | "no_access" | "env" | "passenv")

  let default_attr =
    attr_one ( "instances" | "banner" | "bind" | "interface" | "per_source"
             | "groups" | "banner_success" | "banner_fail" | "max_load"
             | "v6only" | "umask" | "mdns")
   |attr_lst_eq "cps"       (* really only two values, not a whole list *)
   |attr_lst_op ( "log_type" | "log_on_success" | "log_on_failure" | "disabled"
                | "no_access" | "only_from" | "passenv" | "enabled" )

  (* Note: we would really like to say "the body can contain any of a list *)
  (*       of a list of attributes, each of them at most once"; but that   *)
  (*       would require that we build a lens that matches the permutation *)
  (*       of all attributes; with around 40 individual attributes, that's *)
  (*       not computationally feasible, even if we didn't have to worry   *)
  (*       about how to write that down. The resulting regular expressions *)
  (*       would simply be prohibitively large.                            *)
  let body (attr:lens) = Util.del_str "\n{\n"
                       . (comment|attr)*
                       . del /[ \t]*}[ \t]*\n/ "}\n"

  (* It would be nice if we could use the directories given in include and *)
  (* includedir directives to parse additional files instead of hardcoding *)
  (* all the places where xinetd config files can be found; but that is    *)
  (* currently not possible, and implementing that has a good amount of    *)
  (* hairy corner cases to consider.                                       *)
  let includes = [ key /include|includedir/
                     . Util.del_ws_spc . store /[^ \t\n]+/ . eol ]

  let service =
     let key_re = /[^ \t\n\/]+/ - /include|includedir|defaults/ in
     [ del /service[ \t]+/ "service " . key key_re . body service_attr ]

  let defaults = [ key "defaults" . del /[ \t]*/ "" . body default_attr ]

  let lns = ( comment | includes | defaults | service )*

  let filter = incl "/etc/xinetd.d/*"
             . incl "/etc/xinetd.conf"
             . Util.stdexcl

  let xfm = transform lns filter

(* Local Variables: *)
(* mode: caml       *)
(* End:             *)

(* Process xinetd config files *)
(* The structure of the lens and allowed attributes are ripped directly   *)
(* from xinetd's parser in xinetd/parse.c in xinetd's source checkout     *)
(* The downside of being so precise here is that if attributes are added  *)
(* they need to be added here, too. Writing a catchall entry, and getting *)
(* to typecheck correctly would be a huge pain.                           *)
(* A really enterprising soul could tighten this down even further by     *)
(* restricting the acceptable values for each attribute.                  *)
module Xinetd =
  autoload xfm

  let comment = [ del /(#.*|[ \t]*)\n/ "#\n" ]

  let name = key /[^ \t\n\/+-=]+/
  let opt_spc = del /[ \t]*/ ""
  let spc = del /[ \t]+/ " "
  let eol = del /[ \t]*\n/ "\n"

  let eq = opt_spc . Util.del_str "="

  let op = opt_spc . ([ label "add" . Util.del_str "+=" ]
                 |[ label "del" . Util.del_str "-=" ]
                 |Util.del_str "=")

  let value = store /[^ \t\n]+/

  let attr_one (n:regexp) =
    [ opt_spc . key n . eq . spc . value . eol ]

  let attr_lst (n:regexp) (op_eq: lens) =
    [ opt_spc . key n . op_eq . [label "value" . spc . value]* . eol ]

  let attr_lst_eq (n:regexp) = attr_lst n eq

  let attr_lst_op (n:regexp) = attr_lst n op

  (* Note: it is much faster to combine, for example, all the attr_one    *)
  (* attributes into one regexp and pass that to a lens instead of        *)
  (* using lens union (attr_one "a" | attr_one "b"|..) because the latter *)
  (* causes the type checker to work _very_ hard.                         *)

  let service_attr =
    attr_one ("socket_type" | "protocol" | "wait" | "user" | "group"
             |"server" | "instances"  | "rpc_version" | "rpc_number"
             | "id" | "port" | "nice" | "banner" | "bind" | "interface"
             | "per_source" | "groups" | "banner_success" | "banner_fail"
             | "disable" | "max_load" | "rlimit_as" | "rlimit_cpu"
             | "rlimit_data" | "rlimit_rss" | "rlimit_stack" | "v6only"
             | "deny_time" | "umask" | "mdns" | "libwrap")
   (* redirect and cps aren't really lists, they take exactly two values *)
   |attr_lst_eq ("server_args" | "log_type" |  "access_times" | "type"
                | "flags" | "redirect" | "cps")
   |attr_lst_op ( "log_on_success" | "log_on_failure"| "only_from"
                | "no_access" | "env" | "passenv")

  let default_attr =
    attr_one ( "instances" | "banner" | "bind" | "interface" | "per_source"
             | "groups" | "banner_success" | "banner_fail" | "max_load"
             | "v6only" | "umask" | "mdns")
   |attr_lst_eq "cps"       (* really only two values, not a whole list *)
   |attr_lst_op ( "log_type" | "log_on_success" | "log_on_failure" | "disabled"
                | "no_access" | "only_from" | "passenv" | "enabled" )

  (* Note: we would really like to say "the body can contain any of a list *)
  (*       of a list of attributes, each of them at most once"; but that   *)
  (*       would require that we build a lens that matches the permutation *)
  (*       of all attributes; with around 40 individual attributes, that's *)
  (*       not computationally feasible, even if we didn't have to worry   *)
  (*       about how to write that down. The resulting regular expressions *)
  (*       would simply be prohibitively large.                            *)
  let body (attr:lens) = Util.del_str "\n{\n"
                       . (comment|attr)*
                       . Util.del_str "}\n"

  (* It would be nice if we could use the directories given in include and *)
  (* includedir directives to parse additional files instead of hardcoding *)
  (* all the places where xinetd config files can be found; but that is    *)
  (* currently not possible, and implementing that has a good amount of    *)
  (* hairy corner cases to consider.                                       *)
  let includes = [ key /include|includedir/
                     . Util.del_ws_spc . store /[^ \t\n]+/ . eol ]

  let service =
     let key_re = /[^ \t\n\/]+/ - /include|includedir|defaults/ in
     [ del /service[ \t]+/ "service " . key key_re . body service_attr ]

  let defaults = [ key "defaults" . del /[ \t]*/ "" . body default_attr ]

  let lns = ( comment | includes | defaults | service )*

  let filter = incl "/etc/xinetd.d/*"
             . incl "/etc/xinetd.conf"
             . Util.stdexcl

  let xfm = transform lns filter

(* Local Variables: *)
(* mode: caml       *)
(* End:             *)


Test for continuation lines

Test for continuation lines

module Pass_cont_line =

(* Parse a list of words where the list can stretch over multiple lines.
   Mostly there to demonstrate how to deal with continuation lines. *)

let list_elt = [ label "element" . store /[a-z]+/ ]

let ws_cont = /([ \t]+|[ \t]*\\\\\n[ \t]*)/

let sep = del ws_cont " "
let eol = del /[ \t]*\n/ "\n"

let list = list_elt . ( sep . list_elt )* . eol

let exp_tree = { "element" = "a" } { "element" = "b" }

test list get "a  b\n"      = exp_tree
test list get "a  \\\n b\n" = exp_tree
test list get "a\\\nb\n"    = exp_tree

error: pathspec 'tests/modules/pass_cont_line.aug' did not match any file(s) known to git


Upgrade subversion lens to official

Upgrade subversion lens to official

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/raphink/puppet-subversion.git/'


Logrotate (hooks): simplify to speed up typechecker

(* Logrotate module for Augeas                *)
(* Author: Raphael Pinson <raphink@gmail.com> *)
(* Patches from:                              *)
(*   Sean Millichamp <sean@bruenor.org>       *)
(*                                            *)
(* Supported :                                *)
(*   - defaults                               *)
(*   - rules                                  *)
(*   - (pre|post)rotate entries               *)
(*                                            *)
(* Todo :                                     *)
(*                                            *)

module Logrotate =
   autoload xfm

   let sep_spc = Util.del_ws_spc
   let sep_val = del /[ \t]*=[ \t]*|[ \t]+/ " "
   let eol = Util.del_str "\n"
   let num = /[0-9]+/
   let word = /[^,#= \n\t{}]+/
   let size = num . /[kMG]?/

   (* define comments and empty lines *)
   let comment (indent:string) = [ label "#comment" . del /[ \t]*/ indent . del /#[ \t]*/ "# " .  store /([^ \t\n][^\n]*)?/ . eol ]
   let empty   = [ del /[ \t]*\n/ "\n" ]

   (* Useful functions *)

   let list_item = [ sep_spc . key /[^\/+,# \n\t{}]+/ ]
   let select_to_eol (kw:string) (select:regexp) (indent:string) = [ del /[ \t]*/ indent . label kw . store select . eol ]
   let value_to_eol (kw:string) (value:regexp) (indent:string )  = [ del /[ \t]*/ indent . key kw . sep_val . store value . eol ]
   let flag_to_eol (kw:string) (indent:string)                   = [ del /[ \t]*/ indent . key kw . eol ]
   let list_to_eol (kw:string) (indent:string)                   = [ del /[ \t]*/ indent . key kw . list_item+ . eol ]

   (* Defaults *)

   let create (indent:string ) = [ del /[ \t]*/ indent . key "create" .
                       ( sep_spc . [ label "mode" . store num ] . sep_spc .
		       [ label "owner" . store word ] . sep_spc .
		       [ label "group" . store word ])?
		    . eol ]

   let tabooext (indent:string) = [ del /[ \t]*/ indent . key "tabooext" . ( sep_spc . store /\+/ )? . list_item+ . eol ]

   let attrs (indent:string) = select_to_eol "schedule" /(daily|weekly|monthly|yearly)/ indent
                | value_to_eol "rotate" num indent
		| create indent
		| flag_to_eol "nocreate" indent
		| value_to_eol "include" word indent
		| select_to_eol "missingok" /(no)?missingok/ indent
		| select_to_eol "compress" /(no)?compress/ indent
		| select_to_eol "delaycompress" /(no)?delaycompress/ indent
		| select_to_eol "ifempty" /(not)?ifempty/ indent
		| select_to_eol "sharedscripts" /(no)?sharedscripts/ indent
		| value_to_eol "size" size indent
		| tabooext indent
		| value_to_eol "olddir" word indent
		| flag_to_eol "noolddir" indent
		| value_to_eol "mail" word indent
		| flag_to_eol "mailfirst" indent
		| flag_to_eol "maillast" indent
		| flag_to_eol "nomail" indent
		| value_to_eol "errors" word indent
		| value_to_eol "extension" word indent
		| select_to_eol "dateext" /(no)?dateext/ indent
		| value_to_eol "compresscmd" word indent
		| value_to_eol "uncompresscmd" word indent
		| value_to_eol "compressext" word indent
		| list_to_eol "compressoptions" indent
		| select_to_eol "copy" /(no)?copy/ indent
		| select_to_eol "copytruncate" /(no)?copytruncate/ indent
		| value_to_eol "maxage" num indent
		| value_to_eol "minsize" size indent
		| select_to_eol "shred" /(no)?shred/ indent
		| value_to_eol "shredcycles" num indent
		| value_to_eol "start" num indent

   (* Define hooks *)

   let hook_lines =
     let line_re = /.*/ - /[ \t]*endscript[ \t]*/ in
       store ( line_re . ("\n" . line_re)* )? . del "\n" "\n"

   let hooks =
     let hook_names = /(pre|post)rotate|(first|last)action/ in
     [ del /[ \t]*/ "\t" . key hook_names . eol .
       hook_lines .
       del /[ \t]*endscript\n/ "\tendscript\n" ]

   (* Define rule *)

   let body = del /\{[ \t]*\n/ "{\n"
                       . ( comment "\t" | attrs "\t" | hooks | empty )*
                       . del /[ \t]*\}[ \t]*\n/ "}\n"

   let rule =
     [ label "rule" .
         [ label "file" . store word ] .
	 [ del /[ \t]+/ " " . label "file" . store word ]* .
	 del /[ \t\n]*/ " " . body ]

   let lns = ( comment "" | empty | attrs "" | rule )*

   let filter = incl "/etc/logrotate.d/*"
              . incl "/etc/logrotate.conf"
	      . Util.stdexcl

   let xfm = transform lns filter

(* Logrotate module for Augeas                *)
(* Author: Raphael Pinson <raphink@gmail.com> *)
(* Patches from:                              *)
(*   Sean Millichamp <sean@bruenor.org>       *)
(*                                            *)
(* Supported :                                *)
(*   - defaults                               *)
(*   - rules                                  *)
(*   - (pre|post)rotate entries               *)
(*                                            *)
(* Todo :                                     *)
(*                                            *)

module Logrotate =
   autoload xfm

   let sep_spc = Util.del_ws_spc
   let sep_val = del /[ \t]*=[ \t]*|[ \t]+/ " "
   let eol = Util.del_str "\n"
   let num = /[0-9]+/
   let word = /[^,#= \n\t{}]+/
   let size = num . /[kMG]?/

   (* define comments and empty lines *)
   let comment (indent:string) = [ label "#comment" . del /[ \t]*/ indent . del /#[ \t]*/ "# " .  store /([^ \t\n][^\n]*)?/ . eol ]
   let empty   = [ del /[ \t]*\n/ "\n" ]

   (* Useful functions *)

   let list_item = [ sep_spc . key /[^\/+,# \n\t{}]+/ ]
   let select_to_eol (kw:string) (select:regexp) (indent:string) = [ del /[ \t]*/ indent . label kw . store select . eol ]
   let value_to_eol (kw:string) (value:regexp) (indent:string )  = [ del /[ \t]*/ indent . key kw . sep_val . store value . eol ]
   let flag_to_eol (kw:string) (indent:string)                   = [ del /[ \t]*/ indent . key kw . eol ]
   let list_to_eol (kw:string) (indent:string)                   = [ del /[ \t]*/ indent . key kw . list_item+ . eol ]

   (* Defaults *)

   let create (indent:string ) = [ del /[ \t]*/ indent . key "create" .
                       ( sep_spc . [ label "mode" . store num ] . sep_spc .
		       [ label "owner" . store word ] . sep_spc .
		       [ label "group" . store word ])?
		    . eol ]

   let tabooext (indent:string) = [ del /[ \t]*/ indent . key "tabooext" . ( sep_spc . store /\+/ )? . list_item+ . eol ]

   let attrs (indent:string) = select_to_eol "schedule" /(daily|weekly|monthly|yearly)/ indent
                | value_to_eol "rotate" num indent
		| create indent
		| flag_to_eol "nocreate" indent
		| value_to_eol "include" word indent
		| select_to_eol "missingok" /(no)?missingok/ indent
		| select_to_eol "compress" /(no)?compress/ indent
		| select_to_eol "delaycompress" /(no)?delaycompress/ indent
		| select_to_eol "ifempty" /(not)?ifempty/ indent
		| select_to_eol "sharedscripts" /(no)?sharedscripts/ indent
		| value_to_eol "size" size indent
		| tabooext indent
		| value_to_eol "olddir" word indent
		| flag_to_eol "noolddir" indent
		| value_to_eol "mail" word indent
		| flag_to_eol "mailfirst" indent
		| flag_to_eol "maillast" indent
		| flag_to_eol "nomail" indent
		| value_to_eol "errors" word indent
		| value_to_eol "extension" word indent
		| select_to_eol "dateext" /(no)?dateext/ indent
		| value_to_eol "compresscmd" word indent
		| value_to_eol "uncompresscmd" word indent
		| value_to_eol "compressext" word indent
		| list_to_eol "compressoptions" indent
		| select_to_eol "copy" /(no)?copy/ indent
		| select_to_eol "copytruncate" /(no)?copytruncate/ indent
		| value_to_eol "maxage" num indent
		| value_to_eol "minsize" size indent
		| select_to_eol "shred" /(no)?shred/ indent
		| value_to_eol "shredcycles" num indent
		| value_to_eol "start" num indent

   (* Define hooks *)

   let hook_lines =
     let line_re = /.*/ - /[ \t]*endscript[ \t]*/ in
       store ( line_re . ("\n" . line_re)* )? . del "\n" "\n"

   let hook_func (func_type:string) = [
       del /[ \t]*/ "\t" . key func_type . eol .
       hook_lines .
       del /[ \t]*endscript\n/ "\tendscript\n" ]

   let hooks = hook_func "postrotate"
             | hook_func "prerotate"
             | hook_func "firstaction"
             | hook_func "lastaction"

   (* Define rule *)

   let body = del /\{[ \t]*\n/ "{\n"
                       . ( comment "\t" | attrs "\t" | hooks | empty )*
                       . del /[ \t]*\}[ \t]*\n/ "}\n"

   let rule =
     [ label "rule" .
         [ label "file" . store word ] .
	 [ del /[ \t]+/ " " . label "file" . store word ]* .
	 del /[ \t\n]*/ " " . body ]

   let lns = ( comment "" | empty | attrs "" | rule )*

   let filter = incl "/etc/logrotate.d/*"
              . incl "/etc/logrotate.conf"
	      . Util.stdexcl

   let xfm = transform lns filter


Remove trailing spaces in lenses/tests/test_nagiosobjects.aug

module Test_NagiosObjects =
    let conf="
#
# Nagios Objects definitions file
#

define host {
    host_name               plonk
    alias                   plonk
    address                 plonk
    use                     generic_template
    contact_groups          Monitoring-Team,admins
}

define service {
    service_description     gen
    use                     generic_template_passive
    host_name               plonk
    check_command           nopassivecheckreceived
    contact_groups          admins
}
"

    test NagiosObjects.lns get conf =
        {}
        {}
        { "#comment" = "Nagios Objects definitions file" }
        {}
        {}
        { "host"
            { "host_name"       = "plonk" }
            { "alias"           = "plonk" }
            { "address"         = "plonk" }
            { "use"             = "generic_template" }
            { "contact_groups"  = "Monitoring-Team,admins" }
        }
        {}
        { "service"
            { "service_description"     = "gen" }
            { "use"                     = "generic_template_passive" }
            { "host_name"               = "plonk" }
            { "check_command"           = "nopassivecheckreceived" }
            { "contact_groups"          = "admins" }
        }