]> granicus.if.org Git - libevent/blob - checkpatch.sh
cmake: do influence CMAKE_DEBUG_POSTFIX of the outer project (if any)
[libevent] / checkpatch.sh
1 #!/usr/bin/env bash
2
3 # TODO:
4 # - inline replace
5 # - clang-format-diff replacement
6 # - uncrustify for patches (not git refs)
7 # - maybe integrate into travis-ci?
8
9 function usage()
10 {
11     cat <<EOL
12 $0 [ OPTS ] [ file-or-gitref [ ... ] ]
13
14 Example:
15   # Chech HEAD git ref
16   $ $0 -r
17   $ $0 -r HEAD
18
19   # Check patch
20   $ git format-patch --stdout -1 | $0 -p
21   $ git show -1 | $0 -p
22
23   # Or via regular files
24   $ git format-patch --stdout -2
25   $ $0 *.patch
26
27   # Over a file
28   $ $0 -d event.c
29   $ $0 -d < event.c
30
31   # And print the whole file not only summary
32   $ $0 -f event.c
33   $ $0 -f < event.c
34
35 OPTS:
36   -p   - treat as patch
37   -f   - treat as regular file
38   -d   - treat as regular file and print diff
39   -r   - treat as git revision (default)
40   -C   - check using clang-format (default)
41   -U   - check with uncrustify
42   -c   - config for clang-format/uncrustify
43   -h   - print this message
44 EOL
45 }
46 function cfg()
47 {
48     [ -z "${options[cfg]}" ] || {
49         echo "${options[cfg]}"
50         return
51     }
52
53     local dir="$(dirname "${BASH_SOURCE[0]}")"
54     [ "${options[clang]}" -eq 0 ] || {
55         echo "$dir/.clang-format"
56         return
57     }
58     [ "${options[uncrustify]}" -eq 0 ] || {
59         echo "$dir/.uncrustify"
60         return
61     }
62 }
63 function abort()
64 {
65     local msg="$1"
66     shift
67
68     printf "$msg\n" "$@" >&2
69     exit 1
70 }
71 function default_arg()
72 {
73     if [ "${options[ref]}" -eq 1 ]; then
74         echo "HEAD"
75     else
76         [ ! -t 0 ] || abort "<stdin> is a tty"
77         echo "/dev/stdin"
78     fi
79 }
80 function parse_options()
81 {
82     options[patch]=0
83     options[file]=0
84     options[file_diff]=0
85     options[ref]=1
86     options[clang]=1
87     options[uncrustify]=0
88     options[cfg]=
89
90     local OPTARG OPTIND c
91     while getopts "pfrdCUc:h?" c; do
92         case "$c" in
93             p)
94                 options[patch]=1
95                 options[ref]=0
96                 options[file]=0
97                 options[file_diff]=0
98                 ;;
99             f)
100                 options[file]=1
101                 options[ref]=0
102                 options[patch]=0
103                 options[file_diff]=0
104                 ;;
105             r)
106                 options[ref]=1
107                 options[file]=0
108                 options[patch]=0
109                 options[file_diff]=0
110                 ;;
111             d)
112                 options[file_diff]=1
113                 options[file]=0
114                 options[patch]=0
115                 options[ref]=0
116                 ;;
117             C)
118                 options[clang]=1
119                 options[uncrustify]=0
120                 ;;
121             U)
122                 options[uncrustify]=1
123                 options[clang]=0
124                 ;;
125             c) options[cfg]="$OPTIND" ;;
126             ?|h)
127                 usage
128                 exit 0
129                 ;;
130             *)
131                 usage
132                 exit 1
133                 ;;
134         esac
135     done
136
137     options[cfg]="$(cfg)"
138
139     [ -f "${options[cfg]}" ] || \
140         abort "Config '%s' does not exist" "${options[cfg]}"
141
142     shift $((OPTIND - 1))
143     args=( "$@" )
144
145     if [ ${#args[@]} -eq 0 ]; then
146         # exit on error globally, not only in subshell
147         default_arg > /dev/null
148         args=( "$(default_arg)" )
149     fi
150
151     if [ "${args[0]}" = "/dev/stdin" ]; then
152         TMP_FILE="/tmp/libevent.checkpatch.$RANDOM"
153         cat > "$TMP_FILE"
154         trap "rm '$TMP_FILE'" EXIT
155
156         args[0]="$TMP_FILE"
157     fi
158 }
159
160 function diff() { command diff --color=always "$@"; }
161
162 function clang_style()
163 {
164     local c="${options[cfg]}"
165     echo "{ $(sed -e 's/#.*//' -e '/---/d' -e '/\.\.\./d' "$c" | tr $'\n' ,) }"
166 }
167 function clang_format() { clang-format -style="$(clang_style)" "$@"; }
168 function clang_format_diff() { cat "$@" | clang-format-diff -p1 -style="$(clang_style)"; }
169 # for non-bare repo will work
170 function clang_format_git()
171 { git format-patch --stdout "$@" -1 | clang_format_diff; }
172
173 function uncrustify() { command uncrustify -c "${options[cfg]}" "$@"; }
174 function uncrustify_frag() { uncrustify -l C --frag "$@"; }
175 function uncrustify_indent_off() { echo '/* *INDENT-OFF* */'; }
176 function uncrustify_indent_on() { echo '/* *INDENT-ON* */'; }
177 function git_hunk()
178 {
179     local ref=$1 f=$2
180     shift 2
181     git cat-file -p $ref:$f
182 }
183 function uncrustify_git_indent_hunk()
184 {
185     local start=$1 end=$2
186     shift 2
187
188     # Will be beatier with tee(1), but doh bash async substitution
189     { uncrustify_indent_off; git_hunk "$@" | head -n$((start - 1)); }
190     { uncrustify_indent_on;  git_hunk "$@" | head -n$((end - 1)) | tail -n+$start; }
191     { uncrustify_indent_off; git_hunk "$@" | tail -n+$((end + 1)); }
192 }
193 function strip()
194 {
195     local start=$1 end=$2
196     shift 2
197
198     # seek indent_{on,off}()
199     let start+=2
200     head -n$end | tail -n+$start
201 }
202 function patch_ranges()
203 {
204     egrep -o '^@@ -[0-9]+(,[0-9]+|) \+[0-9]+(,[0-9]+|) @@' | \
205         cut -d' ' -f3
206 }
207 function git_ranges()
208 {
209     local ref=$1 f=$2
210     shift 2
211
212     git diff -W $ref^..$ref -- $f | patch_ranges
213 }
214 function diff_substitute()
215 {
216     local f="$1"
217     shift
218
219     sed \
220         -e "s#^--- /dev/fd.*\$#--- a/$f#" \
221         -e "s#^+++ /dev/fd.*\$#+++ b/$f#"
222 }
223 function uncrustify_git()
224 {
225     local ref=$1 r f start end length
226     shift
227
228     local files=( $(git diff --name-only $ref^..$ref | egrep "\.(c|h)$") )
229     for f in "${files[@]}"; do
230         local ranges=( $(git_ranges $ref "$f") )
231         for r in "${ranges[@]}"; do
232             [[ ! "$r" =~ ^\+([0-9]+)(,([0-9]+)|)$ ]] && continue
233             start=${BASH_REMATCH[1]}
234             [ -n "${BASH_REMATCH[3]}" ] && \
235                 length=${BASH_REMATCH[3]} || \
236                 length=1
237             end=$((start + length))
238             echo "Range: $start:$end ($length)" >&2
239
240             diff -u \
241                 <(uncrustify_git_indent_hunk $start $end $ref "$f" | strip $start $end) \
242                 <(uncrustify_git_indent_hunk $start $end $ref "$f" | uncrustify_frag | strip $start $end) \
243             | diff_substitute "$f"
244         done
245     done
246 }
247 function uncrustify_diff() { abort "Not implemented"; }
248 function uncrustify_file() { uncrustify -f "$@"; }
249
250 function checker()
251 {
252     local c=$1 u=$2
253     shift 2
254
255     [ "${options[clang]}" -eq 0 ] || {
256         $c "$@"
257         return
258     }
259     [ "${options[uncrustify]}" -eq 0 ] || {
260         $u "$@"
261         return
262     }
263 }
264 function check_patch() { checker clang_format_diff uncrustify_diff "$@"; }
265 function check_file() { checker clang_format uncrustify_file "$@"; }
266 function check_ref() { checker clang_format_git uncrustify_git "$@"; }
267
268 function check_arg()
269 {
270     [ "${options[patch]}" -eq 0 ] || {
271         check_patch "$@"
272         return
273     }
274     [ "${options[file]}" -eq 0 ] || {
275         check_file "$@"
276         return
277     }
278     [ "${options[file_diff]}" -eq 0 ] || {
279         diff -u "$@" <(check_file "$@") | diff_substitute "$@"
280         return
281     }
282     [ "${options[ref]}" -eq 0 ] || {
283         check_ref "$@"
284         return
285     }
286 }
287
288 function main()
289 {
290     local a
291     for a in "${args}"; do
292         check_arg "$a"
293     done
294 }
295
296 declare -A options
297 parse_options "$@"
298
299 main "$@" | less -FRSX