... | ... |
@@ -11,6 +11,66 @@ functionality equivalent to `include config.d/*`. In essence, it is a glorified |
11 | 11 |
|
12 | 12 |
[`dotd`]: https://git.rcrnstn.net/rcrnstn/dotd |
13 | 13 |
|
14 |
+## Usage |
|
15 |
+ |
|
16 |
+`dotd --help`: |
|
17 |
+ |
|
18 |
+``` |
|
19 |
+dotd 1.0 |
|
20 |
+ |
|
21 |
+Generate a file from fragment files in a directory. |
|
22 |
+ |
|
23 |
+usage: |
|
24 |
+ dotd [options] [--] <file> [<dir>] |
|
25 |
+ dotd -h|--help |
|
26 |
+ dotd --version |
|
27 |
+ |
|
28 |
+arguments: |
|
29 |
+ <file> |
|
30 |
+ File to generate. Specify - to print to stdout instead. |
|
31 |
+ |
|
32 |
+ <dir> |
|
33 |
+ Directory containing fragment files. <file>.d is used if not |
|
34 |
+ specified. |
|
35 |
+ |
|
36 |
+options: |
|
37 |
+ -a, --action <action> |
|
38 |
+ Action to perform on each fragment file. E.g. |
|
39 |
+ printf "#include \"%s\"\n" |
|
40 |
+ [default: cat] |
|
41 |
+ |
|
42 |
+ -c, --comment <comment> |
|
43 |
+ If non-empty, adds comments starting with <comment> that the file is |
|
44 |
+ auto-generated and which fragment corresponds to which file. |
|
45 |
+ [default: ] |
|
46 |
+ |
|
47 |
+ -g, --glob <glob> |
|
48 |
+ Shell glob used to find fragment files, relative to <dir>. |
|
49 |
+ [default: *] |
|
50 |
+ |
|
51 |
+ -v, --validate <validate> |
|
52 |
+ Command to run on the (temporary) generated file. Only if the command |
|
53 |
+ returns success is the file moved to its final destination. E.g. |
|
54 |
+ /usr/sbin/sshd -t -f |
|
55 |
+ [default: ] |
|
56 |
+``` |
|
57 |
+ |
|
58 |
+## Dependencies |
|
59 |
+ |
|
60 |
+All dependencies are [POSIX][] and are highly likely to be installed by default |
|
61 |
+(e.g. they all have the [Debian priority][] `required`). |
|
62 |
+ |
|
63 |
+- `sh` (e.g. from [`bash`][] or [`dash`][]) |
|
64 |
+- `sed` (e.g. from [`sed`][]) |
|
65 |
+- `awk` (e.g. from [`mawk`][]) |
|
66 |
+ |
|
67 |
+[POSIX]: https://en.wikipedia.org/wiki/POSIX |
|
68 |
+[Debian priority]: https://www.debian.org/doc/debian-policy/ch-archive.html#s-priorities |
|
69 |
+[`bash`]: https://packages.debian.org/bash |
|
70 |
+[`dash`]: https://packages.debian.org/dash |
|
71 |
+[`sed`]: https://packages.debian.org/sed |
|
72 |
+[`mawk`]: https://packages.debian.org/mawk |
|
73 |
+ |
|
14 | 74 |
## Related projects |
15 | 75 |
|
16 | 76 |
- [Ansible][]'s [`assemble` module][]. |
17 | 77 |
new file mode 100755 |
... | ... |
@@ -0,0 +1,86 @@ |
1 |
+#!/bin/sh |
|
2 |
+set -euC |
|
3 |
+ |
|
4 |
+##/// dotd 1.0 |
|
5 |
+#//// |
|
6 |
+#//// Generate a file from fragment files in a directory. |
|
7 |
+#//// |
|
8 |
+###// usage: |
|
9 |
+#//// dotd [options] [--] <file> [<dir>] |
|
10 |
+#//// dotd -h|--help |
|
11 |
+#//// dotd --version |
|
12 |
+#//// |
|
13 |
+###// arguments: |
|
14 |
+####/ <file> |
|
15 |
+#//// File to generate. Specify - to print to stdout instead. |
|
16 |
+#//// |
|
17 |
+####/ <dir> |
|
18 |
+#//// Directory containing fragment files. <file>.d is used if not |
|
19 |
+#//// specified. |
|
20 |
+#//// |
|
21 |
+###// options: |
|
22 |
+####/ -a, --action <action> |
|
23 |
+#//// Action to perform on each fragment file. E.g. |
|
24 |
+#//// printf "#include \"%s\"\n" |
|
25 |
+#//// [default: cat] |
|
26 |
+#//// |
|
27 |
+####/ -c, --comment <comment> |
|
28 |
+#//// If non-empty, adds comments starting with <comment> that the file is |
|
29 |
+#//// auto-generated and which fragment corresponds to which file. |
|
30 |
+#//// [default: ] |
|
31 |
+#//// |
|
32 |
+####/ -g, --glob <glob> |
|
33 |
+#//// Shell glob used to find fragment files, relative to <dir>. |
|
34 |
+#//// [default: *] |
|
35 |
+#//// |
|
36 |
+####/ -v, --validate <validate> |
|
37 |
+#//// Command to run on the (temporary) generated file. Only if the command |
|
38 |
+#//// returns success is the file moved to its final destination. E.g. |
|
39 |
+#//// /usr/sbin/sshd -t -f |
|
40 |
+#//// [default: ] |
|
41 |
+ |
|
42 |
+## Messages |
|
43 |
+help() { sed -n 's|^#[#/]*/ \?||p' "$0"; exit 0; } |
|
44 |
+version() { help | awk '/^$/{++p;next}p==0'; exit 0; } |
|
45 |
+usage() { help | awk '/^$/{++p;next}p==2'; exit 0; } |
|
46 |
+parse() { printf '%s: error: %s\n' "$0" "$1"; usage; exit 1; } >&2 |
|
47 |
+error() { printf '%s: error: %s\n' "$0" "$1"; exit 1; } >&2 |
|
48 |
+warning() { printf '%s: warning: %s\n' "$0" "$1"; } >&2 |
|
49 |
+opt() { [ $# -gt 1 ] || parse "option '$1' value not provided"; } |
|
50 |
+arg() { [ $# -gt 1 ] || parse "argument '$1' not provided"; } |
|
51 |
+ |
|
52 |
+## Parse special options |
|
53 |
+case "${1-}" |
|
54 |
+in |
|
55 |
+ '-h'|'--help') help; ;; |
|
56 |
+ '--version') version; ;; |
|
57 |
+esac |
|
58 |
+ |
|
59 |
+## Parse options |
|
60 |
+action='cat' |
|
61 |
+comment='' |
|
62 |
+glob='*' |
|
63 |
+validate='' |
|
64 |
+while [ $# -gt 0 ] |
|
65 |
+do |
|
66 |
+ case "$1" |
|
67 |
+ in |
|
68 |
+ '-a'|'--action') shift; opt 'action' "$@"; action="$1"; ;; |
|
69 |
+ '-c'|'--comment') shift; opt 'comment' "$@"; comment="$1"; ;; |
|
70 |
+ '-g'|'--glob') shift; opt 'glob' "$@"; glob="$1"; ;; |
|
71 |
+ '-v'|'--validate') shift; opt 'validate' "$@"; validate="$1"; ;; |
|
72 |
+ '--') shift; break; ;; |
|
73 |
+ '-'?*) parse "unrecognized option '$1'"; ;; |
|
74 |
+ *) break; ;; |
|
75 |
+ esac |
|
76 |
+ shift |
|
77 |
+done |
|
78 |
+ |
|
79 |
+## Parse required arguments |
|
80 |
+arg 'file' "$@"; file="$1"; shift; |
|
81 |
+ |
|
82 |
+## Parse optional arguments |
|
83 |
+dir="${1-}"; shift $(($#>0)); |
|
84 |
+ |
|
85 |
+## Parse unrecognized arguments |
|
86 |
+[ $# -eq 0 ] || parse "unrecognized argument: '$1'" |