Browse code

Add implementation

Robert Cranston authored on 01/12/2021 00:20:49
Showing 2 changed files

  • README.md index 97b9c83..3e7fd3d 100644
  • dotd index 6ed9b3b..de9b745 100755
... ...
@@ -9,6 +9,10 @@ each other's toes, when the native configuration language does not support
9 9
 functionality equivalent to `include config.d/*`. In essence, it is a glorified
10 10
 `cat config.d/* > config`.
11 11
 
12
+The generated file is compared against any pre-existing file, if they differ a
13
+warning is printed and the pre-existing file is moved to a backup file whose
14
+name includes a time stamp.
15
+
12 16
 [`dotd`]: https://git.rcrnstn.net/rcrnstn/dotd
13 17
 
14 18
 ## Usage
... ...
@@ -41,11 +45,13 @@ options:
41 45
 
42 46
   -c, --comment <comment>
43 47
     If non-empty, adds comments starting with <comment> that the file is
44
-    auto-generated and which fragment corresponds to which file.
48
+    auto-generated and which fragment corresponds to which file. E.g.
49
+      //
45 50
     [default: ]
46 51
 
47 52
   -g, --glob <glob>
48
-    Shell glob used to find fragment files, relative to <dir>.
53
+    Shell glob used to find fragment files, relative to <dir>. E.g
54
+      *.h
49 55
     [default: *]
50 56
 
51 57
   -v, --validate <validate>
... ...
@@ -57,10 +63,13 @@ options:
57 63
 
58 64
 ## Dependencies
59 65
 
60
-All dependencies are [POSIX][] and are highly likely to be installed by default
61
-(e.g. they all have the [Debian priority][] `required`).
66
+All dependencies are [POSIX][] (except `mktemp`, but most Unix-like systems
67
+have some version), and are highly likely to be installed by default (e.g. they
68
+all have the [Debian priority][] `required`).
62 69
 
63 70
 -   `sh` (e.g. from [`bash`][] or [`dash`][])
71
+-   `rm`, `mv`, `tail`, `date`, `mktemp` (e.g. from [`coreutils`][])
72
+-   `cmp` (e.g. from [`diffutils`][])
64 73
 -   `sed` (e.g. from [`sed`][])
65 74
 -   `awk` (e.g. from [`mawk`][])
66 75
 
... ...
@@ -68,6 +77,8 @@ All dependencies are [POSIX][] and are highly likely to be installed by default
68 77
 [Debian priority]: https://www.debian.org/doc/debian-policy/ch-archive.html#s-priorities
69 78
 [`bash`]: https://packages.debian.org/bash
70 79
 [`dash`]: https://packages.debian.org/dash
80
+[`coreutils`]: https://packages.debian.org/coreutils
81
+[`diffutils`]: https://packages.debian.org/diffutils
71 82
 [`sed`]: https://packages.debian.org/sed
72 83
 [`mawk`]: https://packages.debian.org/mawk
73 84
 
... ...
@@ -26,11 +26,13 @@ set -euC
26 26
 #////
27 27
 ####/   -c, --comment <comment>
28 28
 #////     If non-empty, adds comments starting with <comment> that the file is
29
-#////     auto-generated and which fragment corresponds to which file.
29
+#////     auto-generated and which fragment corresponds to which file. E.g.
30
+#////       //
30 31
 #////     [default: ]
31 32
 #////
32 33
 ####/   -g, --glob <glob>
33
-#////     Shell glob used to find fragment files, relative to <dir>.
34
+#////     Shell glob used to find fragment files, relative to <dir>. E.g.
35
+#////       *.h
34 36
 #////     [default: *]
35 37
 #////
36 38
 ####/   -v, --validate <validate>
... ...
@@ -84,3 +86,68 @@ dir="${1-}"; shift $(($#>0));
84 86
 
85 87
 ## Parse unrecognized arguments
86 88
 [ $# -eq 0 ] || parse "unrecognized argument: '$1'"
89
+
90
+## Helpers
91
+first='y'
92
+comment_last="$(printf '%s' "$comment" | tail -c 1)"
93
+empty()
94
+{
95
+  ! [ "$first" ] && [ "$comment" ] || return 0
96
+  printf '\n'
97
+}
98
+comment()
99
+{
100
+  [ "$comment" ] || return 0
101
+  printf '%s\n' "$1"
102
+  first=''
103
+}
104
+action()
105
+{
106
+  eval "$action" "'$1'"
107
+  first=''
108
+}
109
+
110
+## Set dir
111
+if ! [ "$dir" ]
112
+then
113
+  [ "$file" != "-" ] || error "must specify <dir> when printing to stdout"
114
+  dir="$file.d"
115
+fi
116
+
117
+## Generate temporary file
118
+file_tmp="$(mktemp)"
119
+{
120
+  comment "$comment This file was auto-generated from '$dir/$glob'."
121
+  for path in $dir/$glob
122
+  do
123
+    [ -r "$path" ] || continue
124
+    empty
125
+    comment "$comment$comment_last BEGIN $path"
126
+    action "$path"
127
+    comment "$comment END $path"
128
+  done
129
+} >> "$file_tmp"
130
+
131
+## Validate
132
+if [ "$validate" ] && ! eval "$validate" "'$file_tmp'"
133
+then
134
+  rm "$file_tmp"
135
+  error "validation failed: $validate"
136
+fi
137
+
138
+## Optionally print to stdout
139
+if [ "$file" = "-" ]
140
+then
141
+  cat "$file_tmp"
142
+  rm "$file_tmp"
143
+  exit 0
144
+fi
145
+
146
+## Otherwise move files
147
+if [ -e "$file" ] && ! cmp -s "$file_tmp" "$file"
148
+then
149
+  file_bak="$(mktemp "$file.$(date "+%Y-%m-%d_%H:%M:%S").XXXX.bak")"
150
+  warning "moving '$file' to '$file_bak'"
151
+  mv "$file" "$file_bak"
152
+fi
153
+mv -f "$file_tmp" "$file"