Edit

kc3-lang/libevent/checkpatch.sh

Branch :

  • Show log

    Commit

  • Author : Azat Khuzhin
    Date : 2020-05-27 20:33:25
    Hash : 89d313b1
    Message : checkpatch.sh: fix clang-format-diff usage - strip 1 path component - it does not accept file name anymore, only stdin Refs: #1029

  • checkpatch.sh
  • #!/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