The post you're referring to is from 2008. Augeas has since been able to parse recursive configuration files, using the rec
keyword. See for example lvm.aug
, which is quite similar to what you're trying to achieve.
Parsing a simple libconfuse file with Augeas
-
30-05-2022 - |
Question
I'm using libconfuse
for my program's configuration files, and that's working nicely. Now I'm interested to parse the configuration files using Augeas. I found a mailing list post which says that there's no generic Augeas lens for libconfuse
files, because it's a "context-free file format" (in essence, it allows infinite nesting).
My program's configuration files are quite simple, with just one level of sections containing configuration parameters. E.g.:
serial {
serial-device = "/dev/ttyUSB0"
baudrate = 115200
}
server-socket {
host = "localhost"
port = 12345
}
What would be involved in writing a generic Augeas lens for this simple variety of libconfuse
configuration file? Are there any examples around? What would be the most straight-forward way to handle this?
Solution
OTHER TIPS
Thanks to ℝaphink's answer, I started with the lvm.aug
lens, which was a good starting point, and improved it a little.
Here's what I've got at the moment, which supports only a subset of libconfuse
syntax—if you test it with the libconfuse
example test.conf
, it will fail in several places. But it works for the sub-set of the syntax in the config files I'm currently using, so it's "good enough" for my current purposes. Although I'd like to figure out how to get indentation of nested blocks looking nice (like the json.aug
lens does; I haven't figured out how it does it yet).
(*
Module: LibconfuseSimple
Based on Module LVM
*)
module LibconfuseSimple =
(* See lvm2/libdm/libdm-config.c for tokenisation;
* libdm uses a blacklist but I prefer the safer whitelist approach. *)
(* View: identifier
* The left hand side of a definition *)
let identifier = /[a-zA-Z0-9_-]+/
(* strings can contain backslash-escaped dquotes, but I don't know
* how to get the message across to augeas *)
let str = [label "str". Quote.do_quote (store /[^"]*/)]
let int = [label "int". store Rx.integer]
let env = [label "env". del "${" "${" . store /[^}]*/ . del "}" "}"]
let const (r:regexp) = [ label "const" . store r ]
let rawstr = [label "rawstr". store Rx.space_in]
(* View: flat_literal
* A literal without structure *)
let flat_literal = int|str|env|const /true|false|null/|rawstr
(* allow multiline and mixed int/str, used for raids and stripes *)
(* View: list
* A list containing flat literals *)
let list = [
label "list" . counter "list"
. del /\[[ \t\n]*/ "["
.([seq "list". flat_literal . del /,[ \t\n]*/ ", "]*
. [seq "list". flat_literal . del /[ \t\n]*/ ""])?
. Util.del_str "]"]
(* View: val
* Any value that appears on the right hand side of an assignment *)
let val = flat_literal | list
(* View: comments
* Comments of various sorts *)
let comments =
Util.comment
| Util.comment_c_style
| Util.comment_multiline
(* View: nondef
* A line that doesn't contain a statement *)
let nondef =
Util.empty
| comments
(* View: indent
* Remove any input indentation; output 4 spaces indentation. *)
let indent = del /[ \t]*/ " "
(* Build.block couldn't be reused, because of recursion and
* a different philosophy of whitespace handling. *)
(* View: def
* An assignment, or a block containing definitions *)
let rec def = [
key identifier . (
(del /[ \t]*/ " " . [label "title" . store identifier])? . del /[ \t]*\{\n?/ " {\n"
.[label "dict" . (Util.empty | indent . comments | indent . def)*]
. Util.indent . Util.del_str "}\n"
|Sep.space_equal . val . Util.comment_or_eol)]
(* View: lns
* The main lens *)
let lns = (nondef | (Util.indent . def))*