Ntp: allow deprecated 'authenticate' setting

(* NTP module for Augeas                      *)
(* Author: Raphael Pinson <raphink@gmail.com> *)
(*                                            *)
(* Status: basic settings supported           *)

module Ntp =
  autoload xfm

    (* Define useful shortcuts *)

    let eol = del /[ \t]*/ "" . [ label "#comment" . store /#.*/]?
            . Util.del_str "\n"
    let sep_spc = Util.del_ws_spc
    let word = /[^,# \n\t]+/
    let num  = /[0-9]+/

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

    let kv (k:regexp) (v:regexp) =
      [ key k . sep_spc. store v . eol ]

    (* Define generic record *)
    let record (kw:regexp) (value:lens) =
      [ key kw . sep_spc . store word . value . eol ]

    (* Define a command record; see confopt.html#cfg in the ntp docs *)
    let command_record =
      let opt = [ sep_spc . key /minpoll|maxpoll|ttl|version|key/ .
                      sep_spc . store word ]
        | [ sep_spc . key (/autokey|burst|iburst|noselect|preempt/ |
                           /prefer|true|dynamic/) ] in
      let cmd = /server|peer|broadcast|manycastclient/
        | /multicastclient|manycastserver/ in
        record cmd opt*

    let broadcastclient =
      [ key "broadcastclient" . [ sep_spc . key "novolley" ]? . eol ]

    (* Define a fudge record *)
    let fudge_opt_re = "refid" | "stratum"
    let fudge_opt  = [ sep_spc . key fudge_opt_re . sep_spc . store word ]
    let fudge_record = record "fudge" fudge_opt?

    (* Define simple settings, see miscopt.html in ntp docs *)
    let flags =
      let flags_re = /auth|bclient|calibrate|kernel|monitor|ntp|pps|stats/ in
      let flag = [ label "flag" . store flags_re ] in
        [ key /enable|disable/ . (sep_spc . flag)* . eol ]

    let simple_setting (k:regexp) = kv k word

    (* Still incomplete, misses logconfig, phone, setvar, tos,
       trap, ttl *)
    let simple_settings =
        kv "broadcastdelay" Rx.decimal
      | flags
      | simple_setting /driftfile|leapfile|logfile|includefile/
	  | simple_setting "statsdir"

    (* Misc commands, see miscopt.html in ntp docs *)

    (* Define restrict *)
    let restrict_record   =
      let action    = [ label "action" . sep_spc . store word ] in
      [ key "restrict" . sep_spc .
          [ label "ipv6" . Util.del_str "-6" . sep_spc ]? .
          store (word - "-6") . action* . eol ]

    (* Define statistics *)
    let statistics_flag (kw:string) = [ sep_spc . key kw ]

    let statistics_opts = statistics_flag "loopstats"
                        | statistics_flag "peerstats"
			| statistics_flag "clockstats"
			| statistics_flag "rawstats"

    let statistics_record = [ key "statistics" . statistics_opts* . eol ]

    (* Define filegen *)
    let filegen = del /filegen[ \t]+/ "filegen " . store word
    let filegen_opt (kw:string) = [ sep_spc . key kw . sep_spc . store word ]
    (* let filegen_flag (kw:string) = [ label kw . sep_spc . store word ] *)
    let filegen_select (kw:string) (select:regexp) = [ label kw . sep_spc . store select ]

    let filegen_opts = filegen_opt "file"
                     | filegen_opt "type"
		     | filegen_select "enable" /(en|dis)able/
		     | filegen_select "link" /(no)?link/

    let filegen_record = [ label "filegen" . filegen . filegen_opts* . eol ]

    (* Authentication commands, see authopt.html#cmd; incomplete *)
    let auth_command =
      [ key /controlkey|keys|keysdir|requestkey|authenticate/ .
            sep_spc . store word . eol ]
     | [ key /autokey|revoke/ . [sep_spc . store word]? . eol ]
     | [ key /trustedkey/ . [ sep_spc . label "key" . store word ]+ . eol ]

    (* tinker [step step | panic panic | dispersion dispersion |
               stepout stepout | minpoll minpoll | allan allan | huffpuff huffpuff] *)
    let tinker =
      let arg_names = /step|panic|dispersion|stepout|minpoll|allan|huffpuff/ in
      let arg = [ key arg_names . sep_spc . store Rx.decimal ] in
      [ key "tinker" . (sep_spc . arg)* . eol ]

    (* Define lens *)

    let lns = ( comment | empty | command_record | fudge_record
              | restrict_record | simple_settings | statistics_record
              | filegen_record | broadcastclient
              | auth_command | tinker )*

    let filter = (incl "/etc/ntp.conf")
        . Util.stdexcl

    let xfm = transform lns filter

(* NTP module for Augeas                      *)
(* Author: Raphael Pinson <raphink@gmail.com> *)
(*                                            *)
(* Status: basic settings supported           *)

module Ntp =
  autoload xfm

    (* Define useful shortcuts *)

    let eol = del /[ \t]*/ "" . [ label "#comment" . store /#.*/]?
            . Util.del_str "\n"
    let sep_spc = Util.del_ws_spc
    let word = /[^,# \n\t]+/
    let num  = /[0-9]+/

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

    let kv (k:regexp) (v:regexp) =
      [ key k . sep_spc. store v . eol ]

    (* Define generic record *)
    let record (kw:regexp) (value:lens) =
      [ key kw . sep_spc . store word . value . eol ]

    (* Define a command record; see confopt.html#cfg in the ntp docs *)
    let command_record =
      let opt = [ sep_spc . key /minpoll|maxpoll|ttl|version|key/ .
                      sep_spc . store word ]
        | [ sep_spc . key (/autokey|burst|iburst|noselect|preempt/ |
                           /prefer|true|dynamic/) ] in
      let cmd = /server|peer|broadcast|manycastclient/
        | /multicastclient|manycastserver/ in
        record cmd opt*

    let broadcastclient =
      [ key "broadcastclient" . [ sep_spc . key "novolley" ]? . eol ]

    (* Define a fudge record *)
    let fudge_opt_re = "refid" | "stratum"
    let fudge_opt  = [ sep_spc . key fudge_opt_re . sep_spc . store word ]
    let fudge_record = record "fudge" fudge_opt?

    (* Define simple settings, see miscopt.html in ntp docs *)
    let flags =
      let flags_re = /auth|bclient|calibrate|kernel|monitor|ntp|pps|stats/ in
      let flag = [ label "flag" . store flags_re ] in
        [ key /enable|disable/ . (sep_spc . flag)* . eol ]

    let simple_setting (k:regexp) = kv k word

    (* Still incomplete, misses logconfig, phone, setvar, tos,
       trap, ttl *)
    let simple_settings =
        kv "broadcastdelay" Rx.decimal
      | flags
      | simple_setting /driftfile|leapfile|logfile|includefile/
	  | simple_setting "statsdir"

    (* Misc commands, see miscopt.html in ntp docs *)

    (* Define restrict *)
    let restrict_record   =
      let action    = [ label "action" . sep_spc . store word ] in
      [ key "restrict" . sep_spc .
          [ label "ipv6" . Util.del_str "-6" . sep_spc ]? .
          store (word - "-6") . action* . eol ]

    (* Define statistics *)
    let statistics_flag (kw:string) = [ sep_spc . key kw ]

    let statistics_opts = statistics_flag "loopstats"
                        | statistics_flag "peerstats"
			| statistics_flag "clockstats"
			| statistics_flag "rawstats"

    let statistics_record = [ key "statistics" . statistics_opts* . eol ]

    (* Define filegen *)
    let filegen = del /filegen[ \t]+/ "filegen " . store word
    let filegen_opt (kw:string) = [ sep_spc . key kw . sep_spc . store word ]
    (* let filegen_flag (kw:string) = [ label kw . sep_spc . store word ] *)
    let filegen_select (kw:string) (select:regexp) = [ label kw . sep_spc . store select ]

    let filegen_opts = filegen_opt "file"
                     | filegen_opt "type"
		     | filegen_select "enable" /(en|dis)able/
		     | filegen_select "link" /(no)?link/

    let filegen_record = [ label "filegen" . filegen . filegen_opts* . eol ]

    (* Authentication commands, see authopt.html#cmd; incomplete *)
    let auth_command =
      [ key /controlkey|keys|keysdir|requestkey/ .
            sep_spc . store word . eol ]
     | [ key /autokey|revoke/ . [sep_spc . store word]? . eol ]
     | [ key /trustedkey/ . [ sep_spc . label "key" . store word ]+ . eol ]

    (* tinker [step step | panic panic | dispersion dispersion |
               stepout stepout | minpoll minpoll | allan allan | huffpuff huffpuff] *)
    let tinker =
      let arg_names = /step|panic|dispersion|stepout|minpoll|allan|huffpuff/ in
      let arg = [ key arg_names . sep_spc . store Rx.decimal ] in
      [ key "tinker" . (sep_spc . arg)* . eol ]

    (* Define lens *)

    let lns = ( comment | empty | command_record | fudge_record
              | restrict_record | simple_settings | statistics_record
              | filegen_record | broadcastclient
              | auth_command | tinker )*

    let filter = (incl "/etc/ntp.conf")
        . Util.stdexcl

    let xfm = transform lns filter


Fix examples note in inifile.aug

Fix examples note in inifile.aug

(*
Module: IniFile
  Generic module to create INI files lenses

Author: Raphael Pinson <raphink@gmail.com>

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

About: TODO
  Things to add in the future
  - Support double quotes in value

About: Lens usage
  This lens is made to provide generic primitives to construct INI File lenses.
  See <Puppet>, <PHP>, <MySQL> or <Dput> for examples of real life lenses using it.

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

module IniFile  =

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

(* Group: Internal primitives *)

(*
Variable: eol
  End of line, inherited from <Util.eol>
*)
let eol                = Util.eol

(*
View: empty
  Empty line, an <eol> subnode
*)
let empty              = [ eol ]

(* Group: Separators *)

(*
Variable: sep
  Generic separator

  Parameters:
    pat:regexp - the pattern to delete
    default:string - the default string to use
*)
let sep (pat:regexp) (default:string)
                       = Sep.opt_space . del pat default

(*
Variable: sep_re
  The default regexp for a separator
*)

let sep_re             = /[=:]/

(*
Variable: sep_default
  The default separator value
*)
let sep_default        = "="

(* Group: Stores *)

(*
Variable: sto_to_eol
  Store until end of line
*)
let sto_to_eol         = Sep.opt_space . store Rx.space_in

(*
Variable: to_comment_re
  Regex until comment
*)
let to_comment_re = /[^;# \t\n][^;#\n]*[^;# \t\n]|[^;# \t\n]/

(*
Variable: sto_to_comment
  Store until comment
*)
let sto_to_comment = Sep.opt_space . store to_comment_re

let sto_multiline = Sep.opt_space
         . store (to_comment_re
               . (/[ \t]*\n/ . Rx.space . to_comment_re)*)

(* Group: Define comment and defaults *)

(*
View: comment
  Map comments into "#comment" nodes

  Parameters:
    pat:regexp - pattern to delete before commented data
    default:string - default pattern before commented data

  Sample Usage:
  (start code)
    let comment  = IniFile.comment "#" "#"
    let comment  = IniFile.comment IniFile.comment_re IniFile.comment_default
  (end code)
*)
let comment (pat:regexp) (default:string)
                       = [ label "#comment" . sep pat default
		         . sto_to_eol? . eol ]
(*
Variable: comment_re
  Default regexp for <comment> pattern
*)

let comment_re         = /[;#]/

(*
Variable: comment_default
  Default value for <comment> pattern
*)
let comment_default    = ";"

(************************************************************************
 * Group:                     ENTRY
 *************************************************************************)

(* Group: entry includes comments *)

(*
View: entry
  Generic INI File entry

  Parameters:
    kw:regexp    - keyword regexp for the label
    sep:lens     - lens to use as key/value separator
    comment:lens - lens to use as comment

  Sample Usage:
     > let entry = IniFile.entry setting sep comment
*)
let entry (kw:regexp) (sep:lens) (comment:lens)
                       = [ key kw . sep . sto_to_comment? . (comment|eol) ] | comment

let entry_multiline (kw:regexp) (sep:lens) (comment:lens)
                       = [ key kw . sep . sto_multiline? . (comment|eol) ] | comment

(*
View: indented_entry
  Generic INI File entry that might be indented with an arbitrary
  amount of whitespace

  Parameters:
    kw:regexp    - keyword regexp for the label
    sep:lens     - lens to use as key/value separator
    comment:lens - lens to use as comment

  Sample Usage:
     > let entry = IniFile.indented_entry setting sep comment
*)
let indented_entry (kw:regexp) (sep:lens) (comment:lens)
                       = [ Util.del_opt_ws "" .
                           key kw . sep . sto_to_comment? .
                           (comment|eol)
                         ]
                         | comment

(*
Variable: entry_re
  Default regexp for <entry> keyword
*)
let entry_re           = ( /[A-Za-z][A-Za-z0-9._-]+/ )

(************************************************************************
 * Group:                      RECORD
 *************************************************************************)

(* Group: Title definition *)

(*
View: title
  Title for <record>. This maps the title of a record as a node in the abstract tree.

  Parameters:
    kw:regexp - keyword regexp for the label

  Sample Usage:
    > let title   = IniFile.title IniFile.record_re
*)
let title (kw:regexp)
                       = Util.del_str "[" . key kw
                         . Util.del_str "]". eol

(*
View: indented_title
  Title for <record>. This maps the title of a record as a node in the abstract tree. The title may be indented with arbitrary amounts of whitespace

  Parameters:
    kw:regexp - keyword regexp for the label

  Sample Usage:
    > let title   = IniFile.title IniFile.record_re
*)
let indented_title (kw:regexp)
                       = Util.indent . title kw

(*
View: title_label
  Title for <record>. This maps the title of a record as a value in the abstract tree.

  Parameters:
    name:string - name for the title label
    kw:regexp   - keyword regexp for the label

  Sample Usage:
    > let title   = IniFile.title_label "target" IniFile.record_label_re
*)
let title_label (name:string) (kw:regexp)
                       = label name
                         . Util.del_str "[" . store kw
                         . Util.del_str "]". eol

(*
View: indented_title_label
  Title for <record>. This maps the title of a record as a value in the abstract tree. The title may be indented with arbitrary amounts of whitespace

  Parameters:
    name:string - name for the title label
    kw:regexp   - keyword regexp for the label

  Sample Usage:
    > let title   = IniFile.title_label "target" IniFile.record_label_re
*)
let indented_title_label (name:string) (kw:regexp)
                       = Util.indent . title_label name kw

(*
Variable: record_re
  Default regexp for <title> keyword pattern
*)
let record_re          = ( /[^]\n\/]+/ - /#comment/ )

(*
Variable: record_label_re
  Default regexp for <title_label> keyword pattern
*)
let record_label_re    = /[^]\n]+/

(* Group: Record definition *)

(*
View: record_noempty
  INI File Record with no empty lines allowed.

  Parameters:
    title:lens - lens to use for title. Use either <title> or <title_label>.
    entry:lens - lens to use for entries in the record. See <entry>.
*)
let record_noempty (title:lens) (entry:lens)
                       = [ title
		       . entry* ]

(*
View: record
  Generic INI File record

  Parameters:
    title:lens - lens to use for title. Use either <title> or <title_label>.
    entry:lens - lens to use for entries in the record. See <entry>.

  Sample Usage:
    > let record  = IniFile.record title entry
*)
let record (title:lens) (entry:lens)
                       = record_noempty title ( entry | empty )

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

(*

Group: Lens definition

View: lns_noempty
  Generic INI File lens with no empty lines

  Parameters:
    record:lens  - record lens to use. See <record_noempty>.
    comment:lens - comment lens to use. See <comment>.

  Sample Usage:
    > let lns     = IniFile.lns_noempty record comment
*)
let lns_noempty (record:lens) (comment:lens)
                       = comment* . record*

(*
View: lns
  Generic INI File lens

  Parameters:
    record:lens  - record lens to use. See <record>.
    comment:lens - comment lens to use. See <comment>.

  Sample Usage:
    > let lns     = IniFile.lns record comment
*)
let lns (record:lens) (comment:lens)
                       = lns_noempty record (comment|empty)

(*
Module: IniFile
  Generic module to create INI files lenses

Author: Raphael Pinson <raphink@gmail.com>

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

About: TODO
  Things to add in the future
  - Support double quotes in value

About: Lens usage
  This lens is made to provide generic primitives to construct INI File lenses.
  See <Puppet>, <PHP>, <MySQL> or <Dput> for examples of real life lenses using it.

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

module IniFile  =

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

(* Group: Internal primitives *)

(*
Variable: eol
  End of line, inherited from <Util.eol>
*)
let eol                = Util.eol

(*
View: empty
  Empty line, an <eol> subnode
*)
let empty              = [ eol ]

(* Group: Separators *)

(*
Variable: sep
  Generic separator

  Parameters:
    pat:regexp - the pattern to delete
    default:string - the default string to use
*)
let sep (pat:regexp) (default:string)
                       = Sep.opt_space . del pat default

(*
Variable: sep_re
  The default regexp for a separator
*)

let sep_re             = /[=:]/

(*
Variable: sep_default
  The default separator value
*)
let sep_default        = "="

(* Group: Stores *)

(*
Variable: sto_to_eol
  Store until end of line
*)
let sto_to_eol         = Sep.opt_space . store Rx.space_in

(*
Variable: to_comment_re
  Regex until comment
*)
let to_comment_re = /[^;# \t\n][^;#\n]*[^;# \t\n]|[^;# \t\n]/

(*
Variable: sto_to_comment
  Store until comment
*)
let sto_to_comment = Sep.opt_space . store to_comment_re

let sto_multiline = Sep.opt_space
         . store (to_comment_re
               . (/[ \t]*\n/ . Rx.space . to_comment_re)*)

(* Group: Define comment and defaults *)

(*
View: comment
  Map comments into "#comment" nodes

  Parameters:
    pat:regexp - pattern to delete before commented data
    default:string - default pattern before commented data

  Sample Usage:
  (start code)
    let comment  = IniFile.comment "#" "#"
    let comment  = IniFile.comment IniFile.comment_re IniFile.comment_default
  (end code)
*)
let comment (pat:regexp) (default:string)
                       = [ label "#comment" . sep pat default
		         . sto_to_eol? . eol ]
(*
Variable: comment_re
  Default regexp for <comment> pattern
*)

let comment_re         = /[;#]/

(*
Variable: comment_default
  Default value for <comment> pattern
*)
let comment_default    = ";"

(************************************************************************
 * Group:                     ENTRY
 *************************************************************************)

(* Group: entry includes comments *)

(*
View: entry
  Generic INI File entry

  Parameters:
    kw:regexp    - keyword regexp for the label
    sep:lens     - lens to use as key/value separator
    comment:lens - lens to use as comment

  Sample Usage:
     > let entry = IniFile.entry setting sep comment
*)
let entry (kw:regexp) (sep:lens) (comment:lens)
                       = [ key kw . sep . sto_to_comment? . (comment|eol) ] | comment

let entry_multiline (kw:regexp) (sep:lens) (comment:lens)
                       = [ key kw . sep . sto_multiline? . (comment|eol) ] | comment

(*
View: indented_entry
  Generic INI File entry that might be indented with an arbitrary
  amount of whitespace

  Parameters:
    kw:regexp    - keyword regexp for the label
    sep:lens     - lens to use as key/value separator
    comment:lens - lens to use as comment

  Sample Usage:
     > let entry = IniFile.indented_entry setting sep comment
*)
let indented_entry (kw:regexp) (sep:lens) (comment:lens)
                       = [ Util.del_opt_ws "" .
                           key kw . sep . sto_to_comment? .
                           (comment|eol)
                         ]
                         | comment

(*
Variable: entry_re
  Default regexp for <entry> keyword
*)
let entry_re           = ( /[A-Za-z][A-Za-z0-9._-]+/ )

(************************************************************************
 * Group:                      RECORD
 *************************************************************************)

(* Group: Title definition *)

(*
View: title
  Title for <record>. This maps the title of a record as a node in the abstract tree.

  Parameters:
    kw:regexp - keyword regexp for the label

  Sample Usage:
    > let title   = IniFile.title IniFile.record_re
*)
let title (kw:regexp)
                       = Util.del_str "[" . key kw
                         . Util.del_str "]". eol

(*
View: indented_title
  Title for <record>. This maps the title of a record as a node in the abstract tree. The title may be indented with arbitrary amounts of whitespace

  Parameters:
    kw:regexp - keyword regexp for the label

  Sample Usage:
    > let title   = IniFile.title IniFile.record_re
*)
let indented_title (kw:regexp)
                       = Util.indent . title kw

(*
View: title_label
  Title for <record>. This maps the title of a record as a value in the abstract tree.

  Parameters:
    name:string - name for the title label
    kw:regexp   - keyword regexp for the label

  Sample Usage:
    > let title   = IniFile.title_label "target" IniFile.record_label_re
*)
let title_label (name:string) (kw:regexp)
                       = label name
                         . Util.del_str "[" . store kw
                         . Util.del_str "]". eol

(*
View: indented_title_label
  Title for <record>. This maps the title of a record as a value in the abstract tree. The title may be indented with arbitrary amounts of whitespace

  Parameters:
    name:string - name for the title label
    kw:regexp   - keyword regexp for the label

  Sample Usage:
    > let title   = IniFile.title_label "target" IniFile.record_label_re
*)
let indented_title_label (name:string) (kw:regexp)
                       = Util.indent . title_label name kw

(*
Variable: record_re
  Default regexp for <title> keyword pattern
*)
let record_re          = ( /[^]\n\/]+/ - /#comment/ )

(*
Variable: record_label_re
  Default regexp for <title_label> keyword pattern
*)
let record_label_re    = /[^]\n]+/

(* Group: Record definition *)

(*
View: record_noempty
  INI File Record with no empty lines allowed.

  Parameters:
    title:lens - lens to use for title. Use either <title> or <title_label>.
    entry:lens - lens to use for entries in the record. See <entry>.
*)
let record_noempty (title:lens) (entry:lens)
                       = [ title
		       . entry* ]

(*
View: record
  Generic INI File record

  Parameters:
    title:lens - lens to use for title. Use either <title> or <title_label>.
    entry:lens - lens to use for entries in the record. See <entry>.

  Sample Usage:
    > let record  = IniFile.record title entry
*)
let record (title:lens) (entry:lens)
                       = record_noempty title ( entry | empty )

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

(*

Group: Lens definition

View: lns_noempty
  Generic INI File lens with no empty lines

  Parameters:
    record:lens  - record lens to use. See <record_noempty>.
    comment:lens - comment lens to use. See <comment>.

  Sample Usage:
    > let lns     = IniFile.lns_noempty record comment
*)
let lns_noempty (record:lens) (comment:lens)
                       = comment* . record*

(*
View: lns
  Generic INI File lens

  Parameters:
    record:lens  - record lens to use. See <record>.
    comment:lens - comment lens to use. See <comment>.

  Sample Usage:
    > let lns     = IniFile.lns record comment
*)
let lns (record:lens) (comment:lens)
                       = lns_noempty record (comment|empty)


Ignore allow.pamlist from pam.aug

Ignore allow.pamlist from pam.aug

(*
Module: Pam
  Parses /etc/pam.conf and /etc/pam.d/* service files

Author: David Lutterkort <lutter@redhat.com>

About: Reference
  This lens tries to keep as close as possible to `man pam.conf` where
  possible.

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

About: Lens Usage

About: Configuration files
  This lens autoloads /etc/pam.d/* for service specific files. See <filter>.
  It provides a lens for /etc/pam.conf, which is used in the PamConf module.
*)
module Pam =
  autoload xfm

  let eol = Util.eol
  let indent = Util.indent

  (* For the control syntax of [key=value ..] we could split the key value *)
  (* pairs into an array and generate a subtree control/N/KEY = VALUE      *)
  let control = /(\[[^]#\n]*\]|[^[ \t][^ \t]*)/
  let word = /[^# \t\n]+/
  (* Allowed types *)
  let types = /(auth|session|account|password)/i

  (* This isn't entirely right: arguments enclosed in [ .. ] are allowed   *)
  (* and should be parsed as one                                           *)
  let argument = /[^#\n \t]+/

  let comment = Util.comment
  let comment_or_eol = Util.comment_or_eol
  let empty   = Util.empty

  (* Not mentioned in the man page, but Debian uses the syntax             *)
  (*   @include module                                                     *)
  (* quite a bit                                                           *)
  let include = [ indent . Util.del_str "@" . key "include" .
                  Util.del_ws_spc . store word . eol ]

  (* Shared with PamConf *)
  let record = [ label "optional" . del "-" "-" ]? .
               [ label "type" . store types ] .
               Util.del_ws_tab .
               [ label "control" . store control] .
               Util.del_ws_tab .
               [ label "module" . store word ] .
               [ Util.del_ws_tab . label "argument" . store argument ]* .
               comment_or_eol

  let record_svc = [ seq "record" . indent . record ]

  let lns = ( empty | comment | include | record_svc ) *

  let filter = incl "/etc/pam.d/*"
             . excl "/etc/pam.d/allow.pamlist"
             . Util.stdexcl

  let xfm = transform lns filter

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

(*
Module: Pam
  Parses /etc/pam.conf and /etc/pam.d/* service files

Author: David Lutterkort <lutter@redhat.com>

About: Reference
  This lens tries to keep as close as possible to `man pam.conf` where
  possible.

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

About: Lens Usage

About: Configuration files
  This lens autoloads /etc/pam.d/* for service specific files. See <filter>.
  It provides a lens for /etc/pam.conf, which is used in the PamConf module.
*)
module Pam =
  autoload xfm

  let eol = Util.eol
  let indent = Util.indent

  (* For the control syntax of [key=value ..] we could split the key value *)
  (* pairs into an array and generate a subtree control/N/KEY = VALUE      *)
  let control = /(\[[^]#\n]*\]|[^[ \t][^ \t]*)/
  let word = /[^# \t\n]+/
  (* Allowed types *)
  let types = /(auth|session|account|password)/i

  (* This isn't entirely right: arguments enclosed in [ .. ] are allowed   *)
  (* and should be parsed as one                                           *)
  let argument = /[^#\n \t]+/

  let comment = Util.comment
  let comment_or_eol = Util.comment_or_eol
  let empty   = Util.empty

  (* Not mentioned in the man page, but Debian uses the syntax             *)
  (*   @include module                                                     *)
  (* quite a bit                                                           *)
  let include = [ indent . Util.del_str "@" . key "include" .
                  Util.del_ws_spc . store word . eol ]

  (* Shared with PamConf *)
  let record = [ label "optional" . del "-" "-" ]? .
               [ label "type" . store types ] .
               Util.del_ws_tab .
               [ label "control" . store control] .
               Util.del_ws_tab .
               [ label "module" . store word ] .
               [ Util.del_ws_tab . label "argument" . store argument ]* .
               comment_or_eol

  let record_svc = [ seq "record" . indent . record ]

  let lns = ( empty | comment | include | record_svc ) *

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

  let xfm = transform lns filter

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


Add Build.block_setdefault

Add Build.block_setdefault


Prefer comments over kv

Prefer comments over kv

# Parsing inistyle files

grammar inistyle {
  token EOL /\n/ = '\n'

  token INDENT /[ \t]+/ = '\t'

  token EQ /\s*=\s*/ =  '='

  token OPT_WS /[ \t]*/ = ''

  file: ( comment | section ) * {
    @0 { 'yum' }
  }

  comment: OPT_WS ( /#.*/ | 'REM' | 'rem' )? EOL

  section: '[' ... ']' EOL ( comment | kv ) * {
    @1 { $2 }
  }

  kv: ... EQ ... EOL {
    @$3 { $1 = $3 }
  }

  # FIXME: need to deal with continuation lines in values
  # Using something like this in the 'kv' rule may work
  # (when referencing rules from actions is implemented)
  value: ... ( /\n[ \t]+/ ... )*

}

# Parsing inistyle files

grammar inistyle {
  token EOL /\n/ = '\n'

  token INDENT /[ \t]+/ = '\t'

  token EQ /\s*=\s*/ =  '='

  token OPT_WS /[ \t]*/ = ''

  file: ( comment | section ) * {
    @0 { 'yum' }
  }

  comment: OPT_WS ( /#.*/ | 'REM' | 'rem' )? EOL

  section: '[' ... ']' EOL ( kv | comment ) * {
    @1 { $2 }
  }

  kv: ... EQ ... EOL {
    @$3 { $1 = $3 }
  }

  # FIXME: need to deal with continuation lines in values
  # Using something like this in the 'kv' rule may work
  # (when referencing rules from actions is implemented)
  value: ... ( /\n[ \t]+/ ... )*

}


Remove trailing whitespace

Remove trailing whitespace

module Test_nsswitch =

  let conf = "# Sample nsswitch.conf
passwd:         compat

hosts:          files mdns4_minimal [NOTFOUND=return] dns mdns4
networks:       nis [!UNAVAIL=return success=continue] files
protocols:      db files
netgroup:       nis
"

test Nsswitch.lns get conf =
   { "#comment" = "Sample nsswitch.conf" }
   { "database" = "passwd"
      { "service" = "compat" } }
   {}
   { "database" = "hosts"
      { "service" = "files" }
      { "service" = "mdns4_minimal" }
      { "reaction"
           { "status" = "NOTFOUND"
               { "action" = "return" } } }
      { "service" = "dns" }
      { "service" = "mdns4" } }
   { "database" = "networks"
      { "service" = "nis" }
      { "reaction"
           { "status" = "UNAVAIL"
               { "negate" }
               { "action" = "return" } }
           { "status" = "success"
               { "action" = "continue" } } }
      { "service" = "files" } }
   { "database" = "protocols"
      { "service" = "db" }
      { "service" = "files" } }
   { "database" = "netgroup"
      { "service" = "nis" } }


Quote: improve lens usage

Quote: improve lens usage

(*
Module: Quote
  Generic module providing useful primitives for quoting

Author: Raphael Pinson <raphael.pinson@camptocamp.com>

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

About: Lens Usage
   This is a generic module which doesn't apply to files directly.
   You can use its definitions to build lenses that require quoted values.
   It provides several levels of definitions, allowing to define more or less fine-grained quoted values:

     - the quote separators are separators that are useful to define quoted values;
     - the quoting functions are useful wrappers to easily enclose a lens in various kinds of quotes (single, double, any, optional or not);
     - the quoted values definitions are common quoted patterns. They use the quoting functions in order to provide useful shortcuts for commonly met needs. In particular, the <quote_spaces> (and similar) function force values that contain spaces to be quoted, but allow values without spaces to be unquoted.

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

module Quote =

(* Group: QUOTE SEPARATORS *)

(* Variable: dquote
     A double quote *)
let dquote = Util.del_str "\""

(* Variable: dquote_opt
     An optional double quote, default to double *)
let dquote_opt = del /"?/ "\""

(* Variable: dquote_opt_nil
     An optional double quote, default to nothing *)
let dquote_opt_nil = del /"?/ ""

(* Variable: squote
     A single quote *)
let squote = Util.del_str "'"

(* Variable: squote_opt
     An optional single quote, default to single *)
let squote_opt = del /'?/ "'"

(* Variable: squote_opt_nil
     An optional single quote, default to nothing *)
let squote_opt_nil = del /'?/ ""

(* Variable: quote
     A quote, either double or single, default to double *)
let quote = del /["']/ "\""

(* Variable: quote_opt
     An optional quote, either double or single, default to double *)
let quote_opt = del /["']?/ "\""

(* Variable: quote_opt_nil
     An optional quote, either double or single, default to nothing *)
let quote_opt_nil = del /["']?/ ""

(* Group: QUOTING FUNCTIONS *)

(*
View: do_dquote
  Enclose a lens in <dquote>s

  Parameters:
    body:lens - the lens to be enclosed
*)
let do_dquote (body:lens) =
  square dquote body dquote

(*
View: do_dquote_opt
  Enclose a lens in optional <dquote>s,
  use <dquote>s by default.

  Parameters:
    body:lens - the lens to be enclosed
*)
let do_dquote_opt (body:lens) =
  square dquote_opt body dquote_opt

(*
View: do_dquote_opt_nil
  Enclose a lens in optional <dquote>s,
  default to no quotes.

  Parameters:
    body:lens - the lens to be enclosed
*)
let do_dquote_opt_nil (body:lens) =
  square dquote_opt_nil body dquote_opt_nil

(*
View: do_squote
  Enclose a lens in <squote>s

  Parameters:
    body:lens - the lens to be enclosed
*)
let do_squote (body:lens) =
  square squote body squote

(*
View: do_squote_opt
  Enclose a lens in optional <squote>s,
  use <squote>s by default.

  Parameters:
    body:lens - the lens to be enclosed
*)
let do_squote_opt (body:lens) =
  square squote_opt body squote_opt

(*
View: do_squote_opt_nil
  Enclose a lens in optional <squote>s,
  default to no quotes.

  Parameters:
    body:lens - the lens to be enclosed
*)
let do_squote_opt_nil (body:lens) =
  square squote_opt_nil body squote_opt_nil

(*
View: do_quote
  Enclose a lens in <quote>s.

  Parameters:
    body:lens - the lens to be enclosed
*)
let do_quote (body:lens) =
  square quote body quote

(*
View: do_quote
  Enclose a lens in options <quote>s.

  Parameters:
    body:lens - the lens to be enclosed
*)
let do_quote_opt (body:lens) =
  square quote_opt body quote_opt

(*
View: do_quote
  Enclose a lens in options <quote>s,
  default to no quotes.

  Parameters:
    body:lens - the lens to be enclosed
*)
let do_quote_opt_nil (body:lens) =
  square quote_opt_nil body quote_opt_nil

(* Group: QUOTED VALUES *)

(* View: double
     A double-quoted value *)
let double =
     let body = store /[^\n]*/
  in do_dquote body

(* Variable: double_opt_re
     The regexp to store when value
     is optionally double-quoted *)
let double_opt_re = /[^\n\t "]([^\n"]*[^\n\t "])?/

(* View: double_opt
     An optionaly double-quoted value
     Double quotes are not allowed in value
     Value cannot begin or end with spaces *)
let double_opt =
     let body = store double_opt_re
  in do_dquote_opt body

(* View: single
     A single-quoted value *)
let single =
     let body = store /[^\n]*/
  in do_squote body

(* Variable: single_opt_re
     The regexp to store when value
     is optionally single-quoted *)
let single_opt_re = /[^\n\t ']([^\n']*[^\n\t '])?/

(* View: single_opt
     An optionaly single-quoted value
     Single quotes are not allowed in value
     Value cannot begin or end with spaces *)
let single_opt =
     let body = store single_opt_re
  in do_squote_opt body

(* View: any
     A quoted value *)
let any =
     let body = store /[^\n]*/
  in do_quote body

(* Variable: any_opt_re
     The regexp to store when value
     is optionally single- or double-quoted *)
let any_opt_re = /[^\n\t "']([^\n"']*[^\n\t "'])?/

(* View: any_opt
     An optionaly quoted value
     Double or single quotes are not allowed in value
     Value cannot begin or end with spaces *)
let any_opt =
     let body = store any_opt_re
  in do_quote_opt body

(*
View: quote_spaces
  Make quotes mandatory if value contains spaces,
  and optional if value doesn't contain spaces.

Parameters:
  lns:lens - the lens to be enclosed
*)
let quote_spaces (lns:lens) =
     (* bare has no spaces, and is optionally quoted *)
     let bare = Quote.do_quote_opt (store /[^"' \t\n]+/)
     (* quoted has at least one space, and must be quoted *)
  in let quoted = Quote.do_quote (store /[^"' \t\n]*[ \t][^"' \t\n]*/)
  in [ lns . bare ] | [ lns . quoted ]

(*
View: dquote_spaces
  Make double quotes mandatory if value contains spaces,
  and optional if value doesn't contain spaces.

Parameters:
  lns:lens - the lens to be enclosed
*)
let dquote_spaces (lns:lens) =
     (* bare has no spaces, and is optionally quoted *)
     let bare = Quote.do_dquote_opt (store /[^" \t\n]+/)
     (* quoted has at least one space, and must be quoted *)
  in let quoted = Quote.do_dquote (store /[^" \t\n]*[ \t][^" \t\n]*/)
  in [ lns . bare ] | [ lns . quoted ]

(*
View: squote_spaces
  Make single quotes mandatory if value contains spaces,
  and optional if value doesn't contain spaces.

Parameters:
  lns:lens - the lens to be enclosed
*)
let squote_spaces (lns:lens) =
     (* bare has no spaces, and is optionally quoted *)
     let bare = Quote.do_squote_opt (store /[^' \t\n]+/)
     (* quoted has at least one space, and must be quoted *)
  in let quoted = Quote.do_squote (store /[^' \t\n]*[ \t][^' \t\n]*/)
  in [ lns . bare ] | [ lns . quoted ]

(*
Module: Quote
  Generic module providing useful primitives for quoting

Author: Raphael Pinson <raphael.pinson@camptocamp.com>

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

About: Lens Usage
   This is a generic module which doesn't apply to files directly.
   You can use its definitions to build lenses that requires quoted values.
   It provides several levels of definitions, allowing to define more or less fine-grained quoted values.

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

module Quote =

(* Group: QUOTE SEPARATORS *)

(* Variable: dquote
     A double quote *)
let dquote = Util.del_str "\""

(* Variable: dquote_opt
     An optional double quote, default to double *)
let dquote_opt = del /"?/ "\""

(* Variable: dquote_opt_nil
     An optional double quote, default to nothing *)
let dquote_opt_nil = del /"?/ ""

(* Variable: squote
     A single quote *)
let squote = Util.del_str "'"

(* Variable: squote_opt
     An optional single quote, default to single *)
let squote_opt = del /'?/ "'"

(* Variable: squote_opt_nil
     An optional single quote, default to nothing *)
let squote_opt_nil = del /'?/ ""

(* Variable: quote
     A quote, either double or single, default to double *)
let quote = del /["']/ "\""

(* Variable: quote_opt
     An optional quote, either double or single, default to double *)
let quote_opt = del /["']?/ "\""

(* Variable: quote_opt_nil
     An optional quote, either double or single, default to nothing *)
let quote_opt_nil = del /["']?/ ""

(* Group: QUOTING FUNCTIONS *)

(*
View: do_dquote
  Enclose a lens in <dquote>s

  Parameters:
    body:lens - the lens to be enclosed
*)
let do_dquote (body:lens) =
  square dquote body dquote

(*
View: do_dquote_opt
  Enclose a lens in optional <dquote>s,
  use <dquote>s by default.

  Parameters:
    body:lens - the lens to be enclosed
*)
let do_dquote_opt (body:lens) =
  square dquote_opt body dquote_opt

(*
View: do_dquote_opt_nil
  Enclose a lens in optional <dquote>s,
  default to no quotes.

  Parameters:
    body:lens - the lens to be enclosed
*)
let do_dquote_opt_nil (body:lens) =
  square dquote_opt_nil body dquote_opt_nil

(*
View: do_squote
  Enclose a lens in <squote>s

  Parameters:
    body:lens - the lens to be enclosed
*)
let do_squote (body:lens) =
  square squote body squote

(*
View: do_squote_opt
  Enclose a lens in optional <squote>s,
  use <squote>s by default.

  Parameters:
    body:lens - the lens to be enclosed
*)
let do_squote_opt (body:lens) =
  square squote_opt body squote_opt

(*
View: do_squote_opt_nil
  Enclose a lens in optional <squote>s,
  default to no quotes.

  Parameters:
    body:lens - the lens to be enclosed
*)
let do_squote_opt_nil (body:lens) =
  square squote_opt_nil body squote_opt_nil

(*
View: do_quote
  Enclose a lens in <quote>s.

  Parameters:
    body:lens - the lens to be enclosed
*)
let do_quote (body:lens) =
  square quote body quote

(*
View: do_quote
  Enclose a lens in options <quote>s.

  Parameters:
    body:lens - the lens to be enclosed
*)
let do_quote_opt (body:lens) =
  square quote_opt body quote_opt

(*
View: do_quote
  Enclose a lens in options <quote>s,
  default to no quotes.

  Parameters:
    body:lens - the lens to be enclosed
*)
let do_quote_opt_nil (body:lens) =
  square quote_opt_nil body quote_opt_nil

(* Group: QUOTED VALUES *)

(* View: double
     A double-quoted value *)
let double =
     let body = store /[^\n]*/
  in do_dquote body

(* Variable: double_opt_re
     The regexp to store when value
     is optionally double-quoted *)
let double_opt_re = /[^\n\t "]([^\n"]*[^\n\t "])?/

(* View: double_opt
     An optionaly double-quoted value
     Double quotes are not allowed in value
     Value cannot begin or end with spaces *)
let double_opt =
     let body = store double_opt_re
  in do_dquote_opt body

(* View: single
     A single-quoted value *)
let single =
     let body = store /[^\n]*/
  in do_squote body

(* Variable: single_opt_re
     The regexp to store when value
     is optionally single-quoted *)
let single_opt_re = /[^\n\t ']([^\n']*[^\n\t '])?/

(* View: single_opt
     An optionaly single-quoted value
     Single quotes are not allowed in value
     Value cannot begin or end with spaces *)
let single_opt =
     let body = store single_opt_re
  in do_squote_opt body

(* View: any
     A quoted value *)
let any =
     let body = store /[^\n]*/
  in do_quote body

(* Variable: any_opt_re
     The regexp to store when value
     is optionally single- or double-quoted *)
let any_opt_re = /[^\n\t "']([^\n"']*[^\n\t "'])?/

(* View: any_opt
     An optionaly quoted value
     Double or single quotes are not allowed in value
     Value cannot begin or end with spaces *)
let any_opt =
     let body = store any_opt_re
  in do_quote_opt body

(*
View: quote_spaces
  Make quotes mandatory if value contains spaces,
  and optional if value doesn't contain spaces.

Parameters:
  lns:lens - the lens to be enclosed
*)
let quote_spaces (lns:lens) =
     (* bare has no spaces, and is optionally quoted *)
     let bare = Quote.do_quote_opt (store /[^"' \t\n]+/)
     (* quoted has at least one space, and must be quoted *)
  in let quoted = Quote.do_quote (store /[^"' \t\n]*[ \t][^"' \t\n]*/)
  in [ lns . bare ] | [ lns . quoted ]

(*
View: dquote_spaces
  Make double quotes mandatory if value contains spaces,
  and optional if value doesn't contain spaces.

Parameters:
  lns:lens - the lens to be enclosed
*)
let dquote_spaces (lns:lens) =
     (* bare has no spaces, and is optionally quoted *)
     let bare = Quote.do_dquote_opt (store /[^" \t\n]+/)
     (* quoted has at least one space, and must be quoted *)
  in let quoted = Quote.do_dquote (store /[^" \t\n]*[ \t][^" \t\n]*/)
  in [ lns . bare ] | [ lns . quoted ]

(*
View: squote_spaces
  Make single quotes mandatory if value contains spaces,
  and optional if value doesn't contain spaces.

Parameters:
  lns:lens - the lens to be enclosed
*)
let squote_spaces (lns:lens) =
     (* bare has no spaces, and is optionally quoted *)
     let bare = Quote.do_squote_opt (store /[^' \t\n]+/)
     (* quoted has at least one space, and must be quoted *)
  in let quoted = Quote.do_squote (store /[^' \t\n]*[ \t][^' \t\n]*/)
  in [ lns . bare ] | [ lns . quoted ]


/etc/default/whoopsie is an IniFile

/etc/default/whoopsie is an IniFile

(*
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
        . case_entry+
        . keyword "esac" . comment_or_eol ]

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

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

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

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

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

  let filter_ifcfg   = incl "/etc/sysconfig/network-scripts/ifcfg-*"
                     . incl "/etc/sysconfig/network/ifcfg-*"
  let filter_default = incl "/etc/default/*"
                     . 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_ifcfg
             . filter_default
             . filter_misc
             . Util.stdexcl

  let xfm = transform lns filter

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

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

About: License
   This file is licenced under the 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
        . case_entry+
        . keyword "esac" . comment_or_eol ]

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

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

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

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

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

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

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

  let xfm = transform lns filter

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


logrotate: support dateyesterday option (GH #367)

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

module Logrotate =
   autoload xfm

   let sep_spc = Sep.space
   let sep_val = del /[ \t]*=[ \t]*|[ \t]+/ " "
   let eol = Util.eol
   let num = Rx.relinteger
   let word = /[^,#= \n\t{}]+/
   let filename = /\/[^,#= \n\t{}]+/
   let size = num . /[kMG]?/

   let indent = del Rx.opt_space "\t"

   (* define omments and empty lines *)
   let comment = Util.comment
   let empty   = Util.empty

   (* Useful functions *)

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

   (* Defaults *)

   let create =
     let mode = sep_spc . [ label "mode" . store num ] in
     let owner = sep_spc . [ label "owner" . store word ] in
     let group = sep_spc . [ label "group" . store word ] in
     [ key "create" .
         ( mode | mode . owner | mode . owner . group )? ]

   let su =
     let owner = sep_spc . [ label "owner" . store word ] in
     let group = sep_spc . [ label "group" . store word ] in
     [ key "su" .
         ( owner | owner . group )? ]

   let tabooext = [ key "tabooext" . ( sep_spc . store /\+/ )? . list_item+ ]

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

   (* Define hooks *)

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

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

   (* Define rule *)

   let body = Build.block_newlines
                 (indent . (attrs | hooks) . eol)
                 Util.comment

   let rule =
     let filename_entry = [ label "file" . store filename ] in
     let filename_sep = del /[ \t\n]+/ " " in
     let filenames = Build.opt_list filename_entry filename_sep in
     [ label "rule" . Util.indent . filenames . body . eol ]

   let lns = ( comment | empty | (attrs . eol) | rule )*

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

   let xfm = transform lns filter

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

module Logrotate =
   autoload xfm

   let sep_spc = Sep.space
   let sep_val = del /[ \t]*=[ \t]*|[ \t]+/ " "
   let eol = Util.eol
   let num = Rx.relinteger
   let word = /[^,#= \n\t{}]+/
   let filename = /\/[^,#= \n\t{}]+/
   let size = num . /[kMG]?/

   let indent = del Rx.opt_space "\t"

   (* define omments and empty lines *)
   let comment = Util.comment
   let empty   = Util.empty

   (* Useful functions *)

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

   (* Defaults *)

   let create =
     let mode = sep_spc . [ label "mode" . store num ] in
     let owner = sep_spc . [ label "owner" . store word ] in
     let group = sep_spc . [ label "group" . store word ] in
     [ key "create" .
         ( mode | mode . owner | mode . owner . group )? ]

   let su =
     let owner = sep_spc . [ label "owner" . store word ] in
     let group = sep_spc . [ label "group" . store word ] in
     [ key "su" .
         ( owner | owner . group )? ]

   let tabooext = [ key "tabooext" . ( sep_spc . store /\+/ )? . list_item+ ]

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

   (* Define hooks *)

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

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

   (* Define rule *)

   let body = Build.block_newlines
                 (indent . (attrs | hooks) . eol)
                 Util.comment

   let rule =
     let filename_entry = [ label "file" . store filename ] in
     let filename_sep = del /[ \t\n]+/ " " in
     let filenames = Build.opt_list filename_entry filename_sep in
     [ label "rule" . Util.indent . filenames . body . eol ]

   let lns = ( comment | empty | (attrs . eol) | rule )*

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

   let xfm = transform lns filter


* lenses/tests/test_logrotate.aug: add test for files separated by newlines

This test the fix for bug #104, commit fe0453ce.

module Test_logrotate =

   let conf = "# see man logrotate for details
# rotate log files weekly
weekly

# keep 4 weeks worth of backlogs
rotate 4

# create new (empty) log files after rotating old ones
create

# uncomment this if you want your log files compressed
#compress

tabooext + .old .orig .ignore

# packages drop log rotation information into this directory
include /etc/logrotate.d

# no packages own wtmp, or btmp -- we'll rotate them here
/var/log/wtmp
/var/log/wtmp2
{
    missingok
    monthly
    create 0664 root utmp
    rotate 1
}

/var/log/btmp /var/log/btmp* {
    missingok
    # ftpd doesn't handle SIGHUP properly
    monthly
    create 0664 root utmp
    rotate 1
}
/var/log/vsftpd.log {
    # ftpd doesn't handle SIGHUP properly
    nocompress
    missingok
    notifempty
    rotate 4
    weekly
}

/var/log/apache2/*.log {
        weekly
        missingok
        rotate 52
        compress
        delaycompress
        notifempty
        create 640 root adm
        sharedscripts
        prerotate
                if [ -f /var/run/apache2.pid ]; then
                        /etc/init.d/apache2 restart > /dev/null
                fi
        endscript
}
"

   test Logrotate.lns get conf =
      { "#comment" = "see man logrotate for details" }
      { "#comment" = "rotate log files weekly" }
      { "schedule" = "weekly" }
      {}
      { "#comment" = "keep 4 weeks worth of backlogs" }
      { "rotate"   = "4" }
      {}
      { "#comment" = "create new (empty) log files after rotating old ones" }
      { "create" }
      {}
      { "#comment" = "uncomment this if you want your log files compressed" }
      { "#comment" = "compress" }
      {}
      { "tabooext" = "+"  { ".old" } { ".orig" } { ".ignore" } }
      {}
      { "#comment" = "packages drop log rotation information into this directory" }
      { "include" = "/etc/logrotate.d" }
      {}
      { "#comment" = "no packages own wtmp, or btmp -- we'll rotate them here" }
      { "rule"
           { "file"      = "/var/log/wtmp" }
           { "file"      = "/var/log/wtmp2" }
           { "missingok" = "missingok" }
           { "schedule"  = "monthly" }
           { "create"
                { "mode"  = "0664" }
                { "owner" = "root" }
                { "group" = "utmp" } }
           { "rotate" = "1" } }
      {}
      { "rule"
           { "file"      = "/var/log/btmp" }
	   { "file"      = "/var/log/btmp*" }
           { "missingok" = "missingok" }
	   { "#comment"   = "ftpd doesn't handle SIGHUP properly" }
           { "schedule"  = "monthly" }
           { "create"
                { "mode"  = "0664" }
                { "owner" = "root" }
                { "group" = "utmp" } }
           { "rotate" = "1" } }
      { "rule"
           { "file"      = "/var/log/vsftpd.log" }
           { "#comment"   = "ftpd doesn't handle SIGHUP properly" }
           { "compress"  = "nocompress" }
           { "missingok" = "missingok" }
           { "ifempty"   = "notifempty" }
           { "rotate"    = "4" }
           { "schedule"  = "weekly" } }
      {}
      { "rule"
           { "file"          = "/var/log/apache2/*.log" }
           { "schedule"      = "weekly" }
           { "missingok"     = "missingok" }
           { "rotate"        = "52" }
           { "compress"      = "compress" }
           { "delaycompress" = "delaycompress" }
           { "ifempty"       = "notifempty" }
           { "create"
                { "mode"  = "640"  }
                { "owner" = "root" }
                { "group" = "adm"  } }
           { "sharedscripts" = "sharedscripts" }
           { "prerotate" = "                if [ -f /var/run/apache2.pid ]; then
                        /etc/init.d/apache2 restart > /dev/null
                fi" } }

  test Logrotate.lns get "/var/log/file {\n dateext\n}\n" =
    { "rule"
      { "file" = "/var/log/file" }
      { "dateext" = "dateext" } }

  (* Make sure 'minsize 1M' works *)
  test Logrotate.lns get "/avr/log/wtmp {\n minsize 1M\n}\n" =
  { "rule"
      { "file" = "/avr/log/wtmp" }
      { "minsize" = "1M" } }

  (* '=' is a legal separator, file names can be indented *)
   test Logrotate.lns get " \t /file {\n size=5M\n}\n" =
     { "rule"
         { "file" = "/file" }
         { "size" = "5M" } }

  (* Can leave owner/group off a create statement *)
  test Logrotate.lns get "/file {
	create 600\n}\n" =
     { "rule"
         { "file" = "/file" }
         { "create"
             { "mode" = "600" } } }

  test Logrotate.lns put "/file {\n	create 600\n}\n" after
    set "/rule/create/owner" "user"
  = "/file {\n	create 600 user\n}\n"

  (* The newline at the end of a script is optional *)
  test Logrotate.lns put "/file {\n size=5M\n}\n" after
    set "/rule/prerotate" "\tfoobar"
  =
"/file {
 size=5M
\tprerotate
\tfoobar
\tendscript\n}\n"

  test Logrotate.lns put "/file {\n size=5M\n}\n" after
    set "/rule/prerotate" "\tfoobar\n"
  =
"/file {
 size=5M
\tprerotate
\tfoobar\n
\tendscript\n}\n"

(* Bug #101: whitespace at the end of the line *)
test Logrotate.lns get "/file {\n missingok \t\n}\n" =
  { "rule"
    { "file" = "/file" }
    { "missingok" = "missingok" } }

(* Bug #104: file names can be separated by newlines *)
let conf2 = "/var/log/mail.info
/var/log/mail.warn
/var/log/mail.err
{
       weekly
}
"
test Logrotate.lns get conf2 =
  { "rule"
      { "file"      = "/var/log/mail.info" }
      { "file"      = "/var/log/mail.warn" }
      { "file"      = "/var/log/mail.err" }
      { "schedule"  = "weekly" } }


Fix typo in previous commit

Fix typo in previous commit

Adjust comment and remove duplicated test case for primary statement in
dhcpd.conf lens.

module Test_dhcpd =

let lns = Dhcpd.lns

let conf = "#
# Sample configuration file for ISC dhcpd for Debian
#
# Attention: If /etc/ltsp/dhcpd.conf exists, that will be used as
# configuration file instead of this file.
#
# $Id: dhcpd.conf,v 1.1.1.1 2002/05/21 00:07:44 peloy Exp $
#

# The ddns-updates-style parameter controls whether or not the server will
# attempt to do a DNS update when a lease is confirmed. We default to the
# behavior of the version 2 packages ('none', since DHCP v2 didn't
# have support for DDNS.)
ddns-update-style none;

# option definitions common to all supported networks...
option domain-name \"example.org\";
option domain-name-servers ns1.example.org, ns2.example.org;

default-lease-time 600;
max-lease-time 7200;

# If this DHCP server is the official DHCP server for the local
# network, the authoritative directive should be uncommented.
authoritative;

# Use this to send dhcp log messages to a different log file (you also
# have to hack syslog.conf to complete the redirection).
log-facility local7;

# No service will be given on this subnet, but declaring it helps the
# DHCP server to understand the network topology.

subnet 10.152.187.0 netmask 255.255.255.0 {
}

# This is a very basic subnet declaration.

subnet 10.254.239.0 netmask 255.255.255.224 {
  range 10.254.239.10 10.254.239.20;
  option routers rtr-239-0-1.example.org, rtr-239-0-2.example.org;
}

# This declaration allows BOOTP clients to get dynamic addresses,
# which we don't really recommend.

subnet 10.254.239.32 netmask 255.255.255.224 {
  range dynamic-bootp 10.254.239.40 10.254.239.60;
  option broadcast-address 10.254.239.31;
  option routers rtr-239-32-1.example.org;
}

# A slightly different configuration for an internal subnet.
subnet 10.5.5.0 netmask 255.255.255.224 {
  range 10.5.5.26 10.5.5.30;
  option domain-name-servers ns1.internal.example.org;
  option domain-name \"internal.example.org\";
  option routers 10.5.5.1;
  option broadcast-address 10.5.5.31;
  default-lease-time 600;
  max-lease-time 7200;
}

# Hosts which require special configuration options can be listed in
# host statements.   If no address is specified, the address will be
# allocated dynamically (if possible), but the host-specific information
# will still come from the host declaration.

host passacaglia {
  hardware ethernet 0:0:c0:5d:bd:95;
  filename \"vmunix.passacaglia\";
  server-name \"toccata.fugue.com\";
}

# Fixed IP addresses can also be specified for hosts.   These addresses
# should not also be listed as being available for dynamic assignment.
# Hosts for which fixed IP addresses have been specified can boot using
# BOOTP or DHCP.   Hosts for which no fixed address is specified can only
# be booted with DHCP, unless there is an address range on the subnet
# to which a BOOTP client is connected which has the dynamic-bootp flag
# set.
host fantasia {
  hardware ethernet 08:00:07:26:c0:a5;
  fixed-address fantasia.fugue.com;
}

# You can declare a class of clients and then do address allocation
# based on that.   The example below shows a case where all clients
# in a certain class get addresses on the 10.17.224/24 subnet, and all
# other clients get addresses on the 10.0.29/24 subnet.

#class \"foo\" {
#  match if substring (option vendor-class-identifier, 0, 4) = \"SUNW\";
#}

shared-network 224-29 {
  subnet 10.17.224.0 netmask 255.255.255.0 {
    option routers rtr-224.example.org;
  }
  subnet 10.0.29.0 netmask 255.255.255.0 {
    option routers rtr-29.example.org;
  }
  pool {
    allow members of \"foo\";
    range 10.17.224.10 10.17.224.250;
  }
  pool {
    deny members of \"foo\";
    range 10.0.29.10 10.0.29.230;
  }
}
"

test lns get "authoritative;" = { "authoritative" }
test lns get "ddns-update-style none;" = { "ddns-update-style" = "none" }
test lns get "option domain-name \"example.org\";" =
  { "option"
    { "domain-name"
      { "arg" = "example.org" }
    }
  }

test lns get "option domain-name-servers ns1.example.org, ns2.example.org;" =
  { "option"
    { "domain-name-servers"
      { "arg" = "ns1.example.org" }
      { "arg" = "ns2.example.org" }
    }
  }

test lns get "default-lease-time 600;" = { "default-lease-time" = "600" }
test lns get "range 10.254.239.60;" =
{ "range"
    { "to" = "10.254.239.60" }
  }

test lns get "range dynamic-bootp 10.254.239.60;" =
  { "range"
    { "flag" = "dynamic-bootp" }
    { "to" = "10.254.239.60" }
  }

test lns get "range dynamic-bootp 10.254.239.40 10.254.239.60;" =
  { "range"
    { "flag" = "dynamic-bootp" }
    { "from" = "10.254.239.40" }
    { "to" = "10.254.239.60" }
  }

test lns get "subnet 10.152.187.0 netmask 255.255.255.0 {}\n" =
  { "subnet"
    { "network" = "10.152.187.0" }
    { "netmask" = "255.255.255.0" }
  }

test lns get " pool {
    pool {

    }
}
" =
  { "pool"
    { "pool" }
  }

test lns get "group { host some-host {hardware ethernet 00:00:aa:bb:cc:dd;
fixed-address 10.1.1.1;}}" =
  { "group"
    { "host" = "some-host"
      { "hardware"
        { "type" = "ethernet" }
        { "address" = "00:00:aa:bb:cc:dd" }
      }
      { "fixed-address" = "10.1.1.1" }
    }
  }

test Dhcpd.stmt_secu get "allow members of \"foo\";" =  { "allow-members-of" = "foo" }
test Dhcpd.stmt_option get "option voip-boot-server code 66 = string;" =
  { "rfc-code"
    { "label" = "voip-boot-server" }
    { "code" = "66" }
    { "type" = "string" }
  }

test Dhcpd.lns get "authoritative;
log-facility local7;
ddns-update-style none;
default-lease-time 21600;
max-lease-time 43200;

# Additional options for VOIP
option voip-boot-server code 66 = string;
option voip-vlan-id code 128 = string;
" =
  { "authoritative" }
  { "log-facility" = "local7" }
  { "ddns-update-style" = "none" }
  { "default-lease-time" = "21600" }
  { "max-lease-time" = "43200"
    { "#comment" = "Additional options for VOIP" }
  }
  { "rfc-code"
    { "label" = "voip-boot-server" }
    { "code" = "66" }
    { "type" = "string" }
  }
  { "rfc-code"
    { "label" = "voip-vlan-id" }
    { "code" = "128" }
    { "type" = "string" }
  }

test Dhcpd.lns get "
option domain-name-servers 10.1.1.1, 10.11.2.1, 10.1.3.1;
next-server 10.1.1.1;

failover peer \"redondance01\" {
         primary;
         address 10.1.1.1;
         port 647;
         peer address 10.1.1.1;
         peer port 647;
         max-response-delay 20;
         max-unacked-updates 10;
         mclt 3600;         #comment.
         split 128;         #comment.
         load balance max seconds 3;
       }
" =
  {  }
  { "option"
    { "domain-name-servers"
      { "arg" = "10.1.1.1" }
      { "arg" = "10.11.2.1" }
      { "arg" = "10.1.3.1" }
    }
  }
  { "next-server" = "10.1.1.1" }
  { "failover peer" = "\"redondance01\""
    { "primary" }
    { "address" = "10.1.1.1" }
    { "port" = "647" }
    { "peer address" = "10.1.1.1" }
    { "peer port" = "647" }
    { "max-response-delay" = "20" }
    { "max-unacked-updates" = "10" }
    { "mclt" = "3600"
      { "#comment" = "comment." }
    }
    { "split" = "128"
      { "#comment" = "comment." }
    }
    { "load balance max seconds" = "3" }
  }

test Dhcpd.lns get "
option CallManager code 150 = ip-address;
option slp-directory-agent true 10.1.1.1, 10.2.2.2;
option slp-service-scope true \"SLP-GLOBAL\";
option nds-context \"EXAMPLE\";
option nds-tree-name \"EXAMPLE\";
" =
  {  }
  { "rfc-code"
    { "label" = "CallManager" }
    { "code" = "150" }
    { "type" = "ip-address" }
  }
  { "option"
    { "slp-directory-agent" = "true"
      { "arg" = "10.1.1.1" }
      { "arg" = "10.2.2.2" }
    }
  }
  { "option"
    { "slp-service-scope" = "true"
      { "arg" = "SLP-GLOBAL" }
    }
  }
  { "option"
    { "nds-context"
      { "arg" = "EXAMPLE" }
    }
  }
  { "option"
    { "nds-tree-name"
      { "arg" = "EXAMPLE" }
    }
  }

test Dhcpd.lns get "option voip-vlan-id \"VLAN=1234;\";" =
  { "option"
    { "voip-vlan-id"
      { "arg" = "VLAN=1234;" }
    }
  }

test Dhcpd.lns get "option domain-name \"x.example.com y.example.com z.example.com\";" =
  { "option"
    { "domain-name"
      { "arg" = "x.example.com y.example.com z.example.com" }
    }
  }

test Dhcpd.lns get "include \"/etc/dhcpd.master\";" =
  { "include" = "/etc/dhcpd.master" }

test Dhcpd.lns put "\n" after set "/include" "/etc/dhcpd.master" =
  "\ninclude \"/etc/dhcpd.master\";\n"

test Dhcpd.fct_args get "(option dhcp-client-identifier, 1, 3)" =
  { "args"
    { "arg" = "option dhcp-client-identifier" }
    { "arg" = "1" }
    { "arg" = "3" }
  }

test Dhcpd.stmt_match get "match if substring (option dhcp-client-identifier, 1, 3) = \"RAS\";" =
  { "match"
    { "function" = "substring"
      { "args"
        { "arg" = "option dhcp-client-identifier" }
        { "arg" = "1" }
        { "arg" = "3" }
      }
    }
    { "value" = "RAS" }
  }

test Dhcpd.lns get "match pick-first-value (option dhcp-client-identifier, hardware);" =
  { "match"
    { "function" = "pick-first-value"
      { "args"
        { "arg" = "option dhcp-client-identifier" }
        { "arg" = "hardware"  }
      }
    }
  }

test Dhcpd.fct_args get "(16, 32, \"\", substring(hardware, 0, 4))" =
  { "args"
    { "arg" = "16" }
    { "arg" = "32" }
    { "arg" = "\"\"" }
    { "arg" = "substring(hardware, 0, 4)" }
  }

test Dhcpd.stmt_match get "match if binary-to-ascii(16, 32, \"\", substring(hardware, 0, 4)) = \"1525400\";" =
  { "match"
    { "function" = "binary-to-ascii"
      { "args"
        { "arg" = "16" }
        { "arg" = "32" }
        { "arg" = "\"\"" }
        { "arg" = "substring(hardware, 0, 4)" }
      }
    }
    { "value" = "1525400" }
  }

test Dhcpd.lns get "subclass \"allocation-class-1\" 1:8:0:2b:4c:39:ad;" =
  { "subclass"
    { "name" = "allocation-class-1" }
    { "value" = "1:8:0:2b:4c:39:ad" }
  }

(* overall test *)
test Dhcpd.lns put conf after rm "/x" = conf

(* bug #293: primary should support argument *)
let input293 = "zone EXAMPLE.ORG. {
  primary 127.0.0.1;
}"

test Dhcpd.lns get input293 =
  { "zone" = "EXAMPLE.ORG."
    { "primary" = "127.0.0.1" }
  }

module Test_dhcpd =

let lns = Dhcpd.lns

let conf = "#
# Sample configuration file for ISC dhcpd for Debian
#
# Attention: If /etc/ltsp/dhcpd.conf exists, that will be used as
# configuration file instead of this file.
#
# $Id: dhcpd.conf,v 1.1.1.1 2002/05/21 00:07:44 peloy Exp $
#

# The ddns-updates-style parameter controls whether or not the server will
# attempt to do a DNS update when a lease is confirmed. We default to the
# behavior of the version 2 packages ('none', since DHCP v2 didn't
# have support for DDNS.)
ddns-update-style none;

# option definitions common to all supported networks...
option domain-name \"example.org\";
option domain-name-servers ns1.example.org, ns2.example.org;

default-lease-time 600;
max-lease-time 7200;

# If this DHCP server is the official DHCP server for the local
# network, the authoritative directive should be uncommented.
authoritative;

# Use this to send dhcp log messages to a different log file (you also
# have to hack syslog.conf to complete the redirection).
log-facility local7;

# No service will be given on this subnet, but declaring it helps the
# DHCP server to understand the network topology.

subnet 10.152.187.0 netmask 255.255.255.0 {
}

# This is a very basic subnet declaration.

subnet 10.254.239.0 netmask 255.255.255.224 {
  range 10.254.239.10 10.254.239.20;
  option routers rtr-239-0-1.example.org, rtr-239-0-2.example.org;
}

# This declaration allows BOOTP clients to get dynamic addresses,
# which we don't really recommend.

subnet 10.254.239.32 netmask 255.255.255.224 {
  range dynamic-bootp 10.254.239.40 10.254.239.60;
  option broadcast-address 10.254.239.31;
  option routers rtr-239-32-1.example.org;
}

# A slightly different configuration for an internal subnet.
subnet 10.5.5.0 netmask 255.255.255.224 {
  range 10.5.5.26 10.5.5.30;
  option domain-name-servers ns1.internal.example.org;
  option domain-name \"internal.example.org\";
  option routers 10.5.5.1;
  option broadcast-address 10.5.5.31;
  default-lease-time 600;
  max-lease-time 7200;
}

# Hosts which require special configuration options can be listed in
# host statements.   If no address is specified, the address will be
# allocated dynamically (if possible), but the host-specific information
# will still come from the host declaration.

host passacaglia {
  hardware ethernet 0:0:c0:5d:bd:95;
  filename \"vmunix.passacaglia\";
  server-name \"toccata.fugue.com\";
}

# Fixed IP addresses can also be specified for hosts.   These addresses
# should not also be listed as being available for dynamic assignment.
# Hosts for which fixed IP addresses have been specified can boot using
# BOOTP or DHCP.   Hosts for which no fixed address is specified can only
# be booted with DHCP, unless there is an address range on the subnet
# to which a BOOTP client is connected which has the dynamic-bootp flag
# set.
host fantasia {
  hardware ethernet 08:00:07:26:c0:a5;
  fixed-address fantasia.fugue.com;
}

# You can declare a class of clients and then do address allocation
# based on that.   The example below shows a case where all clients
# in a certain class get addresses on the 10.17.224/24 subnet, and all
# other clients get addresses on the 10.0.29/24 subnet.

#class \"foo\" {
#  match if substring (option vendor-class-identifier, 0, 4) = \"SUNW\";
#}

shared-network 224-29 {
  subnet 10.17.224.0 netmask 255.255.255.0 {
    option routers rtr-224.example.org;
  }
  subnet 10.0.29.0 netmask 255.255.255.0 {
    option routers rtr-29.example.org;
  }
  pool {
    allow members of \"foo\";
    range 10.17.224.10 10.17.224.250;
  }
  pool {
    deny members of \"foo\";
    range 10.0.29.10 10.0.29.230;
  }
}
"

test lns get "authoritative;" = { "authoritative" }
test lns get "ddns-update-style none;" = { "ddns-update-style" = "none" }
test lns get "option domain-name \"example.org\";" =
  { "option"
    { "domain-name"
      { "arg" = "example.org" }
    }
  }

test lns get "option domain-name-servers ns1.example.org, ns2.example.org;" =
  { "option"
    { "domain-name-servers"
      { "arg" = "ns1.example.org" }
      { "arg" = "ns2.example.org" }
    }
  }

test lns get "default-lease-time 600;" = { "default-lease-time" = "600" }
test lns get "range 10.254.239.60;" =
{ "range"
    { "to" = "10.254.239.60" }
  }

test lns get "range dynamic-bootp 10.254.239.60;" =
  { "range"
    { "flag" = "dynamic-bootp" }
    { "to" = "10.254.239.60" }
  }

test lns get "range dynamic-bootp 10.254.239.40 10.254.239.60;" =
  { "range"
    { "flag" = "dynamic-bootp" }
    { "from" = "10.254.239.40" }
    { "to" = "10.254.239.60" }
  }

test lns get "subnet 10.152.187.0 netmask 255.255.255.0 {}\n" =
  { "subnet"
    { "network" = "10.152.187.0" }
    { "netmask" = "255.255.255.0" }
  }

test lns get " pool {
    pool {

    }
}
" =
  { "pool"
    { "pool" }
  }

test lns get "group { host some-host {hardware ethernet 00:00:aa:bb:cc:dd;
fixed-address 10.1.1.1;}}" =
  { "group"
    { "host" = "some-host"
      { "hardware"
        { "type" = "ethernet" }
        { "address" = "00:00:aa:bb:cc:dd" }
      }
      { "fixed-address" = "10.1.1.1" }
    }
  }

test Dhcpd.stmt_secu get "allow members of \"foo\";" =  { "allow-members-of" = "foo" }
test Dhcpd.stmt_option get "option voip-boot-server code 66 = string;" =
  { "rfc-code"
    { "label" = "voip-boot-server" }
    { "code" = "66" }
    { "type" = "string" }
  }

test Dhcpd.lns get "authoritative;
log-facility local7;
ddns-update-style none;
default-lease-time 21600;
max-lease-time 43200;

# Additional options for VOIP
option voip-boot-server code 66 = string;
option voip-vlan-id code 128 = string;
" =
  { "authoritative" }
  { "log-facility" = "local7" }
  { "ddns-update-style" = "none" }
  { "default-lease-time" = "21600" }
  { "max-lease-time" = "43200"
    { "#comment" = "Additional options for VOIP" }
  }
  { "rfc-code"
    { "label" = "voip-boot-server" }
    { "code" = "66" }
    { "type" = "string" }
  }
  { "rfc-code"
    { "label" = "voip-vlan-id" }
    { "code" = "128" }
    { "type" = "string" }
  }

test Dhcpd.lns get "
option domain-name-servers 10.1.1.1, 10.11.2.1, 10.1.3.1;
next-server 10.1.1.1;

failover peer \"redondance01\" {
         primary;
         address 10.1.1.1;
         port 647;
         peer address 10.1.1.1;
         peer port 647;
         max-response-delay 20;
         max-unacked-updates 10;
         mclt 3600;         #comment.
         split 128;         #comment.
         load balance max seconds 3;
       }
" =
  {  }
  { "option"
    { "domain-name-servers"
      { "arg" = "10.1.1.1" }
      { "arg" = "10.11.2.1" }
      { "arg" = "10.1.3.1" }
    }
  }
  { "next-server" = "10.1.1.1" }
  { "failover peer" = "\"redondance01\""
    { "primary" }
    { "address" = "10.1.1.1" }
    { "port" = "647" }
    { "peer address" = "10.1.1.1" }
    { "peer port" = "647" }
    { "max-response-delay" = "20" }
    { "max-unacked-updates" = "10" }
    { "mclt" = "3600"
      { "#comment" = "comment." }
    }
    { "split" = "128"
      { "#comment" = "comment." }
    }
    { "load balance max seconds" = "3" }
  }

test Dhcpd.lns get "
option CallManager code 150 = ip-address;
option slp-directory-agent true 10.1.1.1, 10.2.2.2;
option slp-service-scope true \"SLP-GLOBAL\";
option nds-context \"EXAMPLE\";
option nds-tree-name \"EXAMPLE\";
" =
  {  }
  { "rfc-code"
    { "label" = "CallManager" }
    { "code" = "150" }
    { "type" = "ip-address" }
  }
  { "option"
    { "slp-directory-agent" = "true"
      { "arg" = "10.1.1.1" }
      { "arg" = "10.2.2.2" }
    }
  }
  { "option"
    { "slp-service-scope" = "true"
      { "arg" = "SLP-GLOBAL" }
    }
  }
  { "option"
    { "nds-context"
      { "arg" = "EXAMPLE" }
    }
  }
  { "option"
    { "nds-tree-name"
      { "arg" = "EXAMPLE" }
    }
  }

test Dhcpd.lns get "option voip-vlan-id \"VLAN=1234;\";" =
  { "option"
    { "voip-vlan-id"
      { "arg" = "VLAN=1234;" }
    }
  }

test Dhcpd.lns get "option domain-name \"x.example.com y.example.com z.example.com\";" =
  { "option"
    { "domain-name"
      { "arg" = "x.example.com y.example.com z.example.com" }
    }
  }

test Dhcpd.lns get "include \"/etc/dhcpd.master\";" =
  { "include" = "/etc/dhcpd.master" }

test Dhcpd.lns put "\n" after set "/include" "/etc/dhcpd.master" =
  "\ninclude \"/etc/dhcpd.master\";\n"

test Dhcpd.fct_args get "(option dhcp-client-identifier, 1, 3)" =
  { "args"
    { "arg" = "option dhcp-client-identifier" }
    { "arg" = "1" }
    { "arg" = "3" }
  }

test Dhcpd.stmt_match get "match if substring (option dhcp-client-identifier, 1, 3) = \"RAS\";" =
  { "match"
    { "function" = "substring"
      { "args"
        { "arg" = "option dhcp-client-identifier" }
        { "arg" = "1" }
        { "arg" = "3" }
      }
    }
    { "value" = "RAS" }
  }

test Dhcpd.lns get "match pick-first-value (option dhcp-client-identifier, hardware);" =
  { "match"
    { "function" = "pick-first-value"
      { "args"
        { "arg" = "option dhcp-client-identifier" }
        { "arg" = "hardware"  }
      }
    }
  }

test Dhcpd.fct_args get "(16, 32, \"\", substring(hardware, 0, 4))" =
  { "args"
    { "arg" = "16" }
    { "arg" = "32" }
    { "arg" = "\"\"" }
    { "arg" = "substring(hardware, 0, 4)" }
  }

test Dhcpd.stmt_match get "match if binary-to-ascii(16, 32, \"\", substring(hardware, 0, 4)) = \"1525400\";" =
  { "match"
    { "function" = "binary-to-ascii"
      { "args"
        { "arg" = "16" }
        { "arg" = "32" }
        { "arg" = "\"\"" }
        { "arg" = "substring(hardware, 0, 4)" }
      }
    }
    { "value" = "1525400" }
  }

test Dhcpd.lns get "subclass \"allocation-class-1\" 1:8:0:2b:4c:39:ad;" =
  { "subclass"
    { "name" = "allocation-class-1" }
    { "value" = "1:8:0:2b:4c:39:ad" }
  }

(* overall test *)
test Dhcpd.lns put conf after rm "/x" = conf

(* bug #293: primary should support primary with argument *)
let input293 = "zone EXAMPLE.ORG. {
  primary 127.0.0.1;
}"

test Dhcpd.lns get input293 =
  { "zone" = "EXAMPLE.ORG."
    { "primary" = "127.0.0.1" }
  }

test Dhcpd.lns get input293 =
  { "zone" = "EXAMPLE.ORG."
    { "primary" = "127.0.0.1" }
  }


Fix NagiosCfg lense so /etc/nagios/nrpe.cfg is parsed by Nrpe and not NagiosCfg

Currently NagiosCfg lense is in charge of parsing all files under /etc/nagios/*.cfg
but /etc/nagios/commands.cfg. /etc/nagios/nrpe.cfg should be also added as an exception
since it needs to be parsed by the Nrpe lense. This issue currently causes the following
error : "Lenses @NagiosCfg and @Nrpe could be used to load this file"

(*
Module: NagiosConfig
  Parses /etc/{nagios{3,},icinga/*.cfg

Authors: Sebastien Aperghis-Tramoni <sebastien@aperghis.net>
         Raphaël Pinson <raphink@gmail.com>

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 /etc/{nagios{3,},icinga/*.cfg. See <filter>.
*)

module NagiosCfg =
autoload xfm

(************************************************************************
 * Group: Utility variables/functions
 ************************************************************************)
(* View: param_def
    define a field *)
let param_def =
     let space_in  = /[^ \t\n][^\n=]*[^ \t\n]|[^ \t\n]/
  in key /[A-Za-z0-9_]+/
   . Sep.space_equal
   . store space_in

(* View: macro_def
    Macro line, as used in resource.cfg *)
let macro_def =
     let macro = /\$[A-Za-z0-9]+\$/
       in let macro_decl = Rx.word | Rx.fspath
     in key macro . Sep.space_equal . store macro_decl

(************************************************************************
 * Group: Entries
 ************************************************************************)
(* View: param
    Params can have sub params *)
let param =
     [ Util.indent . param_def
     . [ Sep.space . param_def ]*
     . Util.eol ]

(* View: macro *)
let macro = [ Util.indent . macro_def . Util.eol ]

(************************************************************************
 * Group: Lens
 ************************************************************************)
(* View: entry
    Define the accepted entries, such as param for regular configuration
    files, and macro for resources.cfg .*)
let entry = param
	  | macro

(* View: lns
    main structure *)
let lns = ( Util.empty | Util.comment | entry )*

(* View: filter *)
let filter = incl "/etc/nagios3/*.cfg"
           . incl "/etc/nagios/*.cfg"
	   . incl "/etc/icinga/*.cfg"
	   . excl "/etc/nagios3/commands.cfg"
	   . excl "/etc/nagios/commands.cfg"
	   . excl "/etc/nagios/nrpe.cfg"
	   . incl "/etc/icinga/commands.cfg"

let xfm = transform lns filter

(*
Module: NagiosConfig
  Parses /etc/{nagios{3,},icinga/*.cfg

Authors: Sebastien Aperghis-Tramoni <sebastien@aperghis.net>
         Raphaël Pinson <raphink@gmail.com>

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 /etc/{nagios{3,},icinga/*.cfg. See <filter>.
*)

module NagiosCfg =
autoload xfm

(************************************************************************
 * Group: Utility variables/functions
 ************************************************************************)
(* View: param_def
    define a field *)
let param_def =
     let space_in  = /[^ \t\n][^\n=]*[^ \t\n]|[^ \t\n]/
  in key /[A-Za-z0-9_]+/
   . Sep.space_equal
   . store space_in

(* View: macro_def
    Macro line, as used in resource.cfg *)
let macro_def =
     let macro = /\$[A-Za-z0-9]+\$/
       in let macro_decl = Rx.word | Rx.fspath
     in key macro . Sep.space_equal . store macro_decl

(************************************************************************
 * Group: Entries
 ************************************************************************)
(* View: param
    Params can have sub params *)
let param =
     [ Util.indent . param_def
     . [ Sep.space . param_def ]*
     . Util.eol ]

(* View: macro *)
let macro = [ Util.indent . macro_def . Util.eol ]

(************************************************************************
 * Group: Lens
 ************************************************************************)
(* View: entry
    Define the accepted entries, such as param for regular configuration
    files, and macro for resources.cfg .*)
let entry = param
	  | macro

(* View: lns
    main structure *)
let lns = ( Util.empty | Util.comment | entry )*

(* View: filter *)
let filter = incl "/etc/nagios3/*.cfg"
           . incl "/etc/nagios/*.cfg"
	   . incl "/etc/icinga/*.cfg"
	   . excl "/etc/nagios3/commands.cfg"
	   . excl "/etc/nagios/commands.cfg"
	   . incl "/etc/icinga/commands.cfg"

let xfm = transform lns filter