1#!/bin/sh
2
3# Checks some of the GNU style formatting rules in a set of patches.
4# Copyright (C) 2010, 2012, 2016  Free Software Foundation, Inc.
5# Contributed by Sebastian Pop <sebastian.pop@amd.com>
6
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 3 of the License, or
10# (at your option) any later version.
11
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, see the file COPYING3.  If not,
19# see <http://www.gnu.org/licenses/>.
20
21# Set to empty in the environment to override.
22: ${color:---color=always}
23
24usage() {
25    cat <<EOF
26check_GNU_style.sh [patch]...
27
28    Checks the patches for some of the GNU style formatting problems.
29    When FILE is -, read standard input.
30
31    Please note that these checks are not always accurate, and
32    complete.  The reference documentation of the GNU Coding Standards
33    can be found here: http://www.gnu.org/prep/standards_toc.html
34    and there are also some additional coding conventions for GCC:
35    http://gcc.gnu.org/codingconventions.html
36
37EOF
38    exit 1
39}
40
41test $# -eq 0 && usage
42nfiles=$#
43files="$*"
44
45stdin=false
46stdin_tmp=""
47if [ $nfiles -eq 1 ] && [ "$files" = "-" ]; then
48    stdin=true
49
50    # By putting stdin into a temp file, we can handle it just like any other
51    # file.  F.i., we can cat it twice, which we can't do with stdin.
52    stdin_tmp=check_GNU_style.stdin
53    cat - > $stdin_tmp
54    files=$stdin_tmp
55else
56    for f in $files; do
57	if [ "$f" = "-" ]; then
58	    # Let's keep things simple.  Either we read from stdin, or we read
59	    # from files specified on the command line, not both.
60	    usage
61	fi
62	if [ ! -f "$f" ]; then
63	    echo "error: could not read file: $f"
64	    exit 1
65	fi
66    done
67fi
68
69inp=check_GNU_style.inp
70tmp=check_GNU_style.tmp
71tmp2=check_GNU_style.2.tmp
72tmp3=check_GNU_style.3.tmp
73
74# Remove $tmp on exit and various signals.
75trap "rm -f $inp $tmp $tmp2 $tmp3 $stdin_tmp" 0
76trap "rm -f $inp $tmp $tmp2 $tmp3 $stdin_tmp; exit 1" 1 2 3 5 9 13 15
77
78if [ $nfiles -eq 1 ]; then
79    # There's no need for the file prefix if we're dealing only with one file.
80    format="-n"
81else
82    format="-nH"
83fi
84
85# Remove the testsuite part of the diff.  We don't care about GNU style
86# in testcases and the dg-* directives give too many false positives.
87remove_testsuite ()
88{
89  awk 'BEGIN{testsuite=0} /^(.*:)?([1-9][0-9]*:)?\+\+\+ / && ! /testsuite\//{testsuite=0} \
90       {if (!testsuite) print} /^(.*:)?([1-9][0-9]*:)?\+\+\+ (.*\/)?testsuite\//{testsuite=1}'
91}
92
93grep $format '^+' $files \
94    | remove_testsuite \
95    | grep -v ':+++' \
96    > $inp
97
98cat_with_prefix ()
99{
100    local f="$1"
101
102    if [ "$prefix" = "" ]; then
103	cat "$f"
104    else
105	awk "{printf \"%s%s\n\", \"$prefix\", \$0}" $f
106    fi
107}
108
109# Grep
110g (){
111    local msg="$1"
112    local arg="$2"
113
114    local found=false
115    cat $inp \
116	| egrep $color -- "$arg" \
117	> "$tmp" && found=true
118
119    if $found; then
120	printf "\n$msg\n"
121	cat "$tmp"
122    fi
123}
124
125# And Grep
126ag (){
127    local msg="$1"
128    local arg1="$2"
129    local arg2="$3"
130
131    local found=false
132    cat $inp \
133	| egrep $color -- "$arg1" \
134	| egrep $color -- "$arg2" \
135	> "$tmp" && found=true
136
137    if $found; then
138	printf "\n$msg\n"
139	cat "$tmp"
140    fi
141}
142
143# reVerse Grep
144vg (){
145    local msg="$1"
146    local varg="$2"
147    local arg="$3"
148
149    local found=false
150    cat $inp \
151	| egrep -v -- "$varg" \
152	| egrep $color -- "$arg" \
153	> "$tmp" && found=true
154
155    if $found; then
156	printf "\n$msg\n"
157	cat "$tmp"
158    fi
159}
160
161col (){
162    local msg="$1"
163
164    local first=true
165    local f
166    for f in $files; do
167	prefix=""
168	if [ $nfiles -ne 1 ]; then
169	    prefix="$f:"
170	fi
171
172	# Don't reuse $inp, which may be generated using -H and thus contain a
173	# file prefix.  Re-remove the testsuite since we're not using $inp.
174	cat $f | remove_testsuite \
175	    | grep -n '^+' \
176	    | grep -v ':+++' \
177	    > $tmp
178
179	# Keep only line number prefix and patch modifier '+'.
180	cat "$tmp" \
181	    | sed 's/\(^[0-9][0-9]*:+\).*/\1/' \
182	    > "$tmp2"
183
184	# Remove line number prefix and patch modifier '+'.
185	# Expand tabs to spaces according to tab positions.
186	# Keep long lines, make short lines empty.  Print the part past 80 chars
187	# in red.
188	cat "$tmp" \
189	    | sed 's/^[0-9]*:+//' \
190	    | expand \
191	    | awk '{ \
192		     if (length($0) > 80) \
193		       printf "%s\033[1;31m%s\033[0m\n", \
194			      substr($0,1,80), \
195			      substr($0,81); \
196		     else \
197		       print "" \
198		   }' \
199	    > "$tmp3"
200
201	# Combine prefix back with long lines.
202	# Filter out empty lines.
203	local found=false
204	paste -d '\0' "$tmp2" "$tmp3" \
205	    | grep -v '^[0-9][0-9]*:+$' \
206	    > "$tmp" && found=true
207
208	if $found; then
209	    if $first; then
210		printf "\n$msg\n"
211		first=false
212	    fi
213	    cat_with_prefix "$tmp"
214	fi
215    done
216}
217
218
219col 'Lines should not exceed 80 characters.'
220
221g 'Blocks of 8 spaces should be replaced with tabs.' \
222    ' {8}'
223
224g 'Trailing whitespace.' \
225    '[[:space:]]$'
226
227g 'Space before dot.' \
228    '[[:alnum:]][[:blank:]]+\.'
229
230g 'Dot, space, space, new sentence.' \
231    '[[:alnum:]]\.([[:blank:]]|[[:blank:]]{3,})[A-Z0-9]'
232
233g 'Dot, space, space, end of comment.' \
234    '[[:alnum:]]\.([[:blank:]]{0,1}|[[:blank:]]{3,})\*/'
235
236g 'Sentences should end with a dot.  Dot, space, space, end of the comment.' \
237    '[[:alnum:]][[:blank:]]*\*/'
238
239vg 'There should be exactly one space between function name and parenthesis.' \
240    '\#define' \
241    '[[:alnum:]]([[:blank:]]{2,})?\('
242
243g 'There should be no space before a left square bracket.' \
244   '[[:alnum:]][[:blank:]]+\['
245
246g 'There should be no space before closing parenthesis.' \
247    '[[:graph:]][[:blank:]]+\)'
248
249# This will give false positives for C99 compound literals.
250g 'Braces should be on a separate line.' \
251    '(\)|else)[[:blank:]]*{'
252
253# Does this apply to definitions of aggregate objects?
254ag 'Trailing operator.' \
255  '^[1-9][0-9]*:\+[[:space:]]' \
256  '(([^a-zA-Z_]\*)|([-%<=&|^?])|([^*]/)|([^:][+]))$'
257