| ... | ... |
@@ -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" |