diff --git a/.augeas.metadata b/.augeas.metadata
new file mode 100644
index 0000000..066252a
--- /dev/null
+++ b/.augeas.metadata
@@ -0,0 +1 @@
+27b563bb173eed6163d76b5b21f3a1566c82351b SOURCES/augeas-1.4.0.tar.gz
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..eeed9c4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+SOURCES/augeas-1.4.0.tar.gz
diff --git a/SOURCES/0001-Syslog-restored-Augeas-1.1.0-tree-compatibility-for-.patch b/SOURCES/0001-Syslog-restored-Augeas-1.1.0-tree-compatibility-for-.patch
new file mode 100644
index 0000000..9a5e106
--- /dev/null
+++ b/SOURCES/0001-Syslog-restored-Augeas-1.1.0-tree-compatibility-for-.patch
@@ -0,0 +1,101 @@
+From 706266780350ba61d36ec387dc18e9f4764ed872 Mon Sep 17 00:00:00 2001
+From: Yanis Guenane <yguenane@gmail.com>
+Date: Sun, 8 Sep 2013 09:30:17 -0400
+Subject: [PATCH] Syslog: restored Augeas 1.1.0 tree compatibility for UDP
+ hosts
+
+UDP host entries don't require the { "protocol" = "@" } node, it's only
+used for TCP hosts.
+---
+ lenses/syslog.aug             | 10 +++++++---
+ lenses/tests/test_rsyslog.aug |  1 -
+ lenses/tests/test_syslog.aug  |  7 +++----
+ 3 files changed, 10 insertions(+), 8 deletions(-)
+
+diff --git a/lenses/syslog.aug b/lenses/syslog.aug
+index f8b99c40..17c0f903 100644
+--- a/lenses/syslog.aug
++++ b/lenses/syslog.aug
+@@ -66,6 +66,10 @@ module Syslog =
+ 	 Deletes a semicolon and default to it
+ 	 *)
+ 	let semicolon  = sep_tab_opt . Util.del_str ";" . sep_tab_opt
++	(* Variable: at
++	 Deletes a at and default to it
++	 *)
++	let at         = Util.del_str "@"
+ 	(* Variable: dot
+ 	 Deletes a dot and default to it
+ 	 *)
+@@ -105,10 +109,10 @@ module Syslog =
+         let comparison = /(!|[<=>]+|![<=>]+)/
+ 
+ 	(* Variable: protocol
+-	  @ means UDP
++	  @ means UDP (removed, no protocol node is used for compatibility with 1.1.0)
+     @@ means TCP
+ 	  *)
+-        let protocol      = /@{1,2}/
++        let protocol      = /@@/
+ 
+ 	(* Variable: token
+ 	  alphanum or "*"
+@@ -185,7 +189,7 @@ module Syslog =
+ 	(* View: loghost
+ 	 a loghost is an @  sign followed by the hostname and a possible port
+ 	 *)
+-	let loghost = [label "protocol" . store protocol] . [ label "hostname" . store loghost_r ] .
++	let loghost = ([label "protocol" . store protocol] | at) . [ label "hostname" . store loghost_r ] .
+ 	    (colon . [ label "port" . store /[0-9]+/ ] )?
+ 
+ 	(* View: users
+diff --git a/lenses/tests/test_rsyslog.aug b/lenses/tests/test_rsyslog.aug
+index e696139c..46b24c3c 100644
+--- a/lenses/tests/test_rsyslog.aug
++++ b/lenses/tests/test_rsyslog.aug
+@@ -86,7 +86,6 @@ test Rsyslog.lns get conf =
+       { "level" = "*" }
+     }
+     { "action"
+-      { "protocol" = "@" }
+       { "hostname" = "2.7.4.1" }
+     }
+   }
+diff --git a/lenses/tests/test_syslog.aug b/lenses/tests/test_syslog.aug
+index 2ad1c165..329d121e 100644
+--- a/lenses/tests/test_syslog.aug
++++ b/lenses/tests/test_syslog.aug
+@@ -91,11 +91,11 @@ daemon.info                                     /var/log/cvsupd.log
+ 	  }
+ 	  { "entry"
+ 	    { "selector" { "facility" = "*" } { "level" = "*" } }
+-	    { "action" { "protocol" = "@" } { "hostname" = "syslog.far.away" } }
++	    { "action" { "hostname" = "syslog.far.away" } }
+ 	  }
+ 	  { "entry"
+ 	    { "selector" { "facility" = "*" } { "level" = "*" } }
+-	    { "action" { "protocol" = "@" } { "hostname" = "syslog.far.away" } { "port" = "123" } }
++	    { "action" { "hostname" = "syslog.far.away" } { "port" = "123" } }
+ 	  }
+ 	  { "entry"
+ 	    { "selector" { "facility" = "*" } { "level" = "*" } }
+@@ -107,7 +107,7 @@ daemon.info                                     /var/log/cvsupd.log
+ 	  }
+ 	  { "entry"
+ 	    { "selector" { "facility" = "*" } { "level" = "*" } }
+-	    { "action" { "protocol" = "@" } { "hostname" = "[2001::1]" } { "port" = "514" } }
++	    { "action" { "hostname" = "[2001::1]" } { "port" = "514" } }
+ 	  }
+ 	  { "entry"
+ 	    { "selector" { "facility" = "*" } { "level" = "*" } }
+@@ -260,7 +260,6 @@ daemon.info                                     /var/log/cvsupd.log
+ 	(* changing file to remote host *)
+ 	test Syslog.lns put "*.* /var\n" after
+ 	  rm "/entry/action/file" ;
+-	  set "/entry/action/protocol"  "@" ;
+ 	  set "/entry/action/hostname" "far.far.away"
+ 	  = "*.* @far.far.away\n"
+ 
+-- 
+2.17.2
+
diff --git a/SOURCES/0002-Revert-Use-Quote-module-in-dovecot.patch b/SOURCES/0002-Revert-Use-Quote-module-in-dovecot.patch
new file mode 100644
index 0000000..c249934
--- /dev/null
+++ b/SOURCES/0002-Revert-Use-Quote-module-in-dovecot.patch
@@ -0,0 +1,86 @@
+From 2765da41b09cdfcf2f28653a484462ee7109116a Mon Sep 17 00:00:00 2001
+From: Dominic Cleal <dcleal@redhat.com>
+Date: Fri, 12 Jun 2015 09:32:56 +0100
+Subject: [PATCH] Revert "Use Quote module in dovecot"
+
+This reverts commit 735caf587959019d214a51a7c0cce57da9bb88f6 for
+compatibility with Augeas 1.1.0.
+
+Values will have quotes in them as they did in 1.1.0, not stripped out.
+
+Conflicts:
+	lenses/dovecot.aug
+---
+ lenses/dovecot.aug            | 28 +++++-----------------------
+ lenses/tests/test_dovecot.aug |  2 +-
+ 2 files changed, 6 insertions(+), 24 deletions(-)
+
+diff --git a/lenses/dovecot.aug b/lenses/dovecot.aug
+index f7afc8f9..c6376e4b 100644
+--- a/lenses/dovecot.aug
++++ b/lenses/dovecot.aug
+@@ -56,6 +56,9 @@ let value = any . (Rx.space . any)*
+ (* View: command_start *)
+ let command_start = Util.del_str "!"
+ 
++(* View: block_args 
++Map block arguments after block name and before "{" *)
++let block_args = Sep.space . store /([A-Za-z0-9\/\\_-]+|\"[A-Za-z0-9 ]*\")/
+ 
+ (******************************************************************
+  * Group:                        ENTRIES
+@@ -79,28 +82,7 @@ let entry = [ indent . key keys. eq . (Sep.opt_space . store value)? . eol ]
+ Map commands started with "!". *)
+ let command = [ command_start . key commands . Sep.space . store Rx.fspath . eol ]
+ 
+-(*
+-View: dquote_spaces
+-  Make double quotes mandatory if value contains spaces,
+-  and optional if value doesn't contain spaces.
+-
+-Based off Quote.dquote_spaces
+-
+-Parameters:
+-  lns1:lens - the lens before
+-  lns2:lens - the lens after
+-*)
+-let dquote_spaces (lns1:lens) (lns2:lens) =
+-     (* bare has no spaces, and is optionally quoted *)
+-     let bare = Quote.do_dquote_opt (store /[^" \t\n]+/)
+-     (* quoted has at least one space, and must be quoted *)
+-  in let quoted = Quote.do_dquote (store /[^"\n]*[ \t]+[^"\n]*/)
+-  in [ lns1 . bare . lns2 ] | [ lns1 . quoted . lns2 ]
+-
+-let mailbox = indent
+-            . dquote_spaces
+-               (key /mailbox/ . Sep.space)
+-               (Build.block_newlines_spc entry comment . eol)
++let mailbox = [ indent . key /mailbox/ . block_args? . Build.block_newlines_spc (entry) comment . eol ]
+ 
+ let block_ldelim_newlines_re = /[ \t]+\{([ \t\n]*\n)?/
+ 
+@@ -114,7 +96,7 @@ let block_newlines (entry:lens) (comment:lens) =
+ Map block enclosed in brackets recursively.
+ Block may be indented and have optional argument.
+ Block body may have entries, comments, empty lines, and nested blocks recursively. *)
+-let rec block = [ indent . key block_names . (Sep.space . Quote.do_dquote_opt (store /[\/A-Za-z0-9_-]+/))? . block_newlines (entry|block|mailbox) comment . eol ]
++let rec block = [ indent . key block_names . block_args? . block_newlines (entry|block|mailbox) comment . eol ]
+ 
+ 
+ (******************************************************************
+diff --git a/lenses/tests/test_dovecot.aug b/lenses/tests/test_dovecot.aug
+index 33ea16f1..8e8c083b 100644
+--- a/lenses/tests/test_dovecot.aug
++++ b/lenses/tests/test_dovecot.aug
+@@ -536,7 +536,7 @@ test Dovecot.lns get mail_conf =
+     { "hidden" = "no" }
+     { "list" = "yes" }
+     { "subscriptions" = "yes" }
+-    { "mailbox" = "Sent Messages" 
++    { "mailbox" = "\"Sent Messages\"" 
+       { "special_use" = "\Sent" }
+     }
+   }
+-- 
+2.17.2
+
diff --git a/SOURCES/0003-Revert-Jaas-add-several-improvements-to-cover-more-v.patch b/SOURCES/0003-Revert-Jaas-add-several-improvements-to-cover-more-v.patch
new file mode 100644
index 0000000..fed2fed
--- /dev/null
+++ b/SOURCES/0003-Revert-Jaas-add-several-improvements-to-cover-more-v.patch
@@ -0,0 +1,143 @@
+From cd586dfa4ade4ea574aae0fa6ac034c07c77b25a Mon Sep 17 00:00:00 2001
+From: Dominic Cleal <dcleal@redhat.com>
+Date: Fri, 12 Jun 2015 09:47:48 +0100
+Subject: [PATCH] Revert "Jaas: add several improvements to cover more valid
+ syntax"
+
+This partially reverts commit d17f2fbc30a52edb3719fc6df4c24f01552dd203
+for compatibility with Augeas 1.1.0.
+
+Trailing semicolons remain part of the last value.  Only support for
+"naked" booleans has been kept from 1.4.0.
+
+Conflicts:
+	NEWS
+---
+ lenses/jaas.aug            | 29 ++++++++++++-----------------
+ lenses/tests/test_jaas.aug | 21 ++++++++++-----------
+ 2 files changed, 22 insertions(+), 28 deletions(-)
+
+diff --git a/lenses/jaas.aug b/lenses/jaas.aug
+index 4f06bad9..a13682e3 100644
+--- a/lenses/jaas.aug
++++ b/lenses/jaas.aug
+@@ -1,36 +1,31 @@
+ (* Module Jaas *)
+-(* Original Author: Simon Vocella <voxsim@gmail.com> *)
+-(* Updated by: Steve Shipway <steve@steveshipway.org> *)
+-(* Changes: allow comments within Modules, allow optionless flags,  *)
+-(* allow options without linebreaks, allow naked true/false options *)
+-(* Trailing ';' terminator should not be included in option value   *)
+-(* Note: requires latest Util.aug for multiline comments to work    *)
++(* Author: Simon Vocella <voxsim@gmail.com> *)
+ 
+ module Jaas =
+ 
+ autoload xfm
+ 
+ let space_equal = del (/[ \t]*/ . "=" . /[ \t]*/) (" = ")
+-let lbrace = del (/[ \t\n]*\{[ \t]*\n/) " {\n"
+-let rbrace = del (/[ \t]*}[ \t]*;/) " };"
++let lbrace = del (/[ \t\n]*/ . "{") "{"
++let rbrace = del ("};") "};"
+ let word = /[A-Za-z0-9_.-]+/
+-let wsnl = del (/[ \t\n]+/) ("\n")
+-let endflag = del ( /[ \t]*;/ ) ( ";" )
++let wordonly = /[A-Za-z0-9_.-]+;/
+ 
+ let value_re =
+         let value_squote = /'[^\n']*'/
++        in let value_squote_2 = /'[^\n']*';/
+         in let value_dquote = /"[^\n"]*"/
+-        in let value_tf = /(true|false)/
+-        in value_squote | value_dquote | value_tf
++        in let value_dquote_2 = /"[^\n"]*";/
++        in let value_tf = /(true|false);/
++        in let value_tf_2 = /(true|false);/
++        in value_squote | value_squote_2 | value_dquote | value_dquote_2 | value_tf | value_tf_2
+ 
+-let moduleOption = [  wsnl . key word . space_equal . (store value_re) ]
+-let moduleSuffix = ( moduleOption  | Util.eol . Util.comment_c_style | Util.comment_multiline  )
+-let flag = [ Util.del_ws_spc . label "flag" . (store word) . moduleSuffix* . endflag ]
+-let loginModuleClass = [( Util.del_opt_ws "" . label "loginModuleClass" . (store word) . flag ) ]
++let moduleOption = [Util.del_opt_ws "" . key word . space_equal . (store value_re . Util.comment_or_eol)]
++let flag = [label "flag" . ((store word . Util.eol . moduleOption+) | (store wordonly . Util.eol))]
++let loginModuleClass = [Util.del_opt_ws "" . label "loginModuleClass" . (store word . Util.del_ws_spc) . flag]
+ 
+ let content = (Util.empty | Util.comment_c_style | Util.comment_multiline | loginModuleClass)*
+ let loginModule = [Util.del_opt_ws "" . label "login" . (store word . lbrace) . (content . rbrace)]
+-
+ let lns = (Util.empty | Util.comment_c_style | Util.comment_multiline | loginModule)*
+ let filter = incl "/opt/shibboleth-idp/conf/login.config"
+ let xfm = transform lns filter
+diff --git a/lenses/tests/test_jaas.aug b/lenses/tests/test_jaas.aug
+index 5f2bfb13..d5ede698 100644
+--- a/lenses/tests/test_jaas.aug
++++ b/lenses/tests/test_jaas.aug
+@@ -54,7 +54,6 @@ ShibUserPassAuth {
+       serviceCredential = \"ldappassword\"
+       ssl = \"false\"
+       userField = \"uid\"
+-      // Example comment within definition
+       subtreeSearch = \"true\";
+ };
+ 
+@@ -64,8 +63,9 @@ NetAccountAuth {
+ };
+ 
+ com.sun.security.jgss.krb5.initiate {
+-   // Test of omitted linebreaks and naked boolean
+-   com.sun.security.auth.module.Krb5LoginModule required useTicketCache=true;
++   // Test of naked boolean
++   com.sun.security.auth.module.Krb5LoginModule required
++      useTicketCache=true;
+ };"
+ 
+ test Jaas.lns get conf =
+@@ -89,6 +89,7 @@ test Jaas.lns get conf =
+   {  }
+   {  }
+   { "login" = "ShibUserPassAuth"
++    {  }
+     {  }
+     { "#comment" = "Example LDAP authentication" }
+     { "#comment" = "See: https://wiki.shibboleth.net/confluence/display/SHIB2/IdPAuthUserPass" }
+@@ -116,29 +117,27 @@ test Jaas.lns get conf =
+         { "serviceCredential" = "\"ldappassword\"" }
+         { "ssl" = "\"false\"" }
+         { "userField" = "\"uid\"" }
+-        { "#comment" = "Example comment within definition" }
+-        { "subtreeSearch" = "\"true\"" }
++        { "subtreeSearch" = "\"true\";" }
+       }
+     }
+-    {  }
+   }
+   {  }
+   {  }
+   { "login" = "NetAccountAuth"
++    {  }
+     { "#comment" = "Test of optionless flag" }
+     { "loginModuleClass" = "nz.ac.auckland.jaas.Krb5LoginModule"
+-      { "flag" = "required" }
++      { "flag" = "required;" }
+     }
+-    {  }
+   }
+   {  }
+   {  }
+   { "login" = "com.sun.security.jgss.krb5.initiate"
+-    { "#comment" = "Test of omitted linebreaks and naked boolean" }
++    {  }
++    { "#comment" = "Test of naked boolean" }
+     { "loginModuleClass" = "com.sun.security.auth.module.Krb5LoginModule"
+       { "flag" = "required"
+-        { "useTicketCache" = "true" }
++        { "useTicketCache" = "true;" }
+       }
+     }
+-    {  }
+   }
+-- 
+2.17.2
+
diff --git a/SOURCES/0004-UpdateDB-autoload-etc-updatedb.conf-with-Simplevars.patch b/SOURCES/0004-UpdateDB-autoload-etc-updatedb.conf-with-Simplevars.patch
new file mode 100644
index 0000000..2964e9c
--- /dev/null
+++ b/SOURCES/0004-UpdateDB-autoload-etc-updatedb.conf-with-Simplevars.patch
@@ -0,0 +1,51 @@
+From 898ea6cd04163b38aa59b0c3d9df23cf1837d425 Mon Sep 17 00:00:00 2001
+From: Dominic Cleal <dcleal@redhat.com>
+Date: Fri, 12 Jun 2015 10:46:57 +0100
+Subject: [PATCH] UpdateDB: autoload /etc/updatedb.conf with Simplevars
+
+updatedb.conf was loaded with Simplevars in Augeas 1.1.0, so continue
+for compatibility.  Use aug_transform, augtool --transform etc. to use
+the newer, different, UpdateDB lens.
+---
+ lenses/simplevars.aug | 1 +
+ lenses/updatedb.aug   | 8 --------
+ 2 files changed, 1 insertion(+), 8 deletions(-)
+
+diff --git a/lenses/simplevars.aug b/lenses/simplevars.aug
+index ad9795f0..ff0602cc 100644
+--- a/lenses/simplevars.aug
++++ b/lenses/simplevars.aug
+@@ -41,6 +41,7 @@ let lns = (Util.empty | Util.comment | entry)*
+ (* Variable: filter *)
+ let filter = incl "/etc/kernel-img.conf"
+            . incl "/etc/kerneloops.conf"
++           . incl "/etc/updatedb.conf"
+            . incl "/etc/wgetrc"
+            . incl "/etc/zabbix/*.conf"
+            . incl "/etc/audit/auditd.conf"
+diff --git a/lenses/updatedb.aug b/lenses/updatedb.aug
+index 40cd26fb..c7309b16 100644
+--- a/lenses/updatedb.aug
++++ b/lenses/updatedb.aug
+@@ -22,8 +22,6 @@ About: Examples
+ 
+ module UpdateDB =
+ 
+-autoload xfm
+-
+ (* View: list
+      A list entry *)
+ let list =
+@@ -41,9 +39,3 @@ let bool = [ key "PRUNE_BIND_MOUNTS" . Sep.space_equal
+ (* View: lns
+      The <UpdateDB> lens *)
+ let lns = (Util.empty|Util.comment|list|bool)*
+-
+-(* Variable: filter
+-      The filter *)
+-let filter = incl "/etc/updatedb.conf"
+-
+-let xfm = transform lns filter
+-- 
+2.17.2
+
diff --git a/SOURCES/0005-Revert-Dnsmasq-add-structure-to-address-and-server-o.patch b/SOURCES/0005-Revert-Dnsmasq-add-structure-to-address-and-server-o.patch
new file mode 100644
index 0000000..9020437
--- /dev/null
+++ b/SOURCES/0005-Revert-Dnsmasq-add-structure-to-address-and-server-o.patch
@@ -0,0 +1,138 @@
+From 94b0373abfcb4a6474ee9bb7866fe28f9bbe876d Mon Sep 17 00:00:00 2001
+From: Dominic Cleal <dcleal@redhat.com>
+Date: Fri, 12 Jun 2015 10:48:58 +0100
+Subject: [PATCH] Revert "Dnsmasq: add structure to 'address' and 'server'
+ options"
+
+This reverts commit 3e20740056bd548f714cd50e4adc6b76a249ddfc for tree
+compatibility with Augeas 1.1.0.  address and server options will be
+unstructured.
+
+Conflicts:
+	NEWS
+---
+ lenses/dnsmasq.aug            | 34 +++++++------------------
+ lenses/tests/test_dnsmasq.aug | 47 -----------------------------------
+ 2 files changed, 9 insertions(+), 72 deletions(-)
+
+diff --git a/lenses/dnsmasq.aug b/lenses/dnsmasq.aug
+index f7ef9070..e19aab83 100644
+--- a/lenses/dnsmasq.aug
++++ b/lenses/dnsmasq.aug
+@@ -17,42 +17,26 @@ module Dnsmasq =
+  *                           USEFUL PRIMITIVES
+  *************************************************************************)
+ 
+-let eol          = Util.eol
+-let spc          = Util.del_ws_spc
+-let comment      = Util.comment
+-let empty        = Util.empty
++let eol        = Util.eol
++let spc        = Util.del_ws_spc
++let comment    = Util.comment
++let empty      = Util.empty
+ 
+-let sep_eq       = Sep.equal
+-let sto_to_eol   = store /([^ \t\n].*[^ \t\n]|[^ \t\n])/
+-
+-let slash        = Util.del_str "/"
+-let sto_no_slash = store /([^\/ \t\n]+)/
+-let domains      = slash . [ label "domain" . sto_no_slash . slash ]+
++let sep_eq     = del /=/ "="
++let sto_to_eol = store /([^ \t\n].*[^ \t\n]|[^ \t\n])/
+ 
+ (************************************************************************
+- *                            SIMPLE ENTRIES
++ *                               ENTRIES
+  *************************************************************************)
+ 
+-let entry_re   = Rx.word - /(address|server)/
++let entry_re   = /[A-Za-z0-9._-]+/
+ let entry      = [ key entry_re . (sep_eq . sto_to_eol)? . eol ]
+ 
+-(************************************************************************
+- *                          STRUCTURED ENTRIES
+- *************************************************************************)
+-
+-let address       = [ key "address" . sep_eq . domains . sto_no_slash . eol ]
+-
+-let server        =
+-     let port     = [ Build.xchgs "#" "port" . store Rx.integer ]
+-  in let source   = [ Build.xchgs "@" "source" . store /[^#\/ \t\n]+/ . port? ]
+-  in let srv_spec = store /(#|([^#@\/ \t\n]+))/ . port? . source?
+-  in [ key "server" . sep_eq . domains? . srv_spec? . eol ]
+-
+ (************************************************************************
+  *                                LENS
+  *************************************************************************)
+ 
+-let lns = (comment|empty|address|server|entry) *
++let lns = (comment|empty|entry) *
+ 
+ let filter            = incl "/etc/dnsmasq.conf"
+                       . incl "/etc/dnsmasq.d/*"
+diff --git a/lenses/tests/test_dnsmasq.aug b/lenses/tests/test_dnsmasq.aug
+index c6a63d96..0abfa6bd 100644
+--- a/lenses/tests/test_dnsmasq.aug
++++ b/lenses/tests/test_dnsmasq.aug
+@@ -6,16 +6,6 @@ let conf = "# Configuration file for dnsmasq.
+ 
+ conf-dir=/etc/dnsmasq.d
+ selfmx
+-
+-address=/foo.com/bar.net/10.1.2.3
+-
+-server=10.4.5.6#1234
+-server=/bar.com/foo.net/10.7.8.9
+-server=/foo.org/bar.org/10.3.2.1@eth0#5678
+-server=/baz.org/#
+-server=/baz.net/#@eth1
+-server=10.6.5.4#1234@eth0#5678
+-server=/qux.com/qux.net/
+ "
+ 
+ test Dnsmasq.lns get conf =
+@@ -25,40 +15,3 @@ test Dnsmasq.lns get conf =
+   {}
+   { "conf-dir" = "/etc/dnsmasq.d" }
+   { "selfmx" }
+-  {}
+-  { "address" = "10.1.2.3"
+-    { "domain" = "foo.com" }
+-    { "domain" = "bar.net" }
+-  }
+-  {}
+-  { "server" = "10.4.5.6"
+-    { "port" = "1234" }
+-  }
+-  { "server" = "10.7.8.9"
+-    { "domain" = "bar.com" }
+-    { "domain" = "foo.net" }
+-  }
+-  { "server" = "10.3.2.1"
+-    { "domain" = "foo.org" }
+-    { "domain" = "bar.org" }
+-    { "source" = "eth0"
+-      { "port" = "5678" }
+-    }
+-  }
+-  { "server" = "#"
+-    { "domain" = "baz.org" }
+-  }
+-  { "server" = "#"
+-    { "domain" = "baz.net" }
+-    { "source" = "eth1" }
+-  }
+-  { "server" = "10.6.5.4"
+-    { "port" = "1234" }
+-    { "source" = "eth0"
+-      { "port" = "5678" }
+-    }
+-  }
+-  { "server"
+-    { "domain" = "qux.com" }
+-    { "domain" = "qux.net" }
+-  }
+-- 
+2.17.2
+
diff --git a/SOURCES/0006-Sshd-revert-Sshd-module-to-1.1.0-compatible-add-Sshd.patch b/SOURCES/0006-Sshd-revert-Sshd-module-to-1.1.0-compatible-add-Sshd.patch
new file mode 100644
index 0000000..d041f82
--- /dev/null
+++ b/SOURCES/0006-Sshd-revert-Sshd-module-to-1.1.0-compatible-add-Sshd.patch
@@ -0,0 +1,490 @@
+From 1c936bb38d39b238001364e1a7ee5479bdfff053 Mon Sep 17 00:00:00 2001
+From: Dominic Cleal <dcleal@redhat.com>
+Date: Fri, 12 Jun 2015 11:01:57 +0100
+Subject: [PATCH] Sshd: revert Sshd module to 1.1.0-compatible, add Sshd_140
+
+In order to keep the default sshd config lens compatible with 1.1.0,
+the lens from 1.4.0 has been kept in the Sshd_140 module and is not
+loaded by default.  Use aug_transform, augtool --transform etc. to use
+it instead of Sshd.
+---
+ lenses/sshd.aug                |  52 +++++-------
+ lenses/sshd_140.aug            | 141 +++++++++++++++++++++++++++++++++
+ lenses/tests/test_sshd.aug     |  56 -------------
+ lenses/tests/test_sshd_140.aug | 136 +++++++++++++++++++++++++++++++
+ tests/Makefile.am              |   1 +
+ 5 files changed, 298 insertions(+), 88 deletions(-)
+ create mode 100644 lenses/sshd_140.aug
+ create mode 100644 lenses/tests/test_sshd_140.aug
+
+diff --git a/lenses/sshd.aug b/lenses/sshd.aug
+index 55f6c4f7..785102ec 100644
+--- a/lenses/sshd.aug
++++ b/lenses/sshd.aug
+@@ -70,55 +70,41 @@ module Sshd =
+ 
+    let sep = Util.del_ws_spc
+ 
+-   let indent = del /[ \t]*/ "  "
+-
+    let key_re = /[A-Za-z0-9]+/
+-         - /MACs|Match|AcceptEnv|Subsystem|Ciphers|KexAlgorithms|(Allow|Deny)(Groups|Users)/i
++         - /MACs|Match|AcceptEnv|Subsystem|(Allow|Deny)(Groups|Users)/
+ 
+    let comment = Util.comment
+-   let comment_noindent = Util.comment_noindent
+    let empty = Util.empty
+ 
+-   let array_entry (kw:regexp) (sq:string) =
++   let array_entry (k:string) =
+      let value = store /[^ \t\n]+/ in
+-     [ key kw . [ sep . seq sq . value]* . eol ]
++     [ key k . [ sep . seq k . value]* . eol ]
+ 
+    let other_entry =
+      let value = store /[^ \t\n]+([ \t]+[^ \t\n]+)*/ in
+      [ key key_re . sep . value . eol ]
+ 
+-   let accept_env = array_entry /AcceptEnv/i "AcceptEnv"
++   let accept_env = array_entry "AcceptEnv"
+ 
+-   let allow_groups = array_entry /AllowGroups/i "AllowGroups"
+-   let allow_users = array_entry /AllowUsers/i "AllowUsers"
+-   let deny_groups = array_entry /DenyGroups/i "DenyGroups"
+-   let deny_users = array_entry /DenyUsers/i "DenyUsers"
++   let allow_groups = array_entry "AllowGroups"
++   let allow_users = array_entry "AllowUsers"
++   let deny_groups = array_entry "DenyGroups"
++   let deny_users = array_entry "DenyUsers"
+ 
+    let subsystemvalue =
+      let value = store (/[^ \t\n](.*[^ \t\n])?/) in
+      [ key /[A-Za-z0-9\-]+/ . sep . value . eol ]
+ 
+    let subsystem =
+-     [ key /Subsystem/i .  sep .  subsystemvalue ]
++     [ key "Subsystem" .  sep .  subsystemvalue ]
+ 
+-   let list (kw:regexp) (sq:string) =
+-     let value = store /[^, \t\n]+/ in
+-     [ key kw . sep .
+-         [ seq sq . value ] .
+-         ([ seq sq . Util.del_str "," . value])* .
++   let macs =
++     let mac_value = store /[^, \t\n]+/ in
++     [ key "MACs" . sep .
++         [ seq "macs" . mac_value ] .
++         ([ seq "macs" . Util.del_str "," . mac_value])* .
+          eol ]
+ 
+-   let macs = list /MACs/i "MACs"
+-
+-   let ciphers = list /Ciphers/i "Ciphers"
+-
+-   let kexalgorithms = list /KexAlgorithms/i "KexAlgorithms"
+-
+-   let entry = accept_env | allow_groups | allow_users
+-             | deny_groups | subsystem | deny_users
+-             | macs | ciphers | kexalgorithms
+-             | other_entry
+-
+    let condition_entry =
+     let value = store  /[^ \t\n]+/ in
+     [ sep . key /[A-Za-z0-9]+/ . sep . value ]
+@@ -126,15 +112,17 @@ module Sshd =
+    let match_cond =
+      [ label "Condition" . condition_entry+ . eol ]
+ 
+-   let match_entry = indent . (entry | comment_noindent)
+-                   | empty 
++   let match_entry =
++     ( comment | empty | (Util.indent . other_entry) )
+ 
+    let match =
+-     [ key /Match/i . match_cond
++     [ key "Match" . match_cond
+         . [ label "Settings" .  match_entry+ ]
+      ]
+ 
+-  let lns = (entry | comment | empty)* . match* 
++  let lns = (comment | empty | accept_env | allow_groups | allow_users
++          | deny_groups | subsystem | deny_users | macs
++          | other_entry ) * . match*
+ 
+   let xfm = transform lns (incl "/etc/ssh/sshd_config")
+ 
+diff --git a/lenses/sshd_140.aug b/lenses/sshd_140.aug
+new file mode 100644
+index 00000000..8a7f176f
+--- /dev/null
++++ b/lenses/sshd_140.aug
+@@ -0,0 +1,141 @@
++(*
++Module: Sshd_140
++  Parses /etc/ssh/sshd_config
++
++  This module is compatible with Augeas 1.4.0, but is not loaded by default.
++
++Author: David Lutterkort lutter@redhat.com
++        Dominique Dumont dominique.dumont@hp.com
++
++About: Reference
++  sshd_config man page.
++  See http://www.openbsd.org/cgi-bin/man.cgi?query=sshd_config&sektion=5
++
++About: License
++  This file is licensed under the LGPL v2+.
++
++About: Lens Usage
++  Sample usage of this lens in augtool:
++
++    * Get your current setup
++      > print /files/etc/ssh/sshd_config
++      ...
++
++    * Set X11Forwarding to "no"
++      > set /files/etc/ssh/sshd_config/X11Forwarding "no"
++
++  More advanced usage:
++
++    * Set a Match section
++      > set /files/etc/ssh/sshd_config/Match[1]/Condition/User "foo"
++      > set /files/etc/ssh/sshd_config/Match[1]/Settings/X11Forwarding "yes"
++
++  Saving your file:
++
++      > save
++
++
++About: CAVEATS
++
++  In sshd_config, Match blocks must be located at the end of the file.
++  This means that any new "global" parameters (i.e. outside of a Match
++  block) must be written before the first Match block. By default,
++  Augeas will write new parameters at the end of the file.
++
++  I.e. if you have a Match section and no ChrootDirectory parameter,
++  this command:
++
++     > set /files/etc/ssh/sshd_config/ChrootDirectory "foo"
++
++  will be stored in a new node after the Match section and Augeas will
++  refuse to save sshd_config file.
++
++  To create a new parameter as the right place, you must first create
++  a new Augeas node before the Match section:
++
++     > ins ChrootDirectory before /files/etc/ssh/sshd_config/Match
++
++  Then, you can set the parameter
++
++     > set /files/etc/ssh/sshd_config/ChrootDirectory "foo"
++
++
++About: Configuration files
++  This lens applies to /etc/ssh/sshd_config
++
++*)
++
++module Sshd_140 =
++   let eol = del /[ \t]*\n/ "\n"
++
++   let sep = Util.del_ws_spc
++
++   let indent = del /[ \t]*/ "  "
++
++   let key_re = /[A-Za-z0-9]+/
++         - /MACs|Match|AcceptEnv|Subsystem|Ciphers|KexAlgorithms|(Allow|Deny)(Groups|Users)/i
++
++   let comment = Util.comment
++   let comment_noindent = Util.comment_noindent
++   let empty = Util.empty
++
++   let array_entry (kw:regexp) (sq:string) =
++     let value = store /[^ \t\n]+/ in
++     [ key kw . [ sep . seq sq . value]* . eol ]
++
++   let other_entry =
++     let value = store /[^ \t\n]+([ \t]+[^ \t\n]+)*/ in
++     [ key key_re . sep . value . eol ]
++
++   let accept_env = array_entry /AcceptEnv/i "AcceptEnv"
++
++   let allow_groups = array_entry /AllowGroups/i "AllowGroups"
++   let allow_users = array_entry /AllowUsers/i "AllowUsers"
++   let deny_groups = array_entry /DenyGroups/i "DenyGroups"
++   let deny_users = array_entry /DenyUsers/i "DenyUsers"
++
++   let subsystemvalue =
++     let value = store (/[^ \t\n](.*[^ \t\n])?/) in
++     [ key /[A-Za-z0-9\-]+/ . sep . value . eol ]
++
++   let subsystem =
++     [ key /Subsystem/i .  sep .  subsystemvalue ]
++
++   let list (kw:regexp) (sq:string) =
++     let value = store /[^, \t\n]+/ in
++     [ key kw . sep .
++         [ seq sq . value ] .
++         ([ seq sq . Util.del_str "," . value])* .
++         eol ]
++
++   let macs = list /MACs/i "MACs"
++
++   let ciphers = list /Ciphers/i "Ciphers"
++
++   let kexalgorithms = list /KexAlgorithms/i "KexAlgorithms"
++
++   let entry = accept_env | allow_groups | allow_users
++             | deny_groups | subsystem | deny_users
++             | macs | ciphers | kexalgorithms
++             | other_entry
++
++   let condition_entry =
++    let value = store  /[^ \t\n]+/ in
++    [ sep . key /[A-Za-z0-9]+/ . sep . value ]
++
++   let match_cond =
++     [ label "Condition" . condition_entry+ . eol ]
++
++   let match_entry = indent . (entry | comment_noindent)
++                   | empty 
++
++   let match =
++     [ key /Match/i . match_cond
++        . [ label "Settings" .  match_entry+ ]
++     ]
++
++  let lns = (entry | comment | empty)* . match* 
++
++(* Local Variables: *)
++(* mode: caml       *)
++(* End:             *)
+diff --git a/lenses/tests/test_sshd.aug b/lenses/tests/test_sshd.aug
+index 5954e16f..788a12f0 100644
+--- a/lenses/tests/test_sshd.aug
++++ b/lenses/tests/test_sshd.aug
+@@ -1,4 +1,3 @@
+-(* Module: Test_sshd *)
+ module Test_sshd =
+ 
+   let accept_env = "Protocol 2
+@@ -75,61 +74,6 @@ Match User sarko Group pres.*
+ Match User bush Group pres.* Host white.house.*
+ Banner /etc/welcome.txt\n"
+ 
+-(* Test: Sshd.lns
+-   Indent when adding to a Match group *)
+-  test Sshd.lns put match_blocks after
+-    set "Match[1]/Settings/PermitRootLogin" "yes";
+-    set "Match[1]/Settings/#comment" "a comment" =
+-"X11Forwarding yes
+-Match User sarko Group pres.*
+-  Banner /etc/bienvenue.txt
+-  X11Forwarding no
+-  PermitRootLogin yes
+-  # a comment
+-Match User bush Group pres.* Host white.house.*
+-Banner /etc/welcome.txt\n"
+-
+-
+-(* Test: Sshd.lns
+-     Parse Ciphers and KexAlgorithms as lists (GH issue #69) *)
+-test Sshd.lns get "Ciphers aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes128-ctr
+-KexAlgorithms diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1\n" =
+-  { "Ciphers"
+-    { "1" = "aes256-gcm@openssh.com" }
+-    { "2" = "aes128-gcm@openssh.com" }
+-    { "3" = "aes256-ctr" }
+-    { "4" = "aes128-ctr" }
+-  }
+-  { "KexAlgorithms"
+-    { "1" = "diffie-hellman-group-exchange-sha256" }
+-    { "2" = "diffie-hellman-group14-sha1" }
+-    { "3" = "diffie-hellman-group-exchange-sha1" }
+-  }
+-
+-(* Test: Sshd.lns
+-     Keys are case-insensitive *)
+-test Sshd.lns get "ciPheRs aes256-gcm@openssh.com,aes128-ctr
+-maTcH User foo
+-  x11forwarding no\n" =
+-  { "ciPheRs"
+-    { "1" = "aes256-gcm@openssh.com" }
+-    { "2" = "aes128-ctr" }
+-  }
+-  { "maTcH"
+-    { "Condition"
+-      { "User" = "foo" }
+-    }
+-    { "Settings"
+-      { "x11forwarding" = "no" }
+-    }
+-  }
+-
+-(* Test: Sshd.lns
+-     Allow AllowGroups in Match groups (GH issue #75) *)
+-test Sshd.lns get "Match User foo
+-AllowGroups users\n" =
+-  { "Match" { "Condition" { "User" = "foo" } }
+-    { "Settings" { "AllowGroups" { "1" = "users" } } } }
+ 
+ (* Local Variables: *)
+ (* mode: caml       *)
+diff --git a/lenses/tests/test_sshd_140.aug b/lenses/tests/test_sshd_140.aug
+new file mode 100644
+index 00000000..056c53f9
+--- /dev/null
++++ b/lenses/tests/test_sshd_140.aug
+@@ -0,0 +1,136 @@
++(* Module: Test_sshd_140 *)
++module Test_sshd_140 =
++
++  let accept_env = "Protocol 2
++AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
++AcceptEnv LC_IDENTIFICATION LC_ALL\n"
++
++  test Sshd_140.lns get accept_env =
++    { "Protocol" = "2" }
++    { "AcceptEnv"
++        { "1" = "LC_PAPER" }
++        { "2" = "LC_NAME" }
++        { "3" = "LC_ADDRESS" }
++        { "4" = "LC_TELEPHONE" }
++        { "5" = "LC_MEASUREMENT" } }
++    { "AcceptEnv"
++        { "6" = "LC_IDENTIFICATION" }
++        { "7" = "LC_ALL" } }
++
++
++  test Sshd_140.lns get "HostKey /etc/ssh/ssh_host_rsa_key
++HostKey /etc/ssh/ssh_host_dsa_key\n" =
++    { "HostKey" = "/etc/ssh/ssh_host_rsa_key" }
++    { "HostKey" = "/etc/ssh/ssh_host_dsa_key" }
++
++
++  test Sshd_140.lns put accept_env after
++      rm "AcceptEnv";
++      rm "AcceptEnv";
++      set "Protocol" "1.5";
++      set "X11Forwarding" "yes"
++   = "Protocol 1.5\nX11Forwarding yes\n"
++
++  test Sshd_140.lns get "AuthorizedKeysFile  %h/.ssh/authorized_keys\n" =
++    { "AuthorizedKeysFile" = "%h/.ssh/authorized_keys" }
++
++  test Sshd_140.lns get "Subsystem sftp /usr/lib/openssh/sftp-server\n" =
++    { "Subsystem"
++	{ "sftp" = "/usr/lib/openssh/sftp-server" } }
++
++  test Sshd_140.lns get "Subsystem sftp-test /usr/lib/openssh/sftp-server\n" =
++    { "Subsystem"
++	{ "sftp-test" = "/usr/lib/openssh/sftp-server" } }
++
++
++
++  let match_blocks = "X11Forwarding yes
++Match User sarko Group pres.*
++  Banner /etc/bienvenue.txt
++  X11Forwarding no
++Match User bush Group pres.* Host white.house.*
++Banner /etc/welcome.txt
++"
++  test Sshd_140.lns get match_blocks =
++    { "X11Forwarding" = "yes"}
++      { "Match"
++	  { "Condition" { "User" = "sarko"   }
++	                { "Group" = "pres.*" } }
++	  { "Settings"  { "Banner" = "/etc/bienvenue.txt" }
++       	                { "X11Forwarding" = "no" } } }
++      { "Match"
++	  { "Condition" { "User" = "bush"    }
++	                { "Group" = "pres.*" }
++	                { "Host"  = "white.house.*" } }
++	  { "Settings"  { "Banner" = "/etc/welcome.txt" } } }
++
++  test Sshd_140.lns put match_blocks after
++    insb "Subsystem" "/Match[1]";
++    set "/Subsystem/sftp" "/usr/libexec/openssh/sftp-server"
++  = "X11Forwarding yes
++Subsystem sftp /usr/libexec/openssh/sftp-server
++Match User sarko Group pres.*
++  Banner /etc/bienvenue.txt
++  X11Forwarding no
++Match User bush Group pres.* Host white.house.*
++Banner /etc/welcome.txt\n"
++
++(* Test: Sshd_140.lns
++   Indent when adding to a Match group *)
++  test Sshd_140.lns put match_blocks after
++    set "Match[1]/Settings/PermitRootLogin" "yes";
++    set "Match[1]/Settings/#comment" "a comment" =
++"X11Forwarding yes
++Match User sarko Group pres.*
++  Banner /etc/bienvenue.txt
++  X11Forwarding no
++  PermitRootLogin yes
++  # a comment
++Match User bush Group pres.* Host white.house.*
++Banner /etc/welcome.txt\n"
++
++
++(* Test: Sshd_140.lns
++     Parse Ciphers and KexAlgorithms as lists (GH issue #69) *)
++test Sshd_140.lns get "Ciphers aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes128-ctr
++KexAlgorithms diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1\n" =
++  { "Ciphers"
++    { "1" = "aes256-gcm@openssh.com" }
++    { "2" = "aes128-gcm@openssh.com" }
++    { "3" = "aes256-ctr" }
++    { "4" = "aes128-ctr" }
++  }
++  { "KexAlgorithms"
++    { "1" = "diffie-hellman-group-exchange-sha256" }
++    { "2" = "diffie-hellman-group14-sha1" }
++    { "3" = "diffie-hellman-group-exchange-sha1" }
++  }
++
++(* Test: Sshd_140.lns
++     Keys are case-insensitive *)
++test Sshd_140.lns get "ciPheRs aes256-gcm@openssh.com,aes128-ctr
++maTcH User foo
++  x11forwarding no\n" =
++  { "ciPheRs"
++    { "1" = "aes256-gcm@openssh.com" }
++    { "2" = "aes128-ctr" }
++  }
++  { "maTcH"
++    { "Condition"
++      { "User" = "foo" }
++    }
++    { "Settings"
++      { "x11forwarding" = "no" }
++    }
++  }
++
++(* Test: Sshd_140.lns
++     Allow AllowGroups in Match groups (GH issue #75) *)
++test Sshd_140.lns get "Match User foo
++AllowGroups users\n" =
++  { "Match" { "Condition" { "User" = "foo" } }
++    { "Settings" { "AllowGroups" { "1" = "users" } } } }
++
++(* Local Variables: *)
++(* mode: caml       *)
++(* End:             *)
+diff --git a/tests/Makefile.am b/tests/Makefile.am
+index b4563540..387ac7d2 100644
+--- a/tests/Makefile.am
++++ b/tests/Makefile.am
+@@ -189,6 +189,7 @@ lens_tests =			\
+   lens-squid.sh			\
+   lens-ssh.sh			\
+   lens-sshd.sh			\
++  lens-sshd_140.sh			\
+   lens-sssd.sh			\
+   lens-stunnel.sh			\
+   lens-subversion.sh		\
+-- 
+2.17.2
+
diff --git a/SOURCES/0007-Dhcpd-revert-Dhcpd-module-to-1.1.0-compatible-add-Dh.patch b/SOURCES/0007-Dhcpd-revert-Dhcpd-module-to-1.1.0-compatible-add-Dh.patch
new file mode 100644
index 0000000..f9270c3
--- /dev/null
+++ b/SOURCES/0007-Dhcpd-revert-Dhcpd-module-to-1.1.0-compatible-add-Dh.patch
@@ -0,0 +1,1750 @@
+From c93dc5ca4c87f38772c98e3134ddc6662a98bc02 Mon Sep 17 00:00:00 2001
+From: Dominic Cleal <dcleal@redhat.com>
+Date: Fri, 12 Jun 2015 11:09:09 +0100
+Subject: [PATCH] Dhcpd: revert Dhcpd module to 1.1.0-compatible, add Dhcpd_140
+
+In order to keep the default sshd config lens compatible with 1.1.0,
+the lens from 1.4.0 has been kept in the Dhcpd_140 module and is not
+loaded by default.  Use aug_transform, augtool --transform etc. to use
+it instead of Dhcpd.
+---
+ lenses/dhcpd.aug                | 182 ++--------
+ lenses/dhcpd_140.aug            | 512 +++++++++++++++++++++++++++
+ lenses/tests/test_dhcpd.aug     | 214 +----------
+ lenses/tests/test_dhcpd_140.aug | 606 ++++++++++++++++++++++++++++++++
+ tests/Makefile.am               |   1 +
+ 5 files changed, 1152 insertions(+), 363 deletions(-)
+ create mode 100644 lenses/dhcpd_140.aug
+ create mode 100644 lenses/tests/test_dhcpd_140.aug
+
+diff --git a/lenses/dhcpd.aug b/lenses/dhcpd.aug
+index f84a409c..21a5ce60 100644
+--- a/lenses/dhcpd.aug
++++ b/lenses/dhcpd.aug
+@@ -65,12 +65,12 @@ let eos               = comment?
+ let sep_spc           = del /[ \t]+/ " "
+ let sep_osp           = del /[ \t]*/ ""
+ let sep_scl           = del /[ \t]*;([ \t]*\n)*/ ";\n"
+-let sep_obr           = del /[ \t\n]*\{([ \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\n]*=[ \t\n]*/ "="
++let sep_eq            = del /[ \t]*=[ \t]*/ "="
+ let scl               = del ";" ";"
+ 
+ (* Define basic types *)
+@@ -94,11 +94,6 @@ let ip                = Rx.ipv4
+   let bare = del qchar? "" . store (bchar+) . del qchar? ""
+   let quote = Quote.do_quote (store (bchar* . /[ \t'\/]/ . bchar*)+)
+   let dquote = Quote.do_dquote (store (bchar+))
+-  (* these two are for special cases.  bare_to_scl is for any bareword that is
+-   * space or semicolon terminated.  dquote_any allows almost any character in
+-   * between the quotes. *)
+-  let bare_to_scl = Quote.do_dquote_opt (store /[^" \t\n;]+/)
+-  let dquote_any = Quote.do_dquote (store /[^"\n]*[ \t]+[^"\n]*/)
+ 
+ let sto_to_spc        = store /[^\\#,;\{\}" \t\n]+|"[^\\#"\n]+"/
+ let sto_to_scl        = store /[^ \t;][^;\n=]+[^ \t;]|[^ \t;=]+/
+@@ -197,7 +192,6 @@ let stmt_string_re    = "ddns-update-style"
+                       | "use-lease-addr-for-default-route"
+                       | "vendor-option-space"
+                       | "primary"
+-                      | "omapi-key"
+ 
+ let stmt_string_tpl (kw:regexp) (l:lens) = [ indent
+                         . key kw
+@@ -230,51 +224,26 @@ let stmt_range        = [ indent
+ let stmt_hardware     = [ indent
+                         . key "hardware"
+                         . sep_spc
+-                        . [ label "type" . store /ethernet|tokenring|fddi/ ]
++                        . [ label "type" . store /ethernet|tokenring/ ]
+                         . sep_spc
+                         . [ label "address" . store /[a-fA-F0-9:-]+/ ]
+                         . sep_scl
+                         . eos ]
+ 
+-(************************************************************************
+- *                         SET STATEMENTS
+- *************************************************************************)
+-let stmt_set          = [ indent
+-                        . key "set"
+-                        . sep_spc
+-                        . store word
+-                        . sep_spc
+-                        . Sep.equal
+-                        . sep_spc
+-                        . [ label "value" . sto_to_scl ]
+-                        . 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_value = /((array of[ \t]+)?(((un)?signed[ \t]+)?integer (8|16|32)|string|ip6?-address|boolean|domain-list|text)|encapsulate [A-Za-z0-9_.-]+)/
+ 
+ let stmt_option_list  = ([ label "arg" . bare ] | [ label "arg" . quote ])
+                         . ( sep_com . ([ label "arg" . bare ] | [ label "arg" . quote ]))*
+ 
+-let del_trail_spc = del /[ \t\n]*/ ""
+-
+-let stmt_record = counter "record" . Util.del_str "{"
+-                . sep_spc
+-                . ([seq "record" . store stmt_option_value . sep_com]*
+-                .  [seq "record" . store stmt_option_value . del_trail_spc])?
+-                . Util.del_str "}"
+-
+-let stmt_option_code  = [ label "label" . store word . sep_spc ]
+-                        . [ key "code" . sep_spc . store word ]
+-                        . sep_eq
+-                        . ([ label "type" . store stmt_option_value ]
+-                          |[ label "record" . stmt_record ]) 
+-
+ 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 ]
+ 
+@@ -302,13 +271,10 @@ let stmt_option = stmt_option1 | stmt_option2
+ (* 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" .  bare_to_scl ]|[ label "name" .  dquote_any ] )
+-                      . sep_spc 
+-                      . ( [ label "value" . bare_to_scl ]|[ label "value" . dquote_any ] ) 
+-                      . sep_scl 
+-                      . eos ]
+-
++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
+@@ -316,18 +282,10 @@ let stmt_subclass = [ indent . key "subclass" . sep_spc
+ (* We have to use special key for allow/deny members of
+   to avoid ambiguity in the put direction *)
+ 
+-let allow_deny_re     = /unknown(-|[ ]+)clients/
+-                      | /known(-|[ ]+)clients/
+-                      | /all[ ]+clients/
++let allow_deny_re     = "unknown-clients"
+                       | /dynamic[ ]+bootp[ ]+clients/
+                       | /authenticated[ ]+clients/
+                       | /unauthenticated[ ]+clients/
+-                      | "bootp"
+-                      | "booting"
+-                      | "duplicates"
+-                      | "declines"
+-                      | "client-updates"
+-                      | "leasequery"
+ 
+ let stmt_secu_re      = "allow"
+                       | "deny"
+@@ -335,17 +293,9 @@ let stmt_secu_re      = "allow"
+ let del_allow = del /allow[ ]+members[ ]+of/ "allow members of"
+ let del_deny  = del /deny[ \t]+members[ \t]+of/ "deny members of"
+ 
+-(* bare is anything but whitespace, quote marks or semicolon.
+- * technically this should be locked down to mostly alphanumerics, but the
+- * idea right now is just to make things work.  Also ideally I would use
+- * dquote_space but I had a whale of a time with it.  It doesn't like
+- * semicolon termination and my attempts to fix that led me to 3 hours of
+- * frustration and back to this :)
+- *)
+ let stmt_secu_tpl (l:lens) (s:string) =
+-                  [ indent . l . sep_spc . label s . bare_to_scl . sep_scl . eos ] |
+-                  [ indent . l . sep_spc . label s . dquote_any . sep_scl . eos ]
+-
++                  [ 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 ] |
+@@ -356,62 +306,17 @@ let stmt_secu         = [ indent . key stmt_secu_re . sep_spc .
+  *                         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]*\([^)]*\)/
+-(* this is already the most complicated part of this module and it's about to
+- * get worse.  match statements can be way more complicated than this
+- *
+- * examples:
+- *      using or:
+- *      match if ((option vendor-class-identifier="Banana Bready") or (option vendor-class-identifier="Cherry Sunfire"));
+- *      unneeded parenthesis:
+- *      match if (option vendor-class-identifier="Hello");
+- *
+- *      and of course the fact that the above two rules used one of infinately
+- *      many potential options instead of a builtin function.
+- *)
+-(* sto_com doesn't support quoted strings as arguments.  It also doesn't
+-   support single arguments (needs to match a comma) It will need to be
+-   updated for lcase, ucase and log to be workable.
+-
+-   it also doesn't support no arguments, so gethostbyname() doesn't work.
+-
+-   option and config-option are considered operators.  They should be matched
+-   in stmt_entry but also available under "match if" and "if" conditionals
+-   leased-address, host-decl-name, both take no args and return a value.  We
+-   might need to treat them as variable names in the parser.
+-
+-   things like this may be near-impossible to parse even with recursion
+-   because we have no way of knowing when or if a subfunction takes arguments
+-   set ClientMac = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6));
+-
+-   even if we could parse it, they could get arbitrarily complicated like:
+-   binary-to-ascii(16, 8, ":", substring(hardware, 1, 6) and substring(hardware, 2, 3));
+-
+-   so at some point we may need to programmatically knock it off and tell
+-   people to put weird stuff in an include file that augeas doesn't parse.
+-
+-   the other option is to change the API to not parse the if statement at all,
+-   just pull in the conditional as a string.
+- *)
+-
+-let fct_re = "substring" | "binary-to-ascii" | "suffix" | "lcase" | "ucase"
+-             | "gethostbyname" | "packet"
+-             | "concat" | "reverse" | "encode-int"
+-             | "extract-int" | "lease-time" | "client-state" | "exists" | "known" | "static"
+-             | "pick-first-value" | "log" | "execute"
+-
+-(* not needs to be different because it's a negation of whatever happens next *)
+-let op_re = "~="|"="|"~~"|"and"|"or"
++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_ifopt = [ dels "if" . sep_spc . key "option" . sep_spc . store word .
+-                      sep_eq . ([ label "value" . bare_to_scl ]|[ label "value" . dquote_any ]) ]
+-
+-let stmt_match_func = [ store fct_re . sep_osp . label "function" . fct_args ] .
+-                      sep_eq . ([ label "value" . bare_to_scl ]|[ label "value" . dquote_any ])
++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 .
+@@ -422,7 +327,7 @@ let stmt_match_pfv = [ label "function" . store "pick-first-value" . sep_spc .
+ 
+ let stmt_match_tpl (l:lens) = [ indent . key "match" . sep_spc . l . sep_scl . eos ]
+ 
+-let stmt_match = stmt_match_tpl (dels "if" . sep_spc . stmt_match_func | stmt_match_pfv | stmt_match_ifopt)
++let stmt_match = stmt_match_tpl (stmt_match_if | stmt_match_pfv )
+ 
+ (************************************************************************
+  *                         BLOCK STATEMENTS
+@@ -438,11 +343,12 @@ let stmt_entry        =   stmt_secu
+                         | stmt_noarg
+                         | stmt_match
+                         | stmt_subclass
+-                        | stmt_set
+                         | empty
+                         | comment
+ 
+-let stmt_block_noarg_re = "pool" | "group"
++let stmt_block_noarg_re = "pool"
++                        | "group"
++                        | "allow-update"
+ 
+ let stmt_block_noarg (body:lens)
+                         = [ indent
+@@ -456,14 +362,16 @@ let stmt_block_arg_re = "host"
+                       | "shared-network"
+                       | /failover[ ]+peer/
+                       | "zone"
+-                      | "group"
+-                      | "on"
++                      | "key"
+ 
+ let stmt_block_arg (body:lens)
+-                      = ([ indent . key stmt_block_arg_re . sep_spc . dquote_any . sep_obr . body* . sep_cbr ]
+-                         |[ indent . key stmt_block_arg_re . sep_spc . bare_to_scl . sep_obr . body* . sep_cbr ]
+-                         |[ indent . del /key/ "key" . label "key_block" . sep_spc . dquote_any . sep_obr . body* . sep_cbr . del /(;([ \t]*\n)*)?/ ""  ]
+-                         |[ indent . del /key/ "key" . label "key_block" . sep_spc . bare_to_scl . sep_obr . body* . sep_cbr . del /(;([ \t]*\n)*)?/ "" ])
++                      = [ indent
++                        . key stmt_block_arg_re
++                        . sep_spc
++                        . sto_to_spc
++                        . sep_obr
++                        . body*
++                        . sep_cbr ]
+ 
+ let stmt_block_subnet (body:lens)
+                       = [ indent
+@@ -476,37 +384,11 @@ let stmt_block_subnet (body:lens)
+                         . body*
+                         . sep_cbr ]
+ 
+-let conditional (body:lens) =
+-     let condition         = /[^{ \r\t\n][^{\n]*[^{ \r\t\n]|[^{ \t\n\r]/
+-  in let elsif = [ indent
+-                 . Build.xchgs "elsif" "@elsif"
+-                 . sep_spc
+-                 . store condition
+-                 . sep_obr
+-                 . body*
+-                 . sep_cbr ]
+-  in let else = [  indent
+-                 . Build.xchgs "else" "@else"
+-                 . sep_obr
+-                 . body*
+-                 . sep_cbr ]
+-  in [ indent
+-     . Build.xchgs "if" "@if"
+-     . sep_spc
+-     . store condition
+-     . sep_obr
+-     . body*
+-     . sep_cbr
+-     . elsif*
+-     . else? ]
+-
+-
+ 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
+-    let lns4 = conditional body in
+-    (lns1 | lns2 | lns3 | lns4 | stmt_entry)
++    (lns1 | lns2 | lns3 | stmt_entry)
+ 
+ let rec lns_staging = stmt_entry|all_block lns_staging
+ let lns = (lns_staging)*
+diff --git a/lenses/dhcpd_140.aug b/lenses/dhcpd_140.aug
+new file mode 100644
+index 00000000..c9072990
+--- /dev/null
++++ b/lenses/dhcpd_140.aug
+@@ -0,0 +1,512 @@
++(*
++Module: Dhcpd_140
++  BIND dhcp 3 server configuration module for Augeas
++
++  This module is compatible with Augeas 1.4.0, but is not loaded by default.
++
++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_140 =
++
++(************************************************************************
++ *                           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\n]*\{([ \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\n]*=[ \t\n]*/ "="
++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 dquote = Quote.do_dquote (store (bchar+))
++  (* these two are for special cases.  bare_to_scl is for any bareword that is
++   * space or semicolon terminated.  dquote_any allows almost any character in
++   * between the quotes. *)
++  let bare_to_scl = Quote.do_dquote_opt (store /[^" \t\n;]+/)
++  let dquote_any = Quote.do_dquote (store /[^"\n]*[ \t]+[^"\n]*/)
++
++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"
++                      | "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"
++                      | "omapi-key"
++
++let stmt_string_tpl (kw:regexp) (l:lens) = [ indent
++                        . key kw
++                        . sep_spc
++                        . l
++                        . sep_scl
++                        . eos ]
++
++let stmt_string  = stmt_string_tpl stmt_string_re bare
++                 | stmt_string_tpl stmt_string_re quote
++                 | stmt_string_tpl "filename" dquote
++
++(************************************************************************
++ *                         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|fddi/ ]
++                        . sep_spc
++                        . [ label "address" . store /[a-fA-F0-9:-]+/ ]
++                        . sep_scl
++                        . eos ]
++
++(************************************************************************
++ *                         SET STATEMENTS
++ *************************************************************************)
++let stmt_set          = [ indent
++                        . key "set"
++                        . sep_spc
++                        . store word
++                        . sep_spc
++                        . Sep.equal
++                        . sep_spc
++                        . [ label "value" . sto_to_scl ]
++                        . sep_scl
++                        . eos ]
++
++(************************************************************************
++ *                         OPTION STATEMENTS
++ *************************************************************************)
++(* The general case is considering options as a list *)
++
++
++let stmt_option_value = /((array of[ \t]+)?(((un)?signed[ \t]+)?integer (8|16|32)|string|ip6?-address|boolean|domain-list|text)|encapsulate [A-Za-z0-9_.-]+)/
++
++let stmt_option_list  = ([ label "arg" . bare ] | [ label "arg" . quote ])
++                        . ( sep_com . ([ label "arg" . bare ] | [ label "arg" . quote ]))*
++
++let del_trail_spc = del /[ \t\n]*/ ""
++
++let stmt_record = counter "record" . Util.del_str "{"
++                . sep_spc
++                . ([seq "record" . store stmt_option_value . sep_com]*
++                .  [seq "record" . store stmt_option_value . del_trail_spc])?
++                . Util.del_str "}"
++
++let stmt_option_code  = [ label "label" . store word . sep_spc ]
++                        . [ key "code" . sep_spc . store word ]
++                        . sep_eq
++                        . ([ label "type" . store stmt_option_value ]
++                          |[ label "record" . stmt_record ]) 
++
++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" .  bare_to_scl ]|[ label "name" .  dquote_any ] )
++                      . sep_spc 
++                      . ( [ label "value" . bare_to_scl ]|[ label "value" . dquote_any ] ) 
++                      . 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/
++                      | /known(-|[ ]+)clients/
++                      | /all[ ]+clients/
++                      | /dynamic[ ]+bootp[ ]+clients/
++                      | /authenticated[ ]+clients/
++                      | /unauthenticated[ ]+clients/
++                      | "bootp"
++                      | "booting"
++                      | "duplicates"
++                      | "declines"
++                      | "client-updates"
++                      | "leasequery"
++
++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"
++
++(* bare is anything but whitespace, quote marks or semicolon.
++ * technically this should be locked down to mostly alphanumerics, but the
++ * idea right now is just to make things work.  Also ideally I would use
++ * dquote_space but I had a whale of a time with it.  It doesn't like
++ * semicolon termination and my attempts to fix that led me to 3 hours of
++ * frustration and back to this :)
++ *)
++let stmt_secu_tpl (l:lens) (s:string) =
++                  [ indent . l . sep_spc . label s . bare_to_scl . sep_scl . eos ] |
++                  [ indent . l . sep_spc . label s . dquote_any . 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_com = /[^ \t\n,\(\)][^,\(\)]*[^ \t\n,\(\)]|[^ \t\n,\(\)]+/ | word . /[ \t]*\([^)]*\)/
++(* this is already the most complicated part of this module and it's about to
++ * get worse.  match statements can be way more complicated than this
++ *
++ * examples:
++ *      using or:
++ *      match if ((option vendor-class-identifier="Banana Bready") or (option vendor-class-identifier="Cherry Sunfire"));
++ *      unneeded parenthesis:
++ *      match if (option vendor-class-identifier="Hello");
++ *
++ *      and of course the fact that the above two rules used one of infinately
++ *      many potential options instead of a builtin function.
++ *)
++(* sto_com doesn't support quoted strings as arguments.  It also doesn't
++   support single arguments (needs to match a comma) It will need to be
++   updated for lcase, ucase and log to be workable.
++
++   it also doesn't support no arguments, so gethostbyname() doesn't work.
++
++   option and config-option are considered operators.  They should be matched
++   in stmt_entry but also available under "match if" and "if" conditionals
++   leased-address, host-decl-name, both take no args and return a value.  We
++   might need to treat them as variable names in the parser.
++
++   things like this may be near-impossible to parse even with recursion
++   because we have no way of knowing when or if a subfunction takes arguments
++   set ClientMac = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6));
++
++   even if we could parse it, they could get arbitrarily complicated like:
++   binary-to-ascii(16, 8, ":", substring(hardware, 1, 6) and substring(hardware, 2, 3));
++
++   so at some point we may need to programmatically knock it off and tell
++   people to put weird stuff in an include file that augeas doesn't parse.
++
++   the other option is to change the API to not parse the if statement at all,
++   just pull in the conditional as a string.
++ *)
++
++let fct_re = "substring" | "binary-to-ascii" | "suffix" | "lcase" | "ucase"
++             | "gethostbyname" | "packet"
++             | "concat" | "reverse" | "encode-int"
++             | "extract-int" | "lease-time" | "client-state" | "exists" | "known" | "static"
++             | "pick-first-value" | "log" | "execute"
++
++(* not needs to be different because it's a negation of whatever happens next *)
++let op_re = "~="|"="|"~~"|"and"|"or"
++
++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_ifopt = [ dels "if" . sep_spc . key "option" . sep_spc . store word .
++                      sep_eq . ([ label "value" . bare_to_scl ]|[ label "value" . dquote_any ]) ]
++
++let stmt_match_func = [ store fct_re . sep_osp . label "function" . fct_args ] .
++                      sep_eq . ([ label "value" . bare_to_scl ]|[ label "value" . dquote_any ])
++
++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 (dels "if" . sep_spc . stmt_match_func | stmt_match_pfv | stmt_match_ifopt)
++
++(************************************************************************
++ *                         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
++                        | stmt_set
++                        | empty
++                        | comment
++
++let stmt_block_noarg_re = "pool" | "group"
++
++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"
++                      | "group"
++                      | "on"
++
++let stmt_block_arg (body:lens)
++                      = ([ indent . key stmt_block_arg_re . sep_spc . dquote_any . sep_obr . body* . sep_cbr ]
++                         |[ indent . key stmt_block_arg_re . sep_spc . bare_to_scl . sep_obr . body* . sep_cbr ]
++                         |[ indent . del /key/ "key" . label "key_block" . sep_spc . dquote_any . sep_obr . body* . sep_cbr . del /(;([ \t]*\n)*)?/ ""  ]
++                         |[ indent . del /key/ "key" . label "key_block" . sep_spc . bare_to_scl . sep_obr . body* . sep_cbr . del /(;([ \t]*\n)*)?/ "" ])
++
++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 conditional (body:lens) =
++     let condition         = /[^{ \r\t\n][^{\n]*[^{ \r\t\n]|[^{ \t\n\r]/
++  in let elsif = [ indent
++                 . Build.xchgs "elsif" "@elsif"
++                 . sep_spc
++                 . store condition
++                 . sep_obr
++                 . body*
++                 . sep_cbr ]
++  in let else = [  indent
++                 . Build.xchgs "else" "@else"
++                 . sep_obr
++                 . body*
++                 . sep_cbr ]
++  in [ indent
++     . Build.xchgs "if" "@if"
++     . sep_spc
++     . store condition
++     . sep_obr
++     . body*
++     . sep_cbr
++     . elsif*
++     . else? ]
++
++
++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
++    let lns4 = conditional body in
++    (lns1 | lns2 | lns3 | lns4 | stmt_entry)
++
++let rec lns_staging = stmt_entry|all_block lns_staging
++let lns = (lns_staging)*
+diff --git a/lenses/tests/test_dhcpd.aug b/lenses/tests/test_dhcpd.aug
+index 0af337c2..96630296 100644
+--- a/lenses/tests/test_dhcpd.aug
++++ b/lenses/tests/test_dhcpd.aug
+@@ -28,9 +28,6 @@ max-lease-time 7200;
+ # network, the authoritative directive should be uncommented.
+ authoritative;
+ 
+-allow booting;
+-allow bootp;
+-
+ # Use this to send dhcp log messages to a different log file (you also
+ # have to hack syslog.conf to complete the redirection).
+ log-facility local7;
+@@ -182,12 +179,7 @@ fixed-address 10.1.1.1;}}" =
+     }
+   }
+ 
+-test lns get "group fan-tas_tic { }" =
+-  { "group" = "fan-tas_tic" }
+-
+ test Dhcpd.stmt_secu get "allow members of \"foo\";" =  { "allow-members-of" = "foo" }
+-test Dhcpd.stmt_secu get "allow booting;" =  { "allow" = "booting" }
+-test Dhcpd.stmt_secu get "allow bootp;" =  { "allow" = "bootp" }
+ test Dhcpd.stmt_option get "option voip-boot-server code 66 = string;" =
+   { "rfc-code"
+     { "label" = "voip-boot-server" }
+@@ -195,30 +187,6 @@ test Dhcpd.stmt_option get "option voip-boot-server code 66 = string;" =
+     { "type" = "string" }
+   }
+ 
+-test Dhcpd.stmt_option get "option special-option code 25 = array of string;" =
+-  { "rfc-code"
+-    { "label" = "special-option" }
+-    { "code" = "25" }
+-    { "type" = "array of string" }
+-  }
+-
+-test Dhcpd.stmt_option get "option special-option code 25 = integer 32;" =
+-  { "rfc-code"
+-    { "label" = "special-option" }
+-    { "code" = "25" }
+-    { "type" = "integer 32" }
+-  }
+-
+-
+-test Dhcpd.stmt_option get "option special-option code 25 = array of integer 32;" =
+-  { "rfc-code"
+-    { "label" = "special-option" }
+-    { "code" = "25" }
+-    { "type" = "array of integer 32" }
+-  }
+-
+-
+-
+ test Dhcpd.lns get "authoritative;
+ log-facility local7;
+ ddns-update-style none;
+@@ -274,7 +242,7 @@ failover peer \"redondance01\" {
+     }
+   }
+   { "next-server" = "10.1.1.1" }
+-  { "failover peer" = "redondance01"
++  { "failover peer" = "\"redondance01\""
+     { "primary" }
+     { "address" = "10.1.1.1" }
+     { "port" = "647" }
+@@ -291,26 +259,6 @@ failover peer \"redondance01\" {
+     { "load balance max seconds" = "3" }
+   }
+ 
+-
+-(* test get and put for record types *)
+-let record_test = "option test_records code 123 = { string, ip-address, integer 32, ip6-address, domain-list };"
+-
+-test Dhcpd.lns get record_test =
+- { "rfc-code"
+-   { "label" = "test_records" }
+-   { "code" = "123" }
+-     { "record"
+-        { "1" = "string" }
+-        { "2" = "ip-address" }
+-        { "3" = "integer 32" }
+-        { "4" = "ip6-address" }
+-        { "5" = "domain-list" }
+-     }
+- }
+-
+-test Dhcpd.lns put record_test after set "/rfc-code[1]/code" "124" = 
+-  "option test_records code 124 = { string, ip-address, integer 32, ip6-address, domain-list };"
+-
+ test Dhcpd.lns get "
+ option CallManager code 150 = ip-address;
+ option slp-directory-agent true 10.1.1.1, 10.2.2.2;
+@@ -386,25 +334,6 @@ test Dhcpd.stmt_match get "match if substring (option dhcp-client-identifier, 1,
+     { "value" = "RAS" }
+   }
+ 
+-test Dhcpd.stmt_match get "match if suffix (option dhcp-client-identifier, 4) = \"RAS\";" =
+-  { "match"
+-    { "function" = "suffix"
+-      { "args"
+-        { "arg" = "option dhcp-client-identifier" }
+-        { "arg" = "4" }
+-      }
+-    }
+-    { "value" = "RAS" }
+-  }
+-
+-test Dhcpd.stmt_match get "match if option vendor-class-identifier=\"RAS\";" =
+-  { "match"
+-    { "option" = "vendor-class-identifier"
+-      { "value" = "RAS" }
+-    }
+-  }
+-
+-
+ test Dhcpd.lns get "match pick-first-value (option dhcp-client-identifier, hardware);" =
+   { "match"
+     { "function" = "pick-first-value"
+@@ -436,26 +365,12 @@ test Dhcpd.stmt_match get "match if binary-to-ascii(16, 32, \"\", substring(hard
+     { "value" = "1525400" }
+   }
+ 
+-test Dhcpd.lns get "subclass allocation-class-1 1:8:0:2b:4c:39:ad;" =
+-  { "subclass"
+-    { "name" = "allocation-class-1" }
+-    { "value" = "1:8:0:2b:4c:39:ad" }
+-  }
+-
+-
+ test Dhcpd.lns get "subclass \"allocation-class-1\" 1:8:0:2b:4c:39:ad;" =
+   { "subclass"
+     { "name" = "allocation-class-1" }
+     { "value" = "1:8:0:2b:4c:39:ad" }
+   }
+ 
+-test Dhcpd.lns get "subclass \"quoted class\" \"quoted value\";" =
+-  { "subclass"
+-    { "name" = "quoted class" }
+-    { "value" = "quoted value" }
+-  }
+-
+-
+ (* overall test *)
+ test Dhcpd.lns put conf after rm "/x" = conf
+ 
+@@ -477,130 +392,3 @@ filename \"pxelinux.0\";
+ test Dhcpd.lns put "subnet 172.16.0.0 netmask 255.255.255.0 {
+ }" after
+   set "subnet/filename" "pxelinux.0" = input311
+-
+-(* GH issue #34: support conditional structures *)
+-let gh34_empty = "if exists dhcp-parameter-request-list {
+-}\n"
+-
+-test Dhcpd.lns get gh34_empty =
+-  { "@if" = "exists dhcp-parameter-request-list" }
+-
+-let gh34_empty_multi = "subnet 192.168.100.0 netmask 255.255.255.0 {
+- if true {
+- } elsif false {
+- } else {
+- }
+-}\n"
+-
+-test Dhcpd.lns get gh34_empty_multi =
+-  { "subnet"
+-    { "network" = "192.168.100.0" }
+-    { "netmask" = "255.255.255.0" }
+-    { "@if" = "true"
+-      { "@elsif" = "false" }
+-      { "@else" } }
+-  }
+-
+-let gh34_simple = "if exists dhcp-parameter-request-list {
+-  default-lease-time 600;
+-  } else {
+-default-lease-time 200;
+-}\n"
+-
+-test Dhcpd.lns get gh34_simple =
+-  { "@if" = "exists dhcp-parameter-request-list"
+-    { "default-lease-time" = "600" }
+-    { "@else"
+-      { "default-lease-time" = "200" } } }
+-
+-test Dhcpd.lns get "omapi-key fookey;" =
+-  { "omapi-key" = "fookey" }
+-
+-(* almost all DHCP groups should support braces starting on the next line *)
+-test Dhcpd.lns get "class introduction
+-{
+-}" =
+-  { "class" = "introduction" }
+-
+-(* equals should work the same *)
+-test Dhcpd.lns get "option test_records code 123 =
+-                             string;" =
+- { "rfc-code"
+-   { "label" = "test_records" }
+-   { "code" = "123" }
+-   { "type" = "string" }
+- }
+-
+-test Dhcpd.lns get "deny members of \"Are things like () allowed?\";" =
+-  { "deny-members-of" = "Are things like () allowed?" }
+-
+-test Dhcpd.lns get "deny unknown clients;" =
+-  { "deny" = "unknown clients" }
+-test Dhcpd.lns get "deny known-clients;" =
+-  { "deny" = "known-clients" }
+-
+-test Dhcpd.lns get "set ClientMac = binary-to-ascii(16, 8, \":\" , substring(hardware, 1, 6));" =
+-  { "set" = "ClientMac"
+-    { "value" = "binary-to-ascii(16, 8, \":\" , substring(hardware, 1, 6))" }
+-  }
+-
+-test Dhcpd.lns get "set myvariable = foo;" =
+-  { "set" = "myvariable"
+-    { "value" = "foo" }
+-  }
+-
+-test Dhcpd.stmt_hardware get "hardware fddi 00:01:02:03:04:05;" =
+-  { "hardware"
+-    { "type" = "fddi" }
+-    { "address" = "00:01:02:03:04:05" }
+-  }
+-
+-test Dhcpd.lns get "on commit
+-{
+-  set test = thing;
+-}" =
+-  { "on" = "commit"
+-    { "set" = "test"
+-      { "value" = "thing" }
+-    }
+-  }
+-
+-(* key block get/put/set test *)
+-let key_tests = "key sample {
+-    algorithm hmac-md5;
+-    secret \"secret==\";
+-}
+-
+-key \"interesting\" { };
+-
+-key \"third key\" {
+-    secret \"two==\";
+-}"
+-
+-test Dhcpd.lns get key_tests =
+-  { "key_block" = "sample"
+-    { "algorithm"  = "hmac-md5" }
+-    { "secret" = "secret==" }
+-  }
+-  { "key_block" = "interesting" }
+-  { "key_block" = "third key"
+-    { "secret" = "two==" }
+-  }
+-
+-test Dhcpd.lns put key_tests after set "/key_block[1]" "sample2" =
+-  "key sample2 {
+-    algorithm hmac-md5;
+-    secret \"secret==\";
+-}
+-
+-key \"interesting\" { };
+-
+-key \"third key\" {
+-    secret \"two==\";
+-}"
+-
+-test Dhcpd.lns get "group \"hello\" { }" =
+-  { "group" = "hello" }
+-
+-test Dhcpd.lns get "class \"testing class with spaces and quotes and ()\" {}" =
+-  { "class" = "testing class with spaces and quotes and ()" }
+diff --git a/lenses/tests/test_dhcpd_140.aug b/lenses/tests/test_dhcpd_140.aug
+new file mode 100644
+index 00000000..9d6fdc88
+--- /dev/null
++++ b/lenses/tests/test_dhcpd_140.aug
+@@ -0,0 +1,606 @@
++module Test_dhcpd_140 =
++
++let lns = Dhcpd_140.lns
++
++let conf = "#
++# Sample configuration file for ISC dhcpd for Debian
++#
++# Attention: If /etc/ltsp/dhcpd.conf exists, that will be used as
++# configuration file instead of this file.
++#
++# $Id: dhcpd.conf,v 1.1.1.1 2002/05/21 00:07:44 peloy Exp $
++#
++
++# The ddns-updates-style parameter controls whether or not the server will
++# attempt to do a DNS update when a lease is confirmed. We default to the
++# behavior of the version 2 packages ('none', since DHCP v2 didn't
++# have support for DDNS.)
++ddns-update-style none;
++
++# option definitions common to all supported networks...
++option domain-name \"example.org\";
++option domain-name-servers ns1.example.org, ns2.example.org;
++
++default-lease-time 600;
++max-lease-time 7200;
++
++# If this DHCP server is the official DHCP server for the local
++# network, the authoritative directive should be uncommented.
++authoritative;
++
++allow booting;
++allow bootp;
++
++# Use this to send dhcp log messages to a different log file (you also
++# have to hack syslog.conf to complete the redirection).
++log-facility local7;
++
++# No service will be given on this subnet, but declaring it helps the
++# DHCP server to understand the network topology.
++
++subnet 10.152.187.0 netmask 255.255.255.0 {
++}
++
++# This is a very basic subnet declaration.
++
++subnet 10.254.239.0 netmask 255.255.255.224 {
++  range 10.254.239.10 10.254.239.20;
++  option routers rtr-239-0-1.example.org, rtr-239-0-2.example.org;
++}
++
++# This declaration allows BOOTP clients to get dynamic addresses,
++# which we don't really recommend.
++
++subnet 10.254.239.32 netmask 255.255.255.224 {
++  range dynamic-bootp 10.254.239.40 10.254.239.60;
++  option broadcast-address 10.254.239.31;
++  option routers rtr-239-32-1.example.org;
++}
++
++# A slightly different configuration for an internal subnet.
++subnet 10.5.5.0 netmask 255.255.255.224 {
++  range 10.5.5.26 10.5.5.30;
++  option domain-name-servers ns1.internal.example.org;
++  option domain-name \"internal.example.org\";
++  option routers 10.5.5.1;
++  option broadcast-address 10.5.5.31;
++  default-lease-time 600;
++  max-lease-time 7200;
++}
++
++# Hosts which require special configuration options can be listed in
++# host statements.   If no address is specified, the address will be
++# allocated dynamically (if possible), but the host-specific information
++# will still come from the host declaration.
++
++host passacaglia {
++  hardware ethernet 0:0:c0:5d:bd:95;
++  filename \"vmunix.passacaglia\";
++  server-name \"toccata.fugue.com\";
++}
++
++# Fixed IP addresses can also be specified for hosts.   These addresses
++# should not also be listed as being available for dynamic assignment.
++# Hosts for which fixed IP addresses have been specified can boot using
++# BOOTP or DHCP.   Hosts for which no fixed address is specified can only
++# be booted with DHCP, unless there is an address range on the subnet
++# to which a BOOTP client is connected which has the dynamic-bootp flag
++# set.
++host fantasia {
++  hardware ethernet 08:00:07:26:c0:a5;
++  fixed-address fantasia.fugue.com;
++}
++
++# You can declare a class of clients and then do address allocation
++# based on that.   The example below shows a case where all clients
++# in a certain class get addresses on the 10.17.224/24 subnet, and all
++# other clients get addresses on the 10.0.29/24 subnet.
++
++#class \"foo\" {
++#  match if substring (option vendor-class-identifier, 0, 4) = \"SUNW\";
++#}
++
++shared-network 224-29 {
++  subnet 10.17.224.0 netmask 255.255.255.0 {
++    option routers rtr-224.example.org;
++  }
++  subnet 10.0.29.0 netmask 255.255.255.0 {
++    option routers rtr-29.example.org;
++  }
++  pool {
++    allow members of \"foo\";
++    range 10.17.224.10 10.17.224.250;
++  }
++  pool {
++    deny members of \"foo\";
++    range 10.0.29.10 10.0.29.230;
++  }
++}
++"
++
++test lns get "authoritative;" = { "authoritative" }
++test lns get "ddns-update-style none;" = { "ddns-update-style" = "none" }
++test lns get "option domain-name \"example.org\";" =
++  { "option"
++    { "domain-name"
++      { "arg" = "example.org" }
++    }
++  }
++
++test lns get "option domain-name-servers ns1.example.org, ns2.example.org;" =
++  { "option"
++    { "domain-name-servers"
++      { "arg" = "ns1.example.org" }
++      { "arg" = "ns2.example.org" }
++    }
++  }
++
++test lns get "default-lease-time 600;" = { "default-lease-time" = "600" }
++test lns get "range 10.254.239.60;" =
++{ "range"
++    { "to" = "10.254.239.60" }
++  }
++
++test lns get "range dynamic-bootp 10.254.239.60;" =
++  { "range"
++    { "flag" = "dynamic-bootp" }
++    { "to" = "10.254.239.60" }
++  }
++
++test lns get "range dynamic-bootp 10.254.239.40 10.254.239.60;" =
++  { "range"
++    { "flag" = "dynamic-bootp" }
++    { "from" = "10.254.239.40" }
++    { "to" = "10.254.239.60" }
++  }
++
++test lns get "subnet 10.152.187.0 netmask 255.255.255.0 {}\n" =
++  { "subnet"
++    { "network" = "10.152.187.0" }
++    { "netmask" = "255.255.255.0" }
++  }
++
++test lns get " pool {
++    pool {
++
++    }
++}
++" =
++  { "pool"
++    { "pool" }
++  }
++
++test lns get "group { host some-host {hardware ethernet 00:00:aa:bb:cc:dd;
++fixed-address 10.1.1.1;}}" =
++  { "group"
++    { "host" = "some-host"
++      { "hardware"
++        { "type" = "ethernet" }
++        { "address" = "00:00:aa:bb:cc:dd" }
++      }
++      { "fixed-address" = "10.1.1.1" }
++    }
++  }
++
++test lns get "group fan-tas_tic { }" =
++  { "group" = "fan-tas_tic" }
++
++test Dhcpd_140.stmt_secu get "allow members of \"foo\";" =  { "allow-members-of" = "foo" }
++test Dhcpd_140.stmt_secu get "allow booting;" =  { "allow" = "booting" }
++test Dhcpd_140.stmt_secu get "allow bootp;" =  { "allow" = "bootp" }
++test Dhcpd_140.stmt_option get "option voip-boot-server code 66 = string;" =
++  { "rfc-code"
++    { "label" = "voip-boot-server" }
++    { "code" = "66" }
++    { "type" = "string" }
++  }
++
++test Dhcpd_140.stmt_option get "option special-option code 25 = array of string;" =
++  { "rfc-code"
++    { "label" = "special-option" }
++    { "code" = "25" }
++    { "type" = "array of string" }
++  }
++
++test Dhcpd_140.stmt_option get "option special-option code 25 = integer 32;" =
++  { "rfc-code"
++    { "label" = "special-option" }
++    { "code" = "25" }
++    { "type" = "integer 32" }
++  }
++
++
++test Dhcpd_140.stmt_option get "option special-option code 25 = array of integer 32;" =
++  { "rfc-code"
++    { "label" = "special-option" }
++    { "code" = "25" }
++    { "type" = "array of integer 32" }
++  }
++
++
++
++test Dhcpd_140.lns get "authoritative;
++log-facility local7;
++ddns-update-style none;
++default-lease-time 21600;
++max-lease-time 43200;
++
++# Additional options for VOIP
++option voip-boot-server code 66 = string;
++option voip-vlan-id code 128 = string;
++" =
++  { "authoritative" }
++  { "log-facility" = "local7" }
++  { "ddns-update-style" = "none" }
++  { "default-lease-time" = "21600" }
++  { "max-lease-time" = "43200"
++    { "#comment" = "Additional options for VOIP" }
++  }
++  { "rfc-code"
++    { "label" = "voip-boot-server" }
++    { "code" = "66" }
++    { "type" = "string" }
++  }
++  { "rfc-code"
++    { "label" = "voip-vlan-id" }
++    { "code" = "128" }
++    { "type" = "string" }
++  }
++
++
++test Dhcpd_140.lns get "
++option domain-name-servers 10.1.1.1, 10.11.2.1, 10.1.3.1;
++next-server 10.1.1.1;
++
++failover peer \"redondance01\" {
++         primary;
++         address 10.1.1.1;
++         port 647;
++         peer address 10.1.1.1;
++         peer port 647;
++         max-response-delay 20;
++         max-unacked-updates 10;
++         mclt 3600;         #comment.
++         split 128;         #comment.
++         load balance max seconds 3;
++       }
++" =
++  {  }
++  { "option"
++    { "domain-name-servers"
++      { "arg" = "10.1.1.1" }
++      { "arg" = "10.11.2.1" }
++      { "arg" = "10.1.3.1" }
++    }
++  }
++  { "next-server" = "10.1.1.1" }
++  { "failover peer" = "redondance01"
++    { "primary" }
++    { "address" = "10.1.1.1" }
++    { "port" = "647" }
++    { "peer address" = "10.1.1.1" }
++    { "peer port" = "647" }
++    { "max-response-delay" = "20" }
++    { "max-unacked-updates" = "10" }
++    { "mclt" = "3600"
++      { "#comment" = "comment." }
++    }
++    { "split" = "128"
++      { "#comment" = "comment." }
++    }
++    { "load balance max seconds" = "3" }
++  }
++
++
++(* test get and put for record types *)
++let record_test = "option test_records code 123 = { string, ip-address, integer 32, ip6-address, domain-list };"
++
++test Dhcpd_140.lns get record_test =
++ { "rfc-code"
++   { "label" = "test_records" }
++   { "code" = "123" }
++     { "record"
++        { "1" = "string" }
++        { "2" = "ip-address" }
++        { "3" = "integer 32" }
++        { "4" = "ip6-address" }
++        { "5" = "domain-list" }
++     }
++ }
++
++test Dhcpd_140.lns put record_test after set "/rfc-code[1]/code" "124" = 
++  "option test_records code 124 = { string, ip-address, integer 32, ip6-address, domain-list };"
++
++test Dhcpd_140.lns get "
++option CallManager code 150 = ip-address;
++option slp-directory-agent true 10.1.1.1, 10.2.2.2;
++option slp-service-scope true \"SLP-GLOBAL\";
++option nds-context \"EXAMPLE\";
++option nds-tree-name \"EXAMPLE\";
++" =
++  {  }
++  { "rfc-code"
++    { "label" = "CallManager" }
++    { "code" = "150" }
++    { "type" = "ip-address" }
++  }
++  { "option"
++    { "slp-directory-agent" = "true"
++      { "arg" = "10.1.1.1" }
++      { "arg" = "10.2.2.2" }
++    }
++  }
++  { "option"
++    { "slp-service-scope" = "true"
++      { "arg" = "SLP-GLOBAL" }
++    }
++  }
++  { "option"
++    { "nds-context"
++      { "arg" = "EXAMPLE" }
++    }
++  }
++  { "option"
++    { "nds-tree-name"
++      { "arg" = "EXAMPLE" }
++    }
++  }
++
++
++test Dhcpd_140.lns get "option voip-vlan-id \"VLAN=1234;\";" =
++  { "option"
++    { "voip-vlan-id"
++      { "arg" = "VLAN=1234;" }
++    }
++  }
++
++test Dhcpd_140.lns get "option domain-name \"x.example.com y.example.com z.example.com\";" =
++  { "option"
++    { "domain-name"
++      { "arg" = "x.example.com y.example.com z.example.com" }
++    }
++  }
++
++test Dhcpd_140.lns get "include \"/etc/dhcpd.master\";" =
++  { "include" = "/etc/dhcpd.master" }
++
++test Dhcpd_140.lns put "\n" after set "/include" "/etc/dhcpd.master" =
++  "\ninclude \"/etc/dhcpd.master\";\n"
++
++test Dhcpd_140.fct_args get "(option dhcp-client-identifier, 1, 3)" =
++  { "args"
++    { "arg" = "option dhcp-client-identifier" }
++    { "arg" = "1" }
++    { "arg" = "3" }
++  }
++
++test Dhcpd_140.stmt_match get "match if substring (option dhcp-client-identifier, 1, 3) = \"RAS\";" =
++  { "match"
++    { "function" = "substring"
++      { "args"
++        { "arg" = "option dhcp-client-identifier" }
++        { "arg" = "1" }
++        { "arg" = "3" }
++      }
++    }
++    { "value" = "RAS" }
++  }
++
++test Dhcpd_140.stmt_match get "match if suffix (option dhcp-client-identifier, 4) = \"RAS\";" =
++  { "match"
++    { "function" = "suffix"
++      { "args"
++        { "arg" = "option dhcp-client-identifier" }
++        { "arg" = "4" }
++      }
++    }
++    { "value" = "RAS" }
++  }
++
++test Dhcpd_140.stmt_match get "match if option vendor-class-identifier=\"RAS\";" =
++  { "match"
++    { "option" = "vendor-class-identifier"
++      { "value" = "RAS" }
++    }
++  }
++
++
++test Dhcpd_140.lns get "match pick-first-value (option dhcp-client-identifier, hardware);" =
++  { "match"
++    { "function" = "pick-first-value"
++      { "args"
++        { "arg" = "option dhcp-client-identifier" }
++        { "arg" = "hardware"  }
++      }
++    }
++  }
++
++test Dhcpd_140.fct_args get "(16, 32, \"\", substring(hardware, 0, 4))" =
++  { "args"
++    { "arg" = "16" }
++    { "arg" = "32" }
++    { "arg" = "\"\"" }
++    { "arg" = "substring(hardware, 0, 4)" }
++  }
++
++test Dhcpd_140.stmt_match get "match if binary-to-ascii(16, 32, \"\", substring(hardware, 0, 4)) = \"1525400\";" =
++  { "match"
++    { "function" = "binary-to-ascii"
++      { "args"
++        { "arg" = "16" }
++        { "arg" = "32" }
++        { "arg" = "\"\"" }
++        { "arg" = "substring(hardware, 0, 4)" }
++      }
++    }
++    { "value" = "1525400" }
++  }
++
++test Dhcpd_140.lns get "subclass allocation-class-1 1:8:0:2b:4c:39:ad;" =
++  { "subclass"
++    { "name" = "allocation-class-1" }
++    { "value" = "1:8:0:2b:4c:39:ad" }
++  }
++
++
++test Dhcpd_140.lns get "subclass \"allocation-class-1\" 1:8:0:2b:4c:39:ad;" =
++  { "subclass"
++    { "name" = "allocation-class-1" }
++    { "value" = "1:8:0:2b:4c:39:ad" }
++  }
++
++test Dhcpd_140.lns get "subclass \"quoted class\" \"quoted value\";" =
++  { "subclass"
++    { "name" = "quoted class" }
++    { "value" = "quoted value" }
++  }
++
++
++(* overall test *)
++test Dhcpd_140.lns put conf after rm "/x" = conf
++
++(* bug #293: primary should support argument *)
++let input293 = "zone EXAMPLE.ORG. {
++  primary 127.0.0.1;
++}"
++
++test Dhcpd_140.lns get input293 = 
++  { "zone" = "EXAMPLE.ORG."
++    { "primary" = "127.0.0.1" }
++  }
++
++(* bug #311: filename should be quoted *)
++let input311 = "subnet 172.16.0.0 netmask 255.255.255.0 {
++filename \"pxelinux.0\";
++}"
++
++test Dhcpd_140.lns put "subnet 172.16.0.0 netmask 255.255.255.0 {
++}" after
++  set "subnet/filename" "pxelinux.0" = input311
++
++(* GH issue #34: support conditional structures *)
++let gh34_empty = "if exists dhcp-parameter-request-list {
++}\n"
++
++test Dhcpd_140.lns get gh34_empty =
++  { "@if" = "exists dhcp-parameter-request-list" }
++
++let gh34_empty_multi = "subnet 192.168.100.0 netmask 255.255.255.0 {
++ if true {
++ } elsif false {
++ } else {
++ }
++}\n"
++
++test Dhcpd_140.lns get gh34_empty_multi =
++  { "subnet"
++    { "network" = "192.168.100.0" }
++    { "netmask" = "255.255.255.0" }
++    { "@if" = "true"
++      { "@elsif" = "false" }
++      { "@else" } }
++  }
++
++let gh34_simple = "if exists dhcp-parameter-request-list {
++  default-lease-time 600;
++  } else {
++default-lease-time 200;
++}\n"
++
++test Dhcpd_140.lns get gh34_simple =
++  { "@if" = "exists dhcp-parameter-request-list"
++    { "default-lease-time" = "600" }
++    { "@else"
++      { "default-lease-time" = "200" } } }
++
++test Dhcpd_140.lns get "omapi-key fookey;" =
++  { "omapi-key" = "fookey" }
++
++(* almost all DHCP groups should support braces starting on the next line *)
++test Dhcpd_140.lns get "class introduction
++{
++}" =
++  { "class" = "introduction" }
++
++(* equals should work the same *)
++test Dhcpd_140.lns get "option test_records code 123 =
++                             string;" =
++ { "rfc-code"
++   { "label" = "test_records" }
++   { "code" = "123" }
++   { "type" = "string" }
++ }
++
++test Dhcpd_140.lns get "deny members of \"Are things like () allowed?\";" =
++  { "deny-members-of" = "Are things like () allowed?" }
++
++test Dhcpd_140.lns get "deny unknown clients;" =
++  { "deny" = "unknown clients" }
++test Dhcpd_140.lns get "deny known-clients;" =
++  { "deny" = "known-clients" }
++
++test Dhcpd_140.lns get "set ClientMac = binary-to-ascii(16, 8, \":\" , substring(hardware, 1, 6));" =
++  { "set" = "ClientMac"
++    { "value" = "binary-to-ascii(16, 8, \":\" , substring(hardware, 1, 6))" }
++  }
++
++test Dhcpd_140.lns get "set myvariable = foo;" =
++  { "set" = "myvariable"
++    { "value" = "foo" }
++  }
++
++test Dhcpd_140.stmt_hardware get "hardware fddi 00:01:02:03:04:05;" =
++  { "hardware"
++    { "type" = "fddi" }
++    { "address" = "00:01:02:03:04:05" }
++  }
++
++test Dhcpd_140.lns get "on commit
++{
++  set test = thing;
++}" =
++  { "on" = "commit"
++    { "set" = "test"
++      { "value" = "thing" }
++    }
++  }
++
++(* key block get/put/set test *)
++let key_tests = "key sample {
++    algorithm hmac-md5;
++    secret \"secret==\";
++}
++
++key \"interesting\" { };
++
++key \"third key\" {
++    secret \"two==\";
++}"
++
++test Dhcpd_140.lns get key_tests =
++  { "key_block" = "sample"
++    { "algorithm"  = "hmac-md5" }
++    { "secret" = "secret==" }
++  }
++  { "key_block" = "interesting" }
++  { "key_block" = "third key"
++    { "secret" = "two==" }
++  }
++
++test Dhcpd_140.lns put key_tests after set "/key_block[1]" "sample2" =
++  "key sample2 {
++    algorithm hmac-md5;
++    secret \"secret==\";
++}
++
++key \"interesting\" { };
++
++key \"third key\" {
++    secret \"two==\";
++}"
++
++test Dhcpd_140.lns get "group \"hello\" { }" =
++  { "group" = "hello" }
++
++test Dhcpd_140.lns get "class \"testing class with spaces and quotes and ()\" {}" =
++  { "class" = "testing class with spaces and quotes and ()" }
+diff --git a/tests/Makefile.am b/tests/Makefile.am
+index 387ac7d2..315cac9c 100644
+--- a/tests/Makefile.am
++++ b/tests/Makefile.am
+@@ -58,6 +58,7 @@ lens_tests =			\
+   lens-device_map.sh    \
+   lens-dhclient.sh		\
+   lens-dhcpd.sh		\
++  lens-dhcpd_140.sh		\
+   lens-dns_zone.sh		\
+   lens-dnsmasq.sh		\
+   lens-dovecot.sh		\
+-- 
+2.17.2
+
diff --git a/SOURCES/0008-Slapd-revert-Slapd-module-to-1.1.0-compatible-add-Sl.patch b/SOURCES/0008-Slapd-revert-Slapd-module-to-1.1.0-compatible-add-Sl.patch
new file mode 100644
index 0000000..6f2846a
--- /dev/null
+++ b/SOURCES/0008-Slapd-revert-Slapd-module-to-1.1.0-compatible-add-Sl.patch
@@ -0,0 +1,418 @@
+From faf60bc7b1cb727482a17de9a2483998763978c0 Mon Sep 17 00:00:00 2001
+From: Dominic Cleal <dcleal@redhat.com>
+Date: Fri, 12 Jun 2015 11:14:32 +0100
+Subject: [PATCH] Slapd: revert Slapd module to 1.1.0-compatible, add Slapd_140
+
+In order to keep the default sshd config lens compatible with 1.1.0,
+the lens from 1.4.0 has been kept in the Slapd_140 module and is not
+loaded by default.  Use aug_transform, augtool --transform etc. to use
+it instead of Slapd.
+---
+ lenses/slapd.aug                |  18 ++--
+ lenses/slapd_140.aug            | 158 ++++++++++++++++++++++++++++++++
+ lenses/tests/test_slapd.aug     |  55 +++--------
+ lenses/tests/test_slapd_140.aug |  94 +++++++++++++++++++
+ tests/Makefile.am               |   1 +
+ 5 files changed, 273 insertions(+), 53 deletions(-)
+ create mode 100644 lenses/slapd_140.aug
+ create mode 100644 lenses/tests/test_slapd_140.aug
+
+diff --git a/lenses/slapd.aug b/lenses/slapd.aug
+index e1195655..afe074b1 100644
+--- a/lenses/slapd.aug
++++ b/lenses/slapd.aug
+@@ -18,6 +18,7 @@ let sep         = del /[ \t\n]+/ " "
+ 
+ let sto_to_eol  = store /([^ \t\n].*[^ \t\n]|[^ \t\n])/
+ let sto_to_spc  = store /[^\\# \t\n]+/
++let sto_to_by   = store (/[^\\# \t\n]+/ - "by")
+ 
+ let comment     = Util.comment
+ let empty       = Util.empty
+@@ -27,14 +28,12 @@ let empty       = Util.empty
+  *************************************************************************)
+ 
+ let access_re   = "access to"
+-let control_re  = "stop" | "continue" | "break"
+-let what        = [ spc . label "access"
+-                  . store (/[^\\# \t\n]+/ - ("by" | control_re)) ]
++let who         = [ spc . label "who"     . sto_to_spc ]
++let what        = [ spc . label "what"    . sto_to_spc ]
+ 
+ (* TODO: parse the control field, see man slapd.access (5) *)
+-let control     = [ spc . label "control" . store control_re ]
+-let by          = [ sep . key "by" . spc . sto_to_spc
+-                  . what? . control? ]
++let control     = [ spc . label "control" . sto_to_by  ]
++let by          = [ sep . key "by". who . what. control? ]
+ 
+ let access      = [ key access_re . spc. sto_to_spc . by+ . eol ]
+ 
+@@ -134,21 +133,18 @@ let database_re = "suffix"
+                 | "restrict"
+                 | "rootdn"
+                 | "rootpw"
++                | "suffix"
+                 | "subordinate"
+                 | "syncrepl rid"
+                 | "updatedn"
+                 | "updateref"
+                 | database_hdb
+ 
+-let database_entry =
+-     let val = Quote.double_opt
+-  in Build.key_value_line database_re Sep.space val
+-
+ let database    = [ key "database"
+                   . spc
+                   . sto_to_eol
+                   . eol
+-                  . (comment|empty|database_entry|access)* ]
++                  . (comment|empty|Build.key_ws_value database_re|access)* ]
+ 
+ (************************************************************************
+  *                              LENS
+diff --git a/lenses/slapd_140.aug b/lenses/slapd_140.aug
+new file mode 100644
+index 00000000..8d1cd074
+--- /dev/null
++++ b/lenses/slapd_140.aug
+@@ -0,0 +1,158 @@
++(* Slapd module for Augeas
++   This module is compatible with Augeas 1.4.0, but is not loaded by default.
++
++   Author: Free Ekanayaka <free@64studio.com>
++
++   Reference: man slapd.conf(5), man slapd.access (5)
++
++*)
++
++module Slapd_140 =
++
++(************************************************************************
++ *                           USEFUL PRIMITIVES
++ *************************************************************************)
++
++let eol         = Util.eol
++let spc         = Util.del_ws_spc
++let sep         = del /[ \t\n]+/ " "
++
++let sto_to_eol  = store /([^ \t\n].*[^ \t\n]|[^ \t\n])/
++let sto_to_spc  = store /[^\\# \t\n]+/
++
++let comment     = Util.comment
++let empty       = Util.empty
++
++(************************************************************************
++ *                           ACCESS TO
++ *************************************************************************)
++
++let access_re   = "access to"
++let control_re  = "stop" | "continue" | "break"
++let what        = [ spc . label "access"
++                  . store (/[^\\# \t\n]+/ - ("by" | control_re)) ]
++
++(* TODO: parse the control field, see man slapd.access (5) *)
++let control     = [ spc . label "control" . store control_re ]
++let by          = [ sep . key "by" . spc . sto_to_spc
++                  . what? . control? ]
++
++let access      = [ key access_re . spc. sto_to_spc . by+ . eol ]
++
++(************************************************************************
++ *                             GLOBAL
++ *************************************************************************)
++
++(* TODO: parse special field separately, see man slapd.conf (5) *)
++let global_re   = "allow"
++                | "argsfile"
++                | "attributeoptions"
++                | "attributetype"
++                | "authz-policy"
++                | "ldap"
++                | "dn"
++                | "concurrency"
++                | "cron_max_pending"
++                | "conn_max_pending_auth"
++                | "defaultsearchbase"
++                | "disallow"
++                | "ditcontentrule"
++                | "gentlehup"
++                | "idletimeout"
++                | "include"
++                | "index_substr_if_minlen"
++                | "index_substr_if_maxlen"
++                | "index_substr_any_len"
++                | "index_substr_any_step"
++                | "localSSF"
++                | "loglevel"
++                | "moduleload"
++                | "modulepath"
++                | "objectclass"
++                | "objectidentifier"
++                | "password-hash"
++                | "password-crypt-salt-format"
++                | "pidfile"
++                | "referral"
++                | "replica-argsfile"
++                | "replica-pidfile"
++                | "replicationinterval"
++                | "require"
++                | "reverse-lookup"
++                | "rootDSE"
++                | "sasl-host "
++                | "sasl-realm"
++                | "sasl-secprops"
++                | "schemadn"
++                | "security"
++                | "sizelimit"
++                | "sockbuf_max_incoming "
++                | "sockbuf_max_incoming_auth"
++                | "threads"
++                | "timelimit time"
++                | "tool-threads"
++                | "TLSCipherSuite"
++                | "TLSCACertificateFile"
++                | "TLSCACertificatePath"
++                | "TLSCertificateFile"
++                | "TLSCertificateKeyFile"
++                | "TLSDHParamFile"
++                | "TLSRandFile"
++                | "TLSVerifyClient"
++                | "TLSCRLCheck"
++                | "backend"
++
++let global     = Build.key_ws_value global_re
++
++(************************************************************************
++ *                             DATABASE
++ *************************************************************************)
++
++(* TODO: support all types of database backend *)
++let database_hdb = "cachesize"
++                | "cachefree"
++                | "checkpoint"
++                | "dbconfig"
++                | "dbnosync"
++                | "directory"
++                | "dirtyread"
++                | "idlcachesize"
++                | "index"
++                | "linearindex"
++                | "lockdetect"
++                | "mode"
++                | "searchstack"
++                | "shm_key"
++
++let database_re = "suffix"
++                | "lastmod"
++                | "limits"
++                | "maxderefdepth"
++                | "overlay"
++                | "readonly"
++                | "replica uri"
++                | "replogfile"
++                | "restrict"
++                | "rootdn"
++                | "rootpw"
++                | "subordinate"
++                | "syncrepl rid"
++                | "updatedn"
++                | "updateref"
++                | database_hdb
++
++let database_entry =
++     let val = Quote.double_opt
++  in Build.key_value_line database_re Sep.space val
++
++let database    = [ key "database"
++                  . spc
++                  . sto_to_eol
++                  . eol
++                  . (comment|empty|database_entry|access)* ]
++
++(************************************************************************
++ *                              LENS
++ *************************************************************************)
++
++let lns         = (comment|empty|global|access)* . (database)*
+diff --git a/lenses/tests/test_slapd.aug b/lenses/tests/test_slapd.aug
+index a4bbb4e9..e477342a 100644
+--- a/lenses/tests/test_slapd.aug
++++ b/lenses/tests/test_slapd.aug
+@@ -48,47 +48,18 @@ test Slapd.lns get conf =
+   { "database" = "hdb"
+      {}
+      { "#comment" = "The base of your directory in database #1" }
+-     { "suffix"   = "dc=nodomain" }
++     { "suffix"   = "\"dc=nodomain\"" }
+      {}
+      { "access to" = "attrs=userPassword,shadowLastChange"
+-        { "by" = "dn=\"cn=admin,dc=nodomain\""
+-           { "access" = "write" } }
+-        { "by" = "anonymous"
+-           { "access" = "auth" } }
+-        { "by" = "self"
+-           { "access" = "write" } }
+-        { "by" = "*"
+-           { "access" = "none" } } } }
+-
+-(* Test: Slapd.lns
+-     Full access test with who/access/control *)
+-test Slapd.lns get "access to dn.subtree=\"dc=example,dc=com\"
+-  by self write stop\n" =
+-  { "access to" = "dn.subtree=\"dc=example,dc=com\""
+-    { "by" = "self"
+-      { "access" = "write" }
+-      { "control" = "stop" } } }
+-
+-(* Test: Slapd.lns
+-     access test with who *)
+-test Slapd.lns get "access to dn.subtree=\"dc=example,dc=com\"
+-  by self\n" =
+-  { "access to" = "dn.subtree=\"dc=example,dc=com\""
+-    { "by" = "self" } }
+-
+-(* Test: Slapd.lns
+-     access test with who/access *)
+-test Slapd.lns get "access to dn.subtree=\"dc=example,dc=com\"
+-  by self write\n" =
+-  { "access to" = "dn.subtree=\"dc=example,dc=com\""
+-    { "by" = "self"
+-      { "access" = "write" } } }
+-
+-(* Test: Slapd.lns
+-     access test with who/control *)
+-test Slapd.lns get "access to dn.subtree=\"dc=example,dc=com\"
+-  by self stop\n" =
+-  { "access to" = "dn.subtree=\"dc=example,dc=com\""
+-    { "by" = "self"
+-      { "control" = "stop" } } }
+-
++        { "by"
++           { "who" = "dn=\"cn=admin,dc=nodomain\"" }
++           { "what" = "write" } }
++        { "by"
++           { "who" = "anonymous" }
++           { "what" = "auth" } }
++        { "by"
++           { "who" = "self" }
++           { "what" = "write" } }
++        { "by"
++           { "who" = "*" }
++           { "what" = "none" } } } }
+diff --git a/lenses/tests/test_slapd_140.aug b/lenses/tests/test_slapd_140.aug
+new file mode 100644
+index 00000000..0118f030
+--- /dev/null
++++ b/lenses/tests/test_slapd_140.aug
+@@ -0,0 +1,94 @@
++module Test_slapd_140 =
++
++let conf = "# This is the main slapd configuration file. See slapd.conf(5) for more
++# info on the configuration options.
++
++#######################################################################
++# Global Directives:
++
++# Features to permit
++#allow bind_v2
++
++# Schema and objectClass definitions
++include         /etc/ldap/schema/core.schema
++
++#######################################################################
++# Specific Directives for database #1, of type hdb:
++# Database specific directives apply to this databasse until another
++# 'database' directive occurs
++database        hdb
++
++# The base of your directory in database #1
++suffix          \"dc=nodomain\"
++
++access to attrs=userPassword,shadowLastChange
++        by dn=\"cn=admin,dc=nodomain\" write
++        by anonymous auth
++        by self write
++        by * none
++"
++
++test Slapd_140.lns get conf =
++  { "#comment" = "This is the main slapd configuration file. See slapd.conf(5) for more" }
++  { "#comment" = "info on the configuration options." }
++  {}
++  { "#comment" = "######################################################################" }
++  { "#comment" = "Global Directives:"}
++  {}
++  { "#comment" = "Features to permit" }
++  { "#comment" = "allow bind_v2" }
++  {}
++  { "#comment" = "Schema and objectClass definitions" }
++  { "include"  = "/etc/ldap/schema/core.schema" }
++  {}
++  { "#comment" = "######################################################################" }
++  { "#comment" = "Specific Directives for database #1, of type hdb:" }
++  { "#comment" = "Database specific directives apply to this databasse until another" }
++  { "#comment" = "'database' directive occurs" }
++  { "database" = "hdb"
++     {}
++     { "#comment" = "The base of your directory in database #1" }
++     { "suffix"   = "dc=nodomain" }
++     {}
++     { "access to" = "attrs=userPassword,shadowLastChange"
++        { "by" = "dn=\"cn=admin,dc=nodomain\""
++           { "access" = "write" } }
++        { "by" = "anonymous"
++           { "access" = "auth" } }
++        { "by" = "self"
++           { "access" = "write" } }
++        { "by" = "*"
++           { "access" = "none" } } } }
++
++(* Test: Slapd_140.lns
++     Full access test with who/access/control *)
++test Slapd_140.lns get "access to dn.subtree=\"dc=example,dc=com\"
++  by self write stop\n" =
++  { "access to" = "dn.subtree=\"dc=example,dc=com\""
++    { "by" = "self"
++      { "access" = "write" }
++      { "control" = "stop" } } }
++
++(* Test: Slapd_140.lns
++     access test with who *)
++test Slapd_140.lns get "access to dn.subtree=\"dc=example,dc=com\"
++  by self\n" =
++  { "access to" = "dn.subtree=\"dc=example,dc=com\""
++    { "by" = "self" } }
++
++(* Test: Slapd_140.lns
++     access test with who/access *)
++test Slapd_140.lns get "access to dn.subtree=\"dc=example,dc=com\"
++  by self write\n" =
++  { "access to" = "dn.subtree=\"dc=example,dc=com\""
++    { "by" = "self"
++      { "access" = "write" } } }
++
++(* Test: Slapd_140.lns
++     access test with who/control *)
++test Slapd_140.lns get "access to dn.subtree=\"dc=example,dc=com\"
++  by self stop\n" =
++  { "access to" = "dn.subtree=\"dc=example,dc=com\""
++    { "by" = "self"
++      { "control" = "stop" } } }
++
+diff --git a/tests/Makefile.am b/tests/Makefile.am
+index 315cac9c..65d8993e 100644
+--- a/tests/Makefile.am
++++ b/tests/Makefile.am
+@@ -182,6 +182,7 @@ lens_tests =			\
+   lens-simplevars.sh	\
+   lens-sip_conf.sh	    \
+   lens-slapd.sh			\
++  lens-slapd_140.sh			\
+   lens-smbusers.sh			\
+   lens-solaris_system.sh		\
+   lens-soma.sh			\
+-- 
+2.17.2
+
diff --git a/SOURCES/0009-Rhsm-new-lens-to-parse-subscription-manager-s-rhsm.c.patch b/SOURCES/0009-Rhsm-new-lens-to-parse-subscription-manager-s-rhsm.c.patch
new file mode 100644
index 0000000..d5f28ac
--- /dev/null
+++ b/SOURCES/0009-Rhsm-new-lens-to-parse-subscription-manager-s-rhsm.c.patch
@@ -0,0 +1,258 @@
+From a90c028eae871422588037ea1a21aff080f77fd2 Mon Sep 17 00:00:00 2001
+From: Dominic Cleal <dcleal@redhat.com>
+Date: Fri, 3 Jul 2015 12:05:30 +0100
+Subject: [PATCH] Rhsm: new lens to parse subscription-manager's rhsm.conf
+
+(cherry picked from commit abdb9fbc4e8c0975f51a62e34ee2e22ed2d5c39f)
+
+Conflicts:
+	NEWS
+---
+ doc/naturaldocs/conf/lenses/Menu.txt |   2 +
+ lenses/rhsm.aug                      |  42 ++++++++
+ lenses/tests/test_rhsm.aug           | 151 +++++++++++++++++++++++++++
+ tests/Makefile.am                    |   1 +
+ 4 files changed, 196 insertions(+)
+ create mode 100644 lenses/rhsm.aug
+ create mode 100644 lenses/tests/test_rhsm.aug
+
+diff --git a/doc/naturaldocs/conf/lenses/Menu.txt b/doc/naturaldocs/conf/lenses/Menu.txt
+index c245446b..e74cd13a 100644
+--- a/doc/naturaldocs/conf/lenses/Menu.txt
++++ b/doc/naturaldocs/conf/lenses/Menu.txt
+@@ -151,6 +151,7 @@ Group: Specific Modules  {
+    File: Redis  (redis.aug)
+    File: Reprepro_Uploaders  (reprepro_uploaders.aug)
+    File: Resolv  (resolv.aug)
++   File: Rhsm  (rhsm.aug)
+    File: Rmt  (rmt.aug)
+    File: Rsyslog  (rsyslog.aug)
+    File: Schroot  (schroot.aug)
+@@ -261,6 +262,7 @@ Group: Tests and Examples  {
+    File: Test_Rabbitmq  (tests/test_rabbitmq.aug)
+    File: Test_Redis  (tests/test_redis.aug)
+    File: Test_Reprepro_Uploaders  (tests/test_reprepro_uploaders.aug)
++   File: Test_Rhsm  (tests/test_rhsm.aug)
+    File: Test_Rmt  (tests/test_rmt.aug)
+    File: Test_Rsyslog  (tests/test_rsyslog.aug)
+    File: Test_Simplelines  (tests/test_simplelines.aug)
+diff --git a/lenses/rhsm.aug b/lenses/rhsm.aug
+new file mode 100644
+index 00000000..56cc82ea
+--- /dev/null
++++ b/lenses/rhsm.aug
+@@ -0,0 +1,42 @@
++(*
++Module: Rhsm
++  Parses subscription-manager config files
++
++Author: Dominic Cleal <dcleal@redhat.com>
++
++About: Reference
++  This lens tries to keep as close as possible to rhsm.conf(5) and
++  Python's SafeConfigParser.  All settings must be in sections without
++  indentation.  Semicolons and hashes are permitted for comments.
++
++About: License
++  This file is licenced under the LGPL v2+, like the rest of Augeas.
++
++About: Lens Usage
++  To be documented
++
++About: Configuration files
++  This lens applies to:
++    /etc/rhsm/rhsm.conf
++
++  See <filter>.
++*)
++
++module Rhsm =
++  autoload xfm
++
++(* Semicolons and hashes are permitted for comments *)
++let comment = IniFile.comment IniFile.comment_re "#"
++(* Equals and colons are permitted for separators *)
++let sep     = IniFile.sep IniFile.sep_re IniFile.sep_default
++
++(* All settings must be in sections without indentation *)
++let entry   = IniFile.entry_multiline IniFile.entry_re sep comment
++let title   = IniFile.title IniFile.record_re
++let record  = IniFile.record title entry
++
++let lns     = IniFile.lns record comment
++
++let filter  = incl "/etc/rhsm/rhsm.conf"
++
++let xfm     = transform lns filter
+diff --git a/lenses/tests/test_rhsm.aug b/lenses/tests/test_rhsm.aug
+new file mode 100644
+index 00000000..219a5be7
+--- /dev/null
++++ b/lenses/tests/test_rhsm.aug
+@@ -0,0 +1,151 @@
++(*
++Module: Test_Rhsm
++  Provides unit tests and examples for the <Rhsm> lens.
++*)
++
++module Test_rhsm =
++
++  (* Variable: conf
++     A full rhsm.conf *)
++  let conf = "# Red Hat Subscription Manager Configuration File:
++
++# Unified Entitlement Platform Configuration
++[server]
++# Server hostname:
++hostname = subscription.rhn.redhat.com
++
++# Server prefix:
++prefix = /subscription
++
++# Server port:
++port = 443
++
++# Set to 1 to disable certificate validation:
++insecure = 0
++
++# Set the depth of certs which should be checked
++# when validating a certificate
++ssl_verify_depth = 3
++
++# an http proxy server to use
++proxy_hostname =
++
++# port for http proxy server
++proxy_port =
++
++# user name for authenticating to an http proxy, if needed
++proxy_user =
++
++# password for basic http proxy auth, if needed
++proxy_password =
++
++[rhsm]
++# Content base URL:
++baseurl= https://cdn.redhat.com
++
++# Server CA certificate location:
++ca_cert_dir = /etc/rhsm/ca/
++
++# Default CA cert to use when generating yum repo configs:
++repo_ca_cert = %(ca_cert_dir)sredhat-uep.pem
++
++# Where the certificates should be stored
++productCertDir = /etc/pki/product
++entitlementCertDir = /etc/pki/entitlement
++consumerCertDir = /etc/pki/consumer
++
++# Manage generation of yum repositories for subscribed content:
++manage_repos = 1
++
++# Refresh repo files with server overrides on every yum command
++full_refresh_on_yum = 0
++
++# If set to zero, the client will not report the package profile to
++# the subscription management service.
++report_package_profile = 1
++
++# The directory to search for subscription manager plugins
++pluginDir = /usr/share/rhsm-plugins
++
++# The directory to search for plugin configuration files
++pluginConfDir = /etc/rhsm/pluginconf.d
++
++[rhsmcertd]
++# Interval to run cert check (in minutes):
++certCheckInterval = 240
++# Interval to run auto-attach (in minutes):
++autoAttachInterval = 1440
++"
++
++  test Rhsm.lns get conf =
++    { "#comment" = "Red Hat Subscription Manager Configuration File:" }
++    {  }
++    { "#comment" = "Unified Entitlement Platform Configuration" }
++    { "server"
++      { "#comment" = "Server hostname:" }
++      { "hostname" = "subscription.rhn.redhat.com" }
++      {  }
++      { "#comment" = "Server prefix:" }
++      { "prefix" = "/subscription" }
++      {  }
++      { "#comment" = "Server port:" }
++      { "port" = "443" }
++      {  }
++      { "#comment" = "Set to 1 to disable certificate validation:" }
++      { "insecure" = "0" }
++      {  }
++      { "#comment" = "Set the depth of certs which should be checked" }
++      { "#comment" = "when validating a certificate" }
++      { "ssl_verify_depth" = "3" }
++      {  }
++      { "#comment" = "an http proxy server to use" }
++      { "proxy_hostname" }
++      {  }
++      { "#comment" = "port for http proxy server" }
++      { "proxy_port" }
++      {  }
++      { "#comment" = "user name for authenticating to an http proxy, if needed" }
++      { "proxy_user" }
++      {  }
++      { "#comment" = "password for basic http proxy auth, if needed" }
++      { "proxy_password" }
++      {  }
++    }
++    { "rhsm"
++      { "#comment" = "Content base URL:" }
++      { "baseurl" = "https://cdn.redhat.com" }
++      {  }
++      { "#comment" = "Server CA certificate location:" }
++      { "ca_cert_dir" = "/etc/rhsm/ca/" }
++      {  }
++      { "#comment" = "Default CA cert to use when generating yum repo configs:" }
++      { "repo_ca_cert" = "%(ca_cert_dir)sredhat-uep.pem" }
++      {  }
++      { "#comment" = "Where the certificates should be stored" }
++      { "productCertDir" = "/etc/pki/product" }
++      { "entitlementCertDir" = "/etc/pki/entitlement" }
++      { "consumerCertDir" = "/etc/pki/consumer" }
++      {  }
++      { "#comment" = "Manage generation of yum repositories for subscribed content:" }
++      { "manage_repos" = "1" }
++      {  }
++      { "#comment" = "Refresh repo files with server overrides on every yum command" }
++      { "full_refresh_on_yum" = "0" }
++      {  }
++      { "#comment" = "If set to zero, the client will not report the package profile to" }
++      { "#comment" = "the subscription management service." }
++      { "report_package_profile" = "1" }
++      {  }
++      { "#comment" = "The directory to search for subscription manager plugins" }
++      { "pluginDir" = "/usr/share/rhsm-plugins" }
++      {  }
++      { "#comment" = "The directory to search for plugin configuration files" }
++      { "pluginConfDir" = "/etc/rhsm/pluginconf.d" }
++      {  }
++    }
++    { "rhsmcertd"
++      { "#comment" = "Interval to run cert check (in minutes):" }
++      { "certCheckInterval" = "240" }
++      { "#comment" = "Interval to run auto-attach (in minutes):" }
++      { "autoAttachInterval" = "1440" }
++    }
+diff --git a/tests/Makefile.am b/tests/Makefile.am
+index 65d8993e..4d2b2605 100644
+--- a/tests/Makefile.am
++++ b/tests/Makefile.am
+@@ -167,6 +167,7 @@ lens_tests =			\
+   lens-redis.sh		\
+   lens-reprepro_uploaders.sh		\
+   lens-resolv.sh		\
++  lens-rhsm.sh			\
+   lens-rmt.sh			\
+   lens-rsyncd.sh		\
+   lens-rsyslog.sh		\
+-- 
+2.17.2
+
diff --git a/SOURCES/0010-Fix-sudoers-lens-recognize-match_group_by_gid.patch b/SOURCES/0010-Fix-sudoers-lens-recognize-match_group_by_gid.patch
new file mode 100644
index 0000000..15481ba
--- /dev/null
+++ b/SOURCES/0010-Fix-sudoers-lens-recognize-match_group_by_gid.patch
@@ -0,0 +1,29 @@
+From 9a65d8e4a428f05e392658fac498ea99d3b3405f Mon Sep 17 00:00:00 2001
+From: Luigi Toscano <ltoscano@redhat.com>
+Date: Thu, 24 Aug 2017 16:21:49 +0200
+Subject: [PATCH] Fix sudoers lens: recognize "match_group_by_gid"
+
+The option is now enabled by default in the default sudoers of
+RHEL 7.4 (and probably soon CentOS 7).
+
+Closes #482
+---
+ lenses/sudoers.aug | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/lenses/sudoers.aug b/lenses/sudoers.aug
+index 7567772d..0437daae 100644
+--- a/lenses/sudoers.aug
++++ b/lenses/sudoers.aug
+@@ -307,7 +307,7 @@ let parameter_flag_kw    = "always_set_home" | "authenticate" | "env_editor"
+                          | "tty_tickets" | "visiblepw" | "closefrom_override"
+                          | "closefrom_override" | "compress_io" | "fast_glob"
+                          | "log_input" | "log_output" | "pwfeedback"
+-                         | "umask_override" | "use_pty"
++                         | "umask_override" | "use_pty" | "match_group_by_gid"
+ 
+ let parameter_flag       = [ del_negate . negate_node?
+                                . key parameter_flag_kw ]
+-- 
+2.17.2
+
diff --git a/SOURCES/0011-src-pathx.c-parse_name-correctly-handle-trailing-whi.patch b/SOURCES/0011-src-pathx.c-parse_name-correctly-handle-trailing-whi.patch
new file mode 100644
index 0000000..6ccffd6
--- /dev/null
+++ b/SOURCES/0011-src-pathx.c-parse_name-correctly-handle-trailing-whi.patch
@@ -0,0 +1,162 @@
+From d157f330acfe94a1f61bf766b485beb0e0dd7177 Mon Sep 17 00:00:00 2001
+From: David Lutterkort <lutter@watzmann.net>
+Date: Fri, 4 Aug 2017 17:13:52 -0700
+Subject: [PATCH] * src/pathx.c (parse_name): correctly handle trailing
+ whitespace in names
+
+When a name ended in whitespace, we incorrectly assumed it was always ok to
+trim that whitespace. That is not true if that whitespace is escaped,
+i.e. if the path expression is something like '/x\ '. In that case, the
+name really needs to be literally 'x ', i.e., we can not trim that
+whitespace.
+
+The incorrect behavior led to turning '/x\ ' first into 'x\' and then,
+because we assume that '\' is always followed by a character inside the
+string, when we removed the escaping '\', we would read beyond the end of
+the intermediate string result; if we were lucky, that would lead to a
+crash, otherwise we'd continue with junk.
+
+We now make sure that escaped whitespace at the end of a string does not
+get stripped, avoiding all these headaches.
+
+Fixes RHBZ https://bugzilla.redhat.com/show_bug.cgi?id=1475621
+---
+ src/pathx.c        | 27 ++++++++++++++++-----
+ tests/test-xpath.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 80 insertions(+), 6 deletions(-)
+
+diff --git a/src/pathx.c b/src/pathx.c
+index 8d8dbbbe..a726a032 100644
+--- a/src/pathx.c
++++ b/src/pathx.c
+@@ -1643,6 +1643,16 @@ int pathx_escape_name(const char *in, char **out) {
+     return 0;
+ }
+ 
++/* Return true if POS is preceded by an odd number of backslashes, i.e., if
++ * POS is escaped. Stop the search when we get to START */
++static bool backslash_escaped(const char *pos, const char *start) {
++    bool result=false;
++    while (pos-- > start && *pos == '\\') {
++        result = !result;
++    }
++    return result;
++}
++
+ /*
+  * NameNoWS ::= [^][|/\= \t\n] | \\.
+  * NameWS   ::= [^][|/\=] | \\.
+@@ -1652,11 +1662,14 @@ static char *parse_name(struct state *state) {
+     const char *s = state->pos;
+     char *result;
+ 
++    /* Advance state->pos until it points to the first character that is
++     * not part of a name. */
+     while (*state->pos != '\0' && strchr(name_follow, *state->pos) == NULL) {
+-        /* This is a hack: since we allow spaces in names, we need to avoid
+-         * gobbling up stuff that is in follow(Name), e.g. 'or' so that
+-         * things like [name1 or name2] still work.
+-         */
++        /* Since we allow spaces in names, we need to avoid gobbling up
++         * stuff that is in follow(Name), e.g. 'or' so that things like
++         * [name1 or name2] still work. In other words, we'll parse 'x frob
++         * y' as one name, but for 'x or y', we consider 'x' a name in its
++         * own right. */
+         if (STREQLEN(state->pos, " or ", strlen(" or ")) ||
+             STREQLEN(state->pos, " and ", strlen(" and ")))
+             break;
+@@ -1671,10 +1684,12 @@ static char *parse_name(struct state *state) {
+         state->pos += 1;
+     }
+ 
+-    /* Strip trailing white space */
++    /* Strip trailing white space. Make sure we respect escaped whitespace
++     * and don't strip it as in "x\\ " */
+     if (state->pos > s) {
+         state->pos -= 1;
+-        while (isspace(*state->pos) && state->pos >= s)
++        while (isspace(*state->pos) && state->pos > s
++               && !backslash_escaped(state->pos, s))
+             state->pos -= 1;
+         state->pos += 1;
+     }
+diff --git a/tests/test-xpath.c b/tests/test-xpath.c
+index 335e7bf8..dbba29e0 100644
+--- a/tests/test-xpath.c
++++ b/tests/test-xpath.c
+@@ -331,6 +331,62 @@ static int test_wrong_regexp_flag(struct augeas *aug) {
+     return -1;
+ }
+ 
++static int test_trailing_ws_in_name(struct augeas *aug) {
++    int r;
++
++    printf("%-30s ... ", "trailing_ws_in_name");
++
++    /* We used to incorrectly lop escaped whitespace off the end of a
++     * name. Make sure that we really create a tree node with label 'x '
++     * with the below set, and look for it in a number of ways to ensure we
++     * are not lopping off trailing whitespace. */
++    r = aug_set(aug, "/ws\\ ", "1");
++    if (r < 0) {
++        fprintf(stderr, "failed to set '/ws ': %d\n", r);
++        goto fail;
++    }
++    /* We did not create a node with label 'ws' */
++    r = aug_get(aug, "/ws", NULL);
++    if (r != 0) {
++        fprintf(stderr, "created '/ws' instead: %d\n", r);
++        goto fail;
++    }
++
++    /* We did not create a node with label 'ws\t' (this also checks that we
++     * don't create something like 'ws\\' by dropping the last whitespace
++     * character. */
++    r = aug_get(aug, "/ws\\\t", NULL);
++    if (r != 0) {
++        fprintf(stderr, "found '/ws\\t': %d\n", r);
++        goto fail;
++    }
++
++    /* But we did create 'ws ' */
++    r = aug_get(aug, "/ws\\ ", NULL);
++    if (r != 1) {
++        fprintf(stderr, "could not find '/ws ': %d\n", r);
++        goto fail;
++    }
++
++    /* If the whitespace is preceded by an even number of '\\' chars,
++     * whitespace must be stripped */
++    r = aug_set(aug, "/nows\\\\ ", "1");
++    if (r < 0) {
++        fprintf(stderr, "set of '/nows' failed: %d\n", r);
++        goto fail;
++    }
++    r = aug_get(aug, "/nows\\\\", NULL);
++    if (r != 1) {
++        fprintf(stderr, "could not get '/nows\\'\n");
++        goto fail;
++    }
++    printf("PASS\n");
++    return 0;
++ fail:
++    printf("FAIL\n");
++    return -1;
++}
++
+ static int run_tests(struct test *tests, int argc, char **argv) {
+     char *lensdir;
+     struct augeas *aug = NULL;
+@@ -374,6 +430,9 @@ static int run_tests(struct test *tests, int argc, char **argv) {
+ 
+         if (test_wrong_regexp_flag(aug) < 0)
+             result = EXIT_FAILURE;
++
++        if (test_trailing_ws_in_name(aug) < 0)
++            result = EXIT_FAILURE;
+     }
+     aug_close(aug);
+ 
+-- 
+2.17.2
+
diff --git a/SOURCES/0012-tests-test-save.c-testSaveNoPermission-skip-when-roo.patch b/SOURCES/0012-tests-test-save.c-testSaveNoPermission-skip-when-roo.patch
new file mode 100644
index 0000000..85d9a56
--- /dev/null
+++ b/SOURCES/0012-tests-test-save.c-testSaveNoPermission-skip-when-roo.patch
@@ -0,0 +1,28 @@
+From f0e1dfad1c8a7c8193f2805c0919d6105344dc17 Mon Sep 17 00:00:00 2001
+From: Dominic Cleal <dcleal@redhat.com>
+Date: Thu, 17 Dec 2015 10:40:45 +0000
+Subject: [PATCH] * tests/test-save.c (testSaveNoPermission): skip when root
+
+---
+ tests/test-save.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/tests/test-save.c b/tests/test-save.c
+index 4b6470f2..8775ba54 100644
+--- a/tests/test-save.c
++++ b/tests/test-save.c
+@@ -355,6 +355,11 @@ static void testPathEscaping(CuTest *tc) {
+  * used to lead to a SEGV
+  */
+ static void testSaveNoPermission(CuTest *tc) {
++    if (getuid() == 0) {
++        puts("pending (testSaveNoPermission): can't test permissions under root account");
++        return;
++    }
++
+     int r;
+     char *path = NULL;
+     const char *v;
+-- 
+2.17.2
+
diff --git a/SOURCES/0013-Chrony-allow-signed-numbers.patch b/SOURCES/0013-Chrony-allow-signed-numbers.patch
new file mode 100644
index 0000000..c5f323f
--- /dev/null
+++ b/SOURCES/0013-Chrony-allow-signed-numbers.patch
@@ -0,0 +1,52 @@
+From deae1ee7eafff09b983ba7b9bcb7e59df92a5cea Mon Sep 17 00:00:00 2001
+From: Miroslav Lichvar <mlichvar@redhat.com>
+Date: Wed, 3 Jun 2015 17:31:07 +0200
+Subject: [PATCH] Chrony: allow signed numbers
+
+---
+ lenses/chrony.aug            | 4 ++--
+ lenses/tests/test_chrony.aug | 4 ++--
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/lenses/chrony.aug b/lenses/chrony.aug
+index 32575688..5e30ee1f 100644
+--- a/lenses/chrony.aug
++++ b/lenses/chrony.aug
+@@ -50,10 +50,10 @@ module Chrony =
+     let word       = Rx.word
+ 
+     (* Variable: integer *)
+-    let integer    = Rx.integer
++    let integer    = Rx.relinteger
+ 
+     (* Variable: decimal *)
+-    let decimal    = Rx.decimal
++    let decimal    = Rx.reldecimal
+ 
+     (* Variable: ip *)
+     let ip         = Rx.ip
+diff --git a/lenses/tests/test_chrony.aug b/lenses/tests/test_chrony.aug
+index c4b552eb..905ecee4 100644
+--- a/lenses/tests/test_chrony.aug
++++ b/lenses/tests/test_chrony.aug
+@@ -25,7 +25,7 @@ peer ntpc1.example.com
+ stratumweight 0
+ driftfile /var/lib/chrony/drift
+ rtcsync
+-makestep 10 3
++makestep 10 -1
+ bindcmdaddress 127.0.0.1
+ bindcmdaddress ::1
+ local stratum 10
+@@ -87,7 +87,7 @@ initstepslew 30 foo.bar.com baz.quz.com
+   { "rtcsync" }
+   { "makestep"
+     { "threshold" = "10" }
+-    { "limit" = "3" }
++    { "limit" = "-1" }
+   }
+   { "bindcmdaddress" = "127.0.0.1" }
+   { "bindcmdaddress" = "::1" }
+-- 
+2.17.2
+
diff --git a/SOURCES/0014-Fix-430-support-Krb5-include-dir.patch b/SOURCES/0014-Fix-430-support-Krb5-include-dir.patch
new file mode 100644
index 0000000..9b4e4d3
--- /dev/null
+++ b/SOURCES/0014-Fix-430-support-Krb5-include-dir.patch
@@ -0,0 +1,52 @@
+From 430f0210d36d3abf2bfbe2a336f8f8d260ccc81b Mon Sep 17 00:00:00 2001
+From: "Jason A. Smith" <smithj4@bnl.gov>
+Date: Fri, 23 Dec 2016 03:19:24 -0500
+Subject: [PATCH] Fix #430 - support Krb5 include(dir)?
+
+Updated the Krb5 lens to support the include(dir)? directives,
+with test case.
+---
+ lenses/krb5.aug            | 9 +++++++--
+ lenses/tests/test_krb5.aug | 9 +++++++++
+ 2 files changed, 16 insertions(+), 2 deletions(-)
+
+diff --git a/lenses/krb5.aug b/lenses/krb5.aug
+index 37778fd8..8936f3a0 100644
+--- a/lenses/krb5.aug
++++ b/lenses/krb5.aug
+@@ -151,8 +151,13 @@ let kdc =
+ let pam =
+   simple_section "pam" name_re
+ 
+-let lns = (comment|empty)* .
++let includes = Build.key_value_line /include(dir)?/ Sep.space (store Rx.fspath)
++
++let lns = (comment|empty|includes)* .
+   (libdefaults|login|appdefaults|realms|domain_realm
+   |logging|capaths|dbdefaults|dbmodules|instance_mapping|kdc|pam)*
+ 
+-let xfm = transform lns (incl "/etc/krb5.conf")
++let filter = (incl "/etc/krb5.conf.d/*.conf")
++           . (incl "/etc/krb5.conf")
++
++let xfm = transform lns filter
+diff --git a/lenses/tests/test_krb5.aug b/lenses/tests/test_krb5.aug
+index e17a659a..743bb375 100644
+--- a/lenses/tests/test_krb5.aug
++++ b/lenses/tests/test_krb5.aug
+@@ -1020,3 +1020,12 @@ default_ccache_name = KEYRING:persistent:%{uid}\n" =
+   { "libdefaults"
+     {  }
+     { "default_ccache_name" = "KEYRING:persistent:%{uid}" } }
++
++(* Include(dir) test *)
++let include_test = "include /etc/krb5.other_conf.d/other.conf
++includedir /etc/krb5.conf.d/
++"
++
++test Krb5.lns get include_test =
++  { "include" = "/etc/krb5.other_conf.d/other.conf" }
++  { "includedir" = "/etc/krb5.conf.d/" }
+-- 
+2.17.2
+
diff --git a/SOURCES/0015-Cgconfig-allow-fperm-dperm-in-admin-task.patch b/SOURCES/0015-Cgconfig-allow-fperm-dperm-in-admin-task.patch
new file mode 100644
index 0000000..720905a
--- /dev/null
+++ b/SOURCES/0015-Cgconfig-allow-fperm-dperm-in-admin-task.patch
@@ -0,0 +1,81 @@
+From de01f104d6ee4b11122aa4a108fc6082d3061886 Mon Sep 17 00:00:00 2001
+From: Pino Toscano <ptoscano@redhat.com>
+Date: Mon, 4 Sep 2017 18:45:05 +0200
+Subject: [PATCH] Cgconfig: allow fperm & dperm in admin & task
+
+These keys are used to control the permissions for files and
+directories.
+---
+ lenses/cgconfig.aug            |  2 +-
+ lenses/tests/test_cgconfig.aug | 45 ++++++++++++++++++++++++++++++++++
+ 2 files changed, 46 insertions(+), 1 deletion(-)
+
+diff --git a/lenses/cgconfig.aug b/lenses/cgconfig.aug
+index 6a5b8603..e766343d 100644
+--- a/lenses/cgconfig.aug
++++ b/lenses/cgconfig.aug
+@@ -30,7 +30,7 @@ module Cgconfig =
+    let name      = /[^#= \n\t{}\/]+/
+    let cont_name = /(cpuacct|cpu|devices|ns|cpuset|memory|freezer|net_cls|blkio|hugetlb|perf_event)/
+    let role_name = /(admin|task)/
+-   let id_name   = /(uid|gid)/
++   let id_name   = /(uid|gid|fperm|dperm)/
+    let address   = /[^#; \n\t{}]+/
+    let qaddress  = address|/"[^#;"\n\t{}]+"/
+ 
+diff --git a/lenses/tests/test_cgconfig.aug b/lenses/tests/test_cgconfig.aug
+index 6cd0856d..84fd2ded 100644
+--- a/lenses/tests/test_cgconfig.aug
++++ b/lenses/tests/test_cgconfig.aug
+@@ -318,3 +318,48 @@ test Cgconfig.lns get group6 =
+     {  }
+   }
+ 
++let group7 ="
++group daemons/www {
++  perm {
++    task {
++      uid = root;
++      gid = root;
++      fperm = 770;
++    }
++    admin {
++      uid = root;
++      gid = root;
++      dperm = 777;
++    }
++  }
++}
++"
++
++test Cgconfig.lns get group7 =
++  {  }
++  { "group" = "daemons/www"
++    {  }
++    { "perm"
++      {  }
++      { "task"
++        {  }
++        { "uid" = "root" }
++        {  }
++        { "gid" = "root" }
++        {  }
++        { "fperm" = "770" }
++        {  } }
++      {  }
++      { "admin"
++        {  }
++        { "uid" = "root" }
++        {  }
++        { "gid" = "root" }
++        {  }
++        { "dperm" = "777" }
++        {  } }
++      {  } }
++    {  }
++  }
++  {  }
++
+-- 
+2.17.2
+
diff --git a/SOURCES/0016-Grub-handle-top-level-boot-directive-494.patch b/SOURCES/0016-Grub-handle-top-level-boot-directive-494.patch
new file mode 100644
index 0000000..dada3fd
--- /dev/null
+++ b/SOURCES/0016-Grub-handle-top-level-boot-directive-494.patch
@@ -0,0 +1,55 @@
+From 06b3a79ee2bfdb4ae3675232e82ae3d06bbba353 Mon Sep 17 00:00:00 2001
+From: Pino Toscano <ptoscano@redhat.com>
+Date: Tue, 12 Sep 2017 10:58:46 +0200
+Subject: [PATCH] Grub: handle top-level "boot" directive (#494)
+
+Grub 1 effectively ignores commands in the configuration which work only
+in the command line.  The generated configuration by anaconda included
+also a commented "boot=device" entry at the beginning: uncommenting that
+does not make the configuration invalid, but makes the Grub lens not
+able to parse it.
+
+Since there is no harm in representing a configuration key that will be
+effectively ignored, accept top-level "boot" entries as well.
+---
+ lenses/grub.aug            | 1 +
+ lenses/tests/test_grub.aug | 4 ++--
+ 2 files changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/lenses/grub.aug b/lenses/grub.aug
+index c52d16c6..9866f3f7 100644
+--- a/lenses/grub.aug
++++ b/lenses/grub.aug
+@@ -148,6 +148,7 @@ module Grub =
+                      | kw_menu_arg "foreground"
+                      | kw_menu_arg "background"
+                      | kw_menu_arg "verbose"
++                     | kw_menu_arg "boot" (* only for CLI, ignored in conf *)
+                      | serial
+                      | terminal
+                      | password_arg
+diff --git a/lenses/tests/test_grub.aug b/lenses/tests/test_grub.aug
+index f022ef72..8a0d9f4a 100644
+--- a/lenses/tests/test_grub.aug
++++ b/lenses/tests/test_grub.aug
+@@ -8,7 +8,7 @@ module Test_grub =
+ #          root (hd0,0)
+ #          kernel /vmlinuz-version ro root=/dev/vg00/lv00
+ #          initrd /initrd-version.img
+-#boot=/dev/sda
++boot=/dev/sda
+ device (hd0) HD(1,800,64000,9895c137-d4b2-4e3b-a93b-dc9ac4)
+ password --md5 $1$M9NLj$p2gs87vwNv48BUu.wAfVw0
+ default=0
+@@ -53,7 +53,7 @@ title othermenu
+     { "#comment" = "root (hd0,0)" }
+     { "#comment" = "kernel /vmlinuz-version ro root=/dev/vg00/lv00" }
+     { "#comment" = "initrd /initrd-version.img" }
+-    { "#comment" = "boot=/dev/sda" }
++    { "boot" = "/dev/sda" }
+     { "device"   = "(hd0)"
+         { "file" = "HD(1,800,64000,9895c137-d4b2-4e3b-a93b-dc9ac4)" } }
+     { "password" = "$1$M9NLj$p2gs87vwNv48BUu.wAfVw0"
+-- 
+2.17.2
+
diff --git a/SOURCES/0017-Fstab-allow-leading-whitespace-in-lines-with-spec-54.patch b/SOURCES/0017-Fstab-allow-leading-whitespace-in-lines-with-spec-54.patch
new file mode 100644
index 0000000..608225c
--- /dev/null
+++ b/SOURCES/0017-Fstab-allow-leading-whitespace-in-lines-with-spec-54.patch
@@ -0,0 +1,50 @@
+From 2565e67f724032cdc6217c2a2d2c1bc25ca23605 Mon Sep 17 00:00:00 2001
+From: Pino Toscano <ptoscano@redhat.com>
+Date: Tue, 13 Feb 2018 07:21:55 +0100
+Subject: [PATCH] Fstab: allow leading whitespace in lines with spec (#544)
+
+The documentation does not explicitly mention this possibility, but
+tooling that parses fstab actually supports this: hence, allow leading
+whitespace in lines with filesystem specification.
+---
+ lenses/fstab.aug            | 1 +
+ lenses/tests/test_fstab.aug | 4 ++++
+ 2 files changed, 5 insertions(+)
+
+diff --git a/lenses/fstab.aug b/lenses/fstab.aug
+index bceaddd1..d67a3067 100644
+--- a/lenses/fstab.aug
++++ b/lenses/fstab.aug
+@@ -23,6 +23,7 @@ module Fstab =
+          Build.opt_list lns comma
+ 
+   let record = [ seq "mntent" .
++                   Util.indent .
+                    [ label "spec" . store spec ] . sep_tab .
+                    [ label "file" . store file ] . sep_tab .
+                    comma_sep_list "vfstype" .
+diff --git a/lenses/tests/test_fstab.aug b/lenses/tests/test_fstab.aug
+index fa044aea..438f619a 100644
+--- a/lenses/tests/test_fstab.aug
++++ b/lenses/tests/test_fstab.aug
+@@ -11,6 +11,8 @@ module Test_fstab =
+         { "dump" = "1" }
+         { "passno" = "1" } }
+ 
++  let leading_ws = "   /dev/vg00/lv00\t /\t ext3\t    defaults        1 1\n"
++
+   let trailing_ws = "/dev/vg00/lv00\t /\t ext3\t    defaults        1 1  \t\n"
+ 
+   let gen_no_passno(passno:string) =
+@@ -60,6 +62,8 @@ module Test_fstab =
+ 
+   test Fstab.lns get simple = simple_tree
+ 
++  test Fstab.lns get leading_ws = simple_tree
++
+   test Fstab.lns get trailing_ws = simple_tree
+ 
+   test Fstab.lns get no_passno = no_passno_tree
+-- 
+2.17.2
+
diff --git a/SOURCES/0018-Grub-tolerate-some-invalid-entries.patch b/SOURCES/0018-Grub-tolerate-some-invalid-entries.patch
new file mode 100644
index 0000000..1e8643a
--- /dev/null
+++ b/SOURCES/0018-Grub-tolerate-some-invalid-entries.patch
@@ -0,0 +1,159 @@
+From ccebad103f3466d5e8fc2b60f6127af618adf8d2 Mon Sep 17 00:00:00 2001
+From: David Lutterkort <lutter@watzmann.net>
+Date: Mon, 4 Jun 2018 10:45:19 -0700
+Subject: [PATCH] Grub: tolerate some invalid entries
+
+Refusing to parse an entire file because of invalid entries is too
+harsh. Try to make the behavior a little friendlier by simply mapping invalid
+entries to '#error' nodes but still parsing the rest of the file.
+
+Also remove del_to_eol, that would delete anything up to eol; it was only
+used in kw_pres, but should have never been used there. kw_pres should only
+match the keyword and eol. A line like 'quiet foo bar baz' should not be
+accepted by (kw_pres "quiet").
+---
+ lenses/grub.aug            | 58 ++++++++++++++++++++++++++++++++------
+ lenses/tests/test_grub.aug | 25 ++++++++++++++++
+ 2 files changed, 75 insertions(+), 8 deletions(-)
+
+diff --git a/lenses/grub.aug b/lenses/grub.aug
+index 9866f3f7..f99a3a92 100644
+--- a/lenses/grub.aug
++++ b/lenses/grub.aug
+@@ -29,9 +29,6 @@ module Grub =
+     (* View: eol *)
+     let eol = Util.eol
+ 
+-    (* View: del_to_eol *)
+-    let del_to_eol = del /[^ \t\n]*/ ""
+-
+     (* View: spc *)
+     let spc = Util.del_ws_spc
+ 
+@@ -92,7 +89,22 @@ module Grub =
+       eol ]
+ 
+     (* View: kw_pres *)
+-    let kw_pres (kw:string) = [ opt_ws . key kw . del_to_eol . eol ]
++    let kw_pres (kw:string) = [ opt_ws . key kw . eol ]
++
++    (* View: error
++     *   Parse a line that looks almost like a valid setting, but isn't,
++     *   into an '#error' node. Any line that starts with letters, but not
++     *   anything matching kw, is considered an error line.
++     *
++     *   Parameters:
++     *     kw:regexp - the valid keywords that are _not_ considered an
++     *                 error
++     *)
++    let error (kw:regexp) =
++      let not_kw = /[a-zA-Z]+/ - kw in
++      [ label "#error" . Util.del_opt_ws "\t"
++        . store (not_kw . /([^a-zA-Z\n].*[^ \t\n])?/) . eol ]
++
+ 
+ (************************************************************************
+  * Group:                 BOOT ENTRIES
+@@ -138,8 +150,8 @@ module Grub =
+         spc . [ label "from" . store Rx.no_spaces ] )? .
+       eol ]
+ 
+-    (* View: menu_setting *)
+-    let menu_setting = kw_menu_arg "default"
++    (* View: menu_entry *)
++    let menu_entry = kw_menu_arg "default"
+                      | kw_menu_arg "fallback"
+                      | kw_pres "hiddenmenu"
+                      | kw_menu_arg "timeout"
+@@ -156,6 +168,21 @@ module Grub =
+                      | device
+                      | setkey
+ 
++    (* View: menu_error
++     *   Accept lines not matching menu_entry and stuff them into
++     *   '#error' nodes
++     *)
++    let menu_error =
++      let kw = /default|fallback|hiddenmenu|timeout|splashimage|gfxmenu/
++             |/foreground|background|verbose|boot|password|title/
++             |/serial|setkey|terminal|color|device/ in
++      error kw
++
++    (* View: menu_setting
++     *   a valid menu setting or a line that looks like one but is an #error
++     *)
++    let menu_setting = menu_entry | menu_error
++
+     (* View: title *)
+     let title = del /title[ \t=]+/ "title " . value_to_eol . eol
+ 
+@@ -206,9 +233,9 @@ module Grub =
+     let configfile =
+       [ command "configfile" "\t" . spc . store Rx.no_spaces . eol ]
+ 
+-    (* View: boot_setting
++    (* View: boot_entry
+         <boot> entries *)
+-    let boot_setting =
++    let boot_entry =
+           let boot_arg_re = "root" | "initrd" | "rootnoverify" | "uuid"
+                           | "findroot" | "bootfs" (* Solaris extensions *)
+        in kw_boot_arg boot_arg_re
+@@ -223,6 +250,21 @@ module Grub =
+         | kw_pres "makeactive"
+         | password_arg
+ 
++    (* View: boot_error
++     *   Accept lines not matching boot_entry and stuff them into
++     *   '#error' nodes
++     *)
++    let boot_error =
++      let kw = /lock|uuid|password|root|initrd|rootnoverify|findroot|bootfs/
++             |/configfile|chainloader|title|boot|quiet|kernel|module/
++             |/makeactive|savedefault|map/ in
++      error kw
++
++    (* View: boot_setting
++     *   a valid boot setting or a line that looks like one but is an #error
++     *)
++    let boot_setting = boot_entry | boot_error
++
+     (* View: boot *)
+     let boot =
+       let line = ((boot_setting|comment)* . boot_setting)? in
+diff --git a/lenses/tests/test_grub.aug b/lenses/tests/test_grub.aug
+index 8a0d9f4a..75657203 100644
+--- a/lenses/tests/test_grub.aug
++++ b/lenses/tests/test_grub.aug
+@@ -257,3 +257,28 @@ password --encrypted ^9^32kwzzX./3WISQ0C /boot/grub/custom.lst
+     { "password" = "secret"
+       { "md5" }
+     } }
++
++  (* Test parsing of invalid entries via menu_error *)
++  test Grub.lns get "default=0\ncrud=no\n" =
++  { "default" = "0" }
++  { "#error" = "crud=no" }
++
++  (* We handle some pretty bizarre bad syntax *)
++  test Grub.lns get "default=0
++crud no
++valid:nope
++nonsense  =   yes
++bad arg1 arg2 arg3=v\n" =
++  { "default" = "0" }
++  { "#error" = "crud no" }
++  { "#error" = "valid:nope" }
++  { "#error" = "nonsense  =   yes" }
++  { "#error" = "bad arg1 arg2 arg3=v" }
++
++  (* Test parsing of invalid entries via boot_error *)
++  test Grub.lns get "title test
++    root (hd0,0)
++    crud foo\n" =
++  { "title" = "test"
++    { "root" = "(hd0,0)" }
++    { "#error" = "crud foo" } }
+-- 
+2.17.2
+
diff --git a/SOURCES/0019-Fix-sudoers-lens-always_query_group_plugin-588.patch b/SOURCES/0019-Fix-sudoers-lens-always_query_group_plugin-588.patch
new file mode 100644
index 0000000..cbdf317
--- /dev/null
+++ b/SOURCES/0019-Fix-sudoers-lens-always_query_group_plugin-588.patch
@@ -0,0 +1,26 @@
+From 4e8c541392486f14715c5ec05da4612fc2e26ad9 Mon Sep 17 00:00:00 2001
+From: Steve Traylen <steve.traylen@cern.ch>
+Date: Thu, 1 Nov 2018 13:54:32 +0100
+Subject: [PATCH] Fix sudoers lens: "always_query_group_plugin" (#588)
+
+The option is now enabled by default in the default sudoers of
+RHEL 7.6 (and probably soon CentOS 7).
+---
+ lenses/sudoers.aug | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/lenses/sudoers.aug b/lenses/sudoers.aug
+index 0437daae..d6140a8b 100644
+--- a/lenses/sudoers.aug
++++ b/lenses/sudoers.aug
+@@ -308,6 +308,7 @@ let parameter_flag_kw    = "always_set_home" | "authenticate" | "env_editor"
+                          | "closefrom_override" | "compress_io" | "fast_glob"
+                          | "log_input" | "log_output" | "pwfeedback"
+                          | "umask_override" | "use_pty" | "match_group_by_gid"
++                         | "always_query_group_plugin"
+ 
+ let parameter_flag       = [ del_negate . negate_node?
+                                . key parameter_flag_kw ]
+-- 
+2.17.2
+
diff --git a/SOURCES/0020-New-lens-Anaconda-597.patch b/SOURCES/0020-New-lens-Anaconda-597.patch
new file mode 100644
index 0000000..cd78b63
--- /dev/null
+++ b/SOURCES/0020-New-lens-Anaconda-597.patch
@@ -0,0 +1,186 @@
+From ba333a3205324a7b0489d93b67317c72b76fe8bf Mon Sep 17 00:00:00 2001
+From: Pino Toscano <ptoscano@redhat.com>
+Date: Wed, 12 Dec 2018 13:54:06 +0100
+Subject: [PATCH] New lens: Anaconda (#597)
+
+Introduce a new lens to parse the INI-like `/etc/sysconfig/anaconda` instead of using `Shellvars`.
+---
+ lenses/anaconda.aug               | 30 +++++++++++
+ lenses/shellvars.aug              |  1 +
+ lenses/tests/test_anaconda.aug    | 89 +++++++++++++++++++++++++++++++
+ tests/Makefile.am                 |  1 +
+ tests/root/etc/sysconfig/anaconda |  5 ++
+ 5 files changed, 126 insertions(+)
+ create mode 100644 lenses/anaconda.aug
+ create mode 100644 lenses/tests/test_anaconda.aug
+ create mode 100644 tests/root/etc/sysconfig/anaconda
+
+diff --git a/lenses/anaconda.aug b/lenses/anaconda.aug
+new file mode 100644
+index 00000000..8f618db2
+--- /dev/null
++++ b/lenses/anaconda.aug
+@@ -0,0 +1,30 @@
++(*
++Module: Anaconda
++    Parses Anaconda's user interaction configuration files.
++
++Author: Pino Toscano <ptoscano@redhat.com>
++
++About: Reference
++    https://anaconda-installer.readthedocs.io/en/latest/user-interaction-config-file-spec.html
++
++About: Configuration file
++    This lens applies to /etc/sysconfig/anaconda.
++
++About: License
++  This file is licensed under the LGPL v2+, like the rest of Augeas.
++*)
++module Anaconda =
++autoload xfm
++
++let comment = IniFile.comment "#" "#"
++let sep     = IniFile.sep "=" "="
++
++let entry   = IniFile.entry IniFile.entry_re sep comment
++let title   = IniFile.title IniFile.record_re
++let record  = IniFile.record title entry
++
++let lns     = IniFile.lns record comment
++
++let filter  = incl "/etc/sysconfig/anaconda"
++
++let xfm     = transform lns filter
+diff --git a/lenses/shellvars.aug b/lenses/shellvars.aug
+index 25bb82b9..03ab921b 100644
+--- a/lenses/shellvars.aug
++++ b/lenses/shellvars.aug
+@@ -198,6 +198,7 @@ module Shellvars =
+ 
+   let filter_sysconfig =
+       sc_incl "*" .
++      sc_excl "anaconda" .
+       sc_excl "bootloader" .
+       sc_excl "hw-uuid" .
+       sc_excl "hwconf" .
+diff --git a/lenses/tests/test_anaconda.aug b/lenses/tests/test_anaconda.aug
+new file mode 100644
+index 00000000..50b0ac22
+--- /dev/null
++++ b/lenses/tests/test_anaconda.aug
+@@ -0,0 +1,89 @@
++(*
++Module: Test_Anaconda
++  Provides unit tests and examples for the <Anaconda> lens.
++
++  - 'exampleN' snippets are taken from the documentation:
++    https://anaconda-installer.readthedocs.io/en/latest/user-interaction-config-file-spec.html
++  - 'installedN' snippets are taken from the resulting files after
++    a successful installation
++*)
++
++module Test_Anaconda =
++
++let example1 = "# comment example - before the section headers
++
++[section_1]
++# comment example - inside section 1
++key_a_in_section1=some_value
++key_b_in_section1=some_value
++
++[section_2]
++# comment example - inside section 2
++key_a_in_section2=some_value
++"
++
++test Anaconda.lns get example1 =
++  { "#comment" = "comment example - before the section headers" }
++  { }
++  { "section_1"
++    { "#comment" = "comment example - inside section 1" }
++    { "key_a_in_section1" = "some_value" }
++    { "key_b_in_section1" = "some_value" }
++    { }
++  }
++  { "section_2"
++    { "#comment" = "comment example - inside section 2" }
++    { "key_a_in_section2" = "some_value" }
++  }
++
++let example2 = "# this is the user interaction config file
++
++[General]
++post_install_tools_disabled=0
++
++[DatetimeSpoke]
++# the date and time spoke has been visited
++visited=1
++changed_timezone=1
++changed_ntp=0
++changed_timedate=1
++
++[KeyboardSpoke]
++# the keyboard spoke has not been visited
++visited=0
++"
++
++test Anaconda.lns get example2 =
++  { "#comment" = "this is the user interaction config file" }
++  { }
++  { "General"
++    { "post_install_tools_disabled" = "0" }
++    { }
++  }
++  { "DatetimeSpoke"
++    { "#comment" = "the date and time spoke has been visited" }
++    { "visited" = "1" }
++    { "changed_timezone" = "1" }
++    { "changed_ntp" = "0" }
++    { "changed_timedate" = "1" }
++    { }
++  }
++  { "KeyboardSpoke"
++    { "#comment" = "the keyboard spoke has not been visited" }
++    { "visited" = "0" }
++  }
++
++let installed1 = "# This file has been generated by the Anaconda Installer 21.48.22.134-1
++
++[ProgressSpoke]
++visited = 1
++
++"
++
++test Anaconda.lns get installed1 =
++  { "#comment" = "This file has been generated by the Anaconda Installer 21.48.22.134-1" }
++  { }
++  { "ProgressSpoke"
++    { "visited" = "1" }
++    { }
++  }
+diff --git a/tests/Makefile.am b/tests/Makefile.am
+index 4d2b2605..08d5dc59 100644
+--- a/tests/Makefile.am
++++ b/tests/Makefile.am
+@@ -22,6 +22,7 @@ lens_tests =			\
+   lens-activemq_xml.sh		\
+   lens-afs_cellalias.sh			\
+   lens-aliases.sh		\
++  lens-anaconda.sh		\
+   lens-anacron.sh		\
+   lens-approx.sh		\
+   lens-apt_update_manager.sh		\
+diff --git a/tests/root/etc/sysconfig/anaconda b/tests/root/etc/sysconfig/anaconda
+new file mode 100644
+index 00000000..73318cf6
+--- /dev/null
++++ b/tests/root/etc/sysconfig/anaconda
+@@ -0,0 +1,5 @@
++# This file has been generated by the Anaconda Installer 21.48.22.134-1
++
++[ProgressSpoke]
++visited = 1
++
+-- 
+2.17.2
+
diff --git a/SPECS/augeas.spec b/SPECS/augeas.spec
new file mode 100644
index 0000000..20e3976
--- /dev/null
+++ b/SPECS/augeas.spec
@@ -0,0 +1,382 @@
+Name:           augeas
+Version:        1.4.0
+Release:        9%{?dist}
+Summary:        A library for changing configuration files
+
+Group:          System Environment/Libraries
+License:        LGPLv2+
+URL:            http://augeas.net/
+Source0:        http://download.augeas.net/%{name}-%{version}.tar.gz
+Patch1:         0001-Syslog-restored-Augeas-1.1.0-tree-compatibility-for-.patch
+Patch2:         0002-Revert-Use-Quote-module-in-dovecot.patch
+Patch3:         0003-Revert-Jaas-add-several-improvements-to-cover-more-v.patch
+Patch4:         0004-UpdateDB-autoload-etc-updatedb.conf-with-Simplevars.patch
+Patch5:         0005-Revert-Dnsmasq-add-structure-to-address-and-server-o.patch
+Patch6:         0006-Sshd-revert-Sshd-module-to-1.1.0-compatible-add-Sshd.patch
+Patch7:         0007-Dhcpd-revert-Dhcpd-module-to-1.1.0-compatible-add-Dh.patch
+Patch8:         0008-Slapd-revert-Slapd-module-to-1.1.0-compatible-add-Sl.patch
+Patch9:         0009-Rhsm-new-lens-to-parse-subscription-manager-s-rhsm.c.patch
+Patch10:        0010-Fix-sudoers-lens-recognize-match_group_by_gid.patch
+Patch11:        0011-src-pathx.c-parse_name-correctly-handle-trailing-whi.patch
+Patch12:        0012-tests-test-save.c-testSaveNoPermission-skip-when-roo.patch
+Patch13:        0013-Chrony-allow-signed-numbers.patch
+Patch14:        0014-Fix-430-support-Krb5-include-dir.patch
+Patch15:        0015-Cgconfig-allow-fperm-dperm-in-admin-task.patch
+Patch16:        0016-Grub-handle-top-level-boot-directive-494.patch
+Patch17:        0017-Fstab-allow-leading-whitespace-in-lines-with-spec-54.patch
+Patch18:        0018-Grub-tolerate-some-invalid-entries.patch
+Patch19:        0019-Fix-sudoers-lens-always_query_group_plugin-588.patch
+Patch20:        0020-New-lens-Anaconda-597.patch
+
+BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+
+BuildRequires:  readline-devel libselinux-devel libxml2-devel
+BuildRequires:  autoconf, automake
+Requires:       %{name}-libs = %{version}-%{release}
+
+%description
+A library for programmatically editing configuration files. Augeas parses
+configuration files into a tree structure, which it exposes through its
+public API. Changes made through the API are written back to the initially
+read files.
+
+The transformation works very hard to preserve comments and formatting
+details. It is controlled by ``lens'' definitions that describe the file
+format and the transformation into a tree.
+
+This package attempts to be compatible with Augeas 1.1.0 as shipped in
+EL7.0, where possible.
+
+%package        devel
+Summary:        Development files for %{name}
+Group:          Development/Libraries
+Requires:       %{name}-libs = %{version}-%{release}
+Requires:       pkgconfig
+
+%description    devel
+The %{name}-devel package contains libraries and header files for
+developing applications that use %{name}.
+
+
+%package        libs
+Summary:        Libraries for %{name}
+Group:          System Environment/Libraries
+
+Provides:       bundled(gnulib)
+
+%description    libs
+The libraries for %{name}.
+
+
+%prep
+%setup -q
+%patch1 -p1
+%patch2 -p1
+%patch3 -p1
+%patch4 -p1
+%patch5 -p1
+%patch6 -p1
+%patch7 -p1
+%patch8 -p1
+%patch9 -p1
+%patch10 -p1
+%patch11 -p1
+%patch12 -p1
+%patch13 -p1
+%patch14 -p1
+%patch15 -p1
+%patch16 -p1
+%patch17 -p1
+%patch18 -p1
+%patch19 -p1
+%patch20 -p1
+
+# Patches affect Makefile.am and configure.ac, so rerun autotools.
+autoreconf
+autoconf
+
+%build
+%configure --disable-static
+make %{?_smp_mflags}
+
+%check
+# Disable test-preserve.sh SELinux testing. This fails when run under mock due
+# to differing SELinux labelling.
+export SKIP_TEST_PRESERVE_SELINUX=1
+
+make %{?_smp_mflags} check || {
+  echo '===== tests/test-suite.log ====='
+  cat tests/test-suite.log
+  exit 1
+}
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT INSTALL="%{__install} -p"
+find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
+
+# The tests/ subdirectory contains lenses used only for testing, and
+# so it shouldn't be packaged.
+rm -r $RPM_BUILD_ROOT%{_datadir}/augeas/lenses/dist/tests
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post libs -p /sbin/ldconfig
+
+%postun libs -p /sbin/ldconfig
+
+%files
+%defattr(-,root,root,-)
+%{_bindir}/augtool
+%{_bindir}/augparse
+%{_bindir}/fadot
+%doc %{_mandir}/man1/*
+%{_datadir}/vim/vimfiles/syntax/augeas.vim
+%{_datadir}/vim/vimfiles/ftdetect/augeas.vim
+
+%files libs
+%defattr(-,root,root,-)
+# %{_datadir}/augeas and %{_datadir}/augeas/lenses are owned
+# by filesystem.
+%{_datadir}/augeas/lenses/dist
+%{_libdir}/*.so.*
+%doc AUTHORS COPYING NEWS
+
+%files devel
+%defattr(-,root,root,-)
+%doc
+%{_includedir}/*
+%{_libdir}/*.so
+%{_libdir}/pkgconfig/augeas.pc
+
+%changelog
+* Wed Dec 19 2018 Pino Toscano <ptoscano@redhat.com> - 1.4.0-9
+- Add "Provides: bundled(gnulib)" to augeas-libs, as it embeds gnulib
+  (RHBZ#1653766)
+- Anaconda: new lens (RHBZ#1657189)
+
+* Tue Nov 13 2018 Pino Toscano <ptoscano@redhat.com> - 1.4.0-8
+- Sudoers: handle "always_query_group_plugin" option (RHBZ#1649287)
+
+* Tue Nov 13 2018 Pino Toscano <ptoscano@redhat.com> - 1.4.0-7
+- Grub: better handle invalid grub.conf files (RHBZ#1582236)
+
+* Thu Mar 29 2018 Pino Toscano <ptoscano@redhat.com> - 1.4.0-6
+- Fstab: allow leading whitespaces (RHBZ#1544520)
+
+* Wed Oct 04 2017 Pino Toscano <ptoscano@redhat.com> - 1.4.0-5
+- Cgconfig: allow fperm & dperm in admin & task (RHBZ#1325741)
+- Grub: handle top-level "boot" directive (RHBZ#1484261)
+
+* Mon Sep 04 2017 Pino Toscano <ptoscano@redhat.com> - 1.4.0-4
+- Fix CVE-2017-7555, improper handling of escaped strings (RHBZ#1481546)
+- Skip testSaveNoPermission when running as root (RHBZ#1269817)
+- Chrony: allow signed numbers (RHBZ#1302017)
+- Krb5: support includedir (RHBZ#1406111)
+
+* Tue Aug 29 2017 Luigi Toscano <ltoscano@redhat.com> - 1.4.0-3
+  Fix sudoers lens: recognize "match_group_by_gid" (RHBZ#1483888)
+
+* Thu Jul 30 2015 Dominic Cleal <dcleal@redhat.com> - 1.4.0-2
+- Rhsm: add to parse subscription-manager config (RHBZ#1141121)
+
+* Fri Jun 12 2015 Dominic Cleal <dcleal@redhat.com> - 1.4.0-1
+- Rebase to Augeas 1.4.0
+- Revert some changes for better compatibility with 1.1.0-17:
+  * Dhcpd: keep 1.1.0 behaviour, add Dhcpd_140 for 1.4.0 features
+  * Dnsmasq: revert splitting of address/server options
+  * Dovecot: restore quotes within values
+  * Jaas: revert semicolon and line break changes
+  * Slapd: keep 1.1.0 behaviour, add Slapd_140 for 1.4.0 features
+  * Sshd: keep 1.1.0 behaviour, add Sshd_140 for 1.4.0 features
+  * Syslog: restore tree without protocol for UDP hosts
+  * UpdateDB: keep Simplevars to load config by default
+
+* Thu Nov 27 2014 Dominic Cleal <dcleal@redhat.com> - 1.1.0-17
+- Device_map: parse all device.map files under /boot (RHBZ#1166582)
+
+* Tue Sep 23 2014 Dominic Cleal <dcleal@redhat.com> - 1.1.0-16
+- Iptables: parse /etc/sysconfig/iptables.save (RHBZ#1144651)
+- Lvm: parse /etc/lvm/lvm.conf (RHBZ#1145495)
+- Shadow: add lens (RHBZ#1145249)
+
+* Thu Sep 18 2014 Dominic Cleal <dcleal@redhat.com> - 1.1.0-15
+- Remove man/augtool.1 patches, always create .1 during build (RHBZ#1143954)
+
+* Thu Sep 18 2014 Dominic Cleal <dcleal@redhat.com> - 1.1.0-14
+- Kdump: parse new options, EOL comments (RHBZ#1139298)
+- Rsyslog: parse property filters and templates (RHBZ#1138402)
+- Systemd: parse semicolons inside entry values (RHBZ#1139498)
+- Systemd: parse environment variables where value is quoted (RHBZ#1138508)
+
+* Thu Sep 04 2014 Dominic Cleal <dcleal@redhat.com> - 1.1.0-13
+- aug_save: return error when unlink fails (RHBZ#1091143)
+- augtool: add aliases to autocomplete (RHBZ#1100076)
+- augtool: remove unused dump-xml arg (RHBZ#1100106)
+- Automounter: parse hostnames with hyphens (RHBZ#1075162)
+- Cgconfig: parse other valid controllers (RHBZ#1112543)
+- Chrony: add lens (RHBZ#1071947)
+- docs: update man page with new commands (RHBZ#1100077)
+- Exports: permit colons for IPv6 client addresses (RHBZ#1067030)
+- Httpd: parse continued, quoted lines (RHBZ#1100551)
+- Ldso: parse hwcap lines (RHBZ#1102629)
+- NagiosCfg: parse nrpe.cfg with Nrpe (RHBZ#1102623)
+- Rmt: add lens (RHBZ#1100549)
+- Services: permit colons in service name (RHBZ#1121527)
+- Shellvars: support arithmetic expansion (RHBZ#1100550)
+- Syslog: parse TCP loghosts (RHBZ#1129386)
+- Syslog: parse IPv6 loghost addresses (RHBZ#1129388)
+- Systemd: parse /etc/sysconfig/*.systemd (RHBZ#1083022)
+- Systemd: parse quoted environment vars (RHBZ#1100547)
+
+* Tue Feb 25 2014 Dominic Cleal <dcleal@redhat.com> - 1.1.0-12
+- Add patch for Dovecot, mailbox and quote support (RHBZ#1064387)
+- Add patch for Keepalived, virtual server fixes (RHBZ#1064388)
+- Add patch for Krb5, parse braces in values (RHBZ#1066419)
+
+* Thu Feb 20 2014 Dominic Cleal <dcleal@redhat.com> - 1.1.0-11
+- Add patch for Yum, split exclude lines (RHBZ#1067039)
+
+* Tue Feb 18 2014 Dominic Cleal <dcleal@redhat.com> - 1.1.0-10
+- Add patch for IPRoute2, hex and hyphen protocols (RHBZ#1063961)
+- Add patch for IPRoute2, slashes in protocols (RHBZ#1063968)
+
+* Mon Feb 10 2014 Dominic Cleal <dcleal@redhat.com> - 1.1.0-9
+- Add patch for yum-cron.conf incl entry (RHBZ#1058409)
+- Add patch for firewalld.conf incl entry (RHBZ#1058411)
+- Add patch for Grub, foreground option (RHBZ#1059426)
+- Add patch for Yum, spaces around equals (RHBZ#1062614)
+- Add patch for Shellvars, case and same-line ;; (RHBZ#1056541)
+
+* Fri Jan 24 2014 Daniel Mach <dmach@redhat.com> - 1.1.0-8
+- Mass rebuild 2014-01-24
+
+* Tue Jan 14 2014 Dominic Cleal <dcleal@redhat.com> - 1.1.0-7
+- Fix CVE-2013-6412, incorrect permissions under strict umask (RHBZ#1036081)
+
+* Thu Jan 02 2014 Dominic Cleal <dcleal@redhat.com> - 1.1.0-6
+- Add patch for Sysconfig module, empty comment lines (RHBZ#1043665)
+- Add check section to run test suite
+- Add patch for testPermsErrorReported test, when root (RHBZ#1043666)
+- Add patch for Shellvars, multivariable exports (RHBZ#1043815)
+
+* Fri Dec 27 2013 Daniel Mach <dmach@redhat.com> - 1.1.0-5
+- Mass rebuild 2013-12-27
+
+* Tue Nov 19 2013 Dominic Cleal <dcleal@redhat.com> - 1.1.0-4
+- Add patch for saving files with // in incl path (RHBZ#1031084)
+
+* Tue Oct 22 2013 Dominic Cleal <dcleal@redhat.com> - 1.1.0-3
+- Add patch for Grub module, setkey/lock support (RHBZ#1019485)
+
+* Mon Aug 12 2013 Dominic Cleal <dcleal@redhat.com> - 1.1.0-2
+- Fix source URL to download.augeas.net (RHBZ#996033)
+
+* Wed Jun 19 2013 David Lutterkort <lutter@redhat.com> - 1.1.0-1
+- Update to 1.1.0; remove all patches
+
+* Tue Jun 18 2013 Richard W.M. Jones <rjones@redhat.com> - 1.0.0-4
+- Fix /etc/sysconfig/network (RHBZ#904222).
+
+* Wed Jun  5 2013 Richard W.M. Jones <rjones@redhat.com> - 1.0.0-3
+- Don't package lenses in tests/ subdirectory.
+
+* Wed Feb 13 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.0.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild
+
+* Fri Jan  4 2013 David Lutterkort <lutter@redhat.com> - 1.0.0-1
+- New version; remove all patches
+
+* Wed Jul 18 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.10.0-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
+
+* Tue Jan 10 2012 David Lutterkort <lutter@redhat.com> - 0.10.0-3
+- Add patches for bugs 247 and 248 (JSON lens)
+
+* Sat Dec  3 2011 Richard W.M. Jones <rjones@redhat.com> - 0.10.0-2
+- Add patch to resolve missing libxml2 requirement in augeas.pc.
+
+* Fri Dec  2 2011 David Lutterkort <lutter@redhat.com> - 0.10.0-1
+- New version
+
+* Mon Jul 25 2011 David Lutterkort <lutter@redhat.com> - 0.9.0-1
+- New version; removed patch pathx-whitespace-ea010d8
+
+* Tue May  3 2011 David Lutterkort <lutter@redhat.com> - 0.8.1-2
+- Add patch pathx-whitespace-ea010d8.patch to fix BZ 700608
+
+* Fri Apr 15 2011 David Lutterkort <lutter@redhat.com> - 0.8.1-1
+- New version
+
+* Wed Feb 23 2011 David Lutterkort <lutter@redhat.com> - 0.8.0-1
+- New version
+
+* Mon Feb 07 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.7.4-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
+
+* Mon Nov 22 2010 Matthew Booth <mbooth@redhat.com> - 0.7.4-1
+- Update to version 0.7.4
+
+* Thu Nov 18 2010 Richard W.M. Jones <rjones@redhat.com> - 0.7.3-2
+- Upstream patch proposed to fix GCC optimization bug (RHBZ#651992).
+
+* Fri Aug  6 2010 David Lutterkort <lutter@redhat.com> - 0.7.3-1
+- Remove upstream patches
+
+* Tue Jun 29 2010 David Lutterkort <lutter@redhat.com> - 0.7.2-2
+- Patches based on upstream fix for BZ 600141
+
+* Tue Jun 22 2010 David Lutterkort <lutter@redhat.com> - 0.7.2-1
+- Fix ownership of /usr/share/augeas. BZ 569393
+
+* Wed Apr 21 2010 David Lutterkort <lutter@redhat.com> - 0.7.1-1
+- New version
+
+* Thu Jan 14 2010 David Lutterkort <lutter@redhat.com> - 0.7.0-1
+- Remove patch vim-ftdetect-syntax.patch. It's upstream
+
+* Tue Dec 15 2009 David Lutterkort <lutter@redhat.com> - 0.6.0-2
+- Fix ftdetect file for vim
+
+* Mon Nov 30 2009 David Lutterkort <lutter@redhat.com> - 0.6.0-1
+- Install vim syntax files
+
+* Mon Sep 14 2009 David Lutterkort <lutter@redhat.com> - 0.5.3-1
+- Remove separate xorg.aug, included in upstream source
+
+* Tue Aug 25 2009 Matthew Booth <mbooth@redhat.com> - 0.5.2-3
+- Include new xorg lens from upstream
+
+* Fri Jul 24 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.5.2-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild
+
+* Mon Jul 13 2009 David Lutterkort <lutter@redhat.com> - 0.5.2-1
+- New version
+
+* Fri Jun  5 2009 David Lutterkort <lutter@redhat.com> - 0.5.1-1
+- Install fadot
+
+* Fri Mar 27 2009 David Lutterkort <lutter@redhat.com> - 0.5.0-2
+- fadot isn't being installed just yet
+
+* Tue Mar 24 2009 David Lutterkort <lutter@redhat.com> - 0.5.0-1
+- New program /usr/bin/fadot
+
+* Mon Mar  9 2009 David Lutterkort <lutter@redhat.com> - 0.4.2-1
+- New version
+
+* Fri Feb 27 2009 David Lutterkort <lutter@redhat.com> - 0.4.1-1
+- New version
+
+* Fri Feb  6 2009 David Lutterkort <lutter@redhat.com> - 0.4.0-1
+- New version
+
+* Mon Jan 26 2009 David Lutterkort <lutter@redhat.com> - 0.3.6-1
+- New version
+
+* Tue Dec 23 2008 David Lutterkort <lutter@redhat.com> - 0.3.5-1
+- New version
+
+* Mon Feb 25 2008 David Lutterkort <dlutter@redhat.com> - 0.0.4-1
+- Initial specfile