tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

checkpatch.sh (7047B)


      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