update-shell-includes
1 #!/usr/bin/env bash 2 # 3 # Arrange for all shell scripts to obtain the common libraries 4 # not via the current working directory, but rather realpath $0, by: 5 # 6 # 1. Checking that no shell scripts have ad hoc `.` or `source`'s 7 # 2. Updating/checking the standard bash-utils.sh include stanza. 8 # 9 # Usage; 10 # update-shell-includes [--check] --all | [--] FILE... 11 # 12 # To edit the standard shell script stanza, edit it here in this script! 13 14 set -euo pipefail 15 16 # this include stanza is automatically maintained by update-shell-includes 17 common_dir=$(realpath "$0") 18 common_dir=$(dirname "$common_dir") 19 # shellcheck source=maint/common/bash-utils.sh 20 . "$common_dir"/bash-utils.sh 21 22 install=true 23 all=false 24 25 while [ $# != 0 ]; do 26 case "$1" in 27 --) shift; break ;; 28 --check) install=false ;; 29 --all) all=true ;; 30 -*) fail "unknown option $1";; 31 *) break ;; 32 esac 33 shift 34 done 35 36 case "$all.$#" in 37 false.0) fail "need --all or one or more script filenames" ;; 38 false.*) ;; 39 true.0) 40 wanted=$( 41 git_grep_for_shell_script_shebangs | grep -vF "${0##*/}" 42 ) 43 # shellcheck disable=SC2086 44 set -- $wanted 45 ;; 46 true.*) fail "script filenames not allowed with --all " ;; 47 esac 48 49 # create the .new files here, with cp, to preserve the permissions 50 for f in "$@"; do 51 cp -- "$f" "$f.new" 52 done 53 54 errors=$(perl -we ' 55 use strict; 56 use POSIX; 57 58 my $msg_re = qr{this include stanza is automatically maintained}; 59 my $stanza_re = qr{ 60 ^ \s* \n # blank line 61 \# \s* $msg_re .* \n 62 (?: .* \S .* \n )+ # some non-blank lines 63 }xm; 64 65 my $bad_re = qr{ 66 ^ [\ \t]* (?: \. | source ) [ \t] [^\$\n] * (?: maint/ | bash-utils\.sh ) .* 67 }xm; 68 69 undef $/; 70 sub slurp ($) { 71 open F, "<", "$_[0]" or die "$_[0]: $!"; 72 $_ = <F>; 73 F->error and die $!; 74 } 75 76 slurp(shift @ARGV); 77 m{$stanza_re} or die "missing stanza in self!"; 78 79 my $stanza = $&; 80 81 foreach my $f (@ARGV) { 82 slurp($f); 83 if (s{$stanza_re}{$stanza}) { 84 } elsif (m{$bad_re}) { 85 print "$f: bad include line, \`$&`\n"; 86 } 87 open O, ">", "$f.new" or die "$f.new: $!"; 88 print O or die $!; 89 close O or die $!; 90 } 91 ' "$0" "$@") 92 93 ok=true 94 95 if [ "$errors" != "" ]; then 96 cat <<END >&2 97 errors searching/checking scripts for include stanzas: 98 $errors 99 END 100 ok=false 101 fi 102 103 for f in "$@"; do 104 if $ok && $install; then 105 mv -f -- "$f.new" "$f" 106 else 107 set +e 108 diff -u -- "$f" "$f.new" 109 rc=$? 110 set -e 111 case "$rc" in 112 0) rm -- "$f.new" ;; 113 1) ok=false;; 114 *) fail 'diff failed';; 115 esac 116 fi 117 done 118 119 if ! $ok; then 120 fail "$0 check/update failed" 121 fi