1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
let ( let@ ) finally fn = Fun.protect ~finally fn
let error_msgf fmt = Fmt.kstr (fun msg -> Error (`Msg msg)) fmt
let pp_error ppf = function
| `Lexer_error (loc, `Invalid_character chr) ->
Fmt.pf ppf "Invalid character %C at %a" chr Bcfg.Txtloc.pp loc
| `Lexer_error (loc, `Message msg) ->
Fmt.pf ppf "%s at %a" msg Bcfg.Txtloc.pp loc
| `Parser_error (loc, _) ->
Fmt.pf ppf "Invalid syntax at %a" Bcfg.Txtloc.pp loc
| `Rejected -> Fmt.pf ppf "Rejected configuration"
let read_and_parse input =
let ic, finally =
match input with
| None -> (stdin, ignore)
| Some filepath ->
let ic = open_in_bin filepath in
(ic, fun () -> close_in ic)
in
let@ () = finally in
let lexbuf = Lexing.from_channel ~with_positions:true ic in
Bcfg.parser lexbuf
let run _quiet cfg in_place input =
match (in_place, input) with
| true, None ->
error_msgf "Cannot use $(b,--in-place) when reading from standard input."
| _ ->
begin match read_and_parse input with
| Error err ->
Fmt.epr "%a: %a.@."
Fmt.(option ~none:(any "<stdin>") string)
input pp_error err;
Fmt.epr "Run $(b,bcfg validate) for a detailed report.@.";
Ok 1
| Ok v ->
let seq = Bcfg.emitter ~cfg v in
(match (in_place, input) with
| true, Some filepath ->
let oc = open_out_bin filepath in
let@ () = fun () -> close_out oc in
Seq.iter (output_string oc) seq
| _ -> Seq.iter (output_string stdout) seq);
Ok 0
end
open Cmdliner
open Bcfg_cli
let input =
let doc = "The configuration file to lint ($(b,-) for standard input)." in
let parser str =
match str with
| "-" -> Ok None
| filepath when Sys.file_exists filepath && is_regular_file filepath ->
Ok (Some filepath)
| filepath ->
error_msgf "%S does not exist or is not a regular file" filepath
in
let pp ppf = function
| None -> Fmt.string ppf "-"
| Some filepath -> Fmt.string ppf filepath
in
let input = Arg.conv (parser, pp) in
let open Arg in
value & pos 0 input None & info [] ~doc ~docv:"FILE"
let in_place =
let doc = "Rewrite the given file in place instead of printing to stdout." in
let open Arg in
value & flag & info [ "i"; "in-place" ] ~doc
let term =
let open Term in
const run $ setup_logs $ setup_output_configuration $ in_place $ input
|> term_result ~usage:false
let cmd =
let doc = "$(tname) reformats (pretty-prints) a configuration file." in
let man =
[
`S Manpage.s_description;
`P
"$(tname) parses a $(b,bcfg) configuration and prints it back in a \
canonical form: it re-indents the directives and wraps long values \
that would otherwise exceed the right margin.";
`P
"The indentation width and the margin (the column at which values are \
wrapped) are configurable, in the spirit of $(b,vim)'s \
$(b,shiftwidth) and $(b,textwidth):";
`Pre "> bcfg lint --indent 4 --margin 100 file.cfg";
`P
"By default the result is written to standard output; use \
$(b,--in-place) (or $(b,-i)) to overwrite the input file.";
]
in
let info = Cmd.info "lint" ~doc ~man in
Cmd.v info term