Browse code

Add argument parsing

Robert Cranston authored on 28/11/2021 17:08:00
Showing 2 changed files

  • README.md index ec865b0..97b9c83 100644
  • dotd index 0000000..6ed9b3b
... ...
@@ -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'"