Grub: simplify comment_re to speed up typecheck

module Grub =
  autoload xfm

    (* This only covers the most basic grub directives. Needs to be *)
    (* expanded to cover more (and more esoteric) directives        *)
    (* It is good enough to handle the grub.conf on my Fedora 8 box *)

    let value_to_eol = store /[^= \t\n][^\n]*[^= \t\n]|[^= \t\n]/
    let eol = Util.eol
    let del_to_eol = del /[^ \t\n]*/ ""
    let opt_ws = Util.del_opt_ws ""
    let value_sep (dflt:string) = del /[ \t]*[ \t=][ \t]*/ dflt

    let kw_arg (kw:string) (indent:string) (dflt_sep:string) =
      [ Util.del_opt_ws indent . key kw . value_sep dflt_sep
          . value_to_eol . eol ]

    let kw_boot_arg (kw:string) = kw_arg kw "\t" " "
    let kw_menu_arg (kw:string) = kw_arg kw "" "="
    let password_arg = [ key "password" .
      (Util.del_ws_spc . [ Util.del_str "--md5" . label "md5" ])? .
      Util.del_ws_spc . store (/[^ \t\n]+/ - "--md5") .
      (Util.del_ws_spc . [ label "file" . store /[^ \t\n]+/ ])? .
      eol ]

    let kw_pres (kw:string) = [ opt_ws . key kw . del_to_eol . eol ]

    let color =
      (* Should we nail it down to exactly the color names that *)
      (* grub supports ? *)
      let color_name = store /[A-Za-z-]+/ in
      let color_spec =
        [ label "foreground" . color_name] .
        Util.del_str "/" .
        [ label "background" . color_name ] in
      [ opt_ws . key "color" .
        Util.del_ws_spc . [ label "normal" . color_spec ] .
        (Util.del_ws_spc . [ label "highlight" . color_spec ])? .
        eol ]

    let menu_setting = kw_menu_arg "default"
                     | kw_menu_arg "fallback"
                     | kw_pres "hiddenmenu"
                     | kw_menu_arg "timeout"
                     | kw_menu_arg "splashimage"
                     | kw_menu_arg "serial"
                     | kw_menu_arg "terminal"
                     | password_arg
                     | color

    let title = del /title[ \t]+/ "title " . value_to_eol . eol

    let module_lines = [ label "modules" .
                            Util.del_ws "\t" .
                             Util.del_str "module" . Util.del_ws_spc
                             . value_to_eol . eol ]

    let boot_setting = kw_boot_arg "root"
                     | kw_boot_arg "kernel"
                     | kw_boot_arg "initrd"
                     | kw_boot_arg "rootnoverify"
                     | kw_boot_arg "chainloader"
                     | kw_boot_arg "uuid"
                     | kw_pres "quiet"  (* Seems to be a Ubuntu extension *)
                     | kw_pres "savedefault"
                     | module_lines

    let boot = [ label "title" . title . boot_setting* ]

    let comment_re = /([^ \t\n].*[^ \t\n]|[^ \t\n])/
                       - /# ## (Start|End) Default Options ##/

    let comment    =
        [ Util.indent . label "#comment" . del /#[ \t]*/ "# "
            . store comment_re . eol ]
    let empty   = Util.empty

    let debian_header  = "## ## Start Default Options ##\n"
    let debian_footer  = "## ## End Default Options ##\n"
    let debian_comment_re = /([^ \t\n].*[^ \t\n]|[^ \t\n])/
                            - "## End Default Options ##"
    let debian_comment =
        [ Util.indent . label "#comment" . del /##[ \t]*/ "## "
            . store debian_comment_re . eol ]
    let debian_value_sep = del /[ \t]*=/ "="
    let debian_kw_arg (kw:regexp) =
      [ Util.del_opt_ws "" . key kw . debian_value_sep
          . value_to_eol? . eol ]

    let debian_setting_re = "kopt"
                          | "groot"
                          | "alternative"
                          | "lockalternative"
                          | "defoptions"
                          | "lockold"
                          | "xenhopt"
                          | "xenkopt"
                          | "altoptions"
                          | "howmany"
                          | "memtest86"
                          | "updatedefaultentry"
                          | "savedefault"

    let debian_setting = debian_kw_arg debian_setting_re

    let debian_entry   = del "#" "#" . debian_setting
    let debian         = [ label "debian"
                    . del debian_header debian_header
                    . (debian_comment|empty|debian_entry)*
                    . del debian_footer debian_footer ]

    let lns = (comment | empty | menu_setting | boot | debian)*
    let xfm = transform lns (incl "/etc/grub.conf")

module Grub =
  autoload xfm

    (* This only covers the most basic grub directives. Needs to be *)
    (* expanded to cover more (and more esoteric) directives        *)
    (* It is good enough to handle the grub.conf on my Fedora 8 box *)

    let value_to_eol = store /[^= \t\n][^\n]*[^= \t\n]|[^= \t\n]/
    let eol = Util.eol
    let del_to_eol = del /[^ \t\n]*/ ""
    let opt_ws = Util.del_opt_ws ""
    let value_sep (dflt:string) = del /[ \t]*[ \t=][ \t]*/ dflt

    let kw_arg (kw:string) (indent:string) (dflt_sep:string) =
      [ Util.del_opt_ws indent . key kw . value_sep dflt_sep
          . value_to_eol . eol ]

    let kw_boot_arg (kw:string) = kw_arg kw "\t" " "
    let kw_menu_arg (kw:string) = kw_arg kw "" "="
    let password_arg = [ key "password" .
      (Util.del_ws_spc . [ Util.del_str "--md5" . label "md5" ])? .
      Util.del_ws_spc . store (/[^ \t\n]+/ - "--md5") .
      (Util.del_ws_spc . [ label "file" . store /[^ \t\n]+/ ])? .
      eol ]

    let kw_pres (kw:string) = [ opt_ws . key kw . del_to_eol . eol ]

    let color =
      (* Should we nail it down to exactly the color names that *)
      (* grub supports ? *)
      let color_name = store /[A-Za-z-]+/ in
      let color_spec =
        [ label "foreground" . color_name] .
        Util.del_str "/" .
        [ label "background" . color_name ] in
      [ opt_ws . key "color" .
        Util.del_ws_spc . [ label "normal" . color_spec ] .
        (Util.del_ws_spc . [ label "highlight" . color_spec ])? .
        eol ]

    let menu_setting = kw_menu_arg "default"
                     | kw_menu_arg "fallback"
                     | kw_pres "hiddenmenu"
                     | kw_menu_arg "timeout"
                     | kw_menu_arg "splashimage"
                     | kw_menu_arg "serial"
                     | kw_menu_arg "terminal"
                     | password_arg
                     | color

    let title = del /title[ \t]+/ "title " . value_to_eol . eol

    let module_lines = [ label "modules" .
                            Util.del_ws "\t" .
                             Util.del_str "module" . Util.del_ws_spc
                             . value_to_eol . eol ]

    let boot_setting = kw_boot_arg "root"
                     | kw_boot_arg "kernel"
                     | kw_boot_arg "initrd"
                     | kw_boot_arg "rootnoverify"
                     | kw_boot_arg "chainloader"
                     | kw_boot_arg "uuid"
                     | kw_pres "quiet"  (* Seems to be a Ubuntu extension *)
                     | kw_pres "savedefault"
                     | module_lines

    let boot = [ label "title" . title . boot_setting* ]

    let comment_re = /([^ \t\n].*[^ \t\n]|[^ \t\n])/
                       - "# ## Start Default Options ##"
                       - "# ## End Default Options ##"

    let comment    =
        [ Util.indent . label "#comment" . del /#[ \t]*/ "# "
            . store comment_re . eol ]
    let empty   = Util.empty

    let debian_header  = "## ## Start Default Options ##\n"
    let debian_footer  = "## ## End Default Options ##\n"
    let debian_comment_re = /([^ \t\n].*[^ \t\n]|[^ \t\n])/
                            - "## End Default Options ##"
    let debian_comment =
        [ Util.indent . label "#comment" . del /##[ \t]*/ "## "
            . store debian_comment_re . eol ]
    let debian_value_sep = del /[ \t]*=/ "="
    let debian_kw_arg (kw:regexp) =
      [ Util.del_opt_ws "" . key kw . debian_value_sep
          . value_to_eol? . eol ]

    let debian_setting_re = "kopt"
                          | "groot"
                          | "alternative"
                          | "lockalternative"
                          | "defoptions"
                          | "lockold"
                          | "xenhopt"
                          | "xenkopt"
                          | "altoptions"
                          | "howmany"
                          | "memtest86"
                          | "updatedefaultentry"
                          | "savedefault"

    let debian_setting = debian_kw_arg debian_setting_re

    let debian_entry   = del "#" "#" . debian_setting
    let debian         = [ label "debian"
                    . del debian_header debian_header
                    . (debian_comment|empty|debian_entry)*
                    . del debian_footer debian_footer ]

    let lns = (comment | empty | menu_setting | boot | debian)*
    let xfm = transform lns (incl "/etc/grub.conf")


