1#!/bin/sh 2# 3# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) 4# 5# SPDX-License-Identifier: BSD-2-Clause 6# 7 8# Exit on unhandled error exit statuses; turn off filename expansion 9# ("globbing"); warn on expansion of unset parameters. 10set -efu 11 12PROGNAME=${0##*/} 13DIRNAME=${0%/*} 14 15die () { 16 echo "$PROGNAME: fatal error: $*" >&2 17 exit 3 18} 19 20is_shell_script () { 21 FILE=$1 22 # Use parameter expansion tricks as a poor man's pattern matcher; if the 23 # expansion does not mutate the parameter, then it _didn't_ match the 24 # pattern. 25 if [ "${FILE%.bash}" != "$FILE" ] \ 26 || [ "${FILE%.ksh}" != "$FILE" ] \ 27 || [ "${FILE%.sh}" != "$FILE" ] 28 then 29 # It's claiming to be a Bourne-based script; believe it. 30 return 0 31 fi 32 33 # If it's executable, let file(1) do the hard work of figuring out what kind 34 # of program it is. 35 if [ -x "$FILE" ] 36 then 37 DESCRIPTION=$(file -b "$FILE") 38 39 case "$DESCRIPTION" in 40 # Catch POSIX, Korn, Bourne-Again, and Z shell scripts. 41 ("* shell script*"|"* zsh script*") 42 return 0 43 ;; 44 # Catch indirections through env. Mind ksh88 and ksh93. 45 ("*env *sh script*"|"*env ksh88 script*"|"*env ksh93 script*") 46 return 0 47 ;; 48 esac 49 fi 50 51 # All checks failed; report that the argument is not a shell script. 52 return 1 53} 54 55# Amusingly, this script won't validate itself with "command -v"; the 56# "-v" flag was part of the "User Portability Utilities option" in POSIX 57# Issue 6 (2004), but only promoted to full mandatory status in Issue 7 58# (2017). So instead use "which" (a Debianism, but widely available) 59# until Debian's checkbashisms catches up. 60# 61#if ! command -v checkbashisms > /dev/null 62if ! which checkbashisms > /dev/null 63then 64 die "\"checkbashisms\" command not found; is the \"devscripts\" package" \ 65 "installed?" 66fi 67 68if ! which file > /dev/null 69then 70 die "\"file\" command not found; is the \"file\" package installed?" 71fi 72 73if ! which python3 > /dev/null 74then 75 die "\"python3\" command not found; is the \"python3\" package installed?" 76fi 77 78if [ -f .stylefilter ] 79then 80 set -- $(python3 "$DIRNAME"/filter.py -f .stylefilter "$@") 81fi 82 83for FILE 84do 85 # Is the current file a shell script? If not, skip it. 86 is_shell_script "$FILE" || continue 87 88 # --force checks for non-POSIX constructs even in scripts that declare bash 89 # as the interpreter (useful because we don't want any bash scripts). 90 # 91 # --posix forces full-POSIX checking, ignoring the couple of exceptions to 92 # POSIX-compliance permitted by Debian policy. 93 # 94 # --extra prints out the offending line after the diagnostic. 95 if ! checkbashisms --force --posix --extra "$FILE" 96 then 97 die "script \"$FILE\" has non-POSIX features" 98 fi 99 100 # Now use the shell's own internal parser to find more subtle problems. 101 # Even this is not perfect because in this mode, the shell fails to perform 102 # many expansions due to possible side effects. Also, the shell language is 103 # not decidable. :-| (See Trienen, Jennerod, "Mining Debian Maintainer 104 # Scripts", 105 # https://debconf18.debconf.org/talks/90-mining-debian-maintainer-scripts/ ) 106 # 107 # Here's an example of a construct sh -n doesn't catch that fails when run 108 # for real: 109 # 110 # [[ a =~ a* ]] || echo a does not equal a 111 # 112 # ...and more forgivably, stupid eval tricks like this: 113 # 114 # STRING='${ARRAY_DEREF[1]}'; eval echo $STRING 115 if ! sh -n "$FILE" 116 then 117 die "script \"$FILE\" failed syntax check" 118 fi 119done 120