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