Add a bunch of keywords available on squid-2.7-STABLE6 or squid-3.HEAD-20090308.

The keywords list was generated by the following command:

fgrep TAG squid-2.7.STABLE6/src/squid.conf.default  | awk '{ print "                | \""$3"\"" }' | sort > /tmp/squid2.7

fgrep TAG squid-3.HEAD-20090308/src/squid.conf.documented | awk '{ print "                | \""$3"\"" }' | sort > /tmp/squid3-HEAD

cat /tmp/squid2.7 /tmp/squid3-HEAD | sort | uniq

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

   Reference: the self-documented default squid.conf file

*)

module Squid =
  autoload xfm

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

let eol         = Util.eol
let spc         = Util.del_ws_spc

let word        =  /[A-Za-z0-9!_.-]+(\[[0-9]+\])?/
let sto_to_spc  = store /[^# \t\n]+/

let comment     = Spacevars.comment
let value (kw:string)
                = [ spc . label kw . sto_to_spc ]
let parameters  = [ label "parameters"
                   . counter "parameters"
                   . [ spc . seq "parameters" . sto_to_spc ]+ ]

(************************************************************************
 *                          SPACEVARS SETTINGS
 *************************************************************************)

let entry_re =    "accept_filter"
                | "access_log"
                | "acl_uses_indirect_client"
                | "adaptation_access"
                | "adaptation_service_set"
                | "allow_underscore"
                | "always_direct"
                | "announce_file"
                | "announce_host"
                | "announce_period"
                | "announce_port"
                | "append_domain"
                | "as_whois_server"
                | "authenticate_cache_garbage_interval"
                | "authenticate_ip_shortcircuit_access"
                | "authenticate_ip_shortcircuit_ttl"
                | "authenticate_ip_ttl"
                | "authenticate_ttl"
                | "background_ping_rate"
                | "balance_on_multiple_ip"
                | "broken_posts"
                | "broken_vary_encoding"
                | "buffered_logs"
                | "cache"
                | "cache_dir"
                | "cache_dns_program"
                | "cache_effective_group"
                | "cache_effective_user"
                | "cache_log"
                | "cache_mem"
                | "cache_mgr"
                | "cachemgr_passwd"
                | "cache_peer"
                | "cache_peer_access"
                | "cache_peer_domain"
                | "cache_replacement_policy"
                | "cache_store_log"
                | "cache_swap_high"
                | "cache_swap_low"
                | "cache_swap_state"
                | "cache_vary"
                | "check_hostnames"
                | "chroot"
                | "client_db"
                | "client_lifetime"
                | "client_netmask"
                | "client_persistent_connections"
                | "clientside_tos"
                | "collapsed_forwarding"
                | "connect_timeout"
                | "coredump_dir"
                | "dead_peer_timeout"
                | "debug_options"
                | "delay_access"
                | "delay_class"
                | "delay_initial_bucket_level"
                | "delay_parameters"
                | "delay_pools"
                | "delay_pool_uses_indirect_client"
                | "deny_info"
                | "detect_broken_pconn"
                | "digest_bits_per_entry"
                | "digest_generation"
                | "digest_rebuild_chunk_percentage"
                | "digest_rebuild_period"
                | "digest_rewrite_period"
                | "digest_swapout_chunk_size"
                | "diskd_program"
                | "dns_children"
                | "dns_defnames"
                | "dns_nameservers"
                | "dns_retransmit_interval"
                | "dns_testnames"
                | "dns_timeout"
                | "dns_v4_fallback"
                | "ecap_enable"
                | "ecap_service"
                | "email_err_data"
                | "emulate_httpd_log"
                | "err_html_text"
                | "error_default_language"
                | "error_directory"
                | "error_log_languages"
                | "error_map"
                | "err_page_stylesheet"
                | "esi_parser"
                | "extension_methods"
                | "external_acl_type"
                | "external_refresh_check"
                | "follow_x_forwarded_for"
                | "forwarded_for"
                | "forward_log"
                | "forward_timeout"
                | "fqdncache_size"
                | "ftp_epsv_all"
                | "ftp_list_width"
                | "ftp_passive"
                | "ftp_sanitycheck"
                | "ftp_telnet_protocol"
                | "ftp_user"
                | "global_internal_static"
                | "half_closed_clients"
                | "header_access"
                | "header_replace"
                | "hierarchy_stoplist"
                | "high_memory_warning"
                | "high_page_fault_warning"
                | "high_response_time_warning"
                | "hostname_aliases"
                | "hosts_file"
                | "htcp_access"
                | "htcp_clr_access"
                | "htcp_port"
                | "http_accel_surrogate_remote"
                | "http_access2"
                | "httpd_accel_no_pmtu_disc"
                | "httpd_accel_surrogate_id"
                | "httpd_suppress_version_string"
                | "http_port"
                | "http_reply_access"
                | "https_port"
                | "icap_access"
                | "icap_class"
                | "icap_client_username_encode"
                | "icap_client_username_header"
                | "icap_connect_timeout"
                | "icap_default_options_ttl"
                | "icap_enable"
                | "icap_io_timeout"
                | "icap_persistent_connections"
                | "icap_preview_enable"
                | "icap_preview_size"
                | "icap_send_client_ip"
                | "icap_send_client_username"
                | "icap_service"
                | "icap_service_failure_limit"
                | "icap_service_revival_delay"
                | "icon_directory"
                | "icp_access"
                | "icp_hit_stale"
                | "icp_port"
                | "icp_query_timeout"
                | "ident_lookup_access"
                | "ident_timeout"
                | "ie_refresh"
                | "ignore_expect_100"
                | "ignore_ims_on_miss"
                | "ignore_unknown_nameservers"
                | "incoming_dns_average"
                | "incoming_http_average"
                | "incoming_icp_average"
                | "incoming_rate"
                | "ipcache_high"
                | "ipcache_low"
                | "ipcache_size"
                | "loadable_modules"
                | "location_rewrite_access"
                | "location_rewrite_children"
                | "location_rewrite_concurrency"
                | "location_rewrite_program"
                | "log_access"
                | "logfile_daemon"
                | "logfile_rotate"
                | "logformat"
                | "log_fqdn"
                | "log_icp_queries"
                | "log_ip_on_direct"
                | "log_mime_hdrs"
                | "log_uses_indirect_client"
                | "mail_from"
                | "mail_program"
                | "max_filedescriptors"
                | "maximum_icp_query_timeout"
                | "maximum_object_size"
                | "maximum_object_size_in_memory"
                | "maximum_single_addr_tries"
                | "max_open_disk_fds"
                | "max_stale"
                | "mcast_groups"
                | "mcast_icp_query_timeout"
                | "mcast_miss_addr"
                | "mcast_miss_encode_key"
                | "mcast_miss_port"
                | "mcast_miss_ttl"
                | "memory_pools"
                | "memory_pools_limit"
                | "memory_replacement_policy"
                | "mime_table"
                | "min_dns_poll_cnt"
                | "min_http_poll_cnt"
                | "min_icp_poll_cnt"
                | "minimum_direct_hops"
                | "minimum_direct_rtt"
                | "minimum_expiry_time"
                | "minimum_icp_query_timeout"
                | "minimum_object_size"
                | "miss_access"
                | "negative_dns_ttl"
                | "negative_ttl"
                | "neighbor_type_domain"
                | "netdb_filename"
                | "netdb_high"
                | "netdb_low"
                | "netdb_ping_period"
                | "never_direct"
                | "nonhierarchical_direct"
                | "offline_mode"
                | "pconn_timeout"
                | "peer_connect_timeout"
                | "persistent_connection_after_error"
                | "persistent_request_timeout"
                | "pid_filename"
                | "pinger_enable"
                | "pinger_program"
                | "pipeline_prefetch"
                | "positive_dns_ttl"
                | "prefer_direct"
                | "qos_flows"
                | "query_icmp"
                | "quick_abort_max"
                | "quick_abort_min"
                | "quick_abort_pct"
                | "range_offset_limit"
                | "read_ahead_gap"
                | "read_timeout"
                | "redirector_bypass"
                | "referer_log"
                | "refresh_all_ims"
                | "refresh_pattern"
                | "refresh_stale_hit"
                | "relaxed_header_parser"
                | "reload_into_ims"
                | "reply_body_max_size"
                | "reply_header_access"
                | "reply_header_max_size"
                | "request_body_max_size"
                | "request_entities"
                | "request_header_access"
                | "request_header_max_size"
                | "request_timeout"
                | "retry_on_error"
                | "server_http11"
                | "server_persistent_connections"
                | "short_icon_urls"
                | "shutdown_lifetime"
                | "sleep_after_fork"
                | "snmp_access"
                | "snmp_incoming_address"
                | "snmp_outgoing_address"
                | "snmp_port"
                | "ssl_bump"
                | "ssl_engine"
                | "sslpassword_program"
                | "sslproxy_cafile"
                | "sslproxy_capath"
                | "sslproxy_cert_error"
                | "sslproxy_cipher"
                | "sslproxy_client_certificate"
                | "sslproxy_client_key"
                | "sslproxy_flags"
                | "sslproxy_options"
                | "sslproxy_version"
                | "ssl_unclean_shutdown"
                | "store_avg_object_size"
                | "store_dir_select_algorithm"
                | "store_objects_per_bucket"
                | "storeurl_access"
                | "storeurl_rewrite_children"
                | "storeurl_rewrite_concurrency"
                | "storeurl_rewrite_program"
                | "strip_query_terms"
                | "tcp_outgoing_address"
                | "tcp_outgoing_tos"
                | "tcp_recv_bufsize"
                | "test_reachability"
                | "udp_incoming_address"
                | "udp_outgoing_address"
                | "umask"
                | "unique_hostname"
                | "unlinkd_program"
                | "update_headers"
                | "upgrade_http0.9"
                | "uri_whitespace"
                | "url_rewrite_access"
                | "url_rewrite_bypass"
                | "url_rewrite_children"
                | "url_rewrite_concurrency"
                | "url_rewrite_host_header"
                | "url_rewrite_program"
                | "useragent_log"
                | "vary_ignore_expire"
                | "via"
                | "visible_hostname"
                | "wccp2_address"
                | "wccp2_assignment_method"
                | "wccp2_forwarding_method"
                | "wccp2_rebuild_wait"
                | "wccp2_return_method"
                | "wccp2_router"
                | "wccp2_service"
                | "wccp2_service_info"
                | "wccp2_weight"
                | "wccp_address"
                | "wccp_router"
                | "wccp_version"
                | "windows_ipaddrchangemonitor"
                | "zero_buffers"
                | "zph_local"
                | "zph_mode"
                | "zph_option"
                | "zph_parent"
                | "zph_sibling"
let entry       = Spacevars.entry entry_re

(************************************************************************
 *                                AUTH
 *************************************************************************)

let auth_re     = "auth_param"
let auth        = [ key "auth_param"
                  . value "scheme"
                  . value "parameter"
                  . (value "setting") ?
                  . (eol|comment) ]

(************************************************************************
 *                                ACL
 *************************************************************************)

let acl_re     = "acl"
let acl        = [ key acl_re . spc
                 . [ key word
                   . value "type"
                   . value "setting"
                   . parameters?
                   . (eol|comment) ] ]

(************************************************************************
 *                             HTTP ACCESS
 *************************************************************************)

let http_access_re
               = "http_access"
let http_access
               = [ key http_access_re
                 . spc
                 . [ key /allow|deny/
                   . spc
                   . sto_to_spc
                   . parameters? ]
                 . (eol|comment) ]

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

let lns         = Spacevars.lns (entry|auth|acl|http_access)

let filter      = Util.stdexcl
                . incl "/etc/squid/squid.conf"

let xfm         = transform lns filter

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

   Reference: the self-documented default squid.conf file

*)

