Exclude network/if-up.d/SuSEfirewall2 in shellvars lens

openSUSE has /etc/sysconfig/network/if-up.d/SuSEfirewall2 that is not a
shell variable script, exclude it to avoid errors.

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

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

About: Lens Usage
   To be documented
*)

module Shellvars =
  autoload xfm

  (* Delete a blank line, rather than mapping it *)
  let del_empty = del (Util.empty_generic_re . "\n") "\n"

  let empty   = Util.empty
  let empty_part_re = Util.empty_generic_re . /\n+/
  let eol = del (/[ \t]+|[ \t]*[;\n]/ . empty_part_re*) "\n"
  let semicol_eol = del (/[ \t]*[;\n]/ . empty_part_re*) "\n"
  let brace_eol = del /[ \t\n]+/ "\n"

  let key_re = /[A-Za-z0-9_]+(\[[0-9A-Za-z_,]+\])?/ - ("unset" | "export")
  let matching_re = "${!" . key_re . /[\*@]\}/
  let eq = Util.del_str "="

  let eol_for_comment = del /([ \t]*\n)([ \t]*(#[ \t]*)?\n)*/ "\n"
  let comment = Util.comment_generic_seteol /[ \t]*#[ \t]*/ " # " eol_for_comment
  (* comment_eol in shell MUST begin with a space *)
  let comment_eol = Util.comment_generic_seteol /[ \t]+#[ \t]*/ " # " eol_for_comment
  let comment_or_eol = comment_eol | semicol_eol

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

  let char  = /[^`;()'"&|\n\\# \t]#*|\\\\./
  let dquot =
       let char = /[^"\\]|\\\\./ | Rx.cl
    in "\"" . char* . "\""                    (* " Emacs, relax *)
  let squot = /'[^']*'/
  let bquot = /`[^`\n]+`/
  (* dbquot don't take spaces or semi-colons *)
  let dbquot = /``[^` \t\n;]+``/
  let dollar_assign = /\$\([^\(\)#\n]*\)/
  let dollar_arithm = /\$\(\([^\)#\n]*\)\)/

  let anyquot = (char|dquot|squot|dollar_assign|dollar_arithm)+ | bquot | dbquot
  let sto_to_semicol = store (anyquot . (Rx.cl_or_space . anyquot)*)

  (* 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 anyquot 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 (anyquot | empty_array)?

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

  let var_action (name:string) =
    Util.indent . del name name . Util.del_ws_spc
    . label ("@" . name) . counter "var_action"
    . Build.opt_list [ seq "var_action" . store (key_re | matching_re) ] Util.del_ws_spc

  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]+/

  let shell_builtin_cmds = "ulimit" | "shift" | "exit"

  let eval =
    Util.indent . Util.del_str "eval" . Util.del_ws_spc
    . label "@eval" . store anyquot

  let alias =
    Util.indent . Util.del_str "alias" . Util.del_ws_spc
    . label "@alias" . store key_re . eq
    . [ label "value" . store anyquot ]

  let builtin =
    Util.indent . label "@builtin"
    . store shell_builtin_cmds
    . (Sep.cl_or_space
    . [ label "args" . sto_to_semicol ])?

  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 )?

  let action (operator:string) (lbl:string) (sto:lens) =
       let sp = Rx.cl_or_opt_space | /[ \t\n]+/
    in [ del (sp . operator . sp) (" " . operator . " ")
       . label ("@".lbl) . sto ]

  let action_pipe = action "|" "pipe"
  let action_and = action "&&" "and"
  let action_or = action "||" "or"

  let condition =
    let cond (start:string) (end:string) = [ label "type" . store start ]
                                         . Util.del_ws_spc . sto_to_semicol
                                         . Util.del_ws_spc . Util.del_str end
                                         . ( action_and sto_to_semicol | action_or sto_to_semicol )*
    in Util.indent . label "@condition" . (cond "[" "]" | cond "[[" "]]")

  (* Entry types *)
  let entry_eol_item (item:lens) = [ item . comment_or_eol ]
  let entry_item (item:lens) = [ item ]

  let entry_eol_nocommand =
      entry_eol_item source
        | entry_eol_item kv
        | entry_eol_item unset
        | entry_eol_item bare_export
        | entry_eol_item builtin
        | entry_eol_item return
        | entry_eol_item condition
        | entry_eol_item eval
        | entry_eol_item alias

  let entry_noeol_nocommand =
      entry_item source
        | entry_item kv
        | entry_item unset
        | entry_item bare_export
        | entry_item builtin
        | entry_item return
        | entry_item condition
        | entry_item eval
        | entry_item alias

  (* Command *)
  let rec command =
       let env = [ key key_re . eq . store anyquot . Sep.cl_or_space ]
    in let reserved_key = /exit|shift|return|ulimit|unset|export|source|\.|if|for|select|while|until|then|else|fi|done|case|eval|alias/
    in let word = /[A-Za-z0-9_.-\/]+/
    in let entry_eol = entry_eol_nocommand | entry_eol_item command
    in let entry_noeol = entry_noeol_nocommand | entry_item command
    in let entry = entry_eol | entry_noeol
    in let pipe = action_pipe (entry_eol_item command | entry_item command)
    in let and = action_and entry
    in let or = action_or entry
    in Util.indent . label "@command" . env* . store (word - reserved_key)
     . [ Sep.cl_or_space . label "@arg" . sto_to_semicol]?
     . ( pipe | and | or )?

  let entry_eol = entry_eol_nocommand
                | entry_eol_item command

  let entry_noeol = entry_noeol_nocommand
                  | entry_item command

(************************************************************************
 * 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) (entry_noeol:lens) =
       let pattern = [ label "@pattern" . sto_to_semicol . Sep.opt_space ]
    in let case_entry = [ label "@case_entry"
                       . Util.indent . pattern
                       . (Util.del_str "|" . Sep.opt_space . pattern)*
                       . Util.del_str ")" . eol
                       . entry* . entry_noeol?
                       . Util.indent . Util.del_str ";;" . eol ] in
      [ keyword_label "case" "@case" . Sep.space
        . store (char+ | ("\"" . char+ . "\""))
        . del /[ \t\n]+/ " " . Util.del_str "in" . eol
        . (empty* . comment* . case_entry)*
        . empty* . comment*
        . keyword "esac" . comment_or_eol ]

  let subshell (entry:lens) =
    [ Util.indent . label "@subshell"
    . Util.del_str "{" . brace_eol
    . entry+
    . Util.indent . Util.del_str "}" . eol ]

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

  let rec rec_entry =
    let entry = comment | entry_eol | rec_entry in
        cond_if entry
      | loop_for entry
      | loop_select entry
      | loop_while entry
      | loop_until entry
      | case entry entry_noeol
      | function entry
      | subshell entry

  let lns_norec = del_empty* . (comment | entry_eol) *

  let lns = del_empty* . (comment | entry_eol | 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 "ip*tables.save" .
      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_excl "network/if-up.d/SuSEfirewall2" .
      sc_incl "network/providers/*" .
      sc_excl "network-scripts" .
      sc_incl "network-scripts/ifcfg-*" .
      sc_excl "rhn" .
      sc_incl "rhn/allowed-actions/*" .
      sc_excl "rhn/allowed-actions/script" .
      sc_incl "rhn/allowed-actions/script/*" .
      sc_incl "rhn/rhnsd" .
      sc_excl "SuSEfirewall2.d" .
      sc_incl "SuSEfirewall2.d/cobbler" .
      sc_incl "SuSEfirewall2.d/services/*" .
      sc_excl "SuSEfirewall2.d/services/TEMPLATE" .
      sc_excl "*.systemd"

  let filter_default = incl "/etc/default/*"
                     . excl "/etc/default/grub_installdevice*"
                     . excl "/etc/default/rmt"
                     . excl "/etc/default/whoopsie"
  let filter_misc    = incl "/etc/arno-iptables-firewall/debconf.cfg"
                     . incl "/etc/cron-apt/config"
                     . incl "/etc/environment"
                     . incl "/etc/firewalld/firewalld.conf"
                     . 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/periodic.conf"
                     . incl "/etc/popularity-contest.conf"
                     . incl "/etc/rc.conf"
                     . incl "/etc/rc.conf.local"
                     . incl "/etc/selinux/config"
                     . incl "/etc/ucf.conf"
                     . incl "/etc/locale.conf"
                     . incl "/etc/vconsole.conf"

  let filter = filter_sysconfig
             . 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 LGPL v2+, like the rest of Augeas.

About: Lens Usage
   To be documented
*)

module Shellvars =
  autoload xfm

  (* Delete a blank line, rather than mapping it *)
  let del_empty = del (Util.empty_generic_re . "\n") "\n"

  let empty   = Util.empty
  let empty_part_re = Util.empty_generic_re . /\n+/
  let eol = del (/[ \t]+|[ \t]*[;\n]/ . empty_part_re*) "\n"
  let semicol_eol = del (/[ \t]*[;\n]/ . empty_part_re*) "\n"
  let brace_eol = del /[ \t\n]+/ "\n"

  let key_re = /[A-Za-z0-9_]+(\[[0-9A-Za-z_,]+\])?/ - ("unset" | "export")
  let matching_re = "${!" . key_re . /[\*@]\}/
  let eq = Util.del_str "="

  let eol_for_comment = del /([ \t]*\n)([ \t]*(#[ \t]*)?\n)*/ "\n"
  let comment = Util.comment_generic_seteol /[ \t]*#[ \t]*/ " # " eol_for_comment
  (* comment_eol in shell MUST begin with a space *)
  let comment_eol = Util.comment_generic_seteol /[ \t]+#[ \t]*/ " # " eol_for_comment
  let comment_or_eol = comment_eol | semicol_eol

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

  let char  = /[^`;()'"&|\n\\# \t]#*|\\\\./
  let dquot =
       let char = /[^"\\]|\\\\./ | Rx.cl
    in "\"" . char* . "\""                    (* " Emacs, relax *)
  let squot = /'[^']*'/
  let bquot = /`[^`\n]+`/
  (* dbquot don't take spaces or semi-colons *)
  let dbquot = /``[^` \t\n;]+``/
  let dollar_assign = /\$\([^\(\)#\n]*\)/
  let dollar_arithm = /\$\(\([^\)#\n]*\)\)/

  let anyquot = (char|dquot|squot|dollar_assign|dollar_arithm)+ | bquot | dbquot
  let sto_to_semicol = store (anyquot . (Rx.cl_or_space . anyquot)*)

  (* 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 anyquot 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 (anyquot | empty_array)?

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

  let var_action (name:string) =
    Util.indent . del name name . Util.del_ws_spc
    . label ("@" . name) . counter "var_action"
    . Build.opt_list [ seq "var_action" . store (key_re | matching_re) ] Util.del_ws_spc

  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]+/

  let shell_builtin_cmds = "ulimit" | "shift" | "exit"

  let eval =
    Util.indent . Util.del_str "eval" . Util.del_ws_spc
    . label "@eval" . store anyquot

  let alias =
    Util.indent . Util.del_str "alias" . Util.del_ws_spc
    . label "@alias" . store key_re . eq
    . [ label "value" . store anyquot ]

  let builtin =
    Util.indent . label "@builtin"
    . store shell_builtin_cmds
    . (Sep.cl_or_space
    . [ label "args" . sto_to_semicol ])?

  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 )?

  let action (operator:string) (lbl:string) (sto:lens) =
       let sp = Rx.cl_or_opt_space | /[ \t\n]+/
    in [ del (sp . operator . sp) (" " . operator . " ")
       . label ("@".lbl) . sto ]

  let action_pipe = action "|" "pipe"
  let action_and = action "&&" "and"
  let action_or = action "||" "or"

  let condition =
    let cond (start:string) (end:string) = [ label "type" . store start ]
                                         . Util.del_ws_spc . sto_to_semicol
                                         . Util.del_ws_spc . Util.del_str end
                                         . ( action_and sto_to_semicol | action_or sto_to_semicol )*
    in Util.indent . label "@condition" . (cond "[" "]" | cond "[[" "]]")

  (* Entry types *)
  let entry_eol_item (item:lens) = [ item . comment_or_eol ]
  let entry_item (item:lens) = [ item ]

  let entry_eol_nocommand =
      entry_eol_item source
        | entry_eol_item kv
        | entry_eol_item unset
        | entry_eol_item bare_export
        | entry_eol_item builtin
        | entry_eol_item return
        | entry_eol_item condition
        | entry_eol_item eval
        | entry_eol_item alias

  let entry_noeol_nocommand =
      entry_item source
        | entry_item kv
        | entry_item unset
        | entry_item bare_export
        | entry_item builtin
        | entry_item return
        | entry_item condition
        | entry_item eval
        | entry_item alias

  (* Command *)
  let rec command =
       let env = [ key key_re . eq . store anyquot . Sep.cl_or_space ]
    in let reserved_key = /exit|shift|return|ulimit|unset|export|source|\.|if|for|select|while|until|then|else|fi|done|case|eval|alias/
    in let word = /[A-Za-z0-9_.-\/]+/
    in let entry_eol = entry_eol_nocommand | entry_eol_item command
    in let entry_noeol = entry_noeol_nocommand | entry_item command
    in let entry = entry_eol | entry_noeol
    in let pipe = action_pipe (entry_eol_item command | entry_item command)
    in let and = action_and entry
    in let or = action_or entry
    in Util.indent . label "@command" . env* . store (word - reserved_key)
     . [ Sep.cl_or_space . label "@arg" . sto_to_semicol]?
     . ( pipe | and | or )?

  let entry_eol = entry_eol_nocommand
                | entry_eol_item command

  let entry_noeol = entry_noeol_nocommand
                  | entry_item command

(************************************************************************
 * 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) (entry_noeol:lens) =
       let pattern = [ label "@pattern" . sto_to_semicol . Sep.opt_space ]
    in let case_entry = [ label "@case_entry"
                       . Util.indent . pattern
                       . (Util.del_str "|" . Sep.opt_space . pattern)*
                       . Util.del_str ")" . eol
                       . entry* . entry_noeol?
                       . Util.indent . Util.del_str ";;" . eol ] in
      [ keyword_label "case" "@case" . Sep.space
        . store (char+ | ("\"" . char+ . "\""))
        . del /[ \t\n]+/ " " . Util.del_str "in" . eol
        . (empty* . comment* . case_entry)*
        . empty* . comment*
        . keyword "esac" . comment_or_eol ]

  let subshell (entry:lens) =
    [ Util.indent . label "@subshell"
    . Util.del_str "{" . brace_eol
    . entry+
    . Util.indent . Util.del_str "}" . eol ]

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

  let rec rec_entry =
    let entry = comment | entry_eol | rec_entry in
        cond_if entry
      | loop_for entry
      | loop_select entry
      | loop_while entry
      | loop_until entry
      | case entry entry_noeol
      | function entry
      | subshell entry

  let lns_norec = del_empty* . (comment | entry_eol) *

  let lns = del_empty* . (comment | entry_eol | 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 "ip*tables.save" .
      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_excl "network-scripts" .
      sc_incl "network-scripts/ifcfg-*" .
      sc_excl "rhn" .
      sc_incl "rhn/allowed-actions/*" .
      sc_excl "rhn/allowed-actions/script" .
      sc_incl "rhn/allowed-actions/script/*" .
      sc_incl "rhn/rhnsd" .
      sc_excl "SuSEfirewall2.d" .
      sc_incl "SuSEfirewall2.d/cobbler" .
      sc_incl "SuSEfirewall2.d/services/*" .
      sc_excl "SuSEfirewall2.d/services/TEMPLATE" .
      sc_excl "*.systemd"

  let filter_default = incl "/etc/default/*"
                     . excl "/etc/default/grub_installdevice*"
                     . excl "/etc/default/rmt"
                     . excl "/etc/default/whoopsie"
  let filter_misc    = incl "/etc/arno-iptables-firewall/debconf.cfg"
                     . incl "/etc/cron-apt/config"
                     . incl "/etc/environment"
                     . incl "/etc/firewalld/firewalld.conf"
                     . 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/periodic.conf"
                     . incl "/etc/popularity-contest.conf"
                     . incl "/etc/rc.conf"
                     . incl "/etc/rc.conf.local"
                     . incl "/etc/selinux/config"
                     . incl "/etc/ucf.conf"
                     . incl "/etc/locale.conf"
                     . incl "/etc/vconsole.conf"

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

  let xfm = transform lns filter

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


Improve doc on services.aug

Improve doc on services.aug

(*
Module: Services
 Parses /etc/services

Author: Raphael Pinson <raphink@gmail.com>

About: Reference
 This lens tries to keep as close as possible to 'man services' where possible.

The definitions from 'man services' are put as commentaries for reference
throughout the file. More information can be found in the manual.

About: License
  This file is licensed under the GPL.

About: Lens Usage
  Sample usage of this lens in augtool

    * Get the name of the service running on port 22 with protocol tcp
      > match "/files/etc/services/service-name[port = '22'][protocol = 'tcp']"
    * Remove the tcp entry for "domain" service
      > rm "/files/etc/services/service-name[. = 'domain'][protocol = 'tcp']"
    * Add a tcp service named "myservice" on port 55234
      > ins service-name after /files/etc/services/service-name[last()]
      > set /files/etc/services/service-name[last()] "myservice"
      > set "/files/etc/services/service-name[. = 'myservice']/port" "55234"
      > set "/files/etc/services/service-name[. = 'myservice']/protocol" "tcp"

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

module Services =
  autoload xfm

(************************************************************************
 * Group:                 USEFUL PRIMITIVES
 *************************************************************************)

(* Group: Generic primitives *)

(* Variable: eol *)
let eol         = del /[ \t]*(#)?[ \t]*\n/ "\n"
let indent      = Util.indent
let comment     = Util.comment
let empty       = Util.empty
let protocol_re = /[a-zA-Z]+/
let word_re     = /[a-zA-Z0-9_.+*\/-]+/
let num_re      = /[0-9]+/

(* Group: Separators *)
let sep_spc = Util.del_ws_spc

(************************************************************************
 * Group:                 LENSES
 *************************************************************************)

(* View: port *)
let port = [ label "port" . store num_re ]

(* View: protocol *)
let protocol = [ label "protocol" . store protocol_re ]

(* View: alias *)
let alias = [ label "alias" . store word_re ]

(*
 * View: record
 *   A standard /etc/services record
 *   TODO: make sure a space is added before a comment on new nodes
 *)
let record = [ label "service-name" . store word_re . sep_spc . port
                 . del "/" "/" . protocol . ( sep_spc . alias )*
                 . (comment|eol) ]

(* View: lns
    The services lens is either <empty>, <comment> or <record> *)
let lns = ( empty | comment | record )*

(* View: filter *)
let filter = (incl "/etc/services")

let xfm = transform lns filter

(*
Module: Services
 Parses /etc/services

Author: Raphael Pinson <raphink@gmail.com>

About: Reference
 This lens tries to keep as close as possible to 'man services' where possible.

The definitions from 'man services' are put as commentaries for reference
throughout the file. More information can be found in the manual.

About: License
  This file is licensed under the GPL.

About: Lens Usage
  Sample usage of this lens in augtool

    * Get the name of the service running on port 22 with protocol tcp
      > match "/files/etc/services/service-name[port = '22'][protocol = 'tcp']"
    * Remove the tcp entry for "domain" service
      > rm "/files/etc/services/service-name[. = 'domain'][protocol = 'tcp']"
    * Add a tcp service named "myservice" on port 55234
      > ins service-name after /files/etc/services/service-name[last()]
      > set /files/etc/services/service-name[last()] "myservice"
      > set "/files/etc/services/service-name[. = 'myservice']/port" "55234"
      > set "/files/etc/services/service-name[. = 'myservice']/protocol" "tcp"

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

module Services =
  autoload xfm

(************************************************************************
 * Group:                 USEFUL PRIMITIVES
 *************************************************************************)

(* Group: Generic primitives *)

(* Variable: eol *)
let eol         = del /[ \t]*(#)?[ \t]*\n/ "\n"
let indent      = Util.indent
let comment     = Util.comment
let empty       = Util.empty
let protocol_re = /[a-zA-Z]+/
let word_re     = /[a-zA-Z0-9_.+*\/-]+/
let num_re      = /[0-9]+/

(* Group: Separators *)
let sep_spc = Util.del_ws_spc

(************************************************************************
 * Group:                 LENSES
 *************************************************************************)

(* View: port *)
let port = [ label "port" . store num_re ]

(* View: protocol *)
let protocol = [ label "protocol" . store protocol_re ]

(* View: alias *)
let alias = [ label "alias" . store word_re ]

(*
 * View: record
 *   A standard /etc/services record
 *   TODO: make sure a space is added before a comment on new nodes
 *)
let record = [ label "service-name" . store word_re . sep_spc . port
                 . del "/" "/" . protocol . ( sep_spc . alias )*
                 . (comment|eol) ]

(* View: lns *)
let lns = ( empty | comment | record )*

(*Variable: filter *)
let filter = (incl "/etc/services")

let xfm = transform lns filter


Add an Examples section to reprepro_uploaders.aug's header

(*
Module: Reprepro_Uploaders
  Parses reprepro's uploaders files

Author: Raphael Pinson <raphink@gmail.com>

About: Reference
  This lens tries to keep as close as possible to `man 1 reprepro` where possible.

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

About: Lens Usage
   See <lns>.

About: Configuration files
   This lens applies to reprepro's uploaders files.

About: Examples
   The <Test_Reprepro_Uploaders> file contains various examples and tests.
*)

module Reprepro_Uploaders =

(* View: logic_construct_condition
   A logical construction for <condition> and <condition_list> *)
let logic_construct_condition (kw:string) (lns:lens) =
    [ label kw . lns ]
  . [ Sep.space . key kw . Sep.space . lns ]*

(* View: logic_construct_field
   A generic definition for <condition_field> *)
let logic_construct_field (kw:string) (sep:string) (lns:lens) =
    [ label kw . lns ]
  . [ Build.xchgs sep kw . lns ]*

(* View: condition_re
   A condition can be of several types:

   - source
   - byhand
   - sections
   - sections contain
   - binaries
   - binaries contain
   - architectures
   - architectures contain

   While the lens technically also accepts "source contain"
   and "byhand contain", these are not understood by reprepro.

   The "contain" types are built by adding a "contain" subnode.
   See the <condition_field> definition.

 *)
let condition_re =
    "source"
  | "byhand"
  | "sections"
  | "binaries"
  | "architectures"

(* View: condition_field
   A single condition field is an 'or' node.
   It may contain several values, listed in 'or' subnodes:

   > $reprepro/allow[1]/and/or = "architectures"
   > $reprepro/allow[1]/and/or/or[1] = "i386"
   > $reprepro/allow[1]/and/or/or[2] = "amd64"
   > $reprepro/allow[1]/and/or/or[3] = "all"

 *)
let condition_field =
  let sto_condition = Util.del_str "'" . store /[^'\n]+/ . Util.del_str "'" in
    [ key "not" . Sep.space ]? .
    store condition_re
  . [ Sep.space . key "contain" ]?
  . Sep.space
  . logic_construct_field "or" "|" sto_condition

(* View: condition
   A condition is an 'and' node,
   representing a union of <condition_fields>,
   listed under 'or' subnodes:

   > $reprepro/allow[1]/and
   > $reprepro/allow[1]/and/or = "architectures"
   > $reprepro/allow[1]/and/or/or[1] = "i386"
   > $reprepro/allow[1]/and/or/or[2] = "amd64"
   > $reprepro/allow[1]/and/or/or[3] = "all"

 *)
let condition =
    logic_construct_condition "or" condition_field

(* View: condition_list
   A list of <conditions>, inspired by Debctrl.dependency_list
   An upload condition list is either the wildcard '*', stored verbatim,
   or an intersection of conditions listed under 'and' subnodes:

   > $reprepro/allow[1]/and[1]
   > $reprepro/allow[1]/and[1]/or = "architectures"
   > $reprepro/allow[1]/and[1]/or/or[1] = "i386"
   > $reprepro/allow[1]/and[1]/or/or[2] = "amd64"
   > $reprepro/allow[1]/and[1]/or/or[3] = "all"
   > $reprepro/allow[1]/and[2]
   > $reprepro/allow[1]/and[2]/or = "sections"
   > $reprepro/allow[1]/and[2]/or/contain
   > $reprepro/allow[1]/and[2]/or/or = "main"

 *)
let condition_list =
    store "*"
  | logic_construct_condition "and" condition

(* View: by_key
   When a key is used to authenticate packages,
   the value can either be a key ID or "any":

   > $reprepro/allow[1]/by/key = "ABCD1234"
   > $reprepro/allow[2]/by/key = "any"

 *)
let by_key =
  let any_key   = [ store "any" . Sep.space
                  . key "key" ] in
  let named_key = [ key "key" . Sep.space
                  . store (Rx.word - "any") ] in
    value "key" . (any_key | named_key)

(* View: by
   <by> statements define who is allowed to upload.
   It can be simple keywords, like "anybody" or "unsigned",
   or a key ID, in which case a "key" subnode is added:

   > $reprepro/allow[1]/by/key = "ABCD1234"
   > $reprepro/allow[2]/by/key = "any"
   > $reprepro/allow[3]/by = "anybody"
   > $reprepro/allow[4]/by = "unsigned"

 *)
let by =
    [ key "by" . Sep.space
         . ( store ("anybody"|"unsigned")
           | by_key ) ]

(* View: entry
   An entry is an allow statement, e.g.:

   > $reprepro/allow[1]
   > $reprepro/allow[1]/and[1]
   > $reprepro/allow[1]/and[1]/or = "architectures"
   > $reprepro/allow[1]/and[1]/or/or[1] = "i386"
   > $reprepro/allow[1]/and[1]/or/or[2] = "amd64"
   > $reprepro/allow[1]/and[1]/or/or[3] = "all"
   > $reprepro/allow[1]/and[2]
   > $reprepro/allow[1]/and[2]/or = "sections"
   > $reprepro/allow[1]/and[2]/or/contain
   > $reprepro/allow[1]/and[2]/or/or = "main"
   > $reprepro/allow[1]/by = "key"
   > $reprepro/allow[1]/by/key = "ABCD1234"

 *)
let entry =
    [ key "allow" . Sep.space
  . condition_list . Sep.space
  . by . Util.eol ]

(* View: lns
   The lens is made of <Util.empty>, <Util.comment> and <entry> lines *)
let lns = (Util.empty|Util.comment|entry)*

(*
Module: Reprepro_Uploaders
  Parses reprepro's uploaders files

Author: Raphael Pinson <raphink@gmail.com>

About: Reference
  This lens tries to keep as close as possible to `man 1 reprepro` where possible.

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

About: Lens Usage
   See <lns>.

About: Configuration files
   This lens applies to reprepro's uploaders files.
*)

module Reprepro_Uploaders =

(* View: logic_construct_condition
   A logical construction for <condition> and <condition_list> *)
let logic_construct_condition (kw:string) (lns:lens) =
    [ label kw . lns ]
  . [ Sep.space . key kw . Sep.space . lns ]*

(* View: logic_construct_field
   A generic definition for <condition_field> *)
let logic_construct_field (kw:string) (sep:string) (lns:lens) =
    [ label kw . lns ]
  . [ Build.xchgs sep kw . lns ]*

(* View: condition_re
   A condition can be of several types:

   - source
   - byhand
   - sections
   - sections contain
   - binaries
   - binaries contain
   - architectures
   - architectures contain

   While the lens technically also accepts "source contain"
   and "byhand contain", these are not understood by reprepro.

   The "contain" types are built by adding a "contain" subnode.
   See the <condition_field> definition.

 *)
let condition_re =
    "source"
  | "byhand"
  | "sections"
  | "binaries"
  | "architectures"

(* View: condition_field
   A single condition field is an 'or' node.
   It may contain several values, listed in 'or' subnodes:

   > $reprepro/allow[1]/and/or = "architectures"
   > $reprepro/allow[1]/and/or/or[1] = "i386"
   > $reprepro/allow[1]/and/or/or[2] = "amd64"
   > $reprepro/allow[1]/and/or/or[3] = "all"

 *)
let condition_field =
  let sto_condition = Util.del_str "'" . store /[^'\n]+/ . Util.del_str "'" in
    [ key "not" . Sep.space ]? .
    store condition_re
  . [ Sep.space . key "contain" ]?
  . Sep.space
  . logic_construct_field "or" "|" sto_condition

(* View: condition
   A condition is an 'and' node,
   representing a union of <condition_fields>,
   listed under 'or' subnodes:

   > $reprepro/allow[1]/and
   > $reprepro/allow[1]/and/or = "architectures"
   > $reprepro/allow[1]/and/or/or[1] = "i386"
   > $reprepro/allow[1]/and/or/or[2] = "amd64"
   > $reprepro/allow[1]/and/or/or[3] = "all"

 *)
let condition =
    logic_construct_condition "or" condition_field

(* View: condition_list
   A list of <conditions>, inspired by Debctrl.dependency_list
   An upload condition list is either the wildcard '*', stored verbatim,
   or an intersection of conditions listed under 'and' subnodes:

   > $reprepro/allow[1]/and[1]
   > $reprepro/allow[1]/and[1]/or = "architectures"
   > $reprepro/allow[1]/and[1]/or/or[1] = "i386"
   > $reprepro/allow[1]/and[1]/or/or[2] = "amd64"
   > $reprepro/allow[1]/and[1]/or/or[3] = "all"
   > $reprepro/allow[1]/and[2]
   > $reprepro/allow[1]/and[2]/or = "sections"
   > $reprepro/allow[1]/and[2]/or/contain
   > $reprepro/allow[1]/and[2]/or/or = "main"

 *)
let condition_list =
    store "*"
  | logic_construct_condition "and" condition

(* View: by_key
   When a key is used to authenticate packages,
   the value can either be a key ID or "any":

   > $reprepro/allow[1]/by/key = "ABCD1234"
   > $reprepro/allow[2]/by/key = "any"

 *)
let by_key =
  let any_key   = [ store "any" . Sep.space
                  . key "key" ] in
  let named_key = [ key "key" . Sep.space
                  . store (Rx.word - "any") ] in
    value "key" . (any_key | named_key)

(* View: by
   <by> statements define who is allowed to upload.
   It can be simple keywords, like "anybody" or "unsigned",
   or a key ID, in which case a "key" subnode is added:

   > $reprepro/allow[1]/by/key = "ABCD1234"
   > $reprepro/allow[2]/by/key = "any"
   > $reprepro/allow[3]/by = "anybody"
   > $reprepro/allow[4]/by = "unsigned"

 *)
let by =
    [ key "by" . Sep.space
         . ( store ("anybody"|"unsigned")
           | by_key ) ]

(* View: entry
   An entry is an allow statement, e.g.:

   > $reprepro/allow[1]
   > $reprepro/allow[1]/and[1]
   > $reprepro/allow[1]/and[1]/or = "architectures"
   > $reprepro/allow[1]/and[1]/or/or[1] = "i386"
   > $reprepro/allow[1]/and[1]/or/or[2] = "amd64"
   > $reprepro/allow[1]/and[1]/or/or[3] = "all"
   > $reprepro/allow[1]/and[2]
   > $reprepro/allow[1]/and[2]/or = "sections"
   > $reprepro/allow[1]/and[2]/or/contain
   > $reprepro/allow[1]/and[2]/or/or = "main"
   > $reprepro/allow[1]/by = "key"
   > $reprepro/allow[1]/by/key = "ABCD1234"

 *)
let entry =
    [ key "allow" . Sep.space
  . condition_list . Sep.space
  . by . Util.eol ]

(* View: lns
   The lens is made of <Util.empty>, <Util.comment> and <entry> lines *)
let lns = (Util.empty|Util.comment|entry)*


Fix syntax error; we want to test that missing defaults are noticed

grammar missing-token-default {
  token FOO /#.*\n/
}

grammar missing-token-default {
  token /#.*\n/
}


Proper test for eolwhite space handling

Proper test for eolwhite space handling

By reformating eol of pupperfileserver test, it's possible to make both git and
unit test working.

(* Tests for the PuppetFileserver module *)

module Test_puppetfileserver =

let fileserver = "# This a comment

[mount1]
  # Mount1 options
  path /etc/puppet/files/%h
  allow host.domain1.com
  allow *.domain2.com
  deny badhost.domain2.com
[mount2]
  allow *
  deny *.evil.example.com
  deny badhost.domain2.com
[mount3]
allow *   # Puppet #6026: same line comment
# And trailing whitespace
allow *    	\n"

test PuppetFileserver.lns get fileserver =
	{ "#comment" = "This a comment" }
	{ }
	{ "mount1"
		{ "#comment" = "Mount1 options" }
		{ "path" = "/etc/puppet/files/%h" }
		{ "allow" = "host.domain1.com" }
		{ "allow" = "*.domain2.com" }
		{ "deny" = "badhost.domain2.com" }
	}
	{ "mount2"
		{ "allow" = "*" }
		{ "deny" = "*.evil.example.com" }
		{ "deny" = "badhost.domain2.com" }
	}
	{ "mount3"
		{ "allow" = "*"
			{ "#comment" = "Puppet #6026: same line comment" } }
		{ "#comment" = "And trailing whitespace" }
		{ "allow" = "*" }
	}

(* Tests for the PuppetFileserver module *)

module Test_puppetfileserver =

let fileserver = "# This a comment

[mount1]
  # Mount1 options
  path /etc/puppet/files/%h
  allow host.domain1.com
  allow *.domain2.com
  deny badhost.domain2.com
[mount2]
  allow *
  deny *.evil.example.com
  deny badhost.domain2.com
[mount3]
allow *   # Puppet #6026: same line comment
# And trailing whitespace
allow *
"

test PuppetFileserver.lns get fileserver =
	{ "#comment" = "This a comment" }
	{ }
	{ "mount1"
		{ "#comment" = "Mount1 options" }
		{ "path" = "/etc/puppet/files/%h" }
		{ "allow" = "host.domain1.com" }
		{ "allow" = "*.domain2.com" }
		{ "deny" = "badhost.domain2.com" }
	}
	{ "mount2"
		{ "allow" = "*" }
		{ "deny" = "*.evil.example.com" }
		{ "deny" = "badhost.domain2.com" }
	}
	{ "mount3"
		{ "allow" = "*"
			{ "#comment" = "Puppet #6026: same line comment" } }
		{ "#comment" = "And trailing whitespace" }
		{ "allow" = "*" }
	}


Fix test_inifile.aug's typo

Fix test_inifile.aug's typo

(*
Module: Test_IniFile
  Provides unit tests and examples for the <IniFile> module.

About: Tests to run

   The tests are run with all combinations of the following
   three parameters:

   > separator   :  (a) default (/[:=]/ "=") ;   (b) "=" "="
   > comment     :  (c) default (/[;#]/ ";") ;   (d) ";" ";"
   > empty lines :  (e) default              ;   (f) noempty

*)

module Test_IniFile =

  (* ALL TESTS TO RUN *)

  (* Group: TEST a/c/e *)
  (* Variable: comment_ace *)
  let comment_ace = IniFile.comment IniFile.comment_re IniFile.comment_default
  (* Variable: sep_ace *)
  let sep_ace     = IniFile.sep IniFile.sep_re IniFile.sep_default
  (* Variable: entry_ace *)
  let entry_ace   = IniFile.entry IniFile.entry_re sep_ace comment_ace
  (* Variable: title_ace *)
  let title_ace   = IniFile.title IniFile.record_re
  (* Variable: record_ace *)
  let record_ace  = IniFile.record title_ace entry_ace
  (* Variable: lns_ace *)
  let lns_ace     = IniFile.lns record_ace comment_ace
  (* Variable: conf_ace *)
  let conf_ace    = "# comment with sharp

[section1]
test_ace = value # end of line comment
test_ace =
; comment with colon

"
  (* Test: lns_ace
      Testing the a/c/e combination *)
  test lns_ace get conf_ace =
      { "#comment" = "comment with sharp" }
      {}
      { "section1"
          { "test_ace" = "value"
	     { "#comment" = "end of line comment" } }
	  { "test_ace" }
	  { "#comment"  = "comment with colon" }
	  {} }

  (* Group: TEST a/c/f *)
  (* Variable: comment_acf *)
  let comment_acf = IniFile.comment IniFile.comment_re IniFile.comment_default
  (* Variable: sep_acf *)
  let sep_acf     = IniFile.sep IniFile.sep_re IniFile.sep_default
  (* Variable: entry_acf *)
  let entry_acf   = IniFile.entry IniFile.entry_re sep_acf comment_acf
  (* Variable: title_acf *)
  let title_acf   = IniFile.title IniFile.record_re
  (* Variable: record_acf *)
  let record_acf  = IniFile.record_noempty title_acf entry_acf
  (* Variable: lns_acf *)
  let lns_acf     = IniFile.lns_noempty record_acf comment_acf
  (* Variable: conf_acf *)
  let conf_acf   = "# comment with sharp
[section1]
test_acf = value
test_acf =
test_acf : value2 # end of line comment
; comment with colon
"
  (* Test: lns_acf
      Testing the a/c/f combination *)
  test lns_acf get conf_acf =
      { "#comment" = "comment with sharp" }
      { "section1"
         { "test_acf" = "value" }
	 { "test_acf" }
         { "test_acf" = "value2"
	    { "#comment" = "end of line comment" } }
	 { "#comment"  = "comment with colon" } }

  (* Group: TEST a/d/e *)
  (* Variable: comment_ade *)
  let comment_ade = IniFile.comment ";" ";"
  (* Variable: sep_ade *)
  let sep_ade     = IniFile.sep IniFile.sep_re IniFile.sep_default
  (* Variable: entry_ade *)
  let entry_ade   = IniFile.entry IniFile.entry_re sep_ade comment_ade
  (* Variable: title_ade *)
  let title_ade   = IniFile.title IniFile.record_re
  (* Variable: record_ade *)
  let record_ade  = IniFile.record title_ade entry_ade
  (* Variable: lns_ade *)
  let lns_ade     = IniFile.lns record_ade comment_ade
  (* Variable: conf_ade *)
  let conf_ade    = "; a first comment with colon
[section1]
test_ade = value
test_ade : value2 ; end of line comment
; comment with colon

test_ade =
"
  (* Test: lns_ade
      Testing the a/d/e combination *)
   test lns_ade get conf_ade =
      { "#comment" = "a first comment with colon" }
      { "section1"
         { "test_ade" = "value" }
         { "test_ade" = "value2"
	    { "#comment" = "end of line comment" } }
	 { "#comment"  = "comment with colon" }
	 {}
	 { "test_ade" } }

  (* Group: TEST a/d/f *)
  (* Variable: comment_adf *)
  let comment_adf = IniFile.comment ";" ";"
  (* Variable: sep_adf *)
  let sep_adf     = IniFile.sep IniFile.sep_re IniFile.sep_default
  (* Variable: entry_adf *)
  let entry_adf   = IniFile.entry IniFile.entry_re sep_adf comment_adf
  (* Variable: title_adf *)
  let title_adf   = IniFile.title IniFile.record_re
  (* Variable: record_adf *)
  let record_adf  = IniFile.record_noempty title_adf entry_adf
  (* Variable: lns_adf *)
  let lns_adf     = IniFile.lns_noempty record_adf comment_adf
  (* Variable: conf_adf *)
  let conf_adf    = "; a first comment with colon
[section1]
test_adf = value
test_adf : value2 ; end of line comment
; comment with colon
test_adf =
"
  (* Test: lns_adf
      Testing the a/d/f combination *)
   test lns_adf get conf_adf =
      { "#comment" = "a first comment with colon" }
      { "section1"
         { "test_adf" = "value" }
         { "test_adf" = "value2"
	    { "#comment" = "end of line comment" } }
	 { "#comment"  = "comment with colon" }
	 { "test_adf" } }

  (* Group: TEST b/c/e *)
  (* Variable: comment_bce *)
  let comment_bce = IniFile.comment IniFile.comment_re IniFile.comment_default
  (* Variable: sep_bce *)
  let sep_bce     = IniFile.sep "=" "="
  (* Variable: entry_bce *)
  let entry_bce   = IniFile.entry IniFile.entry_re sep_bce comment_bce
  (* Variable: title_bce *)
  let title_bce   = IniFile.title IniFile.record_re
  (* Variable: record_bce *)
  let record_bce  = IniFile.record title_bce entry_bce
  (* Variable: lns_bce *)
  let lns_bce     = IniFile.lns record_bce comment_bce
  (* Variable: conf_bce *)
  let conf_bce   = "# comment with sharp

[section1]
test_bce = value # end of line comment
; comment with colon

test_bce =
"
  (* Test: lns_bce
      Testing the b/c/e combination *)
  test lns_bce get conf_bce =
      { "#comment" = "comment with sharp" }
      {}
      { "section1"
          { "test_bce" = "value"
	     { "#comment" = "end of line comment" } }
	  { "#comment"  = "comment with colon" }
	  {}
	  { "test_bce" } }

  (* Group: TEST b/c/f *)
  (* Variable: comment_bcf *)
  let comment_bcf = IniFile.comment IniFile.comment_re IniFile.comment_default
  (* Variable: sep_bcf *)
  let sep_bcf     = IniFile.sep "=" "="
  (* Variable: entry_bcf *)
  let entry_bcf   = IniFile.entry IniFile.entry_re sep_bcf comment_bcf
  (* Variable: title_bcf *)
  let title_bcf   = IniFile.title IniFile.record_re
  (* Variable: record_bcf *)
  let record_bcf  = IniFile.record_noempty title_bce entry_bcf
  (* Variable: lns_bcf *)
  let lns_bcf     = IniFile.lns_noempty record_bce comment_bcf
  (* Variable: conf_bcf *)
  let conf_bcf   = "# conf with sharp
[section1]
test_bcf = value # end of line comment
; comment with colon
test_bcf =
"
  (* Test: lns_bcf
      Testing the b/c/f combination *)
  test lns_bcf get conf_bcf =
      { "#comment" = "conf with sharp" }
      { "section1"
          { "test_bcf" = "value"
	     { "#comment" = "end of line comment" } }
	  { "#comment"  = "comment with colon" }
	  { "test_bcf" } }

  (* Group: TEST b/d/e *)
  (* Variable: comment_bde *)
  let comment_bde = IniFile.comment ";" ";"
  (* Variable: sep_bde *)
  let sep_bde     = IniFile.sep "=" "="
  (* Variable: entry_bde *)
  let entry_bde   = IniFile.entry IniFile.entry_re sep_bde comment_bde
  (* Variable: title_bde *)
  let title_bde   = IniFile.title IniFile.record_re
  (* Variable: record_bde *)
  let record_bde  = IniFile.record title_bde entry_bde
  (* Variable: lns_bde *)
  let lns_bde     = IniFile.lns record_bde comment_bde
  (* Variable: conf_bde *)
  let conf_bde    = "; first comment with colon

[section1]
test_bde = value ; end of line comment
; comment with colon

test_bde =
"
  (* Test: lns_bde
      Testing the b/d/e combination *)
  test lns_bde get conf_bde =
      { "#comment" = "first comment with colon" }
      {}
      { "section1"
          { "test_bde" = "value"
	     { "#comment" = "end of line comment" } }
	  { "#comment"  = "comment with colon" }
	  {}
	  { "test_bde" } }

  (* Group: TEST b/d/f *)
  (* Variable: comment_bdf *)
  let comment_bdf = IniFile.comment ";" ";"
  (* Variable: sep_bdf *)
  let sep_bdf     = IniFile.sep "=" "="
  (* Variable: entry_bdf *)
  let entry_bdf   = IniFile.entry IniFile.entry_re sep_bdf comment_bdf
  (* Variable: title_bdf *)
  let title_bdf   = IniFile.title IniFile.record_re
  (* Variable: record_bdf *)
  let record_bdf  = IniFile.record_noempty title_bdf entry_bdf
  (* Variable: lns_bdf *)
  let lns_bdf     = IniFile.lns_noempty record_bdf comment_bdf
  (* Variable: conf_bdf *)
  let conf_bdf    = "; first comment with colon
[section1]
test_bdf = value ; end of line comment
; comment with colon
test_bdf =
"
  (* Test: lns_bdf
      Testing the b/d/f combination *)
  test lns_bdf get conf_bdf =
      { "#comment" = "first comment with colon" }
      { "section1"
          { "test_bdf" = "value"
	     { "#comment" = "end of line comment" } }
	  { "#comment"  = "comment with colon" }
	  { "test_bdf" } }

  (* Group: TEST multiline values *)
  (* Variable: multiline_test *)
  let multiline_test = "test_ace = val1\n  val2\n   val3\n"
  (* Variable: multiline_ace *)
  let multiline_ace = IniFile.entry_multiline IniFile.entry_re sep_ace comment_ace
  (* Test: multiline_ace
       Testing the a/c/e combination with a multiline entry *)
  test multiline_ace get multiline_test =
      { "test_ace" = "val1\n  val2\n   val3" }

(*
Module: Test_IniFile
  Provides unit tests and examples for the <IniFile> module.

About: Tests to run

   The tests are run with all combinations of the following
   three parameters:

   > separator   :  (a) default (/[:=]/ "=") ;   (b) "=" "="
   > comment     :  (c) default (/[;#]/ ";") ;   (d) ";" ";"
   > empty lines :  (e) default              ;   (f) noempty

*)

module Test_IniFile =

  (* ALL TESTS TO RUN *)

  (* Group: TEST a/c/e *)
  (* Variable: comment_ace *)
  let comment_ace = IniFile.comment IniFile.comment_re IniFile.comment_default
  (* Variable: sep_ace *)
  let sep_ace     = IniFile.sep IniFile.sep_re IniFile.sep_default
  (* Variable: entry_ace *)
  let entry_ace   = IniFile.entry IniFile.entry_re sep_ace comment_ace
  (* Variable: title_ace *)
  let title_ace   = IniFile.title IniFile.record_re
  (* Variable: record_ace *)
  let record_ace  = IniFile.record title_ace entry_ace
  (* Variable: lns_ace *)
  let lns_ace     = IniFile.lns record_ace comment_ace
  (* Variable: conf_ace *)
  let conf_ace    = "# comment with sharp

[section1]
test_ace = value # end of line comment
test_ace =
; comment with colon

"
  (* Test: lns_ace
      Testing the a/c/e combination *)
  test lns_ace get conf_ace =
      { "#comment" = "comment with sharp" }
      {}
      { "section1"
          { "test_ace" = "value"
	     { "#comment" = "end of line comment" } }
	  { "test_ace" }
	  { "#comment"  = "comment with colon" }
	  {} }

  (* Group: TEST a/c/f *)
  (* Variable: comment_acf *)
  let comment_acf = IniFile.comment IniFile.comment_re IniFile.comment_default
  (* Variable: sep_acf *)
  let sep_acf     = IniFile.sep IniFile.sep_re IniFile.sep_default
  (* Variable: entry_acf *)
  let entry_acf   = IniFile.entry IniFile.entry_re sep_acf comment_acf
  (* Variable: title_acf *)
  let title_acf   = IniFile.title IniFile.record_re
  (* Variable: record_acf *)
  let record_acf  = IniFile.record_noempty title_acf entry_acf
  (* Variable: lns_acf *)
  let lns_acf     = IniFile.lns_noempty record_acf comment_acf
  (* Variable: conf_acf *)
  let conf_acf   = "# comment with sharp
[section1]
test_acf = value
test_acf =
test_acf : value2 # end of line comment
; comment with colon
"
  (* Test: lns_acf
      Testing the a/c/f combination *)
  test lns_acf get conf_acf =
      { "#comment" = "comment with sharp" }
      { "section1"
         { "test_acf" = "value" }
	 { "test_acf" }
         { "test_acf" = "value2"
	    { "#comment" = "end of line comment" } }
	 { "#comment"  = "comment with colon" } }

  (* Group: TEST a/d/e *)
  (* Variable: comment_ade *)
  let comment_ade = IniFile.comment ";" ";"
  (* Variable: sep_ade *)
  let sep_ade     = IniFile.sep IniFile.sep_re IniFile.sep_default
  (* Variable: entry_ade *)
  let entry_ade   = IniFile.entry IniFile.entry_re sep_ade comment_ade
  (* Variable: title_ade *)
  let title_ade   = IniFile.title IniFile.record_re
  (* Variable: record_ade *)
  let record_ade  = IniFile.record title_ade entry_ade
  (* Variable: lns_ade *)
  let lns_ade     = IniFile.lns record_ade comment_ade
  (* Variable: conf_ade *)
  let conf_ade    = "; a first comment with colon
[section1]
test_ade = value
test_ade : value2 ; end of line comment
; comment with colon

test_ade =
"
  (* Test: lns_ade
      Testing the a/d/e combination *)
   test lns_ade get conf_ade =
      { "#comment" = "a first comment with colon" }
      { "section1"
         { "test_ade" = "value" }
         { "test_ade" = "value2"
	    { "#comment" = "end of line comment" } }
	 { "#comment"  = "comment with colon" }
	 {}
	 { "test_ade" } }

  (* Group: TEST a/d/f *)
  (* Variable: comment_adf *)
  let comment_adf = IniFile.comment ";" ";"
  (* Variable: sep_adf *)
  let sep_adf     = IniFile.sep IniFile.sep_re IniFile.sep_default
  (* Variable: entry_adf *)
  let entry_adf   = IniFile.entry IniFile.entry_re sep_adf comment_adf
  (* Variable: title_adf *)
  let title_adf   = IniFile.title IniFile.record_re
  (* Variable: record_adf *)
  let record_adf  = IniFile.record_noempty title_adf entry_adf
  (* Variable: lns_adf *)
  let lns_adf     = IniFile.lns_noempty record_adf comment_adf
  (* Variable: conf_adf *)
  let conf_adf    = "; a first comment with colon
[section1]
test_adf = value
test_adf : value2 ; end of line comment
; comment with colon
test_adf =
"
  (* Test: lns_adf
      Testing the a/d/f combination *)
   test lns_adf get conf_adf =
      { "#comment" = "a first comment with colon" }
      { "section1"
         { "test_adf" = "value" }
         { "test_adf" = "value2"
	    { "#comment" = "end of line comment" } }
	 { "#comment"  = "comment with colon" }
	 { "test_adf" } }

  (* Group: TEST b/c/e *)
  (* Variable: comment_bce *)
  let comment_bce = IniFile.comment IniFile.comment_re IniFile.comment_default
  (* Variable: sep_bce *)
  let sep_bce     = IniFile.sep "=" "="
  (* Variable: entry_bce *)
  let entry_bce   = IniFile.entry IniFile.entry_re sep_bce comment_bce
  (* Variable: title_bce *)
  let title_bce   = IniFile.title IniFile.record_re
  (* Variable: record_bce *)
  let record_bce  = IniFile.record title_bce entry_bce
  (* Variable: lns_bce *)
  let lns_bce     = IniFile.lns record_bce comment_bce
  (* Variable: conf_bce *)
  let conf_bce   = "# comment with sharp

[section1]
test_bce = value # end of line comment
; comment with colon

test_bce =
"
  (* Test: lns_bce
      Testing the b/c/e combination *)
  test lns_bce get conf_bce =
      { "#comment" = "comment with sharp" }
      {}
      { "section1"
          { "test_bce" = "value"
	     { "#comment" = "end of line comment" } }
	  { "#comment"  = "comment with colon" }
	  {}
	  { "test_bce" } }

  (* Group: TEST b/c/f *)
  (* Variable: comment_bcf *)
  let comment_bcf = IniFile.comment IniFile.comment_re IniFile.comment_default
  (* Variable: sep_bcf *)
  let sep_bcf     = IniFile.sep "=" "="
  (* Variable: entry_bcf *)
  let entry_bcf   = IniFile.entry IniFile.entry_re sep_bcf comment_bcf
  (* Variable: title_bcf *)
  let title_bcf   = IniFile.title IniFile.record_re
  (* Variable: record_bcf *)
  let record_bcf  = IniFile.record_noempty title_bce entry_bcf
  (* Variable: lns_bcf *)
  let lns_bcf     = IniFile.lns_noempty record_bce comment_bcf
  (* Variable: conf_bcf *)
  let conf_bcf   = "# conf with sharp
[section1]
test_bcf = value # end of line comment
; comment with colon
test_bcf =
"
  (* Test: lns_bcf
      Testing the b/c/f combination *)
  test lns_bcf get conf_bcf =
      { "#comment" = "comment with sharp" }
      { "section1"
          { "test_bcf" = "value"
	     { "#comment" = "end of line comment" } }
	  { "#comment"  = "comment with colon" }
	  { "test_bcf" } }

  (* Group: TEST b/d/e *)
  (* Variable: comment_bde *)
  let comment_bde = IniFile.comment ";" ";"
  (* Variable: sep_bde *)
  let sep_bde     = IniFile.sep "=" "="
  (* Variable: entry_bde *)
  let entry_bde   = IniFile.entry IniFile.entry_re sep_bde comment_bde
  (* Variable: title_bde *)
  let title_bde   = IniFile.title IniFile.record_re
  (* Variable: record_bde *)
  let record_bde  = IniFile.record title_bde entry_bde
  (* Variable: lns_bde *)
  let lns_bde     = IniFile.lns record_bde comment_bde
  (* Variable: conf_bde *)
  let conf_bde    = "; first comment with colon

[section1]
test_bde = value ; end of line comment
; comment with colon

test_bde =
"
  (* Test: lns_bde
      Testing the b/d/e combination *)
  test lns_bde get conf_bde =
      { "#comment" = "first comment with colon" }
      {}
      { "section1"
          { "test_bde" = "value"
	     { "#comment" = "end of line comment" } }
	  { "#comment"  = "comment with colon" }
	  {}
	  { "test_bde" } }

  (* Group: TEST b/d/f *)
  (* Variable: comment_bdf *)
  let comment_bdf = IniFile.comment ";" ";"
  (* Variable: sep_bdf *)
  let sep_bdf     = IniFile.sep "=" "="
  (* Variable: entry_bdf *)
  let entry_bdf   = IniFile.entry IniFile.entry_re sep_bdf comment_bdf
  (* Variable: title_bdf *)
  let title_bdf   = IniFile.title IniFile.record_re
  (* Variable: record_bdf *)
  let record_bdf  = IniFile.record_noempty title_bdf entry_bdf
  (* Variable: lns_bdf *)
  let lns_bdf     = IniFile.lns_noempty record_bdf comment_bdf
  (* Variable: conf_bdf *)
  let conf_bdf    = "; first comment with colon
[section1]
test_bdf = value ; end of line comment
; comment with colon
test_bdf =
"
  (* Test: lns_bdf
      Testing the b/d/f combination *)
  test lns_bdf get conf_bdf =
      { "#comment" = "first comment with colon" }
      { "section1"
          { "test_bdf" = "value"
	     { "#comment" = "end of line comment" } }
	  { "#comment"  = "comment with colon" }
	  { "test_bdf" } }

  (* Group: TEST multiline values *)
  (* Variable: multiline_test *)
  let multiline_test = "test_ace = val1\n  val2\n   val3\n"
  (* Variable: multiline_ace *)
  let multiline_ace = IniFile.entry_multiline IniFile.entry_re sep_ace comment_ace
  (* Test: multiline_ace
       Testing the a/c/e combination with a multiline entry *)
  test multiline_ace get multiline_test =
      { "test_ace" = "val1\n  val2\n   val3" }


pg_hba.aug: fix method to avoid clashing with hostname

(*
Module: Pg_Hba
  Parses PostgreSQL's pg_hba.conf

Author: Aurelien Bompard <aurelien@bompard.org>

About: Reference
  The file format is described in PostgreSQL's documentation:
  http://www.postgresql.org/docs/current/static/auth-pg-hba-conf.html

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

About: Configuration files
  This lens applies to pg_hba.conf. See <filter> for exact locations.
*)

module Pg_Hba =
    autoload xfm

    (* Group: Generic primitives *)

    let eol     = Util.eol
    let word    = Rx.neg1
    (* Variable: ipaddr
       CIDR or ip+netmask *)
    let ipaddr   = /[0-9a-fA-F:.]+(\/[0-9]+|[ \t]+[0-9.]+)/
    (* Variable: hostname
       Hostname, FQDN or part of an FQDN possibly
       starting with a dot. Taken from the syslog lens. *)
    let hostname = /\.?[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*/

    let comma_sep_list (l:string) =
        let lns = [ label l . store word ] in
            Build.opt_list lns Sep.comma

    (* Group: Columns definitions *)

    (* View: ipaddr_or_hostname *)
    let ipaddr_or_hostname = ipaddr | hostname
    (* View: database
       TODO: support for quoted strings *)
    let database = comma_sep_list "database"
    (* View: user
       TODO: support for quoted strings *)
    let user = comma_sep_list "user"
    (* View: address *)
    let address = [ label "address" . store ipaddr_or_hostname ]
    (* View: option
       part of <method> *)
    let option = [ label "option" . store word ]
    (* View: method
       can contain an <option> *)
    let method = [ label "method" . store /[A-Za-z][A-Za-z0-9]+/ . ( Sep.tab . option )? ]

    (* Group: Records definitions *)

    (* View: record_local
       when type is "local", there is no "address" field *)
    let record_local = [ label "type" . store "local" ] . Sep.tab .
                       database . Sep.tab . user . Sep.tab . method

    (* Variable: remtypes
       non-local connection types *)
    let remtypes = "host" | "hostssl" | "hostnossl"

    (* View: record_remote *)
    let record_remote = [ label "type" . store remtypes ] . Sep.tab .
                        database . Sep.tab .  user . Sep.tab .
                        address . Sep.tab . method

    (* View: record
        A sequence of <record_local> or <record_remote> entries *)
    let record = [ seq "entries" . (record_local | record_remote) . eol ]

    (* View: filter
        The pg_hba.conf conf file *)
    let filter = (incl "/var/lib/pgsql/data/pg_hba.conf" .
                  incl "/etc/postgresql/*/*/pg_hba.conf" )

    (* View: lns
        The pg_hba.conf lens *)
    let lns = ( record | Util.comment | Util.empty ) *

    let xfm = transform lns filter

(*
Module: Pg_Hba
  Parses PostgreSQL's pg_hba.conf

Author: Aurelien Bompard <aurelien@bompard.org>

About: Reference
  The file format is described in PostgreSQL's documentation:
  http://www.postgresql.org/docs/current/static/auth-pg-hba-conf.html

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

About: Configuration files
  This lens applies to pg_hba.conf. See <filter> for exact locations.
*)

module Pg_Hba =
    autoload xfm

    (* Group: Generic primitives *)

    let eol     = Util.eol
    let word    = Rx.neg1
    (* Variable: ipaddr
       CIDR or ip+netmask *)
    let ipaddr   = /[0-9a-fA-F:.]+(\/[0-9]+|[ \t]+[0-9.]+)/
    (* Variable: hostname
       Hostname, FQDN or part of an FQDN possibly
       starting with a dot. Taken from the syslog lens. *)
    let hostname = /\.?[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*/

    let comma_sep_list (l:string) =
        let lns = [ label l . store word ] in
            Build.opt_list lns Sep.comma

    (* Group: Columns definitions *)

    (* View: ipaddr_or_hostname *)
    let ipaddr_or_hostname = ipaddr | hostname
    (* View: database
       TODO: support for quoted strings *)
    let database = comma_sep_list "database"
    (* View: user
       TODO: support for quoted strings *)
    let user = comma_sep_list "user"
    (* View: address *)
    let address = [ label "address" . store ipaddr_or_hostname ]
    (* View: option
       part of <method> *)
    let option = [ label "option" . store word ]
    (* View: method
       can contain an <option> *)
    let method = [ label "method" . store Rx.word . ( Sep.tab . option )? ]

    (* Group: Records definitions *)

    (* View: record_local
       when type is "local", there is no "address" field *)
    let record_local = [ label "type" . store "local" ] . Sep.tab .
                       database . Sep.tab . user . Sep.tab . method

    (* Variable: remtypes
       non-local connection types *)
    let remtypes = "host" | "hostssl" | "hostnossl"

    (* View: record_remote *)
    let record_remote = [ label "type" . store remtypes ] . Sep.tab .
                        database . Sep.tab .  user . Sep.tab .
                        address . Sep.tab . method

    (* View: record
        A sequence of <record_local> or <record_remote> entries *)
    let record = [ seq "entries" . (record_local | record_remote) . eol ]

    (* View: filter
        The pg_hba.conf conf file *)
    let filter = (incl "/var/lib/pgsql/data/pg_hba.conf" .
                  incl "/etc/postgresql/*/*/pg_hba.conf" )

    (* View: lns
        The pg_hba.conf lens *)
    let lns = ( record | Util.comment | Util.empty ) *

    let xfm = transform lns filter


Add Sep.equal, used in nrpe.aug

Add Sep.equal, used in nrpe.aug

(*
Module: Sep
   Generic separators to build lenses

Author: Raphael Pinson <raphink@gmail.com>

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

module Sep =

let colon = Util.del_str ":"
let comma = Util.del_str ","
let equal = Util.del_str "="
let space = del Rx.space " "
let tab   = del Rx.space "\t"
let opt_space = del Rx.opt_space ""
let opt_tab   = del Rx.opt_space "\t"

(*
Module: Sep
   Generic separators to build lenses

Author: Raphael Pinson <raphink@gmail.com>

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

module Sep =

let colon = Util.del_str ":"
let comma = Util.del_str ","
let space = del Rx.space " "
let tab   = del Rx.space "\t"
let opt_space = del Rx.opt_space ""
let opt_tab   = del Rx.opt_space "\t"


Httpd: remove duplicate inclusion of /etc/httpd/conf/httpd.conf

(* Apache HTTPD lens for Augeas

Authors:
  David Lutterkort <lutter@redhat.com>
  Francis Giraldeau <francis.giraldeau@usherbrooke.ca>
  Raphael Pinson <raphink@gmail.com>

About: Reference
  Online Apache configuration manual: http://httpd.apache.org/docs/trunk/

About: License
    This file is licensed under the LGPLv2+.

About: Lens Usage
  Sample usage of this lens in augtool

  Apache configuration is represented by two main structures, nested sections
  and directives. Sections are used as labels, while directives are kept as a
  value. Sections and directives can have positional arguments inside values
  of "arg" nodes. Arguments of sections must be the firsts child of the
  section node.

  This lens doesn't support automatic string quoting. Hence, the string must
  be quoted when containing a space.

  Create a new VirtualHost section with one directive:
  > clear /files/etc/apache2/sites-available/foo/VirtualHost
  > set /files/etc/apache2/sites-available/foo/VirtualHost/arg "172.16.0.1:80"
  > set /files/etc/apache2/sites-available/foo/VirtualHost/directive "ServerAdmin"
  > set /files/etc/apache2/sites-available/foo/VirtualHost/*[self::directive="ServerAdmin"]/arg "admin@example.com"

About: Configuration files
  This lens applies to files in /etc/httpd and /etc/apache2. See <filter>.

*)

module Httpd =

autoload xfm

(******************************************************************
 *                           Utilities lens
 *****************************************************************)
let dels (s:string)     = del s s

(* deal with continuation lines *)
let sep_spc    =  del /([ \t]+|[ \t]*\\\\\n[ \t]*)/ " "

let sep_osp             = Sep.opt_space
let sep_eq              = del /[ \t]*=[ \t]*/ "="

let nmtoken             = /[a-zA-Z:_][a-zA-Z0-9:_\.-]*/
let word                = /[a-zA-Z][a-zA-Z0-9\._\-]*/

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

(* borrowed from shellvars.aug *)
let char_arg_dir  = /[^ '"\t\n]|\\\\"|\\\\'/
let char_arg_sec  = /[^ '"\t\n>]|\\\\"|\\\\'/
let dquot = /"([^"\\\n]|\\\\.)*"/
let squot = /'([^'\\\n]|\\\\.)*'/

(******************************************************************
 *                            Attributes
 *****************************************************************)

let arg_dir = [ label "arg" . store (char_arg_dir+|dquot|squot) ]
let arg_sec = [ label "arg" . store (char_arg_sec+|dquot|squot) ]

let argv (l:lens) = l . (sep_spc . l)*

let directive = [ indent . label "directive" . store word .
                  (sep_spc . argv arg_dir)? . eol ]

let section (body:lens) =
    let h = (sep_spc . argv arg_sec)? . sep_osp .
             dels ">" . eol . body* . indent . dels "</" in
        [ indent . dels "<" . square word h . del ">" ">" . eol ]

let rec content = section (content|directive|comment|empty)

let lns = (content|directive|comment|empty)*

let filter = (incl "/etc/apache2/apache2.conf") .
             (incl "/etc/apache2/httpd.conf") .
             (incl "/etc/apache2/ports.conf") .
             (incl "/etc/apache2/conf.d/*") .
             (incl "/etc/apache2/mods-available/*") .
             (incl "/etc/apache2/sites-available/*") .
             (incl "/etc/httpd/conf.d/*.conf") .
             (incl "/etc/httpd/httpd.conf") .
             (incl "/etc/httpd/conf/httpd.conf") .
             Util.stdexcl

let xfm = transform lns filter

(* Apache HTTPD lens for Augeas

Authors:
  David Lutterkort <lutter@redhat.com>
  Francis Giraldeau <francis.giraldeau@usherbrooke.ca>
  Raphael Pinson <raphink@gmail.com>

About: Reference
  Online Apache configuration manual: http://httpd.apache.org/docs/trunk/

About: License
    This file is licensed under the LGPLv2+.

About: Lens Usage
  Sample usage of this lens in augtool

  Apache configuration is represented by two main structures, nested sections
  and directives. Sections are used as labels, while directives are kept as a
  value. Sections and directives can have positional arguments inside values
  of "arg" nodes. Arguments of sections must be the firsts child of the
  section node.

  This lens doesn't support automatic string quoting. Hence, the string must
  be quoted when containing a space.

  Create a new VirtualHost section with one directive:
  > clear /files/etc/apache2/sites-available/foo/VirtualHost
  > set /files/etc/apache2/sites-available/foo/VirtualHost/arg "172.16.0.1:80"
  > set /files/etc/apache2/sites-available/foo/VirtualHost/directive "ServerAdmin"
  > set /files/etc/apache2/sites-available/foo/VirtualHost/*[self::directive="ServerAdmin"]/arg "admin@example.com"

About: Configuration files
  This lens applies to files in /etc/httpd and /etc/apache2. See <filter>.

*)

module Httpd =

autoload xfm

(******************************************************************
 *                           Utilities lens
 *****************************************************************)
let dels (s:string)     = del s s

(* deal with continuation lines *)
let sep_spc    =  del /([ \t]+|[ \t]*\\\\\n[ \t]*)/ " "

let sep_osp             = Sep.opt_space
let sep_eq              = del /[ \t]*=[ \t]*/ "="

let nmtoken             = /[a-zA-Z:_][a-zA-Z0-9:_\.-]*/
let word                = /[a-zA-Z][a-zA-Z0-9\._\-]*/

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

(* borrowed from shellvars.aug *)
let char_arg_dir  = /[^ '"\t\n]|\\\\"|\\\\'/
let char_arg_sec  = /[^ '"\t\n>]|\\\\"|\\\\'/
let dquot = /"([^"\\\n]|\\\\.)*"/
let squot = /'([^'\\\n]|\\\\.)*'/

(******************************************************************
 *                            Attributes
 *****************************************************************)

let arg_dir = [ label "arg" . store (char_arg_dir+|dquot|squot) ]
let arg_sec = [ label "arg" . store (char_arg_sec+|dquot|squot) ]

let argv (l:lens) = l . (sep_spc . l)*

let directive = [ indent . label "directive" . store word .
                  (sep_spc . argv arg_dir)? . eol ]

let section (body:lens) =
    let h = (sep_spc . argv arg_sec)? . sep_osp .
             dels ">" . eol . body* . indent . dels "</" in
        [ indent . dels "<" . square word h . del ">" ">" . eol ]

let rec content = section (content|directive|comment|empty)

let lns = (content|directive|comment|empty)*

let filter = (incl "/etc/apache2/apache2.conf") .
             (incl "/etc/apache2/httpd.conf") .
             (incl "/etc/apache2/ports.conf") .
             (incl "/etc/apache2/conf.d/*") .
             (incl "/etc/apache2/mods-available/*") .
             (incl "/etc/apache2/sites-available/*") .
             (incl "/etc/httpd/conf.d/*.conf") .
	     (incl "/etc/httpd/conf/httpd.conf") .
             (incl "/etc/httpd/httpd.conf") .
             (incl "/etc/httpd/conf/httpd.conf") .
             Util.stdexcl

let xfm = transform lns filter


Systemd: Don't use multiple negates in regexps

(*
Module: Systemd
  Parses systemd unit files.

Author: Dominic Cleal <dcleal@redhat.com>

About: Reference
  This lens tries to keep as close as possible to systemd.unit(5) and
  systemd.service(5) etc where possible.

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

About: Lens Usage
  To be documented

About: Configuration files
  This lens applies to /lib/systemd/system/* and /etc/systemd/system/*.
  See <filter>.
*)

module Systemd =
autoload xfm

(************************************************************************
 * Group:                 USEFUL PRIMITIVES
 *************************************************************************)

(* View: eol *)
let eol = Util.eol

(* View: comment
   An <IniFile.comment> entry *)
let comment    = IniFile.comment IniFile.comment_re "#"

(* View: sep
   An <IniFile.sep> entry *)
let sep        = IniFile.sep "=" "="

(* Variable: entry_single_kw *)
let entry_single_kw  = "Description"

(* Variable: entry_command_kw *)
let entry_command_kw = /Exec[A-Za-z][A-Za-z0-9._-]+/

(* Variable: entry_env_kw *)
let entry_env_kw     = "Environment"

(* Variable: entry_multi_kw *)
let entry_multi_kw   =
     let forbidden = entry_single_kw | entry_command_kw | entry_env_kw
  in /[A-Za-z][A-Za-z0-9._-]+/ - forbidden

(* Variable: value_single_re *)
let value_single_re  = /[^;# \t\n\\][^;#\n\\]*[^;# \t\n\\]|[^;# \t\n\\]/

(* View: sto_value_single
   Support multiline values with a backslash *)
let sto_value_single = Util.del_opt_ws ""
                       . store (value_single_re
                                . (/\\\\\n/ . value_single_re)*)

(* View: sto_value *)
let sto_value = store /[^;# \t\n\\]+/

(* Variable: value_sep
   Multi-value entries separated by whitespace or backslash and newline *)
let value_sep = del /[ \t]+|[ \t]*\\\\[ \t]*\n[ \t]*/ " "

(* Variable: value_cmd_re
   Don't parse @ and - prefix flags *)
let value_cmd_re = /[^;#@ \t\n\\-][^;#@ \t\n\\-][^;# \t\n\\]*/

(* Variable: env_key *)
let env_key = /[A-Za-z0-9_]+(\[[0-9]+\])?/

(************************************************************************
 * Group:                 ENTRIES
 *************************************************************************)

(*
Supported entry features, selected by key names:
  * multi-value space separated attrs (the default)
  * single-value attrs (Description)
  * systemd.service: Exec* attrs with flags, command and arguments
  * systemd.service: Environment NAME=arg
*)

(* View: entry_fn
   Prototype for our various key=value lines, with optional comment *)
let entry_fn (kw:regexp) (val:lens) =
    [ key kw . sep . val . (comment|eol) ]

(* View: entry_value
   Store a value that doesn't contain spaces *)
let entry_value  = [ label "value" . sto_value ]

(* View: entry_single
   Entry that takes a single value containing spaces *)
let entry_single = entry_fn entry_single_kw
                     [ label "value" . sto_value_single ]?

(* View: entry_command
   Entry that takes a space separated set of values (the default) *)
let entry_multi  = entry_fn entry_multi_kw
                     ( Util.del_opt_ws ""
                       . Build.opt_list entry_value value_sep )?

(* View: entry_command_flags
   Exec* flags "@" and "-".  Order is important, see systemd.service(8) *)
let entry_command_flags =
     let exit  = [ label "ignoreexit" . Util.del_str "-" ]
  in let arg0  = [ label "arg0" . Util.del_str "@" ]
  in exit? . arg0?

(* View: entry_command
   Entry that takes a command, arguments and the optional prefix flags *)
let entry_command =
     let cmd  = [ label "command" . store value_cmd_re ]
  in let arg  = [ seq "args" . sto_value ]
  in let args = [ counter "args" . label "arguments" . value_sep
                  . ( arg . value_sep )* . arg ]
  in entry_fn entry_command_kw ( entry_command_flags . cmd . args? )?

(* View: entry_env
   Entry that takes a space separated set of ENV=value key/value pairs *)
let entry_env =
     let envkv = [ key env_key . Util.del_str "=" . ( sto_value )? ]
  in entry_fn entry_env_kw ( Build.opt_list envkv value_sep )

(************************************************************************
 * Group:                 LENS
 *************************************************************************)

(* View: entry
   An <IniFile.entry> *)
let entry   = entry_single | entry_multi | entry_command | entry_env | comment

(* View: include
   Includes another file at this position *)
let include = [ key ".include" . Util.del_ws_spc . sto_value
                . (comment|eol) ]

(* View: title
   An <IniFile.title> *)
let title   = IniFile.title IniFile.record_re

(* View: record
   An <IniFile.record> *)
let record = IniFile.record title (entry|include)

(* View: lns
   An <IniFile.lns> *)
let lns    = IniFile.lns record (comment|include)

(* View: filter *)
let filter = incl "/lib/systemd/system/*"
           . incl "/lib/systemd/system/*/*"
           . incl "/etc/systemd/system/*"
           . incl "/etc/systemd/system/*/*"
           . Util.stdexcl

let xfm = transform lns filter

(*
Module: Systemd
  Parses systemd unit files.

Author: Dominic Cleal <dcleal@redhat.com>

About: Reference
  This lens tries to keep as close as possible to systemd.unit(5) and
  systemd.service(5) etc where possible.

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

About: Lens Usage
  To be documented

About: Configuration files
  This lens applies to /lib/systemd/system/* and /etc/systemd/system/*.
  See <filter>.
*)

module Systemd =
autoload xfm

(************************************************************************
 * Group:                 USEFUL PRIMITIVES
 *************************************************************************)

(* View: eol *)
let eol = Util.eol

(* View: comment
   An <IniFile.comment> entry *)
let comment    = IniFile.comment IniFile.comment_re "#"

(* View: sep
   An <IniFile.sep> entry *)
let sep        = IniFile.sep "=" "="

(* Variable: entry_single_kw *)
let entry_single_kw  = "Description"

(* Variable: entry_command_kw *)
let entry_command_kw = /Exec[A-Za-z][A-Za-z0-9._-]+/

(* Variable: entry_env_kw *)
let entry_env_kw     = "Environment"

(* Variable: entry_multi_kw *)
let entry_multi_kw   = /[A-Za-z][A-Za-z0-9._-]+/
                       - entry_single_kw
                       - entry_command_kw
                       - entry_env_kw

(* Variable: value_single_re *)
let value_single_re  = /[^;# \t\n\\][^;#\n\\]*[^;# \t\n\\]|[^;# \t\n\\]/

(* View: sto_value_single
   Support multiline values with a backslash *)
let sto_value_single = Util.del_opt_ws ""
                       . store (value_single_re
                                . (/\\\\\n/ . value_single_re)*)

(* View: sto_value *)
let sto_value = store /[^;# \t\n\\]+/

(* Variable: value_sep
   Multi-value entries separated by whitespace or backslash and newline *)
let value_sep = del /[ \t]+|[ \t]*\\\\[ \t]*\n[ \t]*/ " "

(* Variable: value_cmd_re
   Don't parse @ and - prefix flags *)
let value_cmd_re = /[^;#@ \t\n\\-][^;#@ \t\n\\-][^;# \t\n\\]*/

(* Variable: env_key *)
let env_key = /[A-Za-z0-9_]+(\[[0-9]+\])?/

(************************************************************************
 * Group:                 ENTRIES
 *************************************************************************)

(*
Supported entry features, selected by key names:
  * multi-value space separated attrs (the default)
  * single-value attrs (Description)
  * systemd.service: Exec* attrs with flags, command and arguments
  * systemd.service: Environment NAME=arg
*)

(* View: entry_fn
   Prototype for our various key=value lines, with optional comment *)
let entry_fn (kw:regexp) (val:lens) =
    [ key kw . sep . val . (comment|eol) ]

(* View: entry_value
   Store a value that doesn't contain spaces *)
let entry_value  = [ label "value" . sto_value ]

(* View: entry_single
   Entry that takes a single value containing spaces *)
let entry_single = entry_fn entry_single_kw
                     [ label "value" . sto_value_single ]?

(* View: entry_command
   Entry that takes a space separated set of values (the default) *)
let entry_multi  = entry_fn entry_multi_kw
                     ( Util.del_opt_ws ""
                       . Build.opt_list entry_value value_sep )?

(* View: entry_command_flags
   Exec* flags "@" and "-".  Order is important, see systemd.service(8) *)
let entry_command_flags =
     let exit  = [ label "ignoreexit" . Util.del_str "-" ]
  in let arg0  = [ label "arg0" . Util.del_str "@" ]
  in exit? . arg0?

(* View: entry_command
   Entry that takes a command, arguments and the optional prefix flags *)
let entry_command =
     let cmd  = [ label "command" . store value_cmd_re ]
  in let arg  = [ seq "args" . sto_value ]
  in let args = [ counter "args" . label "arguments" . value_sep
                  . ( arg . value_sep )* . arg ]
  in entry_fn entry_command_kw ( entry_command_flags . cmd . args? )?

(* View: entry_env
   Entry that takes a space separated set of ENV=value key/value pairs *)
let entry_env =
     let envkv = [ key env_key . Util.del_str "=" . ( sto_value )? ]
  in entry_fn entry_env_kw ( Build.opt_list envkv value_sep )

(************************************************************************
 * Group:                 LENS
 *************************************************************************)

(* View: entry
   An <IniFile.entry> *)
let entry   = entry_single | entry_multi | entry_command | entry_env | comment

(* View: include
   Includes another file at this position *)
let include = [ key ".include" . Util.del_ws_spc . sto_value
                . (comment|eol) ]

(* View: title
   An <IniFile.title> *)
let title   = IniFile.title IniFile.record_re

(* View: record
   An <IniFile.record> *)
let record = IniFile.record title (entry|include)

(* View: lns
   An <IniFile.lns> *)
let lns    = IniFile.lns record (comment|include)

(* View: filter *)
let filter = incl "/lib/systemd/system/*"
           . incl "/lib/systemd/system/*/*"
           . incl "/etc/systemd/system/*"
           . incl "/etc/systemd/system/*/*"
           . Util.stdexcl

let xfm = transform lns filter


Fix ambiguity in shellvars.aug by making semi-colons illegal in bquot and arrays.

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

About: License
   This file is licenced under the LGPL v2+, 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
        . ((empty|comment)* . case_entry)*
        . (empty|comment)*
        . 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_excl "network" .
      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_excl "network-scripts" .
      sc_incl "network-scripts/ifcfg-*" .
      sc_excl "rhn" .
      sc_incl "rhn/allowed-actions/*" .
      sc_excl "rhn/allowed-actions/script" .
      sc_incl "rhn/allowed-actions/script/*" .
      sc_incl "rhn/rhnsd" .
      sc_excl "SuSEfirewall2.d" .
      sc_incl "SuSEfirewall2.d/cobbler" .
      sc_incl "SuSEfirewall2.d/services/*" .
      sc_excl "SuSEfirewall2.d/services/TEMPLATE"

  let filter_default = incl "/etc/default/*"
                     . excl "/etc/default/whoopsie"
  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_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 LGPL v2+, 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
        . ((empty|comment)* . case_entry)*
        . (empty|comment)*
        . 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_excl "network" .
      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_excl "network-scripts" .
      sc_incl "network-scripts/ifcfg-*" .
      sc_excl "rhn" .
      sc_incl "rhn/allowed-actions/*" .
      sc_excl "rhn/allowed-actions/script" .
      sc_incl "rhn/allowed-actions/script/*" .
      sc_incl "rhn/rhnsd" .
      sc_excl "SuSEfirewall2.d" .
      sc_incl "SuSEfirewall2.d/cobbler" .
      sc_incl "SuSEfirewall2.d/services/*" .
      sc_excl "SuSEfirewall2.d/services/TEMPLATE"

  let filter_default = incl "/etc/default/*"
                     . excl "/etc/default/whoopsie"
  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_default
             . filter_misc
             . Util.stdexcl

  let xfm = transform lns filter

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