mirror of
https://github.com/libevent/libevent.git
synced 2025-01-09 00:56:20 +08:00
89d313b14d
- strip 1 path component - it does not accept file name anymore, only stdin Refs: #1029
300 lines
6.9 KiB
Bash
Executable File
300 lines
6.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# TODO:
|
|
# - inline replace
|
|
# - clang-format-diff replacement
|
|
# - uncrustify for patches (not git refs)
|
|
# - maybe integrate into travis-ci?
|
|
|
|
function usage()
|
|
{
|
|
cat <<EOL
|
|
$0 [ OPTS ] [ file-or-gitref [ ... ] ]
|
|
|
|
Example:
|
|
# Chech HEAD git ref
|
|
$ $0 -r
|
|
$ $0 -r HEAD
|
|
|
|
# Check patch
|
|
$ git format-patch --stdout -1 | $0 -p
|
|
$ git show -1 | $0 -p
|
|
|
|
# Or via regular files
|
|
$ git format-patch --stdout -2
|
|
$ $0 *.patch
|
|
|
|
# Over a file
|
|
$ $0 -d event.c
|
|
$ $0 -d < event.c
|
|
|
|
# And print the whole file not only summary
|
|
$ $0 -f event.c
|
|
$ $0 -f < event.c
|
|
|
|
OPTS:
|
|
-p - treat as patch
|
|
-f - treat as regular file
|
|
-d - treat as regular file and print diff
|
|
-r - treat as git revision (default)
|
|
-C - check using clang-format (default)
|
|
-U - check with uncrustify
|
|
-c - config for clang-format/uncrustify
|
|
-h - print this message
|
|
EOL
|
|
}
|
|
function cfg()
|
|
{
|
|
[ -z "${options[cfg]}" ] || {
|
|
echo "${options[cfg]}"
|
|
return
|
|
}
|
|
|
|
local dir="$(dirname "${BASH_SOURCE[0]}")"
|
|
[ "${options[clang]}" -eq 0 ] || {
|
|
echo "$dir/.clang-format"
|
|
return
|
|
}
|
|
[ "${options[uncrustify]}" -eq 0 ] || {
|
|
echo "$dir/.uncrustify"
|
|
return
|
|
}
|
|
}
|
|
function abort()
|
|
{
|
|
local msg="$1"
|
|
shift
|
|
|
|
printf "$msg\n" "$@" >&2
|
|
exit 1
|
|
}
|
|
function default_arg()
|
|
{
|
|
if [ "${options[ref]}" -eq 1 ]; then
|
|
echo "HEAD"
|
|
else
|
|
[ ! -t 0 ] || abort "<stdin> is a tty"
|
|
echo "/dev/stdin"
|
|
fi
|
|
}
|
|
function parse_options()
|
|
{
|
|
options[patch]=0
|
|
options[file]=0
|
|
options[file_diff]=0
|
|
options[ref]=1
|
|
options[clang]=1
|
|
options[uncrustify]=0
|
|
options[cfg]=
|
|
|
|
local OPTARG OPTIND c
|
|
while getopts "pfrdCUc:h?" c; do
|
|
case "$c" in
|
|
p)
|
|
options[patch]=1
|
|
options[ref]=0
|
|
options[file]=0
|
|
options[file_diff]=0
|
|
;;
|
|
f)
|
|
options[file]=1
|
|
options[ref]=0
|
|
options[patch]=0
|
|
options[file_diff]=0
|
|
;;
|
|
r)
|
|
options[ref]=1
|
|
options[file]=0
|
|
options[patch]=0
|
|
options[file_diff]=0
|
|
;;
|
|
d)
|
|
options[file_diff]=1
|
|
options[file]=0
|
|
options[patch]=0
|
|
options[ref]=0
|
|
;;
|
|
C)
|
|
options[clang]=1
|
|
options[uncrustify]=0
|
|
;;
|
|
U)
|
|
options[uncrustify]=1
|
|
options[clang]=0
|
|
;;
|
|
c) options[cfg]="$OPTIND" ;;
|
|
?|h)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
options[cfg]="$(cfg)"
|
|
|
|
[ -f "${options[cfg]}" ] || \
|
|
abort "Config '%s' does not exist" "${options[cfg]}"
|
|
|
|
shift $((OPTIND - 1))
|
|
args=( "$@" )
|
|
|
|
if [ ${#args[@]} -eq 0 ]; then
|
|
# exit on error globally, not only in subshell
|
|
default_arg > /dev/null
|
|
args=( "$(default_arg)" )
|
|
fi
|
|
|
|
if [ "${args[0]}" = "/dev/stdin" ]; then
|
|
TMP_FILE="/tmp/libevent.checkpatch.$RANDOM"
|
|
cat > "$TMP_FILE"
|
|
trap "rm '$TMP_FILE'" EXIT
|
|
|
|
args[0]="$TMP_FILE"
|
|
fi
|
|
}
|
|
|
|
function diff() { command diff --color=always "$@"; }
|
|
|
|
function clang_style()
|
|
{
|
|
local c="${options[cfg]}"
|
|
echo "{ $(sed -e 's/#.*//' -e '/---/d' -e '/\.\.\./d' "$c" | tr $'\n' ,) }"
|
|
}
|
|
function clang_format() { clang-format -style="$(clang_style)" "$@"; }
|
|
function clang_format_diff() { cat "$@" | clang-format-diff -p1 -style="$(clang_style)"; }
|
|
# for non-bare repo will work
|
|
function clang_format_git()
|
|
{ git format-patch --stdout "$@" -1 | clang_format_diff; }
|
|
|
|
function uncrustify() { command uncrustify -c "${options[cfg]}" "$@"; }
|
|
function uncrustify_frag() { uncrustify -l C --frag "$@"; }
|
|
function uncrustify_indent_off() { echo '/* *INDENT-OFF* */'; }
|
|
function uncrustify_indent_on() { echo '/* *INDENT-ON* */'; }
|
|
function git_hunk()
|
|
{
|
|
local ref=$1 f=$2
|
|
shift 2
|
|
git cat-file -p $ref:$f
|
|
}
|
|
function uncrustify_git_indent_hunk()
|
|
{
|
|
local start=$1 end=$2
|
|
shift 2
|
|
|
|
# Will be beatier with tee(1), but doh bash async substitution
|
|
{ uncrustify_indent_off; git_hunk "$@" | head -n$((start - 1)); }
|
|
{ uncrustify_indent_on; git_hunk "$@" | head -n$((end - 1)) | tail -n+$start; }
|
|
{ uncrustify_indent_off; git_hunk "$@" | tail -n+$((end + 1)); }
|
|
}
|
|
function strip()
|
|
{
|
|
local start=$1 end=$2
|
|
shift 2
|
|
|
|
# seek indent_{on,off}()
|
|
let start+=2
|
|
head -n$end | tail -n+$start
|
|
}
|
|
function patch_ranges()
|
|
{
|
|
egrep -o '^@@ -[0-9]+(,[0-9]+|) \+[0-9]+(,[0-9]+|) @@' | \
|
|
cut -d' ' -f3
|
|
}
|
|
function git_ranges()
|
|
{
|
|
local ref=$1 f=$2
|
|
shift 2
|
|
|
|
git diff -W $ref^..$ref -- $f | patch_ranges
|
|
}
|
|
function diff_substitute()
|
|
{
|
|
local f="$1"
|
|
shift
|
|
|
|
sed \
|
|
-e "s#^--- /dev/fd.*\$#--- a/$f#" \
|
|
-e "s#^+++ /dev/fd.*\$#+++ b/$f#"
|
|
}
|
|
function uncrustify_git()
|
|
{
|
|
local ref=$1 r f start end length
|
|
shift
|
|
|
|
local files=( $(git diff --name-only $ref^..$ref | egrep "\.(c|h)$") )
|
|
for f in "${files[@]}"; do
|
|
local ranges=( $(git_ranges $ref "$f") )
|
|
for r in "${ranges[@]}"; do
|
|
[[ ! "$r" =~ ^\+([0-9]+)(,([0-9]+)|)$ ]] && continue
|
|
start=${BASH_REMATCH[1]}
|
|
[ -n "${BASH_REMATCH[3]}" ] && \
|
|
length=${BASH_REMATCH[3]} || \
|
|
length=1
|
|
end=$((start + length))
|
|
echo "Range: $start:$end ($length)" >&2
|
|
|
|
diff -u \
|
|
<(uncrustify_git_indent_hunk $start $end $ref "$f" | strip $start $end) \
|
|
<(uncrustify_git_indent_hunk $start $end $ref "$f" | uncrustify_frag | strip $start $end) \
|
|
| diff_substitute "$f"
|
|
done
|
|
done
|
|
}
|
|
function uncrustify_diff() { abort "Not implemented"; }
|
|
function uncrustify_file() { uncrustify -f "$@"; }
|
|
|
|
function checker()
|
|
{
|
|
local c=$1 u=$2
|
|
shift 2
|
|
|
|
[ "${options[clang]}" -eq 0 ] || {
|
|
$c "$@"
|
|
return
|
|
}
|
|
[ "${options[uncrustify]}" -eq 0 ] || {
|
|
$u "$@"
|
|
return
|
|
}
|
|
}
|
|
function check_patch() { checker clang_format_diff uncrustify_diff "$@"; }
|
|
function check_file() { checker clang_format uncrustify_file "$@"; }
|
|
function check_ref() { checker clang_format_git uncrustify_git "$@"; }
|
|
|
|
function check_arg()
|
|
{
|
|
[ "${options[patch]}" -eq 0 ] || {
|
|
check_patch "$@"
|
|
return
|
|
}
|
|
[ "${options[file]}" -eq 0 ] || {
|
|
check_file "$@"
|
|
return
|
|
}
|
|
[ "${options[file_diff]}" -eq 0 ] || {
|
|
diff -u "$@" <(check_file "$@") | diff_substitute "$@"
|
|
return
|
|
}
|
|
[ "${options[ref]}" -eq 0 ] || {
|
|
check_ref "$@"
|
|
return
|
|
}
|
|
}
|
|
|
|
function main()
|
|
{
|
|
local a
|
|
for a in "${args}"; do
|
|
check_arg "$a"
|
|
done
|
|
}
|
|
|
|
declare -A options
|
|
parse_options "$@"
|
|
|
|
main "$@" | less -FRSX
|