xref: /spdk/scripts/check_format.sh (revision 1b1967bdd61daa5ec110e2ca6c73c7b38a60bb89)
1#!/usr/bin/env bash
2#  SPDX-License-Identifier: BSD-3-Clause
3#  Copyright (C) 2015 Intel Corporation
4#  All rights reserved.
5#
6if [[ $(uname -s) == Darwin ]]; then
7	# SPDK is not supported on MacOS, but as a developer
8	# convenience we support running the check_format.sh
9	# script on MacOS.
10	# Running "brew install bash coreutils grep" should be
11	# sufficient to get the correct versions of these utilities.
12	if [[ $(type -t mapfile) != builtin ]]; then
13		# We need bash version >= 4.0 for mapfile builtin
14		echo "Please install bash version >= 4.0"
15		exit 1
16	fi
17	if ! hash greadlink 2> /dev/null; then
18		# We need GNU readlink for -f option
19		echo "Please install GNU readlink"
20		exit 1
21	fi
22	if ! hash ggrep 2> /dev/null; then
23		# We need GNU grep for -P option
24		echo "Please install GNU grep"
25		exit 1
26	fi
27	GNU_READLINK="greadlink"
28	GNU_GREP="ggrep"
29else
30	GNU_READLINK="readlink"
31	GNU_GREP="grep"
32fi
33
34rootdir=$($GNU_READLINK -f "$(dirname "$0")")/..
35source "$rootdir/scripts/common.sh"
36
37cd "$rootdir"
38
39# exit on errors
40set -e
41
42if ! hash nproc 2> /dev/null; then
43
44	function nproc() {
45		echo 8
46	}
47
48fi
49
50function version_lt() {
51	[ $(echo -e "$1\n$2" | sort -V | head -1) != "$1" ]
52}
53
54function array_contains_string() {
55	name="$1[@]"
56	array=("${!name}")
57
58	for element in "${array[@]}"; do
59		if [ "$element" = "$2" ]; then
60			return $(true)
61		fi
62	done
63
64	return $(false)
65}
66
67rc=0
68
69function check_permissions() {
70	local rc=0 files=()
71
72	mapfile -t files < <(git ls-files)
73	mapfile -t files < <(get_diffed_dups "${files[@]}")
74
75	((${#files[@]} > 0)) || return 0
76
77	echo -n "Checking file permissions..."
78
79	while read -r perm _res0 _res1 path; do
80		if [ ! -f "$path" ]; then
81			continue
82		fi
83
84		# Skip symlinks
85		if [[ -L $path ]]; then
86			continue
87		fi
88		fname=$(basename -- "$path")
89
90		case ${fname##*.} in
91			c | h | cpp | cc | cxx | hh | hpp | md | html | js | json | svg | Doxyfile | yml | LICENSE | README | conf | in | Makefile | mk | gitignore | go | txt)
92				# These file types should never be executable
93				if [ "$perm" -eq 100755 ]; then
94					echo "ERROR: $path is marked executable but is a code file."
95					rc=1
96				fi
97				;;
98			*)
99				shebang=$(head -n 1 $path | cut -c1-3)
100
101				# git only tracks the execute bit, so will only ever return 755 or 644 as the permission.
102				if [ "$perm" -eq 100755 ]; then
103					# If the file has execute permission, it should start with a shebang.
104					if [ "$shebang" != "#!/" ]; then
105						echo "ERROR: $path is marked executable but does not start with a shebang."
106						rc=1
107					fi
108				else
109					# If the file does not have execute permissions, it should not start with a shebang.
110					if [ "$shebang" = "#!/" ]; then
111						echo "ERROR: $path is not marked executable but starts with a shebang."
112						rc=1
113					fi
114				fi
115				;;
116		esac
117
118	done < <(git ls-files -s "${files[@]}")
119
120	if [ $rc -eq 0 ]; then
121		echo " OK"
122	fi
123
124	return $rc
125}
126
127function check_c_style() {
128	local rc=0
129
130	if hash astyle; then
131		echo -n "Checking coding style..."
132		if [ "$(astyle -V)" \< "Artistic Style Version 3" ]; then
133			echo -n " Your astyle version is too old so skipping coding style checks. Please update astyle to at least 3.0.1 version..."
134		else
135			rm -f astyle.log
136			touch astyle.log
137			# Exclude DPDK header files copied into our tree
138			git ls-files '*.[ch]' ':!:*/env_dpdk/*/*.h' \
139				| xargs -P$(nproc) -n10 astyle --break-return-type --attach-return-type-decl \
140					--options=.astylerc >> astyle.log
141			git ls-files '*.cpp' '*.cc' '*.cxx' '*.hh' '*.hpp' \
142				| xargs -P$(nproc) -n10 astyle --options=.astylerc >> astyle.log
143			if grep -q "^Formatted" astyle.log; then
144				echo " errors detected"
145				git diff --ignore-submodules=all
146				sed -i -e 's/  / /g' astyle.log
147				grep --color=auto "^Formatted.*" astyle.log
148				echo "Incorrect code style detected in one or more files."
149				echo "The files have been automatically formatted."
150				echo "Remember to add the files to your commit."
151				rc=1
152			else
153				echo " OK"
154			fi
155			rm -f astyle.log
156		fi
157	else
158		echo "You do not have astyle installed so your code style is not being checked!"
159	fi
160	return $rc
161}
162
163function check_comment_style() {
164	local rc=0
165
166	echo -n "Checking comment style..."
167
168	git grep --line-number -e '\/[*][^ *-]' -- '*.[ch]' > comment.log || true
169	git grep --line-number -e '[^ ][*]\/' -- '*.[ch]' ':!lib/rte_vhost*/*' >> comment.log || true
170	git grep --line-number -e '^[*]' -- '*.[ch]' >> comment.log || true
171	git grep --line-number -e '\s\/\/' -- '*.[ch]' >> comment.log || true
172	git grep --line-number -e '^\/\/' -- '*.[ch]' >> comment.log || true
173
174	if [ -s comment.log ]; then
175		echo " Incorrect comment formatting detected"
176		cat comment.log
177		rc=1
178	else
179		echo " OK"
180	fi
181	rm -f comment.log
182
183	return $rc
184}
185
186function check_spaces_before_tabs() {
187	local rc=0
188
189	echo -n "Checking for spaces before tabs..."
190	git grep --line-number $' \t' -- './*' ':!*.patch' > whitespace.log || true
191	if [ -s whitespace.log ]; then
192		echo " Spaces before tabs detected"
193		cat whitespace.log
194		rc=1
195	else
196		echo " OK"
197	fi
198	rm -f whitespace.log
199
200	return $rc
201}
202
203function check_trailing_whitespace() {
204	local rc=0
205
206	echo -n "Checking trailing whitespace in output strings..."
207
208	git grep --line-number -e ' \\n"' -- '*.[ch]' > whitespace.log || true
209
210	if [ -s whitespace.log ]; then
211		echo " Incorrect trailing whitespace detected"
212		cat whitespace.log
213		rc=1
214	else
215		echo " OK"
216	fi
217	rm -f whitespace.log
218
219	return $rc
220}
221
222function check_forbidden_functions() {
223	local rc=0
224
225	echo -n "Checking for use of forbidden library functions..."
226
227	git grep --line-number -w '\(atoi\|atol\|atoll\|strncpy\|strcpy\|strcat\|sprintf\|vsprintf\)' -- './*.c' ':!lib/rte_vhost*/**' > badfunc.log || true
228	if [ -s badfunc.log ]; then
229		echo " Forbidden library functions detected"
230		cat badfunc.log
231		rc=1
232	else
233		echo " OK"
234	fi
235	rm -f badfunc.log
236
237	return $rc
238}
239
240function check_cunit_style() {
241	local rc=0
242
243	echo -n "Checking for use of forbidden CUnit macros..."
244
245	git grep --line-number -w 'CU_ASSERT_FATAL' -- 'test/*' ':!test/spdk_cunit.h' > badcunit.log || true
246	if [ -s badcunit.log ]; then
247		echo " Forbidden CU_ASSERT_FATAL usage detected - use SPDK_CU_ASSERT_FATAL instead"
248		cat badcunit.log
249		rc=1
250	else
251		echo " OK"
252	fi
253	rm -f badcunit.log
254
255	return $rc
256}
257
258function check_eof() {
259	local rc=0
260
261	echo -n "Checking blank lines at end of file..."
262
263	if ! git grep -I -l -e . -z './*' ':!*.patch' \
264		| xargs -0 -P$(nproc) -n1 scripts/eofnl > eofnl.log; then
265		echo " Incorrect end-of-file formatting detected"
266		cat eofnl.log
267		rc=1
268	else
269		echo " OK"
270	fi
271	rm -f eofnl.log
272
273	return $rc
274}
275
276function check_posix_includes() {
277	local rc=0
278
279	echo -n "Checking for POSIX includes..."
280	git grep -I -i -f scripts/posix.txt -- './*' ':!include/spdk/stdinc.h' \
281		':!include/linux/**' ':!scripts/posix.txt' ':!lib/env_dpdk/*/*.h' \
282		':!*.patch' ':!configure' > scripts/posix.log || true
283	if [ -s scripts/posix.log ]; then
284		echo "POSIX includes detected. Please include spdk/stdinc.h instead."
285		cat scripts/posix.log
286		rc=1
287	else
288		echo " OK"
289	fi
290	rm -f scripts/posix.log
291
292	return $rc
293}
294
295function check_naming_conventions() {
296	local rc=0
297
298	echo -n "Checking for proper function naming conventions..."
299	# commit_to_compare = HEAD - 1.
300	commit_to_compare="$(git log --pretty=oneline --skip=1 -n 1 | awk '{print $1}')"
301	failed_naming_conventions=false
302	changed_c_libs=()
303	declared_symbols=()
304
305	# Build an array of all the modified C libraries.
306	mapfile -t changed_c_libs < <(git diff --name-only HEAD $commit_to_compare -- lib/**/*.c module/**/*.c | xargs -r dirname | sort | uniq)
307	# Capture the names of all function declarations
308	mapfile -t declared_symbols < <(grep -Eh 'spdk_[a-zA-Z0-9_]*\(' include/spdk*/*.h \
309		| sed -En 's/.*(spdk[a-z,A-Z,0-9,_]*)\(.*/\1/p')
310
311	for c_lib in "${changed_c_libs[@]}"; do
312		lib_map_file="mk/spdk_blank.map"
313		defined_symbols=()
314		removed_symbols=()
315		exported_symbols=()
316		if ls "$c_lib"/*.map &> /dev/null; then
317			lib_map_file="$(ls "$c_lib"/*.map)"
318		fi
319		# Matching groups are 1. leading +sign. 2, function name 3. argument list / anything after that.
320		# Capture just the names of newly added (or modified) functions that start with "spdk_"
321		mapfile -t defined_symbols < <(git diff -U0 $commit_to_compare HEAD -- $c_lib | sed -En 's/(^[+])(spdk[a-z,A-Z,0-9,_]*)(\(.*)/\2/p')
322		# Capture the names of removed symbols to catch edge cases where we just move definitions around.
323		mapfile -t removed_symbols < <(git diff -U0 $commit_to_compare HEAD -- $c_lib | sed -En 's/(^[-])(spdk[a-z,A-Z,0-9,_]*)(\(.*)/\2/p')
324		for symbol in "${removed_symbols[@]}"; do
325			for i in "${!defined_symbols[@]}"; do
326				if [[ ${defined_symbols[i]} = "$symbol" ]]; then
327					unset -v 'defined_symbols[i]'
328				fi
329			done
330		done
331		# It's possible that we just modified a functions arguments so unfortunately we can't just look at changed lines in this function.
332		# matching groups are 1. All leading whitespace 2. function name. Capture just the symbol name.
333		mapfile -t exported_symbols < <(sed -En 's/(^[[:space:]]*)(spdk[a-z,A-Z,0-9,_]*);/\2/p' < $lib_map_file)
334		for defined_symbol in "${defined_symbols[@]}"; do
335			# if the list of defined symbols is equal to the list of removed symbols, then we are left with a single empty element. skip it.
336			if [ "$defined_symbol" = '' ]; then
337				continue
338			fi
339			not_exported=true
340			not_declared=true
341			if array_contains_string exported_symbols $defined_symbol; then
342				not_exported=false
343			fi
344
345			if array_contains_string declared_symbols $defined_symbol; then
346				not_declared=false
347			fi
348
349			if $not_exported || $not_declared; then
350				if ! $failed_naming_conventions; then
351					echo " found naming convention errors."
352				fi
353				echo "function $defined_symbol starts with spdk_ which is reserved for public API functions."
354				echo "Please add this function to its corresponding map file and a public header or remove the spdk_ prefix."
355				failed_naming_conventions=true
356				rc=1
357			fi
358		done
359	done
360
361	if ! $failed_naming_conventions; then
362		echo " OK"
363	fi
364
365	return $rc
366}
367
368function check_include_style() {
369	local rc=0
370
371	echo -n "Checking #include style..."
372	git grep -I -i --line-number "#include <spdk/" -- '*.[ch]' > scripts/includes.log || true
373	if [ -s scripts/includes.log ]; then
374		echo "Incorrect #include syntax. #includes of spdk/ files should use quotes."
375		cat scripts/includes.log
376		rc=1
377	else
378		echo " OK"
379	fi
380	rm -f scripts/includes.log
381
382	return $rc
383}
384
385function check_python_style() {
386	local rc=0
387
388	if hash pycodestyle 2> /dev/null; then
389		PEP8=pycodestyle
390	elif hash pep8 2> /dev/null; then
391		PEP8=pep8
392	fi
393
394	if [ -n "${PEP8}" ]; then
395		echo -n "Checking Python style..."
396
397		PEP8_ARGS=" --max-line-length=140"
398
399		error=0
400		git ls-files '*.py' | xargs -P$(nproc) -n1 $PEP8 $PEP8_ARGS > pep8.log || error=1
401		if [ $error -ne 0 ]; then
402			echo " Python formatting errors detected"
403			cat pep8.log
404			rc=1
405		else
406			echo " OK"
407		fi
408		rm -f pep8.log
409	else
410		echo "You do not have pycodestyle or pep8 installed so your Python style is not being checked!"
411	fi
412
413	return $rc
414}
415
416function get_bash_files() {
417	local sh shebang
418
419	mapfile -t sh < <(git ls-files '*.sh')
420	mapfile -t shebang < <(git grep -l '^#!.*bash')
421
422	get_diffed_dups "${sh[@]}" "${shebang[@]}"
423}
424
425function check_bash_style() {
426	local rc=0
427
428	# find compatible shfmt binary
429	shfmt_bins=$(compgen -c | grep '^shfmt' | uniq || true)
430	for bin in $shfmt_bins; do
431		shfmt_version=$("$bin" --version)
432		if [ $shfmt_version != "v3.1.0" ]; then
433			echo "$bin version $shfmt_version not used (only v3.1.0 is supported)"
434			echo "v3.1.0 can be installed using 'scripts/pkgdep.sh -d'"
435		else
436			shfmt=$bin
437			break
438		fi
439	done
440
441	if [ -n "$shfmt" ]; then
442		shfmt_cmdline=() sh_files=()
443
444		mapfile -t sh_files < <(get_bash_files)
445
446		if ((${#sh_files[@]})); then
447			printf 'Checking .sh formatting style...'
448
449			shfmt_cmdline+=(-i 0)     # indent_style = tab|indent_size = 0
450			shfmt_cmdline+=(-bn)      # binary_next_line = true
451			shfmt_cmdline+=(-ci)      # switch_case_indent = true
452			shfmt_cmdline+=(-ln bash) # shell_variant = bash (default)
453			shfmt_cmdline+=(-d)       # diffOut - print diff of the changes and exit with != 0
454			shfmt_cmdline+=(-sr)      # redirect operators will be followed by a space
455
456			diff=${output_dir:-$PWD}/$shfmt.patch
457
458			# Explicitly tell shfmt to not look for .editorconfig. .editorconfig is also not looked up
459			# in case any formatting arguments has been passed on its cmdline.
460			if ! SHFMT_NO_EDITORCONFIG=true "$shfmt" "${shfmt_cmdline[@]}" "${sh_files[@]}" > "$diff"; then
461				# In case shfmt detects an actual syntax error it will write out a proper message on
462				# its stderr, hence the diff file should remain empty.
463				if [[ -s $diff ]]; then
464					diff_out=$(< "$diff")
465				fi
466
467				cat <<- ERROR_SHFMT
468
469					* Errors in style formatting have been detected.
470					${diff_out:+* Please, review the generated patch at $diff
471
472					# _START_OF_THE_DIFF
473
474					${diff_out:-ERROR}
475
476					# _END_OF_THE_DIFF
477					}
478
479				ERROR_SHFMT
480				rc=1
481			else
482				rm -f "$diff"
483				printf ' OK\n'
484			fi
485		fi
486	else
487		echo "Supported version of shfmt not detected, Bash style formatting check is skipped"
488	fi
489
490	return $rc
491}
492
493function check_bash_static_analysis() {
494	local rc=0 files=()
495
496	mapfile -t files < <(get_bash_files)
497	((${#files[@]} > 0)) || return 0
498
499	if hash shellcheck 2> /dev/null; then
500		echo -n "Checking Bash static analysis with shellcheck..."
501
502		shellcheck_v=$(shellcheck --version | grep -P "version: [0-9\.]+" | cut -d " " -f2)
503
504		# SHCK_EXCLUDE contains a list of all of the spellcheck errors found in SPDK scripts
505		# currently. New errors should only be added to this list if the cost of fixing them
506		# is deemed too high. For more information about the errors, go to:
507		# https://github.com/koalaman/shellcheck/wiki/Checks
508		# Error descriptions can also be found at: https://github.com/koalaman/shellcheck/wiki
509		# SPDK fails some error checks which have been deprecated in later versions of shellcheck.
510		# We will not try to fix these error checks, but instead just leave the error types here
511		# so that we can still run with older versions of shellcheck.
512		SHCK_EXCLUDE="SC1117"
513		# SPDK has decided to not fix violations of these errors.
514		# We are aware about below exclude list and we want this errors to be excluded.
515		# SC1083: This {/} is literal. Check expression (missing ;/\n?) or quote it.
516		# SC1090: Can't follow non-constant source. Use a directive to specify location.
517		# SC1091: Not following: (error message here)
518		# SC2001: See if you can use ${variable//search/replace} instead.
519		# SC2010: Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.
520		# SC2015: Note that A && B || C is not if-then-else. C may run when A is true.
521		# SC2016: Expressions don't expand in single quotes, use double quotes for that.
522		# SC2034: foo appears unused. Verify it or export it.
523		# SC2046: Quote this to prevent word splitting.
524		# SC2086: Double quote to prevent globbing and word splitting.
525		# SC2119: Use foo "$@" if function's $1 should mean script's $1.
526		# SC2120: foo references arguments, but none are ever passed.
527		# SC2128: Expanding an array without an index only gives the first element.
528		# SC2148: Add shebang to the top of your script.
529		# SC2153: Possible Misspelling: MYVARIABLE may not be assigned, but MY_VARIABLE is.
530		# SC2154: var is referenced but not assigned.
531		# SC2164: Use cd ... || exit in case cd fails.
532		# SC2174: When used with -p, -m only applies to the deepest directory.
533		# SC2178: Variable was used as an array but is now assigned a string.
534		# SC2206: Quote to prevent word splitting/globbing,
535		#         or split robustly with mapfile or read -a.
536		# SC2207: Prefer mapfile or read -a to split command output (or quote to avoid splitting).
537		# SC2223: This default assignment may cause DoS due to globbing. Quote it.
538		SHCK_EXCLUDE="$SHCK_EXCLUDE,SC1083,SC1090,SC1091,SC2010,SC2015,SC2016,SC2034,SC2046,SC2086,\
539SC2119,SC2120,SC2128,SC2148,SC2153,SC2154,SC2164,SC2174,SC2178,SC2001,SC2206,SC2207,SC2223"
540
541		SHCK_FORMAT="tty"
542		SHCK_APPLY=false
543		SHCH_ARGS="-e $SHCK_EXCLUDE -f $SHCK_FORMAT"
544
545		if ge "$shellcheck_v" 0.4.0; then
546			SHCH_ARGS+=" -x"
547		else
548			echo "shellcheck $shellcheck_v detected, recommended >= 0.4.0."
549		fi
550
551		printf '%s\n' "${files[@]}" | xargs -P$(nproc) -n1 shellcheck $SHCH_ARGS &> shellcheck.log
552		if [[ -s shellcheck.log ]]; then
553			echo " Bash shellcheck errors detected!"
554
555			cat shellcheck.log
556			if $SHCK_APPLY; then
557				git apply shellcheck.log
558				echo "Bash errors were automatically corrected."
559				echo "Please remember to add the changes to your commit."
560			fi
561			rc=1
562		else
563			echo " OK"
564		fi
565		rm -f shellcheck.log
566	else
567		echo "You do not have shellcheck installed so your Bash static analysis is not being performed!"
568	fi
569
570	return $rc
571}
572
573function check_changelog() {
574	local rc=0
575
576	# Check if any of the public interfaces were modified by this patch.
577	# Warn the user to consider updating the changelog any changes
578	# are detected.
579	echo -n "Checking whether CHANGELOG.md should be updated..."
580	staged=$(git diff --name-only --cached .)
581	working=$(git status -s --porcelain --ignore-submodules | grep -iv "??" | awk '{print $2}')
582	files="$staged $working"
583	if [[ "$files" = " " ]]; then
584		files=$(git diff-tree --no-commit-id --name-only -r HEAD)
585	fi
586
587	has_changelog=0
588	for f in $files; do
589		if [[ $f == CHANGELOG.md ]]; then
590			# The user has a changelog entry, so exit.
591			has_changelog=1
592			break
593		fi
594	done
595
596	needs_changelog=0
597	if [ $has_changelog -eq 0 ]; then
598		for f in $files; do
599			if [[ $f == include/spdk/* ]] || [[ $f == scripts/rpc.py ]] || [[ $f == etc/* ]]; then
600				echo ""
601				echo -n "$f was modified. Consider updating CHANGELOG.md."
602				needs_changelog=1
603			fi
604		done
605	fi
606
607	if [ $needs_changelog -eq 0 ]; then
608		echo " OK"
609	else
610		echo ""
611	fi
612
613	return $rc
614}
615
616function check_json_rpc() {
617	local rc=0
618
619	echo -n "Checking that all RPCs are documented..."
620	while IFS='"' read -r _ rpc _; do
621		if ! grep -q "^### $rpc" doc/jsonrpc.md; then
622			echo "Missing JSON-RPC documentation for ${rpc}"
623			rc=1
624		fi
625	done < <(git grep -h -E "^SPDK_RPC_REGISTER\(" ':!test/*' ':!examples/*')
626
627	if [ $rc -eq 0 ]; then
628		echo " OK"
629	fi
630	return $rc
631}
632
633function check_markdown_format() {
634	local rc=0
635
636	if hash mdl 2> /dev/null; then
637		echo -n "Checking markdown files format..."
638		mdl -g -s $rootdir/mdl_rules.rb . > mdl.log || true
639		if [ -s mdl.log ]; then
640			echo " Errors in .md files detected:"
641			cat mdl.log
642			rc=1
643		else
644			echo " OK"
645		fi
646		rm -f mdl.log
647	else
648		echo "You do not have markdownlint installed so .md files not being checked!"
649	fi
650
651	return $rc
652}
653
654function check_rpc_args() {
655	local rc=0
656
657	echo -n "Checking rpc.py argument option names..."
658	grep add_argument scripts/rpc.py | $GNU_GREP -oP "(?<=--)[a-z0-9\-\_]*(?=\')" | grep "_" > badargs.log
659
660	if [[ -s badargs.log ]]; then
661		echo "rpc.py arguments with underscores detected!"
662		cat badargs.log
663		echo "Please convert the underscores to dashes."
664		rc=1
665	else
666		echo " OK"
667	fi
668	rm -f badargs.log
669	return $rc
670}
671
672function get_files_for_lic() {
673	local f_shebang="" f_suffix=() f_type=() f_all=() exceptions=""
674
675	f_shebang+="bash|"
676	f_shebang+="make|"
677	f_shebang+="perl|"
678	f_shebang+="python|"
679	f_shebang+="sh"
680
681	f_suffix+=("*.c")
682	f_suffix+=("*.cpp")
683	f_suffix+=("*.h")
684	f_suffix+=("*.go")
685	f_suffix+=("*.mk")
686	f_suffix+=("*.pl")
687	f_suffix+=("*.py")
688	f_suffix+=("*.sh")
689	f_suffix+=("*.yaml")
690
691	f_type+=("*Dockerfile")
692	f_type+=("*Makefile")
693
694	# Exclude files that may match the above types but should not
695	# fall under SPDX check.
696	exceptions+="include/linux|"
697	exceptions+="include/spdk/queue_extras.h"
698
699	mapfile -t f_all < <(
700		git ls-files "${f_suffix[@]}" "${f_type[@]}"
701		git grep -lE "^#!.*($f_shebang)"
702	)
703
704	printf '%s\n' "${f_all[@]}" | sort -u | grep -vE "$exceptions"
705}
706
707function check_spdx_lic() {
708	local files_missing_license_header=() hint=()
709	local rc=0
710
711	hint+=("SPDX-License-Identifier: BSD-3-Clause")
712	hint+=("All rights reserved.")
713
714	printf 'Checking SPDX-license...'
715
716	mapfile -t files_missing_license_header < <(
717		grep -LE "SPDX-License-Identifier:.+" $(get_files_for_lic)
718	)
719
720	if ((${#files_missing_license_header[@]} > 0)); then
721		printf '\nFollowing files are missing SPDX-license header:\n'
722		printf '  @%s\n' "${files_missing_license_header[@]}"
723		printf '\nExample:\n'
724		printf '  #  %s\n' "${hint[@]}"
725		return 1
726	fi
727
728	printf 'OK\n'
729}
730
731function get_diffed_files() {
732	# Get files where changes are meant to be committed
733	git diff --name-only HEAD HEAD~1
734	# Get files from staging
735	git diff --name-only --cached HEAD
736	git diff --name-only HEAD
737}
738
739function get_diffed_dups() {
740	local files=("$@") diff=() _diff=()
741
742	# Sort and get rid of duplicates from the main list
743	mapfile -t files < <(printf '%s\n' "${files[@]}" | sort -u)
744	# Get staged|committed files
745	mapfile -t diff < <(get_diffed_files | sort -u)
746
747	if [[ ! -v CHECK_FORMAT_ONLY_DIFF ]]; then
748		# Just return the main list
749		printf '%s\n' "${files[@]}"
750		return 0
751	fi
752
753	if ((${#diff[@]} > 0)); then
754		# Check diff'ed files against the main list to see if they are a subset
755		# of it. If yes, then we return the duplicates which are the files that
756		# should be committed, modified.
757		mapfile -t _diff < <(
758			printf '%s\n' "${diff[@]}" "${files[@]}" | sort | uniq -d
759		)
760		if ((${#_diff[@]} > 0)); then
761			printf '%s\n' "${_diff[@]}"
762			return 0
763		fi
764	fi
765}
766
767rc=0
768
769check_permissions || rc=1
770check_c_style || rc=1
771
772GIT_VERSION=$(git --version | cut -d' ' -f3)
773
774if version_lt "1.9.5" "${GIT_VERSION}"; then
775	# git <1.9.5 doesn't support pathspec magic exclude
776	echo " Your git version is too old to perform all tests. Please update git to at least 1.9.5 version..."
777	exit $rc
778fi
779
780check_comment_style || rc=1
781check_markdown_format || rc=1
782check_spaces_before_tabs || rc=1
783check_trailing_whitespace || rc=1
784check_forbidden_functions || rc=1
785check_cunit_style || rc=1
786check_eof || rc=1
787check_posix_includes || rc=1
788check_naming_conventions || rc=1
789check_include_style || rc=1
790check_python_style || rc=1
791check_bash_style || rc=1
792check_bash_static_analysis || rc=1
793check_changelog || rc=1
794check_json_rpc || rc=1
795check_rpc_args || rc=1
796check_spdx_lic || rc=1
797
798exit $rc
799