module Squid =
  autoload xfm

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

let eol         = Util.eol
let spc         = Util.del_ws_spc

let word        =  /[A-Za-z0-9!_.-]+(\[[0-9]+\])?/
let sto_to_spc  = store /[^# \t\n]+/

let comment     = Spacevars.comment
let value (kw:string)
                = [ spc . label kw . sto_to_spc ]
let parameters  = [ label "parameters"
                   . counter "parameters"
                   . [ spc . seq "parameters" . sto_to_spc ]+ ]

(************************************************************************
 *                          SPACEVARS SETTINGS
 *************************************************************************)

let entry_re    = "authenticate_cache_garbage_interval"
                | "access_log"
                | "acl_uses_indirect_client"
                | "allow_underscore"
                | "always_direct"
                | "announce_file"
                | "announce_host"
                | "announce_period"
                | "announce_port"
                | "append_domain"
                | "as_whois_server"
                | "authenticate_ip_ttl"
                | "authenticate_ttl"
                | "balance_on_multiple_ip"
                | "broken_posts"
                | "broken_vary_encoding"
                | "buffered_logs"
                | "cache"
                | "cache_dir"
                | "cache_dns_program"
                | "cache_effective_group"
                | "cache_effective_user"
                | "cache_log"
                | "cache_mem"
                | "cache_mgr"
                | "cache_peer"
                | "cache_peer_access"
                | "cache_peer_domain"
                | "cache_replacement_policy"
                | "cache_store_log"
                | "cache_swap_high"
                | "cache_swap_low"
                | "cache_swap_state"
                | "cache_vary"
                | "cachemgr_passwd"
                | "check_hostnames"
                | "chroot"
                | "client_db"
                | "client_lifetime"
                | "client_netmask"
                | "client_persistent_connections"
                | "collapsed_forwarding"
                | "connect_timeout"
                | "coredump_dir"
                | "dead_peer_timeout"
                | "debug_options"
                | "delay_access"
                | "delay_class"
                | "delay_initial_bucket_level"
                | "delay_parameters"
                | "delay_pool_uses_indirect_client"
                | "delay_pools"
                | "deny_info"
                | "detect_broken_pconn"
                | "digest_bits_per_entry"
                | "digest_generation"
                | "digest_rebuild_chunk_percentage"
                | "digest_rebuild_period"
                | "digest_rewrite_period"
                | "digest_swapout_chunk_size"
                | "diskd_program"
                | "dns_children"
                | "dns_defnames"
                | "dns_nameservers"
                | "dns_retransmit_interval"
                | "dns_testnames"
                | "dns_timeout"
                | "emulate_httpd_log"
                | "err_html_text"
                | "error_directory"
                | "error_map"
                | "extension_methods"
                | "external_acl_type"
                | "follow_x_forwarded_for"
                | "forward_log"
                | "forward_timeout"
                | "forwarded_for"
                | "fqdncache_size"
                | "ftp_list_width"
                | "ftp_passive"
                | "ftp_sanitycheck"
                | "ftp_telnet_protocol"
                | "ftp_user"
                | "global_internal_static"
                | "half_closed_clients"
                | "header_access"
                | "header_replace"
                | "hierarchy_stoplist"
                | "high_memory_warning"
                | "high_page_fault_warning"
                | "high_response_time_warning"
                | "hostname_aliases"
                | "hosts_file"
                | "htcp_access"
                | "htcp_clr_access"
                | "htcp_port"
                | "http_access2"
                | "http_port"
                | "http_reply_access"
                | "httpd_accel_no_pmtu_disc"
                | "httpd_suppress_version_string"
                | "https_port"
                | "icon_directory"
                | "icp_access"
                | "icp_hit_stale"
                | "icp_port"
                | "icp_query_timeout"
                | "ident_lookup_access"
                | "ident_timeout"
                | "ie_refresh"
                | "ignore_unknown_nameservers"
                | "incoming_dns_average"
                | "incoming_http_average"
                | "incoming_icp_average"
                | "ipcache_high"
                | "ipcache_low"
                | "ipcache_size"
                | "location_rewrite_access"
                | "location_rewrite_children"
                | "location_rewrite_concurrency"
                | "location_rewrite_program"
                | "log_access"
                | "log_fqdn"
                | "log_icp_queries"
                | "log_ip_on_direct"
                | "log_mime_hdrs"
                | "log_uses_indirect_client"
                | "logfile_rotate"
                | "logformat"
                | "mail_from"
                | "mail_program"
                | "max_open_disk_fds"
                | "maximum_icp_query_timeout"
                | "maximum_object_size"
                | "maximum_object_size_in_memory"
                | "maximum_single_addr_tries"
                | "mcast_groups"
                | "mcast_icp_query_timeout"
                | "mcast_miss_addr"
                | "mcast_miss_encode_key"
                | "mcast_miss_port"
                | "mcast_miss_ttl"
                | "memory_pools"
                | "memory_pools_limit"
                | "memory_replacement_policy"
                | "mime_table"
                | "min_dns_poll_cnt"
                | "min_http_poll_cnt"
                | "min_icp_poll_cnt"
                | "minimum_direct_hops"
                | "minimum_direct_rtt"
                | "minimum_expiry_time"
                | "minimum_object_size"
                | "miss_access"
                | "negative_dns_ttl"
                | "negative_ttl"
                | "neighbor_type_domain"
                | "netdb_high"
                | "netdb_low"
                | "netdb_ping_period"
                | "never_direct"
                | "nonhierarchical_direct"
                | "offline_mode"
                | "pconn_timeout"
                | "peer_connect_timeout"
                | "persistent_connection_after_error"
                | "persistent_request_timeout"
                | "pid_filename"
                | "pinger_program"
                | "pipeline_prefetch"
                | "positive_dns_ttl"
                | "prefer_direct"
                | "query_icmp"
                | "quick_abort_max"
                | "quick_abort_min"
                | "quick_abort_pct"
                | "range_offset_limit"
                | "read_ahead_gap"
                | "read_timeout"
                | "redirector_bypass"
                | "referer_log"
                | "refresh_pattern"
                | "refresh_stale_hit"
                | "relaxed_header_parser"
                | "reload_into_ims"
                | "reply_body_max_size"
                | "reply_header_max_size"
                | "request_body_max_size"
                | "request_entities"
                | "request_header_max_size"
                | "request_timeout"
                | "retry_on_error"
                | "server_persistent_connections"
                | "short_icon_urls"
                | "shutdown_lifetime"
                | "sleep_after_fork"
                | "snmp_access"
                | "snmp_incoming_address"
                | "snmp_outgoing_address"
                | "snmp_port"
                | "ssl_engine"
                | "ssl_unclean_shutdown"
                | "sslpassword_program"
                | "sslproxy_cafile"
                | "sslproxy_capath"
                | "sslproxy_cipher"
                | "sslproxy_client_certificate"
                | "sslproxy_client_key"
                | "sslproxy_flags"
                | "sslproxy_options"
                | "sslproxy_version"
                | "store_avg_object_size"
                | "store_dir_select_algorithm"
                | "store_objects_per_bucket"
                | "strip_query_terms"
                | "tcp_outgoing_address"
                | "tcp_outgoing_tos"
                | "tcp_recv_bufsize"
                | "test_reachability"
                | "udp_incoming_address"
                | "udp_outgoing_address"
                | "umask"
                | "unique_hostname"
                | "unlinkd_program"
                | "uri_whitespace"
                | "url_rewrite_access"
                | "url_rewrite_children"
                | "url_rewrite_concurrency"
                | "url_rewrite_host_header"
                | "url_rewrite_program"
                | "useragent_log"
                | "vary_ignore_expire"
                | "via"
                | "visible_hostname"
                | "wccp2_address"
                | "wccp2_assignment_method"
                | "wccp2_forwarding_method"
                | "wccp2_rebuild_wait"
                | "wccp2_return_method"
                | "wccp2_router"
                | "wccp2_service"
                | "wccp2_service_info"
                | "wccp2_weight"
                | "wccp_address"
                | "wccp_router"
                | "wccp_version"

let entry       = Spacevars.entry entry_re

(************************************************************************
 *                                AUTH
 *************************************************************************)

let auth_re     = "auth_param"
let auth        = [ key "auth_param"
                  . value "scheme"
                  . value "parameter"
                  . (value "setting") ?
                  . (eol|comment) ]

(************************************************************************
 *                                ACL
 *************************************************************************)

let acl_re     = "acl"
let acl        = [ key acl_re . spc
                 . [ key word
                   . value "type"
                   . value "setting"
                   . parameters?
                   . (eol|comment) ] ]

(************************************************************************
 *                             HTTP ACCESS
 *************************************************************************)

let http_access_re
               = "http_access"
let http_access
               = [ key http_access_re
                 . spc
                 . [ key /allow|deny/
                   . spc
                   . sto_to_spc
                   . parameters? ]
                 . (eol|comment) ]

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

let lns         = Spacevars.lns (entry|auth|acl|http_access)

let filter      = Util.stdexcl
                . incl "/etc/squid/squid.conf"

let xfm         = transform lns filter


backport fix in aa2763, remove after 0.5.1 release

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

module Logrotate =
   autoload xfm

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

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

   (* Useful functions *)

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

   (* Defaults *)

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

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

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

   (* Define hooks *)

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

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

   (* Define rule *)

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

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

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

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

   let xfm = transform lns filter

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


A (pretty kludgy) description for cmfm.conf

# A complicated format: cmfm.conf

map
  grammar cmfm
  include '/etc/cmfm.conf' '/system/config/cmfm'
end

grammar cmfm
  # Matches whitespace and comments
  token CWS     /([ \n\t]+|\/\*([^\*]|\*[^\/])*\*\/)*/ = ''

  token WORD    /[a-zA-Z_0-9]+/ = ''

  # Boomerang definition:
  # let pci_elt = "(" . word . ("," . cws_re . word){3} . ")"
  # token PCI_ELT /\(WORD(,CWS WORD){3}\)/
  # let pci_array = "(" . cws_re . pci_elt . ("," . cws_re . pci_elt)* . cws_re . ")"
  # token PCI_ARRAY /\(CWS PCI_ELT (,CWS PCI_ELT)* CWS\)/
  # let value = (word | "\"" . [^"]* . "\"" | pci_array )

  # the above manually expanded and turned into a POSIX regexp since
  # augeas can't concat regexps:
  token VALUE   /([a-zA-Z_0-9]+|"[^"]*"|\(([ \n\t]+|\/\*([^\*]|\*[^\/])*\*\/)*\([a-zA-Z_0-9]+(,([ \n\t]+|\/\*([^\*]|\*[^\/])*\*\/)*[a-zA-Z_0-9]+){3}\)(,([ \n\t]+|\/\*([^\*]|\*[^\/])*\*\/)*\([a-zA-Z_0-9]+(,([ \n\t]+|\/\*([^\*]|\*[^\/])*\*\/)*[a-zA-Z_0-9]+){3}\))*([ \n\t]+|\/\*([^\*]|\*[^\/])*\*\/)*\))/ = '""'

  file: CWS . (entry3)*

  assignment: [ key WORD . CWS . '=' . CWS . store VALUE . ';' . CWS ]

  # Approximate the nesting of sections up to a max of 3
  entry1: assignment | sec1
  sec1: [label '{1}' . [ key WORD . ':' . CWS . '{' . CWS . (assignment)* . '}' . ';' . CWS] ]

  entry2: assignment | sec2
  sec2: [label '{2}' . [ key WORD . ':' . CWS . '{' . CWS . (entry1)* . '}' . ';' . CWS] ]

  entry3: assignment | sec3
  sec3: [ label '{3}' . [ key WORD . ':' . CWS . '{' . CWS . (entry2)* . '}' . ';' . CWS] ]
end

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


backport fix 1a6042, remove after 0.5.1 release

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

module PHP =
  autoload xfm

(************************************************************************
 * INI File settings
 *************************************************************************)

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

(************************************************************************
 *                        ENTRY
 *
 * We have to remove the keyword "section" from possible entry keywords
 * otherwise it would lead to an ambiguity with the "section" label
 * since PHP allows entries outside of sections.
 *************************************************************************)
let entry    = IniFile.entry IniFile.entry_re sep comment

(************************************************************************
 *                         TITLE
 *
 * We use IniFile.title_label because there can be entries
 * outside of sections whose labels would conflict with section names
 *************************************************************************)
let title       = IniFile.title ( IniFile.record_re - ".anon" )
let record      = IniFile.record title entry

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

(************************************************************************
 *                         LENS & FILTER
 * There can be entries before any section
 * IniFile.entry includes comment management, so we just pass entry to lns
 *************************************************************************)
let lns    = record_anon? . record*

let filter = (incl "/etc/php*/*/*.ini")
             . (incl "/etc/php.ini")
             . (incl "/etc/php.d/*.ini")
             . Util.stdexcl

let xfm = transform lns filter

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


Adapt sudoers.aug for NaturalDocs parsing

(*
Module: Sudoers
  Parses /etc/sudoers

Author: Raphael Pinson <raphink@gmail.com>

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

For example, recursive definitions such as

     > Cmnd_Spec_List ::= Cmnd_Spec |
     >                    Cmnd_Spec ',' Cmnd_Spec_List

are replaced by

  >   let cmnd_spec_list = cmnd_spec . ( sep_com . cmnd_spec )*

since Augeas cannot deal with recursive definitions.
The definitions from `man sudoers` are put as commentaries for reference
throughout the file. More information can be found in the manual.

About: License
  This file is licensed under the GPL.

About: Lens Usage
  Sample usage of this lens in augtool

    * Set first Defaults to apply to the "LOCALNET" network alias
      > set /files/etc/sudoers/Defaults[1]/type "@LOCALNET"
    * List all user specifications applying explicitely to the "admin" Unix group
      > match /files/etc/sudoers/spec/user "%admin"
    * Remove the full 3rd user specification
      > rm /files/etc/sudoers/spec[3]

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

module Sudoers =
  autoload xfm

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

(* Group: Generic primitives *)
(* Variable: eol *)
let eol       = del /[ \t]*\n/ "\n"

(* Variable: indent *)
let indent    = del /[ \t]*/ ""

(* Group: Separators *)

(* Variable: sep_spc *)
let sep_spc  = del /[ \t]+/ " "

(* Variable: sep_cont *)
let sep_cont = del /([ \t]+|[ \t]*\\\\\n[ \t]*)/ " "

(* Variable: sep_conf_opt *)
let sep_cont_opt = del /([ \t]*|[ \t]*\\\\\n[ \t]*)/ " "

(* Variable: sep_com *)
let sep_com  = sep_cont_opt . Util.del_str "," . sep_cont_opt

(* Variable: sep_eq *)
let sep_eq   = sep_cont_opt . Util.del_str "=" . sep_cont_opt

(* Variable: sep_col *)
let sep_col  = sep_cont_opt . Util.del_str ":" . sep_cont_opt

(* Group: Stores *)

(* Variable: sto_to_com_cmnd
sto_to_com_cmnd does not begin or end with a space

TODO: there could be a \ in the middle of a command... *)
let sto_to_com_cmnd = store /([^,=:#() \t\n\\\\][^,=:#()\n\\\\]*[^,=:#() \t\n\\\\])|[^,=:#() \t\n\\\\]/

(* Variable: sto_to_com *)
let sto_to_com      = store /[^,=:#() \t\n\\\\]+/

(* Variable: sto_to_com_user *)
let sto_to_com_user = store ( /[^,=:#() \t\n]+/
                              - /(User|Runas|Host|Cmnd)_Alias|Defaults.*/ )

(* Variable: sto_to_eq *)
let sto_to_eq  = store /[^,=:#() \t\n\\\\]+/

(* Variable: sto_to_spc *)
let sto_to_spc = store /[^() \t\n\\\\]+/

(* Group: Comments and empty lines *)

(* View: comment
Map comments in "#comment" nodes *)
let comment =
  let sto_to_eol = store /([^ \t\n].*[^ \t\n]|[^ \t\n])/ in
  [ label "comment" . del /[ \t]*#[ \t]*/ "# " . sto_to_eol . eol ]

(* View: empty
Map empty lines *)
let empty   = [ del /[ \t]*#?[ \t]*\n/ "\n" ]

(************************************************************************
 * Group:                                    ALIASES
 *************************************************************************)

(************************************************************************
 * View: alias_field
 *   Generic alias field to gather all Alias definitions
 *
 *   Definition:
 *     > User_Alias ::= NAME '=' User_List
 *     > Runas_Alias ::= NAME '=' Runas_List
 *     > Host_Alias ::= NAME '=' Host_List
 *     > Cmnd_Alias ::= NAME '=' Cmnd_List
 *
 *   Parameters:
 *     kw:string - the label string
 *     sto:lens  - the store lens
 *************************************************************************)
let alias_field (kw:string) (sto:lens) = [ label kw . sto ]

(* View: alias_list
     List of <alias_fields>, separated by commas *)
let alias_list  (kw:string) (sto:lens) =
  alias_field kw sto . ( sep_com . alias_field kw sto )*

(************************************************************************
 * View: alias_name
 *   Name of an <alias_entry_single>
 *
 *   Definition:
 *     > NAME ::= [A-Z]([A-Z][0-9]_)*
 *************************************************************************)
let alias_name
    = [ label "name" . store /[A-Z][A-Z0-9_]*/ ]

(************************************************************************
 * View: alias_entry_single
 *   Single <alias_entry>, named using <alias_name> and listing <alias_list>
 *
 *   Definition:
 *     > Alias_Type NAME = item1, item2, ...
 *
 *   Parameters:
 *     field:string - the field name, passed to <alias_list>
 *     sto:lens     - the store lens, passed to <alias_list>
 *************************************************************************)
let alias_entry_single (field:string) (sto:lens)
    = [ label "alias" . alias_name . sep_eq . alias_list field sto ]

(************************************************************************
 * View: alias_entry
 *   Alias entry, a list of comma-separated <alias_entry_single> fields
 *
 *   Definition:
 *     > Alias_Type NAME = item1, item2, item3 : NAME = item4, item5
 *
 *   Parameters:
 *     kw:string    - the alias keyword string
 *     field:string - the field name, passed to <alias_entry_single>
 *     sto:lens     - the store lens, passed to <alias_entry_single>
 *************************************************************************)
let alias_entry (kw:string) (field:string) (sto:lens)
    = [ indent . key kw . sep_cont . alias_entry_single field sto
          . ( sep_col . alias_entry_single field sto )* . eol ]

(* TODO: go further in user definitions *)
(* View: user_alias
     User_Alias, see <alias_field> *)
let user_alias  = alias_entry "User_Alias" "user" sto_to_com
(* View: runas_alias
     Run_Alias, see <alias_field> *)
let runas_alias = alias_entry "Runas_Alias" "runas_user" sto_to_com
(* View: host_alias
     Host_Alias, see <alias_field> *)
let host_alias  = alias_entry "Host_Alias" "host" sto_to_com
(* View: cmnd_alias
     Cmnd_Alias, see <alias_field> *)
let cmnd_alias  = alias_entry "Cmnd_Alias" "command" sto_to_com_cmnd

(************************************************************************
 * View: alias
 *   Every kind of Alias entry,
 *     see <user_alias>, <runas_alias>, <host_alias> and <cmnd_alias>
 *
 *   Definition:
 *     > Alias ::= 'User_Alias'  User_Alias (':' User_Alias)* |
 *     >           'Runas_Alias' Runas_Alias (':' Runas_Alias)* |
 *     >           'Host_Alias'  Host_Alias (':' Host_Alias)* |
 *     >           'Cmnd_Alias'  Cmnd_Alias (':' Cmnd_Alias)*
 *************************************************************************)
let alias = user_alias | runas_alias | host_alias | cmnd_alias
(************************************************************************
 * Group:                          DEFAULTS
 *************************************************************************)

(************************************************************************
 * View: default_type
 *   Type definition for <defaults>
 *
 *   Definition:
 *     > Default_Type ::= 'Defaults' |
 *     >                  'Defaults' '@' Host_List |
 *     >                  'Defaults' ':' User_List |
 *     >                  'Defaults' '>' Runas_List
 *************************************************************************)
let default_type     =
  let value = store /[@:>][^ \t\n\\\\]+/ in
  [ label "type" . value ]

(************************************************************************
 * View: parameter
 *   A single parameter for <defaults>
 *
 *   Definition:
 *     > Parameter ::= Parameter '=' Value |
 *     >               Parameter '+=' Value |
 *     >               Parameter '-=' Value |
 *     >               '!'* Parameter
 *************************************************************************)
let parameter        =
  let value = /([^,:= \t\n\\\\][^,\n\\\\]*[^, \t\n\\\\])|[^,:= \t\n\\\\]/ in
  [ label "parameter" . store value ]

(************************************************************************
 * View: paramater_list
 *   A list of comma-separated <parameters> for <defaults>
 *
 *   Definition:
 *     > Parameter_List ::= Parameter |
 *     >                    Parameter ',' Parameter_List
 *************************************************************************)
let parameter_list   = parameter . ( sep_com . parameter )*

(************************************************************************
 * View: defaults
 *   A Defaults entry
 *
 *   Definition:
 *     > Default_Entry ::= Default_Type Parameter_List
 *************************************************************************)
let defaults = [ indent . key "Defaults" . default_type? . sep_cont
                   . parameter_list . eol ]

(************************************************************************
 * Group:                     USER SPECIFICATION
 *************************************************************************)

(************************************************************************
 * View: runas_spec
 *   A runas specification for <spec>, using <alias_list>
 *
 *   Definition:
 *     > Runas_Spec ::= '(' Runas_List ')'
 *************************************************************************)
let runas_spec = Util.del_str "(" . alias_list "runas_user" sto_to_com
    . Util.del_str ")" . sep_cont_opt

(************************************************************************
 * View: tag_spec
 *   Tag specification for <spec>
 *
 *   Definition:
 *     > Tag_Spec ::= ('NOPASSWD:' | 'PASSWD:' | 'NOEXEC:' | 'EXEC:' |
 *     >              'SETENV:' | 'NOSETENV:')
 *************************************************************************)
let tag_spec   =
  [ label "tag" . store /(NO)?(PASSWD|EXEC|SETENV)/ . sep_col ]

(************************************************************************
 * View: cmnd_spec
 *   Command specification for <spec>,
 *     with optional <runas_spec> and any amount of <tag_specs>
 *
 *   Definition:
 *     > Cmnd_Spec ::= Runas_Spec? Tag_Spec* Cmnd
 *************************************************************************)
let cmnd_spec  =
  [ label "command" .  runas_spec? . tag_spec* . sto_to_com_cmnd ]

(************************************************************************
 * View: cmnd_spec_list
 *   A list of comma-separated <cmnd_specs>
 *
 *   Definition:
 *     > Cmnd_Spec_List ::= Cmnd_Spec |
 *     >                    Cmnd_Spec ',' Cmnd_Spec_List
 *************************************************************************)
let cmnd_spec_list = cmnd_spec . ( sep_com . cmnd_spec )*

(************************************************************************
 * View: spec_list
 *   Group of hosts with <cmnd_spec_list>
 *************************************************************************)
let spec_list = [ label "host_group" . alias_list "host" sto_to_com
                    . sep_eq . cmnd_spec_list ]

(************************************************************************
 * View: spec
 *   A user specification, listing colon-separated <spec_lists>
 *
 *   Definition:
 *     > User_Spec ::= User_List Host_List '=' Cmnd_Spec_List \
 *     >               (':' Host_List '=' Cmnd_Spec_List)*
 *************************************************************************)
let spec = [ label "spec" . indent
               . alias_list "user" sto_to_com_user . sep_cont
               . spec_list
               . ( sep_col . spec_list )* . eol ]

(************************************************************************
 * Group:                        LENS & FILTER
 *************************************************************************)

(* View: lns
     The sudoers lens, any amount of
       * <empty> lines
       * <comments>
       * <aliases>
       * <defaults>
       * <specs>
*)
let lns = ( empty | comment | alias | defaults | spec  )*

(* Variable: filter *)
let filter = (incl "/etc/sudoers")
    . Util.stdexcl

let xfm = transform lns filter

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

 Reference: `man sudoers`
 This lens tries to keep as close as possible to `man sudoers` where possible.
 For example, recursive definitions such as:

     Cmnd_Spec_List ::= Cmnd_Spec |
                        Cmnd_Spec ',' Cmnd_Spec_List

 are replaced by

     let cmnd_spec_list = cmnd_spec . ( sep_com . cmnd_spec )*

 since Augeas cannot deal with recursive definitions.
 The definitions from `man sudoers` are put as commentaries for reference
 throughout the file. More information can be found in the manual.
*)

module Sudoers =
  autoload xfm

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

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

(* Define separators *)
let sep_spc  = del /[ \t]+/ " "
let sep_cont = del /([ \t]+|[ \t]*\\\\\n[ \t]*)/ " "
let sep_cont_opt = del /([ \t]*|[ \t]*\\\\\n[ \t]*)/ " "
let sep_com  = sep_cont_opt . Util.del_str "," . sep_cont_opt
let sep_eq   = sep_cont_opt . Util.del_str "=" . sep_cont_opt
let sep_col  = sep_cont_opt . Util.del_str ":" . sep_cont_opt

(* Define fields *)
let sto_to_com_cmnd = store /([^,=:#() \t\n\\\\][^,=:#()\n\\\\]*[^,=:#() \t\n\\\\])|[^,=:#() \t\n\\\\]/
let sto_to_com      = store /[^,=:#() \t\n\\\\]+/

(* sto_to_com does not begin or end with a space *)
(* TODO: there could be a \ in the middle of a command... *)
let sto_to_com_user = store ( /[^,=:#() \t\n]+/
                              - /(User|Runas|Host|Cmnd)_Alias|Defaults.*/ )
let sto_to_eq  = store /[^,=:#() \t\n\\\\]+/
let sto_to_spc = store /[^() \t\n\\\\]+/

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

let empty   = [ del /[ \t]*#?[ \t]*\n/ "\n" ]

(************************************************************************
 *                                     ALIASES
 *************************************************************************)

(************************************************************************
 *   User_Alias ::= NAME '=' User_List
 *   Runas_Alias ::= NAME '=' Runas_List
 *   Host_Alias ::= NAME '=' Host_List
 *   Cmnd_Alias ::= NAME '=' Cmnd_List
 *************************************************************************)
let alias_field (kw:string) (sto:lens) = [ label kw . sto ]
let alias_list  (kw:string) (sto:lens) =
  alias_field kw sto . ( sep_com . alias_field kw sto )*

(************************************************************************
 *  NAME ::= [A-Z]([A-Z][0-9]_)*
 *************************************************************************)
let alias_name
    = [ label "name" . store /[A-Z][A-Z0-9_]*/ ]

(************************************************************************
 *  Alias_Type NAME = item1, item2, ...
 *************************************************************************)
let alias_entry_single (field:string) (sto:lens)
    = [ label "alias" . alias_name . sep_eq . alias_list field sto ]

(************************************************************************
 *  Alias_Type NAME = item1, item2, item3 : NAME = item4, item5
 *************************************************************************)
let alias_entry (kw:string) (field:string) (sto:lens)
    = [ indent . key kw . sep_cont . alias_entry_single field sto
          . ( sep_col . alias_entry_single field sto )* . eol ]

(* TODO: go further in user definitions *)
let user_alias  = alias_entry "User_Alias" "user" sto_to_com
let runas_alias = alias_entry "Runas_Alias" "runas_user" sto_to_com
let host_alias  = alias_entry "Host_Alias" "host" sto_to_com
let cmnd_alias  = alias_entry "Cmnd_Alias" "command" sto_to_com_cmnd

(************************************************************************
 *  Alias ::= 'User_Alias'  User_Alias (':' User_Alias)* |
 *            'Runas_Alias' Runas_Alias (':' Runas_Alias)* |
 *            'Host_Alias'  Host_Alias (':' Host_Alias)* |
 *            'Cmnd_Alias'  Cmnd_Alias (':' Cmnd_Alias)*
 *************************************************************************)
let alias = user_alias | runas_alias | host_alias | cmnd_alias

(************************************************************************
 *                                 DEFAULTS
 *************************************************************************)

(************************************************************************
 *  Default_Type ::= 'Defaults' |
 *                   'Defaults' '@' Host_List |
 *                   'Defaults' ':' User_List |
 *                   'Defaults' '>' Runas_List
 *************************************************************************)
let default_type     =
  let value = store /[@:>][^ \t\n\\\\]+/ in
  [ label "type" . value ]

(************************************************************************
 *  Parameter ::= Parameter '=' Value |
 *                Parameter '+=' Value |
 *                Parameter '-=' Value |
 *                '!'* Parameter
 *************************************************************************)
let parameter        =
  let value = /([^,:= \t\n\\\\][^,\n\\\\]*[^, \t\n\\\\])|[^,:= \t\n\\\\]/ in
  [ label "parameter" . store value ]

(************************************************************************
 *  Parameter_List ::= Parameter |
 *                     Parameter ',' Parameter_List
 *************************************************************************)
let parameter_list   = parameter . ( sep_com . parameter )*

(************************************************************************
 *  Default_Entry ::= Default_Type Parameter_List
 *************************************************************************)
let defaults = [ indent . key "Defaults" . default_type? . sep_cont
                   . parameter_list . eol ]

(************************************************************************
 *                              USER SPECIFICATION
 *************************************************************************)

(************************************************************************
 *  Runas_Spec ::= '(' Runas_List ')'
 *************************************************************************)
let runas_spec = Util.del_str "(" . alias_list "runas_user" sto_to_com
    . Util.del_str ")" . sep_cont_opt

(************************************************************************
 * Tag_Spec ::= ('NOPASSWD:' | 'PASSWD:' | 'NOEXEC:' | 'EXEC:' |
 *              'SETENV:' | 'NOSETENV:')
 *************************************************************************)
let tag_spec   =
  [ label "tag" . store /(NO)?(PASSWD|EXEC|SETENV)/ . sep_col ]

(************************************************************************
 *  Cmnd_Spec ::= Runas_Spec? Tag_Spec* Cmnd
 *************************************************************************)
let cmnd_spec  =
  [ label "command" .  runas_spec? . tag_spec* . sto_to_com_cmnd ]

(************************************************************************
 * Cmnd_Spec_List ::= Cmnd_Spec |
 *                    Cmnd_Spec ',' Cmnd_Spec_List
 *************************************************************************)
let cmnd_spec_list = cmnd_spec . ( sep_com . cmnd_spec )*

(************************************************************************
 * User_Spec ::= User_List Host_List '=' Cmnd_Spec_List \
 *               (':' Host_List '=' Cmnd_Spec_List)*
 *************************************************************************)
let spec_list = [ label "host_group" . alias_list "host" sto_to_com
                    . sep_eq . cmnd_spec_list ]

let spec = [ label "spec" . indent
               . alias_list "user" sto_to_com_user . sep_cont
               . spec_list
    	       . ( sep_col . spec_list )* . eol ]

(************************************************************************
 *                              LENS & FILTER
 *************************************************************************)

let lns = ( empty | comment | alias | defaults | spec  )*

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

let xfm = transform lns filter


Support \r in httpd.aug

Support \r in httpd.aug

(* Apache HTTPD lens for Augeas

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

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

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

About: Lens Usage
  Sample usage of this lens in augtool

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

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

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

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

*)

module Httpd =

autoload xfm

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

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

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

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

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

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

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

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

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

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

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

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

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

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

let xfm = transform lns filter

(* Apache HTTPD lens for Augeas

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

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

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

About: Lens Usage
  Sample usage of this lens in augtool

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

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

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

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

*)

module Httpd =

autoload xfm

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

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

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

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

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

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

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

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

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

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

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

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

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

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

let xfm = transform lns filter


Util: add eol, indent, comment and empty

(* Simple utilities used in several places *)
module Util =
  let del_str (s:string) = del s s

  let del_ws = del /[ \t]+/
  let del_ws_spc = del_ws " "
  let del_ws_tab = del_ws "\t"

  let del_opt_ws = del /[ \t]*/

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

  (* comment and empty
     This is a general definition of comment and empty.
     It allows indentation for comments, removes the leading and trailing spaces
     of comments and stores them in nodes, except for empty comments which are
     ignored together with empty lines
  *)
  let comment =
    [ indent . label "comment" . del /#[ \t]*/ "# "
        . store /([^ \t\n].*[^ \t\n]|[^ \t\n])/ . eol ]

  let empty   = [ del /[ \t]*#?[ \t]*\n/ "" ]

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

  (* Exclusion for files that are commonly not wanted/needed              *)
  let stdexcl = (excl "*~") . (excl "*.rpmnew") . (excl "*.rpmsave")

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

(* Simple utilities used in several places *)
module Util =
  let del_str (s:string) = del s s

  let del_ws = del /[ \t]+/
  let del_ws_spc = del_ws " "
  let del_ws_tab = del_ws "\t"

  let del_opt_ws = del /[ \t]*/

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

  (* Exclusion for files that are commonly not wanted/needed              *)
  let stdexcl = (excl "*~") . (excl "*.rpmnew") . (excl "*.rpmsave")

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