augeas-lens: added all RCODEs according to RFC6895

module Deckard =
  autoload xfm

let del_str = Util.del_str

let space = del /[ \t]+/ " "
let tab = del /[ \t]+/ "\t"
let ws = del /[\t ]*/ ""
let word = /[^\t\n\/#; ]+/

let comment = del /[;#]/ ";" . [label "comment" . store /[^\n]+/]

let eol = del /([ \t]*([;#][^\n]*)?\n)+/ "\n" . Util.indent
let comment_or_eol =  ws . comment? . del_str "\n" . del /([ \t]*([;#][^\n]*)?\n)*/ "\n" . Util.indent

(*let comment_or_eol = [ label "#comment" . counter "comment" . (ws . [del /[;#]/ ";" . label "" . store /[^\n]*/ ]? . del_str "\n")]+ . Util.indent
*)

let domain_re = (/[^.\t\n\/#; ]+(\.[^.\t\n\/#; ]+)*\.?/ | ".") - "SECTION" (*quick n dirty, sorry to whoever will ever own SECTION TLD*)
let class_re = /CLASS[0-9]+/ | "IN" | "CH" | "HS" | "NONE" | "ANY"
let domain = [ label "domain" . store domain_re ]
let ttl = [label "ttl" . store /[0-9]+/]
let class = [label "class" . store class_re ]
let type = [label "type" . store ((/[^0-9#;\n \t][^\t\n\/#; ]*/) - class_re) ]
let data_re = /([^ \t\n#;][^\n#;]*[^ \t\n#;])|[^ \t\n#;]/ (*Can not start nor end with whitespace but can have whitespace in the middle. Disjunction is there so we match strings of length one.*)
let data = [label "data" . store data_re ]

let ip_re = /[0-9a-f.:]+/
let hex_re = /[0-9a-fA-F]+/

let match_option =  "opcode" | "qtype" | "qcase" | "qname" | "subdomain" | "flags" | "rcode" | "question" | "answer" | "authority" | "additional" | "all" | "TCP" | "ttl"
let adjust_option = "copy_id" | "copy_query"
let reply_option = "QR" | "TC" | "AA" | "AD" | "RD" | "RA" | "CD" | "DO" | "NOERROR" | "FORMERR" | "SERVFAIL" | "NXDOMAIN" | "NOTIMP" | "REFUSED" | "YXDOMAIN" | "YXRRSET" | "NXRRSET" | "NOTAUTH" | "NOTZONE" | "BADVERS" | "BADSIG" | "BADKEY" | "BADTIME" | "BADMODE" | "BADNAME" | "BADALG" | "BADTRUNC" | "BADCOOKIE"
let step_option = "REPLY" | "QUERY" | "CHECK_ANSWER" | "CHECK_OUT_QUERY" | /TIME_PASSES[ \t]+ELAPSE/

let mandatory = [del_str "MANDATORY" . label "mandatory" . value "true" . comment_or_eol]
let tsig = [del_str "TSIG" . label "tsig" . space . [label "keyname" . store word] . space . [label "secret" . store word] . comment_or_eol]

let match = (mandatory | tsig)* . del_str "MATCH" . [space . label "match" . store match_option ]+ . comment_or_eol
let adjust =  (mandatory | tsig)* . del_str "ADJUST" . [space . label "adjust" . store adjust_option ]+ . comment_or_eol
let reply =  (mandatory | tsig)* . del ("REPLY" | "FLAGS") "REPLY" .  [space . label "reply" . store reply_option ]+ . comment_or_eol

let question = [label "record" . domain . tab . (class . tab)? . type . comment_or_eol ]
let record = [label "record" . domain . tab . (ttl . tab)? . (class . tab)? . type . tab . data . comment_or_eol]

let section_question = [ label "question" . del_str "SECTION QUESTION" .
                       comment_or_eol . question? ]
let section_answer = [ label "answer" . del_str "SECTION ANSWER" .
                       comment_or_eol . record* ]
let section_authority = [ label "authority" . del_str "SECTION AUTHORITY" .
                          comment_or_eol . record* ]
let section_additional = [ label "additional" . del_str "SECTION ADDITIONAL" .
                           comment_or_eol . record* ]
let sections = [label "section" . section_question? . section_answer? . section_authority? . section_additional?]

let raw = [del_str "RAW" . comment_or_eol . label "raw" . store hex_re  ] . comment_or_eol

(* This is quite dirty hack to match every combination of options given to entry since 'let normal = ((match | adjust | reply | mandatory | tsig)* . sections)' just is not possible *)

let normal = (match . (adjust . reply? | reply . adjust?)? | adjust . (match . reply? | reply . match?)? | reply . (match . adjust? | adjust . match?)?)? . (mandatory | tsig)* . sections

let entry = [label "entry" . del_str "ENTRY_BEGIN" . comment_or_eol . ( normal | raw ) . del_str "ENTRY_END" . eol]

let single_address = [ label "address" . space . store ip_re ]

let addresses = [label "address" . counter "address" . [seq "address" . del_str "ADDRESS" . space . store ip_re . comment_or_eol]+]

let range = [label "range" . del_str "RANGE_BEGIN" . space . [ label "from" . store /[0-9]+/] . space .
            [ label "to" . store /[0-9]+/] . single_address? . comment_or_eol . addresses? . entry* . del_str "RANGE_END" . eol]

let step = [label "step" . del_str "STEP" . space . store /[0-9]+/ . space . [label "type" . store step_option] . [space . label "timestamp" . store /[0-9]+/]? . comment_or_eol .
           entry? ]

let config_record = /[^\n]*/ - ("CONFIG_END" | /STEP.*/ | /SCENARIO.*/ | /RANGE.*/ | /ENTRY.*/)

let config = [ label "config" . counter "config" . [seq "config" . store config_record . del_str "\n"]* . del_str "CONFIG_END" . comment_or_eol ]

let guts = (step | range )*

let scenario = [label "scenario" . del_str "SCENARIO_BEGIN" . space . store data_re . comment_or_eol . guts . del_str "SCENARIO_END" . eol]

let lns = config? . scenario

(* TODO: REPLAY step *)
(* TODO: store all comments into the tree instead of ignoring them *)

(*let filter = incl "/home/test/*.rpl"*)
let filter = incl "/media/test/27159fa1-67d4-4162-8707-cd67900f3b36/stepan/nic/deckard_stable/deckard/sets/resolver/*.rpl"

let xfm = transform lns filter

module Deckard =
  autoload xfm

let del_str = Util.del_str

let space = del /[ \t]+/ " "
let tab = del /[ \t]+/ "\t"
let ws = del /[\t ]*/ ""
let word = /[^\t\n\/#; ]+/

let comment = del /[;#]/ ";" . [label "comment" . store /[^\n]+/]

let eol = del /([ \t]*([;#][^\n]*)?\n)+/ "\n" . Util.indent
let comment_or_eol =  ws . comment? . del_str "\n" . del /([ \t]*([;#][^\n]*)?\n)*/ "\n" . Util.indent

(*let comment_or_eol = [ label "#comment" . counter "comment" . (ws . [del /[;#]/ ";" . label "" . store /[^\n]*/ ]? . del_str "\n")]+ . Util.indent
*)

let domain_re = (/[^.\t\n\/#; ]+(\.[^.\t\n\/#; ]+)*\.?/ | ".") - "SECTION" (*quick n dirty, sorry to whoever will ever own SECTION TLD*)
let class_re = /CLASS[0-9]+/ | "IN" | "CH" | "HS" | "NONE" | "ANY"
let domain = [ label "domain" . store domain_re ]
let ttl = [label "ttl" . store /[0-9]+/]
let class = [label "class" . store class_re ]
let type = [label "type" . store ((/[^0-9#;\n \t][^\t\n\/#; ]*/) - class_re) ]
let data_re = /([^ \t\n#;][^\n#;]*[^ \t\n#;])|[^ \t\n#;]/ (*Can not start nor end with whitespace but can have whitespace in the middle. Disjunction is there so we match strings of length one.*)
let data = [label "data" . store data_re ]

let ip_re = /[0-9a-f.:]+/
let hex_re = /[0-9a-fA-F]+/

let match_option =  "opcode" | "qtype" | "qcase" | "qname" | "subdomain" | "flags" | "rcode" | "question" | "answer" | "authority" | "additional" | "all" | "TCP" | "ttl"
let adjust_option = "copy_id" | "copy_query"
let reply_option = "QR" | "TC" | "AA" | "AD" | "RD" | "RA" | "CD" | "DO" | "NOERROR" | "FORMERR" | "SERVFAIL" | "NXDOMAIN" | "NOTIMP" | "REFUSED" | "YXDOMAIN" | "YXRRSET" | "NXRRSET" | "NOTAUTH" | "NOTZONE" | "BADVERS" | "QUERY" | "IQUERY" | "STATUS" | "NOTIFY" | "UPDATE"
let step_option = "REPLY" | "QUERY" | "CHECK_ANSWER" | "CHECK_OUT_QUERY" | /TIME_PASSES[ \t]+ELAPSE/

let mandatory = [del_str "MANDATORY" . label "mandatory" . value "true" . comment_or_eol]
let tsig = [del_str "TSIG" . label "tsig" . space . [label "keyname" . store word] . space . [label "secret" . store word] . comment_or_eol]

let match = (mandatory | tsig)* . del_str "MATCH" . [space . label "match" . store match_option ]+ . comment_or_eol
let adjust =  (mandatory | tsig)* . del_str "ADJUST" . [space . label "adjust" . store adjust_option ]+ . comment_or_eol
let reply =  (mandatory | tsig)* . del ("REPLY" | "FLAGS") "REPLY" .  [space . label "reply" . store reply_option ]+ . comment_or_eol

let question = [label "record" . domain . tab . (class . tab)? . type . comment_or_eol ]
let record = [label "record" . domain . tab . (ttl . tab)? . (class . tab)? . type . tab . data . comment_or_eol]

let section_question = [ label "question" . del_str "SECTION QUESTION" .
                       comment_or_eol . question? ]
let section_answer = [ label "answer" . del_str "SECTION ANSWER" .
                       comment_or_eol . record* ]
let section_authority = [ label "authority" . del_str "SECTION AUTHORITY" .
                          comment_or_eol . record* ]
let section_additional = [ label "additional" . del_str "SECTION ADDITIONAL" .
                           comment_or_eol . record* ]
let sections = [label "section" . section_question? . section_answer? . section_authority? . section_additional?]

let raw = [del_str "RAW" . comment_or_eol . label "raw" . store hex_re  ] . comment_or_eol

(* This is quite dirty hack to match every combination of options given to entry since 'let normal = ((match | adjust | reply | mandatory | tsig)* . sections)' just is not possible *)

let normal = (match . (adjust . reply? | reply . adjust?)? | adjust . (match . reply? | reply . match?)? | reply . (match . adjust? | adjust . match?)?)? . (mandatory | tsig)* . sections

let entry = [label "entry" . del_str "ENTRY_BEGIN" . comment_or_eol . ( normal | raw ) . del_str "ENTRY_END" . eol]

let single_address = [ label "address" . space . store ip_re ]

let addresses = [label "address" . counter "address" . [seq "address" . del_str "ADDRESS" . space . store ip_re . comment_or_eol]+]

let range = [label "range" . del_str "RANGE_BEGIN" . space . [ label "from" . store /[0-9]+/] . space .
            [ label "to" . store /[0-9]+/] . single_address? . comment_or_eol . addresses? . entry* . del_str "RANGE_END" . eol]

let step = [label "step" . del_str "STEP" . space . store /[0-9]+/ . space . [label "type" . store step_option] . [space . label "timestamp" . store /[0-9]+/]? . comment_or_eol .
           entry? ]

let config_record = /[^\n]*/ - ("CONFIG_END" | /STEP.*/ | /SCENARIO.*/ | /RANGE.*/ | /ENTRY.*/)

let config = [ label "config" . counter "config" . [seq "config" . store config_record . del_str "\n"]* . del_str "CONFIG_END" . comment_or_eol ]

let guts = (step | range )*

let scenario = [label "scenario" . del_str "SCENARIO_BEGIN" . space . store data_re . comment_or_eol . guts . del_str "SCENARIO_END" . eol]

let lns = config? . scenario

(* TODO: REPLAY step *)
(* TODO: store all comments into the tree instead of ignoring them *)

(*let filter = incl "/home/test/*.rpl"*)
let filter = incl "/media/test/27159fa1-67d4-4162-8707-cd67900f3b36/stepan/nic/deckard_stable/deckard/sets/resolver/*.rpl"

let xfm = transform lns filter


graphviz.aug: names can be empty

graphviz.aug: names can be empty

(*
Module: Graphviz
  Parses graphviz files.

Author: Raphael Pinson <raphink@gmail.com>

About: Reference

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

About: Lens Usage
   To be documented

About: Configuration files
   This lens applies to graphviz files.

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

module Graphviz =

(* View: name *)
let name =
   let reserved = "node" | "graph"
   in  store ( ("\"" . /[^"]*/ . "\"")
               | (/[A-Za-z0-9_.]+/ - reserved) )

(* View: eol
     Special eol using semicolons and/or newlines *)
let eol = del /[ \t]*((;\n?)|\n)/ ";\n"

(* View: block
     A block construct, using Build.block_generic
     in order to pass our special definition of <eol> *)
let block (kw:regexp) (entry:lens) =
   [ key kw
   . Sep.space . name
   . Build.block_generic  (* Use our definition of eol *)
        (Util.indent . entry . eol)
        (entry . eol) (Util.indent . entry) entry
        Util.comment Util.comment_noindent
        Build.block_ldelim_re /;?[ \t\n]*\}/ (* Allow semicol *)
        Build.block_ldelim_default Build.block_rdelim_default ]

(* View: variable_generic
     A generic way of parsing variables
     with or without double quotes *)
let variable_generic (reserved:regexp) =
   Build.key_value (Rx.word - reserved) Sep.space_equal name

(* View: variable *)
let variable =
   let reserved = "subgraph" | "node"
   in variable_generic reserved

(* View: options
     A list of options for <nodes> or <links> *)
let options =
      let reserved = "node" | "link_type"
   in let option = variable_generic reserved
   in let comma = del /,[ \t\n]*/ ","
   in Sep.space . del /\[[ \t]*/ "["
                . Build.opt_list option comma
                . del /[ \t]*\]/ "]"

(* View: link *)
let link =
      let link_node = [ label "node" . name ]
   in let link_type = [ label "link_type" . Sep.opt_space
                      . store /\<?\-\-?\>?/ . Sep.opt_space ]
   in [ label "link"
      . Build.list link_node link_type
      . options? ]

(* View: node *)
let node = [ label "node" . name . options? ]

(* View: nodelist
     Several <nodes> can be declared at once in a nodelist.
     This cannot be declared as simply a list of <nodes>,
     since it would be ambiguous in the put direction. *)
let nodelist = [ label "nodelist" . Build.list node Sep.space ]

(* View: default_node
     General settings for <nodes> *)
let default_node = [ Build.xchgs "node" "@node" . options ]

(* View: default_graph
     General settings for <graphs> *)
let default_graph = [ Build.xchgs "graph" "@graph" . options ]

(* View: entry
     A general entry *)
let entry = (link | variable | node | nodelist
           | default_node | default_graph)

(* View: subgraph
     Recursive *)
let rec subgraph = block "subgraph" (subgraph | entry)

(* View: graph
     The top-level graph *)
let graph = block /(di)?graph/ (subgraph | entry) . Util.eol

(* View: lns
     The graphviz lens *)
let lns = (Util.comment | Util.empty)* . graph

(*
Module: Graphviz
  Parses graphviz files.

Author: Raphael Pinson <raphink@gmail.com>

About: Reference

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

About: Lens Usage
   To be documented

About: Configuration files
   This lens applies to graphviz files.

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

module Graphviz =

(* View: name *)
let name =
   let reserved = "node" | "graph"
   in  store ( ("\"" . /[^"]+/ . "\"")
               | (/[A-Za-z0-9_.]+/ - reserved) )

(* View: eol
     Special eol using semicolons and/or newlines *)
let eol = del /[ \t]*((;\n?)|\n)/ ";\n"

(* View: block
     A block construct, using Build.block_generic
     in order to pass our special definition of <eol> *)
let block (kw:regexp) (entry:lens) =
   [ key kw
   . Sep.space . name
   . Build.block_generic  (* Use our definition of eol *)
        (Util.indent . entry . eol)
        (entry . eol) (Util.indent . entry) entry
        Util.comment Util.comment_noindent
        Build.block_ldelim_re /;?[ \t\n]*\}/ (* Allow semicol *)
        Build.block_ldelim_default Build.block_rdelim_default ]

(* View: variable_generic
     A generic way of parsing variables
     with or without double quotes *)
let variable_generic (reserved:regexp) =
   Build.key_value (Rx.word - reserved) Sep.space_equal name

(* View: variable *)
let variable =
   let reserved = "subgraph" | "node"
   in variable_generic reserved

(* View: options
     A list of options for <nodes> or <links> *)
let options =
      let reserved = "node" | "link_type"
   in let option = variable_generic reserved
   in let comma = del /,[ \t\n]*/ ","
   in Sep.space . del /\[[ \t]*/ "["
                . Build.opt_list option comma
                . del /[ \t]*\]/ "]"

(* View: link *)
let link =
      let link_node = [ label "node" . name ]
   in let link_type = [ label "link_type" . Sep.opt_space
                      . store /\<?\-\-?\>?/ . Sep.opt_space ]
   in [ label "link"
      . Build.list link_node link_type
      . options? ]

(* View: node *)
let node = [ label "node" . name . options? ]

(* View: nodelist
     Several <nodes> can be declared at once in a nodelist.
     This cannot be declared as simply a list of <nodes>,
     since it would be ambiguous in the put direction. *)
let nodelist = [ label "nodelist" . Build.list node Sep.space ]

(* View: default_node
     General settings for <nodes> *)
let default_node = [ Build.xchgs "node" "@node" . options ]

(* View: default_graph
     General settings for <graphs> *)
let default_graph = [ Build.xchgs "graph" "@graph" . options ]

(* View: entry
     A general entry *)
let entry = (link | variable | node | nodelist
           | default_node | default_graph)

(* View: subgraph
     Recursive *)
let rec subgraph = block "subgraph" (subgraph | entry)

(* View: graph
     The top-level graph *)
let graph = block /(di)?graph/ (subgraph | entry) . Util.eol

(* View: lns
     The graphviz lens *)
let lns = (Util.comment | Util.empty)* . graph


Shellvars: read /etc/firewalld/firewalld.conf

Fixes https://fedorahosted.org/augeas/ticket/363

(*
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 empty   = Util.empty
  let empty_part_re = Util.empty_generic_re . /\n+/
  let eol = del (/[ \t]+|[ \t]*[;\n]/ . empty_part_re*) "\n"
  let semicol_eol = del (/[ \t]*[;\n]/ . empty_part_re*) "\n"

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

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

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

  let char  = /[^`;() '"\t\n]|\\\\"/
  let dquot = /"([^"\\]|\\\\.)*"/                    (* " Emacs, relax *)
  let squot = /'[^']*'/
  let bquot = /`[^`\n]*`/
  (* dbquot don't take spaces or semi-colons *)
  let dbquot = /``[^` \t\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 | dbquot | dollar_assign | empty_array)

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

  let var_action (name:string) =
    [ Util.indent . xchgs name ("@" . name) . Util.del_ws_spc
    . store (key_re | matching_re) . 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" | "shift" | "exit"

  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+ | ("\"" . char+ . "\""))
        . del /[ \t\n]+/ " " . Util.del_str "in" . eol
        . (empty* . comment* . case_entry)*
        . empty* . comment*
        . keyword "esac" . comment_or_eol ]

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

  let rec rec_entry =
    let entry = comment | 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_norec = empty* . (comment | source | kv | unset | bare_export | builtin | return) *

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

  let filter_default = incl "/etc/default/*"
                     . excl "/etc/default/grub_installdevice*"
                     . excl "/etc/default/whoopsie"
  let filter_misc    = incl "/etc/arno-iptables-firewall/debconf.cfg"
                     . incl "/etc/cron-apt/config"
                     . incl "/etc/environment"
                     . incl "/etc/firewalld/firewalld.conf"
                     . incl "/etc/blkid.conf"
                     . incl "/etc/adduser.conf"
                     . incl "/etc/cowpoke.conf"
                     . incl "/etc/cvs-cron.conf"
                     . incl "/etc/cvs-pserver.conf"
                     . incl "/etc/devscripts.conf"
                     . incl "/etc/lintianrc"
                     . incl "/etc/lsb-release"
                     . incl "/etc/os-release"
                     . incl "/etc/popularity-contest.conf"
                     . incl "/etc/rc.conf"
                     . incl "/etc/rc.conf.local"
                     . incl "/etc/selinux/config"
                     . incl "/etc/ucf.conf"
                     . incl "/etc/locale.conf"
                     . incl "/etc/vconsole.conf"

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

  let xfm = transform lns filter

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

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

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

About: Lens Usage
   To be documented
*)

module Shellvars =
  autoload xfm

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

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

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

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

  let char  = /[^`;() '"\t\n]|\\\\"/
  let dquot = /"([^"\\]|\\\\.)*"/                    (* " Emacs, relax *)
  let squot = /'[^']*'/
  let bquot = /`[^`\n]*`/
  (* dbquot don't take spaces or semi-colons *)
  let dbquot = /``[^` \t\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 | dbquot | dollar_assign | empty_array)

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

  let var_action (name:string) =
    [ Util.indent . xchgs name ("@" . name) . Util.del_ws_spc
    . store (key_re | matching_re) . 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" | "shift" | "exit"

  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+ | ("\"" . char+ . "\""))
        . del /[ \t\n]+/ " " . Util.del_str "in" . eol
        . (empty* . comment* . case_entry)*
        . empty* . comment*
        . keyword "esac" . comment_or_eol ]

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

  let rec rec_entry =
    let entry = comment | 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_norec = empty* . (comment | source | kv | unset | bare_export | builtin | return) *

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

  let filter_default = incl "/etc/default/*"
                     . excl "/etc/default/grub_installdevice*"
                     . 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/rc.conf.local"
                     . incl "/etc/selinux/config"
                     . incl "/etc/ucf.conf"
                     . incl "/etc/locale.conf"
                     . incl "/etc/vconsole.conf"

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

  let xfm = transform lns filter

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


AptPreferences: Simplify/Standardize the module

(*
Module: AptPreferences
  Apt/preferences module for Augeas

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

module AptPreferences =
autoload xfm

(************************************************************************
 * Group: Entries
 ************************************************************************)

(* View: colon *)
let colon        = Util.delim ":"

(* View: pin_gen
     A generic pin

   Parameters:
     lbl:string - the label *)
let pin_gen (lbl:string) = store lbl
                        . [ label lbl . Sep.space . store Rx.no_spaces ]

(* View: pin_keys *)
let pin_keys = Build.key_value /[aclnov]/ Sep.equal (store /[^, \t\n]+/)

(* View: pin_options *)
let pin_options =
    let comma = Util.delim ","
 in store "release" . Sep.space
                    . Build.opt_list pin_keys comma

(* View: version_pin *)
let version_pin = pin_gen "version"

(* View: origin_pin *)
let origin_pin = pin_gen "origin"

(* View: pin *)
let pin =
     let pin_value = pin_options | version_pin | origin_pin
  in Build.key_value_line "Pin" colon pin_value

(* View: entries *)
let entries = Build.key_value_line ("Explanation"|"Package"|"Pin-Priority")
                                   colon (store Rx.space_in)
            | pin
            | Util.comment

(* View: record *)
let record = [ seq "record" . entries+ ]

(************************************************************************
 * Group: Lens
 ************************************************************************)

(* View: lns *)
let lns = Util.empty* . (Build.opt_list record Util.eol+)?

(* View: filter *)
let filter = incl "/etc/apt/preferences"
           . incl "/etc/apt/preferences.d/*"
           . Util.stdexcl

let xfm = transform lns filter


Shellvars: reinstate /etc/sysconfig/network

Fixes #330, RHBZ#904222, RHBZ#920609

(*
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 empty   = Util.empty
  let empty_part_re = Util.empty_generic_re . /\n+/
  let eol = del (/[ \t]+|[ \t]*[;\n]/ . empty_part_re*) "\n"
  let semicol_eol = del (/[ \t]*[;\n]/ . empty_part_re*) "\n"

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

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

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

  let char  = /[^`;() '"\t\n]|\\\\"/
  let dquot = /"([^"\\]|\\\\.)*"/                    (* " Emacs, relax *)
  let squot = /'[^']*'/
  let bquot = /`[^`\n]*`/
  (* dbquot don't take spaces or semi-colons *)
  let dbquot = /``[^` \t\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 | dbquot | dollar_assign | empty_array)

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

  let var_action (name:string) =
    [ Util.indent . xchgs name ("@" . name) . Util.del_ws_spc
    . store (key_re | matching_re) . 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" | "shift" | "exit"

  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+ | ("\"" . char+ . "\""))
        . del /[ \t\n]+/ " " . Util.del_str "in" . eol
        . (empty* . comment* . case_entry)*
        . empty* . comment*
        . keyword "esac" . comment_or_eol ]

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

  let rec rec_entry =
    let entry = comment | 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_norec = empty* . (comment | source | kv | unset | bare_export | builtin | return) *

  let lns = empty* . (comment | 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_excl "network-scripts" .
      sc_incl "network-scripts/ifcfg-*" .
      (* NetworkManager used to write files with '!' in them; this has
         been patched upstream. We need to ignore these files, because
         their name causes trouble during aug_init *)
      sc_excl "network-scripts/ifcfg-*!*" .
      (* Ditto, brackets conflict with paths and cause aug_init failures *)
      sc_excl "network-scripts/ifcfg-*(*" .
      sc_excl "network-scripts/ifcfg-*)*" .
      sc_excl "rhn" .
      sc_incl "rhn/allowed-actions/*" .
      sc_excl "rhn/allowed-actions/script" .
      sc_incl "rhn/allowed-actions/script/*" .
      sc_incl "rhn/rhnsd" .
      sc_excl "SuSEfirewall2.d" .
      sc_incl "SuSEfirewall2.d/cobbler" .
      sc_incl "SuSEfirewall2.d/services/*" .
      sc_excl "SuSEfirewall2.d/services/TEMPLATE"

  let filter_default = incl "/etc/default/*"
                     . excl "/etc/default/grub_installdevice*"
                     . 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/rc.conf.local"
                     . incl "/etc/selinux/config"
                     . incl "/etc/ucf.conf"
                     . incl "/etc/locale.conf"
                     . incl "/etc/vconsole.conf"

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

  let xfm = transform lns filter

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

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

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

About: Lens Usage
   To be documented
*)

module Shellvars =
  autoload xfm

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

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

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

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

  let char  = /[^`;() '"\t\n]|\\\\"/
  let dquot = /"([^"\\]|\\\\.)*"/                    (* " Emacs, relax *)
  let squot = /'[^']*'/
  let bquot = /`[^`\n]*`/
  (* dbquot don't take spaces or semi-colons *)
  let dbquot = /``[^` \t\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 | dbquot | dollar_assign | empty_array)

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

  let var_action (name:string) =
    [ Util.indent . xchgs name ("@" . name) . Util.del_ws_spc
    . store (key_re | matching_re) . 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" | "shift" | "exit"

  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+ | ("\"" . char+ . "\""))
        . del /[ \t\n]+/ " " . Util.del_str "in" . eol
        . (empty* . comment* . case_entry)*
        . empty* . comment*
        . keyword "esac" . comment_or_eol ]

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

  let rec rec_entry =
    let entry = comment | 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_norec = empty* . (comment | source | kv | unset | bare_export | builtin | return) *

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

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

  let filter_sysconfig =
      sc_incl "*" .
      sc_excl "bootloader" .
      sc_excl "hw-uuid" .
      sc_excl "hwconf" .
      sc_excl "ip*tables" .
      sc_excl "kernel" .
      sc_excl "*.pub" .
      sc_excl "sysstat.ioconf" .
      sc_excl "system-config-firewall" .
      sc_excl "system-config-securitylevel" .
      sc_excl "network" .
      sc_incl "network/config" .
      sc_incl "network/dhcp" .
      sc_incl "network/dhcp6r" .
      sc_incl "network/dhcp6s" .
      sc_incl "network/ifcfg-*" .
      sc_incl "network/if-down.d/*" .
      sc_incl "network/ifroute-*" .
      sc_incl "network/if-up.d/*" .
      sc_incl "network/providers/*" .
      sc_excl "network-scripts" .
      sc_incl "network-scripts/ifcfg-*" .
      (* NetworkManager used to write files with '!' in them; this has
         been patched upstream. We need to ignore these files, because
         their name causes trouble during aug_init *)
      sc_excl "network-scripts/ifcfg-*!*" .
      (* Ditto, brackets conflict with paths and cause aug_init failures *)
      sc_excl "network-scripts/ifcfg-*(*" .
      sc_excl "network-scripts/ifcfg-*)*" .
      sc_excl "rhn" .
      sc_incl "rhn/allowed-actions/*" .
      sc_excl "rhn/allowed-actions/script" .
      sc_incl "rhn/allowed-actions/script/*" .
      sc_incl "rhn/rhnsd" .
      sc_excl "SuSEfirewall2.d" .
      sc_incl "SuSEfirewall2.d/cobbler" .
      sc_incl "SuSEfirewall2.d/services/*" .
      sc_excl "SuSEfirewall2.d/services/TEMPLATE"

  let filter_default = incl "/etc/default/*"
                     . excl "/etc/default/grub_installdevice*"
                     . 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/rc.conf.local"
                     . incl "/etc/selinux/config"
                     . incl "/etc/ucf.conf"
                     . incl "/etc/locale.conf"
                     . incl "/etc/vconsole.conf"

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

  let xfm = transform lns filter

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


Use the Quote module in dhcpd.aug

Use the Quote module in dhcpd.aug

(*
Module: Dhcpd
  BIND dhcp 3 server configuration module for Augeas

Author: Francis Giraldeau <francis.giraldeau@usherbrooke.ca>

About: Reference
  Reference: manual of dhcpd.conf and dhcp-eval
  Follow dhclient module for tree structure

About: License
    This file is licensed under the GPL.

About: Lens Usage
  Sample usage of this lens in augtool

  Directive without argument.
  Set this dhcpd server authoritative on the domain.
  > clear /files/etc/dhcp3/dhcpd.conf/authoritative

  Directives with integer or string argument.
  Set max-lease-time to one hour:
  > set /files/etc/dhcp3/dhcpd.conf/max-lease-time 3600

  Options are declared as a list, even for single values.
  Set the domain of the network:
  > set /files/etc/dhcp3/dhcpd.conf/option/domain-name/arg example.org
  Set two name server:
  > set /files/etc/dhcp3/dhcpd.conf/option/domain-name-servers/arg[1] foo.example.org
  > set /files/etc/dhcp3/dhcpd.conf/option/domain-name-servers/arg[2] bar.example.org

  Create the subnet 172.16.0.1 with 10 addresses:
  > clear /files/etc/dhcp3/dhcpd.conf/subnet[last() + 1]
  > set /files/etc/dhcp3/dhcpd.conf/subnet[last()]/network 172.16.0.0
  > set /files/etc/dhcp3/dhcpd.conf/subnet[last()]/netmask 255.255.255.0
  > set /files/etc/dhcp3/dhcpd.conf/subnet[last()]/range/from 172.16.0.10
  > set /files/etc/dhcp3/dhcpd.conf/subnet[last()]/range/to 172.16.0.20

  Create a new group "foo" with one static host. Nodes type and address are ordered.
  > ins group after /files/etc/dhcp3/dhcpd.conf/subnet[network='172.16.0.0']/*[last()]
  > set /files/etc/dhcp3/dhcpd.conf/subnet[network='172.16.0.0']/group[last()]/host foo
  > set /files/etc/dhcp3/dhcpd.conf/subnet[network='172.16.0.0']/group[host='foo']/host/hardware/type "ethernet"
  > set /files/etc/dhcp3/dhcpd.conf/subnet[network='172.16.0.0']/group[host='foo']/host/hardware/address "00:00:00:aa:bb:cc"
  > set /files/etc/dhcp3/dhcpd.conf/subnet[network='172.16.0.0']/group[host='foo']/host/fixed-address 172.16.0.100

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

module Dhcpd =

autoload xfm

(************************************************************************
 *                           USEFUL PRIMITIVES
 *************************************************************************)
let dels (s:string)   = del s s
let eol               = Util.eol
let comment           = Util.comment
let empty             = Util.empty
let indent            = Util.indent
let eos               = comment?

(* Define separators *)
let sep_spc           = del /[ \t]+/ " "
let sep_osp           = del /[ \t]*/ ""
let sep_scl           = del /[ \t]*;([ \t]*\n)*/ ";\n"
let sep_obr           = del /[ \t]*\{([ \t]*\n)*/ " {\n"
let sep_cbr           = del /[ \t]*\}([ \t]*\n)*/ "}\n"
let sep_com           = del /[ \t\n]*,[ \t\n]*/ ", "
let sep_slh           = del "\/" "/"
let sep_col           = del ":" ":"
let sep_eq            = del /[ \t]*=[ \t]*/ "="
let scl               = del ";" ";"

(* Define basic types *)
let word              = /[A-Za-z0-9_.-]+(\[[0-9]+\])?/
let ip                = Rx.ipv4

(* Define fields *)

(* adapted from sysconfig.aug *)
  (* Chars allowed in a bare string *)
  let bchar = /[^ \t\n"'\\{}#,()\/]|\\\\./
  let qchar = /["']/  (* " *)

  (* We split the handling of right hand sides into a few cases:
   *   bare  - strings that contain no spaces, optionally enclosed in
   *           single or double quotes
   *   dquot - strings that contain at least one space, apostrophe or slash
   *           which must be enclosed in double quotes
   *   squot - strings that contain an unescaped double quote
   *)
  let bare = del qchar? "" . store (bchar+) . del qchar? ""
  let quote = Quote.do_quote (store (bchar* . /[ \t'\/]/ . bchar*)+)

let sto_to_spc        = store /[^\\#,;\{\}" \t\n]+|"[^\\#"\n]+"/
let sto_to_scl        = store /[^ \t;][^;\n=]+[^ \t;]|[^ \t;=]+/

let sto_number        = store /[0-9][0-9]*/

(************************************************************************
 *                         NO ARG STATEMENTS
 *************************************************************************)

let stmt_noarg_re     =   "authoritative"
                        | "primary"
                        | "secondary"

let stmt_noarg        = [ indent
                        . key stmt_noarg_re
                        . sep_scl
                        . eos ]

(************************************************************************
 *                         INT ARG STATEMENTS
 *************************************************************************)

let stmt_integer_re   = "default-lease-time"
                      | "max-lease-time"
                      | "min-lease-time"
                      | /lease[ ]+limit/
                      | "port"
                      | /peer[ ]+port/
                      | "max-response-delay"
                      | "max-unacked-updates"
                      | "mclt"
                      | "split"
                      | /load[ ]+balance[ ]+max[ ]+seconds/
                      | "max-lease-misbalance"
                      | "max-lease-ownership"
                      | "min-balance"
                      | "max-balance"
                      | "adaptive-lease-time-threshold"
                      | "dynamic-bootp-lease-length"
                      | "local-port"
                      | "min-sec"
                      | "omapi-port"
                      | "ping-timeout"
                      | "remote-port"

let stmt_integer      = [ indent
                        . key stmt_integer_re
                        . sep_spc
                        . sto_number
                        . sep_scl
                        . eos ]

(************************************************************************
 *                         STRING ARG STATEMENTS
 *************************************************************************)

let stmt_string_re    = "ddns-update-style"
                      | "ddns-updates"
                      | "ddns-hostname"
                      | "ddns-domainname"
                      | "ddns-rev-domainname"
                      | "log-facility"
                      | "filename"
                      | "server-name"
                      | "fixed-address"
                      | /failover[ ]+peer/
                      | "use-host-decl-names"
                      | "next-server"
                      | "address"
                      | /peer[ ]+address/
                      | "type"
                      | "file"
                      | "algorithm"
                      | "secret"
                      | "key"
                      | "include"
                      | "hba"
                      | "boot-unknown-clients"
                      | "db-time-format"
                      | "do-forward-updates"
                      | "dynamic-bootp-lease-cutoff"
                      | "get-lease-hostnames"
                      | "infinite-is-reserved"
                      | "lease-file-name"
                      | "local-address"
                      | "one-lease-per-client"
                      | "pid-file-name"
                      | "ping-check"
                      | "server-identifier"
                      | "site-option-space"
                      | "stash-agent-options"
                      | "update-conflict-detection"
                      | "update-optimization"
                      | "update-static-leases"
                      | "use-host-decl-names"
                      | "use-lease-addr-for-default-route"
                      | "vendor-option-space"
                      | "primary"

let stmt_string_tpl (l:lens) = [ indent
                        . key stmt_string_re
                        . sep_spc
                        . l
                        . sep_scl
                        . eos ]

let stmt_string  = stmt_string_tpl bare |stmt_string_tpl quote

(************************************************************************
 *                         RANGE STATEMENTS
 *************************************************************************)

let stmt_range        = [ indent
                        . key "range"
                        . sep_spc
                        . [ label "flag" . store /dynamic-bootp/ . sep_spc ]?
                        . [ label "from" . store ip . sep_spc ]?
                        . [ label "to" . store ip ]
                        . sep_scl
                        . eos ]

(************************************************************************
 *                         HARDWARE STATEMENTS
 *************************************************************************)

let stmt_hardware     = [ indent
                        . key "hardware"
                        . sep_spc
                        . [ label "type" . store /ethernet|tokenring/ ]
                        . sep_spc
                        . [ label "address" . store /[a-fA-F0-9:-]+/ ]
                        . sep_scl
                        . eos ]

(************************************************************************
 *                         OPTION STATEMENTS
 *************************************************************************)
(* The general case is considering options as a list *)

let stmt_option_code  = [ label "label" . store word . sep_spc ]
                        . [ key "code" . sep_spc . store word ]
                        . sep_eq
                        . [ label "type" . store word ]

let stmt_option_list  = ([ label "arg" . bare ] | [ label "arg" . quote ])
                        . ( sep_com . ([ label "arg" . bare ] | [ label "arg" . quote ]))*

let stmt_option_basic = [ key word . sep_spc . stmt_option_list ]
let stmt_option_extra = [ key word . sep_spc . store /true|false/ . sep_spc . stmt_option_list ]

let stmt_option_body = stmt_option_basic | stmt_option_extra

let stmt_option1  = [ indent
                        . key "option"
                        . sep_spc
                        . stmt_option_body
                        . sep_scl
                        . eos ]

let stmt_option2  = [ indent
                        . dels "option" . label "rfc-code"
                        . sep_spc
                        . stmt_option_code
                        . sep_scl
                        . eos ]

let stmt_option = stmt_option1 | stmt_option2

(************************************************************************
 *                         SUBCLASS STATEMENTS
 *************************************************************************)
(* this statement is not well documented in the manual dhcpd.conf
   we support basic use case *)

let stmt_subclass = [ indent . key "subclass" . sep_spc .
                      ([ label "name" . quote ]|
                       [ label "name" . bare ]) . sep_spc .
                       [ label "value" . bare ] . sep_scl . eos ]

(************************************************************************
 *                         ALLOW/DENY STATEMENTS
 *************************************************************************)
(* We have to use special key for allow/deny members of
  to avoid ambiguity in the put direction *)

let allow_deny_re     = "unknown-clients"
                      | /dynamic[ ]+bootp[ ]+clients/
                      | /authenticated[ ]+clients/
                      | /unauthenticated[ ]+clients/

let stmt_secu_re      = "allow"
                      | "deny"

let del_allow = del /allow[ ]+members[ ]+of/ "allow members of"
let del_deny  = del /deny[ \t]+members[ \t]+of/ "deny members of"

let stmt_secu_tpl (l:lens) (s:string) =
                  [ indent . l . sep_spc . label s . bare . sep_scl . eos ] |
                  [ indent . l . sep_spc . label s . quote . sep_scl . eos ]

let stmt_secu         = [ indent . key stmt_secu_re . sep_spc .
                          store allow_deny_re . sep_scl . eos ] |
                        stmt_secu_tpl del_allow "allow-members-of" |
                        stmt_secu_tpl del_deny "deny-members-of"

(************************************************************************
 *                         MATCH STATEMENTS
 *************************************************************************)

let sto_fct = store (word . /[ \t]*\([^)]*\)/)
let sto_option = store (/option[ ]+/ . word)
let sto_com = /[^ \t\n,\(\)][^,\(\)]*[^ \t\n,\(\)]|[^ \t\n,\(\)]+/ | word . /[ \t]*\([^)]*\)/
let fct_re = "substring" | "binary-to-ascii"

let fct_args = [ label "args" . dels "(" . sep_osp .
                 ([ label "arg" . store sto_com ] . [ label "arg" . sep_com . store sto_com ]+) .
                        sep_osp . dels ")" ]

let stmt_match_if = [ dels "if" . sep_spc . store fct_re . sep_osp . label "function" . fct_args ] .
                      sep_eq . ([ label "value" . bare ]|[ label "value" . quote ])

let stmt_match_pfv = [ label "function" . store "pick-first-value" . sep_spc .
                       dels "(" . sep_osp .
                       [ label "args" .
                         [ label "arg" . store sto_com ] .
                         [ sep_com . label "arg" . store sto_com ]+ ] .
                       dels ")" ]

let stmt_match_tpl (l:lens) = [ indent . key "match" . sep_spc . l . sep_scl . eos ]

let stmt_match = stmt_match_tpl (stmt_match_if | stmt_match_pfv )

(************************************************************************
 *                         BLOCK STATEMENTS
 *************************************************************************)
(* Blocks doesn't support comments at the end of the closing bracket *)

let stmt_entry        =   stmt_secu
                        | stmt_option
                        | stmt_hardware
                        | stmt_range
                        | stmt_string
                        | stmt_integer
                        | stmt_noarg
                        | stmt_match
                        | stmt_subclass
                        | empty
                        | comment

let stmt_block_noarg_re = "pool"
                        | "group"
                        | "allow-update"

let stmt_block_noarg (body:lens)
                        = [ indent
                        . key stmt_block_noarg_re
                        . sep_obr
                        . body*
                        . sep_cbr ]

let stmt_block_arg_re = "host"
                      | "class"
                      | "shared-network"
                      | /failover[ ]+peer/
                      | "zone"
                      | "key"

let stmt_block_arg (body:lens)
                      = [ indent
                        . key stmt_block_arg_re
                        . sep_spc
                        . sto_to_spc
                        . sep_obr
                        . body*
                        . sep_cbr ]

let stmt_block_subnet (body:lens)
                      = [ indent
                        . key "subnet"
                        . sep_spc
                        . [ label "network" . store ip ]
                        . sep_spc
                        . [ key "netmask" . sep_spc . store ip ]
                        . sep_obr
                        . body*
                        . sep_cbr ]

let all_block (body:lens) =
    let lns1 = stmt_block_subnet body in
    let lns2 = stmt_block_arg body in
    let lns3 = stmt_block_noarg body in
    (lns1 | lns2 | lns3 | stmt_entry)

let rec lns_staging = stmt_entry|all_block lns_staging
let lns = (lns_staging)*

let filter = incl "/etc/dhcp3/dhcpd.conf"
           . incl "/etc/dhcp/dhcpd.conf"
           . incl "/etc/dhcpd.conf"
           . Util.stdexcl

let xfm = transform lns filter

(*
Module: Dhcpd
  BIND dhcp 3 server configuration module for Augeas

Author: Francis Giraldeau <francis.giraldeau@usherbrooke.ca>

About: Reference
  Reference: manual of dhcpd.conf and dhcp-eval
  Follow dhclient module for tree structure

About: License
    This file is licensed under the GPL.

About: Lens Usage
  Sample usage of this lens in augtool

  Directive without argument.
  Set this dhcpd server authoritative on the domain.
  > clear /files/etc/dhcp3/dhcpd.conf/authoritative

  Directives with integer or string argument.
  Set max-lease-time to one hour:
  > set /files/etc/dhcp3/dhcpd.conf/max-lease-time 3600

  Options are declared as a list, even for single values.
  Set the domain of the network:
  > set /files/etc/dhcp3/dhcpd.conf/option/domain-name/arg example.org
  Set two name server:
  > set /files/etc/dhcp3/dhcpd.conf/option/domain-name-servers/arg[1] foo.example.org
  > set /files/etc/dhcp3/dhcpd.conf/option/domain-name-servers/arg[2] bar.example.org

  Create the subnet 172.16.0.1 with 10 addresses:
  > clear /files/etc/dhcp3/dhcpd.conf/subnet[last() + 1]
  > set /files/etc/dhcp3/dhcpd.conf/subnet[last()]/network 172.16.0.0
  > set /files/etc/dhcp3/dhcpd.conf/subnet[last()]/netmask 255.255.255.0
  > set /files/etc/dhcp3/dhcpd.conf/subnet[last()]/range/from 172.16.0.10
  > set /files/etc/dhcp3/dhcpd.conf/subnet[last()]/range/to 172.16.0.20

  Create a new group "foo" with one static host. Nodes type and address are ordered.
  > ins group after /files/etc/dhcp3/dhcpd.conf/subnet[network='172.16.0.0']/*[last()]
  > set /files/etc/dhcp3/dhcpd.conf/subnet[network='172.16.0.0']/group[last()]/host foo
  > set /files/etc/dhcp3/dhcpd.conf/subnet[network='172.16.0.0']/group[host='foo']/host/hardware/type "ethernet"
  > set /files/etc/dhcp3/dhcpd.conf/subnet[network='172.16.0.0']/group[host='foo']/host/hardware/address "00:00:00:aa:bb:cc"
  > set /files/etc/dhcp3/dhcpd.conf/subnet[network='172.16.0.0']/group[host='foo']/host/fixed-address 172.16.0.100

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

module Dhcpd =

autoload xfm

(************************************************************************
 *                           USEFUL PRIMITIVES
 *************************************************************************)
let dels (s:string)   = del s s
let eol               = Util.eol
let comment           = Util.comment
let empty             = Util.empty
let indent            = Util.indent
let eos               = comment?

(* Define separators *)
let sep_spc           = del /[ \t]+/ " "
let sep_osp           = del /[ \t]*/ ""
let sep_scl           = del /[ \t]*;([ \t]*\n)*/ ";\n"
let sep_obr           = del /[ \t]*\{([ \t]*\n)*/ " {\n"
let sep_cbr           = del /[ \t]*\}([ \t]*\n)*/ "}\n"
let sep_com           = del /[ \t\n]*,[ \t\n]*/ ", "
let sep_slh           = del "\/" "/"
let sep_col           = del ":" ":"
let sep_eq            = del /[ \t]*=[ \t]*/ "="
let scl               = del ";" ";"

(* Define basic types *)
let word              = /[A-Za-z0-9_.-]+(\[[0-9]+\])?/
let ip                = Rx.ipv4

(* Define fields *)

(* adapted from sysconfig.aug *)
  (* Chars allowed in a bare string *)
  let bchar = /[^ \t\n"'\\{}#,()\/]|\\\\./
  let qchar = /["']/  (* " *)

  (* We split the handling of right hand sides into a few cases:
   *   bare  - strings that contain no spaces, optionally enclosed in
   *           single or double quotes
   *   dquot - strings that contain at least one space, apostrophe or slash
   *           which must be enclosed in double quotes
   *   squot - strings that contain an unescaped double quote
   *)
  let bare = del qchar? "" . store (bchar+) . del qchar? ""
  let dquot =
    del qchar "\"" . store (bchar* . /[ \t'\/]/ . bchar*)+ . del qchar "\""
  let squot =
    dels "'" . store ((bchar|/[ \t\/]/)* . "\"" . (bchar|/[ \t\/]/)*)+ . dels "'"

let sto_to_spc        = store /[^\\#,;\{\}" \t\n]+|"[^\\#"\n]+"/
let sto_to_scl        = store /[^ \t;][^;\n=]+[^ \t;]|[^ \t;=]+/

let sto_number        = store /[0-9][0-9]*/

(************************************************************************
 *                         NO ARG STATEMENTS
 *************************************************************************)

let stmt_noarg_re     =   "authoritative"
                        | "primary"
                        | "secondary"

let stmt_noarg        = [ indent
                        . key stmt_noarg_re
                        . sep_scl
                        . eos ]

(************************************************************************
 *                         INT ARG STATEMENTS
 *************************************************************************)

let stmt_integer_re   = "default-lease-time"
                      | "max-lease-time"
                      | "min-lease-time"
                      | /lease[ ]+limit/
                      | "port"
                      | /peer[ ]+port/
                      | "max-response-delay"
                      | "max-unacked-updates"
                      | "mclt"
                      | "split"
                      | /load[ ]+balance[ ]+max[ ]+seconds/
                      | "max-lease-misbalance"
                      | "max-lease-ownership"
                      | "min-balance"
                      | "max-balance"
                      | "adaptive-lease-time-threshold"
                      | "dynamic-bootp-lease-length"
                      | "local-port"
                      | "min-sec"
                      | "omapi-port"
                      | "ping-timeout"
                      | "remote-port"

let stmt_integer      = [ indent
                        . key stmt_integer_re
                        . sep_spc
                        . sto_number
                        . sep_scl
                        . eos ]

(************************************************************************
 *                         STRING ARG STATEMENTS
 *************************************************************************)

let stmt_string_re    = "ddns-update-style"
                      | "ddns-updates"
                      | "ddns-hostname"
                      | "ddns-domainname"
                      | "ddns-rev-domainname"
                      | "log-facility"
                      | "filename"
                      | "server-name"
                      | "fixed-address"
                      | /failover[ ]+peer/
                      | "use-host-decl-names"
                      | "next-server"
                      | "address"
                      | /peer[ ]+address/
                      | "type"
                      | "file"
                      | "algorithm"
                      | "secret"
                      | "key"
                      | "include"
                      | "hba"
                      | "boot-unknown-clients"
                      | "db-time-format"
                      | "do-forward-updates"
                      | "dynamic-bootp-lease-cutoff"
                      | "get-lease-hostnames"
                      | "infinite-is-reserved"
                      | "lease-file-name"
                      | "local-address"
                      | "one-lease-per-client"
                      | "pid-file-name"
                      | "ping-check"
                      | "server-identifier"
                      | "site-option-space"
                      | "stash-agent-options"
                      | "update-conflict-detection"
                      | "update-optimization"
                      | "update-static-leases"
                      | "use-host-decl-names"
                      | "use-lease-addr-for-default-route"
                      | "vendor-option-space"
                      | "primary"

let stmt_string_tpl (l:lens) = [ indent
                        . key stmt_string_re
                        . sep_spc
                        . l
                        . sep_scl
                        . eos ]

let stmt_string  = stmt_string_tpl bare |stmt_string_tpl squot | stmt_string_tpl dquot

(************************************************************************
 *                         RANGE STATEMENTS
 *************************************************************************)

let stmt_range        = [ indent
                        . key "range"
                        . sep_spc
                        . [ label "flag" . store /dynamic-bootp/ . sep_spc ]?
                        . [ label "from" . store ip . sep_spc ]?
                        . [ label "to" . store ip ]
                        . sep_scl
                        . eos ]

(************************************************************************
 *                         HARDWARE STATEMENTS
 *************************************************************************)

let stmt_hardware     = [ indent
                        . key "hardware"
                        . sep_spc
                        . [ label "type" . store /ethernet|tokenring/ ]
                        . sep_spc
                        . [ label "address" . store /[a-fA-F0-9:-]+/ ]
                        . sep_scl
                        . eos ]

(************************************************************************
 *                         OPTION STATEMENTS
 *************************************************************************)
(* The general case is considering options as a list *)

let stmt_option_code  = [ label "label" . store word . sep_spc ]
                        . [ key "code" . sep_spc . store word ]
                        . sep_eq
                        . [ label "type" . store word ]

let stmt_option_list  = ([ label "arg" . bare ] | [ label "arg" . dquot ] | [ label "arg" . squot ])
                        . ( sep_com . ([ label "arg" . bare ] | [ label "arg" . dquot ] | [ label "arg" . squot ]))*

let stmt_option_basic = [ key word . sep_spc . stmt_option_list ]
let stmt_option_extra = [ key word . sep_spc . store /true|false/ . sep_spc . stmt_option_list ]

let stmt_option_body = stmt_option_basic | stmt_option_extra

let stmt_option1  = [ indent
                        . key "option"
                        . sep_spc
                        . stmt_option_body
                        . sep_scl
                        . eos ]

let stmt_option2  = [ indent
                        . dels "option" . label "rfc-code"
                        . sep_spc
                        . stmt_option_code
                        . sep_scl
                        . eos ]

let stmt_option = stmt_option1 | stmt_option2

(************************************************************************
 *                         SUBCLASS STATEMENTS
 *************************************************************************)
(* this statement is not well documented in the manual dhcpd.conf
   we support basic use case *)

let stmt_subclass = [ indent . key "subclass" . sep_spc .
                      ([ label "name" . dquot ]|
                       [ label "name" . squot ]|
                       [ label "name" . bare ]) . sep_spc .
                       [ label "value" . bare ] . sep_scl . eos ]

(************************************************************************
 *                         ALLOW/DENY STATEMENTS
 *************************************************************************)
(* We have to use special key for allow/deny members of
  to avoid ambiguity in the put direction *)

let allow_deny_re     = "unknown-clients"
                      | /dynamic[ ]+bootp[ ]+clients/
                      | /authenticated[ ]+clients/
                      | /unauthenticated[ ]+clients/

let stmt_secu_re      = "allow"
                      | "deny"

let del_allow = del /allow[ ]+members[ ]+of/ "allow members of"
let del_deny  = del /deny[ \t]+members[ \t]+of/ "deny members of"

let stmt_secu_tpl (l:lens) (s:string) =
                  [ indent . l . sep_spc . label s . bare . sep_scl . eos ] |
                  [ indent . l . sep_spc . label s . squot . sep_scl . eos ] |
                  [ indent . l . sep_spc . label s . dquot . sep_scl . eos ]

let stmt_secu         = [ indent . key stmt_secu_re . sep_spc .
                          store allow_deny_re . sep_scl . eos ] |
                        stmt_secu_tpl del_allow "allow-members-of" |
                        stmt_secu_tpl del_deny "deny-members-of"

(************************************************************************
 *                         MATCH STATEMENTS
 *************************************************************************)

let sto_fct = store (word . /[ \t]*\([^)]*\)/)
let sto_option = store (/option[ ]+/ . word)
let sto_com = /[^ \t\n,\(\)][^,\(\)]*[^ \t\n,\(\)]|[^ \t\n,\(\)]+/ | word . /[ \t]*\([^)]*\)/
let fct_re = "substring" | "binary-to-ascii"

let fct_args = [ label "args" . dels "(" . sep_osp .
                 ([ label "arg" . store sto_com ] . [ label "arg" . sep_com . store sto_com ]+) .
                        sep_osp . dels ")" ]

let stmt_match_if = [ dels "if" . sep_spc . store fct_re . sep_osp . label "function" . fct_args ] .
                      sep_eq . ([ label "value" . bare ]|[ label "value" . squot ]|[ label "value" . dquot ])

let stmt_match_pfv = [ label "function" . store "pick-first-value" . sep_spc .
                       dels "(" . sep_osp .
                       [ label "args" .
                         [ label "arg" . store sto_com ] .
                         [ sep_com . label "arg" . store sto_com ]+ ] .
                       dels ")" ]

let stmt_match_tpl (l:lens) = [ indent . key "match" . sep_spc . l . sep_scl . eos ]

let stmt_match = stmt_match_tpl (stmt_match_if | stmt_match_pfv )

(************************************************************************
 *                         BLOCK STATEMENTS
 *************************************************************************)
(* Blocks doesn't support comments at the end of the closing bracket *)

let stmt_entry        =   stmt_secu
                        | stmt_option
                        | stmt_hardware
                        | stmt_range
                        | stmt_string
                        | stmt_integer
                        | stmt_noarg
                        | stmt_match
                        | stmt_subclass
                        | empty
                        | comment

let stmt_block_noarg_re = "pool"
                        | "group"
                        | "allow-update"

let stmt_block_noarg (body:lens)
                        = [ indent
                        . key stmt_block_noarg_re
                        . sep_obr
                        . body*
                        . sep_cbr ]

let stmt_block_arg_re = "host"
                      | "class"
                      | "shared-network"
                      | /failover[ ]+peer/
                      | "zone"
                      | "key"

let stmt_block_arg (body:lens)
                      = [ indent
                        . key stmt_block_arg_re
                        . sep_spc
                        . sto_to_spc
                        . sep_obr
                        . body*
                        . sep_cbr ]

let stmt_block_subnet (body:lens)
                      = [ indent
                        . key "subnet"
                        . sep_spc
                        . [ label "network" . store ip ]
                        . sep_spc
                        . [ key "netmask" . sep_spc . store ip ]
                        . sep_obr
                        . body*
                        . sep_cbr ]

let all_block (body:lens) =
    let lns1 = stmt_block_subnet body in
    let lns2 = stmt_block_arg body in
    let lns3 = stmt_block_noarg body in
    (lns1 | lns2 | lns3 | stmt_entry)

let rec lns_staging = stmt_entry|all_block lns_staging
let lns = (lns_staging)*

let filter = incl "/etc/dhcp3/dhcpd.conf"
           . incl "/etc/dhcp/dhcpd.conf"
           . incl "/etc/dhcpd.conf"
           . Util.stdexcl

let xfm = transform lns filter


Update group.aug to parse NIS entries

Update group.aug to parse NIS entries

(* Group module for Augeas
 Author: Free Ekanayaka <free@64studio.com>

 Reference: man 5 group

*)

module Group =

   autoload xfm

(************************************************************************
 *                           USEFUL PRIMITIVES
 *************************************************************************)

let eol        = Util.eol
let comment    = Util.comment
let empty      = Util.empty
let dels       = Util.del_str

let colon      = Sep.colon
let comma      = Sep.comma

let sto_to_spc = store Rx.space_in
let sto_to_col  = store /[^:\n]+/
let sto_to_eol = store /([^ \t\n].*[^ \t\n]|[^ \t\n])/

let word    = Rx.word
let password = /[A-Za-z0-9_.!*-]*/
let integer = Rx.integer

(************************************************************************
 *                               ENTRIES
 *************************************************************************)

let user      = [ label "user" . store word ]
let user_list = Build.opt_list user comma
let params    = [ label "password" . store password  . colon ]
                . [ label "gid"      . store integer . colon ]
                . user_list?
let entry     = Build.key_value_line word colon params

(* A NIS entry has nothing bar the +@::: bits. *)
let nisentry =
  let nisuser = /\+\@[A-Za-z0-9_.-]+/ in
  let colons = ":::" in
  [ dels "+@" . label "@nis" . store word . dels colons . eol ]

let nisdefault =
  let overrides =
        colon
      . [ label "password" . store word?    . colon ]
      . [ label "gid"      . store integer? . colon ]
      . user_list? in
  [ dels "+" . label "@nisdefault" . overrides? . eol ]

(************************************************************************
 *                                LENS
 *************************************************************************)

let lns        = (comment|empty|entry|nisentry|nisdefault) *

let filter     = incl "/etc/group"

let xfm        = transform lns filter


Move the 'coding' comment to the end of the file to fix the menu entry.

(*
Module: Crypttab
  Parses /etc/crypttab from the cryptsetup package.

Author: Frédéric Lespez <frederic.lespez@free.fr>

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

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

About: Lens Usage
  Sample usage of this lens in augtool

    * Create a new entry for an encrypted block devices
      > ins 01 after /files/etc/crypttab/*[last()]
      > set /files/etc/crypttab/01/target crypt_sda2
      > set /files/etc/crypttab/01/device /dev/sda2
      > set /files/etc/crypttab/01/password /dev/random
      > set /files/etc/crypttab/01/opt swap
    * Print the entry applying to the "/dev/sda2" device
      > print /files/etc/crypttab/01
    * Remove the entry applying to the "/dev/sda2" device
      > rm /files/etc/crypttab/*[device="/dev/sda2"]

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

module Crypttab =
  autoload xfm

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

  (* Group: Separators *)

  (* Variable: sep_tab *)
  let sep_tab = Sep.tab

  (* Variable: comma *)
  let comma   = Sep.comma

  (* Group: Generic primitives *)

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

  (* Variable: comment *)
  let comment = Util.comment

  (* Variable: empty *)
  let empty   = Util.empty

  (* Variable: word *)
  let word    = Rx.word

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

  (* Variable: target *)
  let target  = Rx.device_name

  (* Variable: fspath *)
  let fspath  = Rx.fspath

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

  (************************************************************************
   * View: comma_sep_list
   *   A comma separated list of options (opt=value or opt)
   *************************************************************************)
  let comma_sep_list (l:string) =
    let value = [ label "value" . Util.del_str "=" . store optval ] in
      let lns = [ label l . store word . value? ] in
         Build.opt_list lns comma

  (************************************************************************
   * View: record
   *   A crypttab record
   *************************************************************************)

  let record = [ seq "entry" .
                   [ label "target" . store target ] . sep_tab .
                   [ label "device" . store fspath ] .
                   (sep_tab . [ label "password" . store fspath ] .
                    ( sep_tab . comma_sep_list "opt")? )?
                 . eol ]

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

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

  let xfm = transform lns filter

(* coding: utf-8 *)

(* -*- coding: utf-8 -*-
Module: Crypttab
  Parses /etc/crypttab from the cryptsetup package.

Author: Frédéric Lespez <frederic.lespez@free.fr>

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

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

About: Lens Usage
  Sample usage of this lens in augtool

    * Create a new entry for an encrypted block devices
      > ins 01 after /files/etc/crypttab/*[last()]
      > set /files/etc/crypttab/01/target crypt_sda2
      > set /files/etc/crypttab/01/device /dev/sda2
      > set /files/etc/crypttab/01/password /dev/random
      > set /files/etc/crypttab/01/opt swap
    * Print the entry applying to the "/dev/sda2" device
      > print /files/etc/crypttab/01
    * Remove the entry applying to the "/dev/sda2" device
      > rm /files/etc/crypttab/*[device="/dev/sda2"]

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

module Crypttab =
  autoload xfm

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

  (* Group: Separators *)

  (* Variable: sep_tab *)
  let sep_tab = Sep.tab

  (* Variable: comma *)
  let comma   = Sep.comma

  (* Group: Generic primitives *)

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

  (* Variable: comment *)
  let comment = Util.comment

  (* Variable: empty *)
  let empty   = Util.empty

  (* Variable: word *)
  let word    = Rx.word

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

  (* Variable: target *)
  let target  = Rx.device_name

  (* Variable: fspath *)
  let fspath  = Rx.fspath

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

  (************************************************************************
   * View: comma_sep_list
   *   A comma separated list of options (opt=value or opt)
   *************************************************************************)
  let comma_sep_list (l:string) =
    let value = [ label "value" . Util.del_str "=" . store optval ] in
      let lns = [ label l . store word . value? ] in
         Build.opt_list lns comma

  (************************************************************************
   * View: record
   *   A crypttab record
   *************************************************************************)

  let record = [ seq "entry" .
                   [ label "target" . store target ] . sep_tab .
                   [ label "device" . store fspath ] .
                   (sep_tab . [ label "password" . store fspath ] .
                    ( sep_tab . comma_sep_list "opt")? )?
                 . eol ]

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

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

  let xfm = transform lns filter


Update splunk.aug

Update splunk.aug

Updated augeas lens for Splunk to latest version from original author's github repo

(*
* Splunk module for Agueas
* Author: Tim Brigham
* Heavily based on inifile.aug by Raphael Pinson
* and mysql.aug by Tim Stoop.
* This file is licensed under the LGPLv2+, like the rest of Augeas.
*)

module Splunk =
autoload xfm

let comment  = IniFile.comment IniFile.comment_re IniFile.comment_default
let sep      = IniFile.sep IniFile.sep_re IniFile.sep_default
let empty    = IniFile.empty

let setting   = IniFile.entry_re
let title   =  IniFile.indented_title_label "target" IniFile.record_label_re
let entry    = [ key IniFile.entry_re . sep . IniFile.sto_to_comment . (comment|IniFile.eol) ] |
               [ key IniFile.entry_re . store // . (comment|IniFile.eol) ] |
               [ key /\![A-Za-z][A-Za-z0-9\._-]+/ . del / / " " . store /\/[A-Za-z0-9\.\/_-]+/ . (comment|IniFile.eol) ] |
               comment

let record    = IniFile.record title entry
let record_anon = [ label ".anon" . ( entry | empty )+ ]

let lns       = record_anon | record*

let filter    = incl "/opt/splunk/etc/system/local/*.conf"
              . incl "/opt/splunk/etc/apps/*/local/*.conf"
let xfm       = transform lns filter

(*
Module: Splunk
  Parses /opt/splunk/etc/*

Author: Tim Brigham

About: Reference
  http://docs.splunk.com/Documentation/Splunk/5.0.2/Admin/AboutConfigurationFiles

About: License
   This file is licenced under the LGPL v2+

About: Lens Usage
   Works like IniFile lens, with anonymous section for entries without enclosing section.

About: Configuration files
   This lens applies to conf files under /opt/splunk/etc See <filter>.

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

module Splunk =
  autoload xfm

  let comment   = IniFile.comment IniFile.comment_re IniFile.comment_default
  let sep       = IniFile.sep IniFile.sep_re IniFile.sep_default
  let empty     = IniFile.empty

  let setting   = IniFile.entry_re
  let title     =  IniFile.indented_title_label "target" IniFile.record_label_re
  let entry     = [ key IniFile.entry_re . sep . IniFile.sto_to_eol? . IniFile.eol ] | comment

  let record    = IniFile.record title entry
  let anon      = [ label ".anon" . (entry|empty)+ ]
  let lns       = anon . (record)* | (record)*

  let filter    = incl "/opt/splunk/etc/system/local/*.conf"
                . incl "/opt/splunk/etc/apps/*/local/*.conf"
  let xfm       = transform lns filter