# # $Id: shlint.awk,v 1.14 2004/05/08 12:39:59 jmmv Exp $ # Verify shell code syntax. # # Copyright (c) 2003 Julio M. Merino Vidal. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # 3. Neither the name of the author nor the names of contributors may # be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # function warn(msg) { print FILENAME "[" FNR "]: " msg > "/dev/stderr" error = 1 } BEGIN { skip = 0 error = 0 emacs_modeline = 0 vim_modeline = 0 } /NOLINT_BEGIN/ { skip = 1 next } /NOLINT_END/ { skip = 0 next } /NOLINT/ { next } { if (skip) next } /vim: syntax=sh/ { vim_modeline = 1 } /mode: shell-script/ { emacs_modeline = 1 } /^[ \t]*#/ { next } /[$ \t]+_[a-zA-Z0-9]+=/ { warn("Variable should not start with an underline") } /\$[^0-9'"$?@#*{(|\/]+/ { warn("Missing braces around variable name") } /=(""|'')/ { warn("Assignment to the empty string does not need quotes"); } /"[a-zA-Z0-9\/.,_-]+"/ { warn("Single word does not need to be quoted"); } /'[a-zA-Z0-9\/.,_-]+'/ { warn("Single word does not need to be quoted"); } /basename[ \t]+/ { warn("Use parameter expansion instead of basename"); } /dirname[ \t]+/ { warn("Use parameter expansion instead of dirname"); } /if[ \t]+(test|![ \t]+test)/ { warn("Use [ instead of test"); } /if.*;[ \t]*fi$/ { warn("Avoid using a single line if conditional"); } END { if (! emacs_modeline || ! vim_modeline) warn("Missing mode lines"); if (error) exit 1 }