set -e
algorithm='md5'
hashext='hash'
append=1
extensionless=1
force_zenity=0
hybrid=1
ignore_extensions=('desktop' 'ini' 'lnk' 'directory' 'nfo' 'm3u' 'pls' 'url' 'sfv')
fallback_dir="$HOME/Hashes"
tmpfile='/tmp/checksum.tmp'
errlog='/tmp/checksum-errors.log'
keep_errlog=0
dh_max=600
show_times=1
mode=
mask=
folder=0
filebad=0
notifypid=0
existing=()
fb=0
diag_height=600
me="${0
path="${!
opath="$path"
here="$(pwd)"
rm -rf "$errlog"
rm -rf "$tmpfile"
start_time=$(date +%s)
SwitchAlgo() {
    case "$1" in
        sha1)
            algo='sha1'
            tool='sha1sum'
            hashlen=40
            ;;
        sha2)
            algo='sha2'
            tool='sha256sum'
            hashlen=64
            ;;
        sha3)
            algo='sha3'
            tool='sha384sum'
            hashlen=96
            ;;
        sha5)
            algo='sha5'
            tool='sha512sum'
            hashlen=128
            ;;
        *)
            algo='md5'
            tool='md5sum'
            hashlen=32
            ;;
    esac
}
CheckMultiHash() {
    cat "$1" | while read line; do
        xhash=${line%% *}
        file=${line
        
        if [[ $xhash =~ ^[[:xdigit:]]+$ ]]; then
            if [[ ! -f "$file" ]]; then
                echo "$file: $algo: MISSING"
                echo "$1: $algo: MISSING: $file" >> "$errlog"
                continue
            fi
            
            case ${
                32)
                    SwitchAlgo 'md5'
                    nnh=$($tool "$file")
                    nnh=${nnh:0:32}
                    ;;
                40)
                    SwitchAlgo 'sha1'
                    nnh=$($tool "$file")
                    nnh=${nnh:0:40}
                    ;;
                64)
                    SwitchAlgo 'sha2'
                    nnh=$($tool "$file")
                    nnh=${nnh:0:64}
                    ;;
                96)
                    SwitchAlgo 'sha3'
                    nnh=$($tool "$file")
                    nnh=${nnh:0:96}
                    ;;
                128)
                    SwitchAlgo 'sha5'
                    nnh=$($tool "$file")
                    nnh=${nnh:0:128}
                    ;;
                *)
                    
                    continue
                    ;;
            esac
            if [[ "$nnh" = "$xhash" ]]; then
                echo "$file: $algo: OK"
            else
                echo "$file: $algo: FAILED"
                echo "$1: $algo: FAILED: $file" >> "$errlog"
            fi
        fi
    done | tee -a "$tmpfile"
}
SetDialogHeight() {
    fl=$(wc -l < "$1" )
    diag_height=$((100+(fl*=30))) 
    if [[ $diag_height -gt $dh_max ]] || [[ $diag_height -lt 50 ]]; then diag_height=$dh_max; fi
}
grabhashes() {
    while read; do
        hash=${REPLY%% *}
        [[ ${
    done < "$1"
}
notify_user() {
    case $dlg_mode in
        0)
            echo
            echo "$2 hashes in \"$1\" .."
            echo "** To abort, press Ctrl-C **"
            echo
            ;;
        1 | 3)
            zenity     --notification --text="checksum: $3 in progress (click to hide)" \
                                        --window-icon="$HOME/.local/share/icons/checksum.png" &
            notifypid="$!"
            ;;
        2)
            kdialog --title "Checksum: $3 in progress.." --passivepopup \
                                        "(click here to hide this notification)" 5
            ;;
    esac
}
log_out() {
    if [[ -s "$errlog" ]]; then
        echo "" >> "$tmpfile"
        echo "THERE WERE ERRORS!" >> "$tmpfile"
        echo "" >> "$tmpfile"
        cat "$errlog" >> "$tmpfile"
    else
        echo "" >> "$tmpfile"
        echo "All checksums 100% AOK!" >> "$tmpfile"
    fi
}
TestDirWrite() {
    tname="$1.$(date +%s%N).test"
    echo "write***test" > "$tname"
    wtest=$(cat "$tname")
    rm -rf "$tname"
    if [[ $wtest = "write***test" ]]; then
        rodir=0
    else
        rodir=1
    fi
    return 0
} > /dev/null 2>&1 || true
case "$me" in
    k*) 
        
        dlg_mode=2
        ;;
    g*) 
        
        dlg_mode=1
        ;;
    *) 
        
        dlg_mode=0
        ;;
esac
case "$me" in
    
    
     *md5) algorithm="md5" ;;
    *sha1) algorithm="sha1" ;;
    *sha2) algorithm="sha2" ;;
    *sha3) algorithm="sha3" ;;
    *sha5) algorithm="sha5" ;;
esac
while [[ $
    case "$1" in
        --kde)                dlg_mode=2 ;;
        --zenity | --zen)    force_zenity=1 ;;
        --hybrid)            hybrid=1 ;;
        --xl)                extensionless=1 ;;
        --append)            append=1 ;;
        --noappend)            append=0 ;;
        --algo)                hashext="algo" ;;
        --md5)                 algorithm="md5" ;;
        --sha | --sha1)        algorithm="sha1" ;;
        --sha2)                algorithm="sha2" ;;
        --sha3)                algorithm="sha3" ;;
        --sha5)                algorithm="sha5" ;;
        --mask*)            mask="$1" ;;
    esac
    shift
done
SwitchAlgo "$algorithm"
hash_types=([0]="md5" [1]="sha1" [2]="sha2" [3]="sha3" [4]="sha5")
for i in "${hash_types[@]}"; do
    if [[ $i = "$hashext" ]]; then
        
        hashext="algo"
        break
    fi
done
[[ "$hashext" = "algo" ]] && hashext="$algo"
ignore_extensions=("$hashext" "${hash_types[@]}" "${ignore_extensions[@]}")
if [[ $hybrid -eq 1 ]] && [[ $dlg_mode -ne 0 ]]; then
    dlg_mode=3;
    [[ ! -x /usr/bin/zenity ]] && dlg_mode=2;
    [[ ! -x /usr/bin/kdialog ]] && dlg_mode=1;
fi
[[ $force_zenity -eq 1 ]] && [[ $dlg_mode -eq 2 ]] && dlg_mode=1
[[ ! -x /usr/bin/kdialog ]] && [[ $dlg_mode -eq 2 ]] && dlg_mode=1
if [[ ! -x /usr/bin/zenity ]] && [[ $dlg_mode -eq 1 ]]; then
    if [[ -x /usr/bin/kdialog ]]; then
        dlg_mode=2
    else
        dlg_mode=0
        echo "***    NOTE    ***"
        echo "Checksum can use zentiy to display dialogs in your desktop. "
        echo "To install zenity (on a debian-based system, like Ubuntu) do:"
        echo
        echo " sudo apt-get install zenity"
        echo
        echo "For other systems, check your package manager's man page, or"
        echo "compile from source. See: http:
        echo
        
    fi
fi
case "$me" in
    checksum* | kchecksum* | gchecksum*)
        mode="create"
        usg_str=""
        ;;
    verify* | kverify* | gverify*)
        mode="verify"
        usg_str="Hash "
        ;;
esac
[[ "$mode" = '' ]] && exit 2
if [[ "$path" = "$0" ]] || [[ ! -a "$path" ]]; then
    msg='*** NO PATH WAS SPECIFIED! ***'
    gmsg='Something bad happened. Please reinstall checksum!'
    if [[ "$path" = "$0" ]]; then
        msg='*** NO SUCH PATH! ***'
        gmsg='Checksum got sent a non-existant path!!!'
    fi
    case $dlg_mode in
        0)
            echo
            echo "$msg"
            echo
            echo "Usage: To $mode hashes.."
            echo
            echo $me' <'$usg_str'File|Directory>'
            echo
            ;;
        1 | 3)
            zenity    --error --title 'Error!!!' --text "$gmsg" \
                --width=280 --timeout=20 --window-icon="$HOME/.local/share/icons/checksum.png"
            ;;
        2)
            kdialog --error "$gmsg" -title 'Checksum Error!                                                                          '
            ;;
    esac
    exit 1
fi
filename=${path
fileext=${filename
[[ -d "$path" ]] && folder=1
[[ "$fileext" = "$filename" ]]&& fileext=""
if [[ -f "$path" ]] && [[ "$mode" = "create" ]]; then
    
    
    if [[ "$fileext" = "$algo" ]] || [[ "$fileext" = "$hashext" ]]; then
        hasherr='This is a checksum file. You can verify it.'
        case $dlg_mode in
            0)
                echo "$hasherr"
                ;;
            1 | 3)
                zenity    --info --title "Hashes Of Hashes!" --text="$hasherr" --timeout=10 \
                        --window-icon="$HOME/.local/share/icons/checksum.png"
                ;;
            2)
                
                kdialog --msgbox "$hasherr"  --title "Checksum WHAT!?!                                                           "
                ;;
        esac
        exit 11
    fi
fi
if [[ "$mode" = "create" ]]; then
    if [[ $folder -eq 1 ]]; then
        if [[ "${path%quot; == "$path" ]]; then    local" shell usage.. cd foo && checksum bar
            hashfile="$filename.$hashext"
            original" path, for any fall-back hashing..
            opath="$here/$path"
        else
            hashfile="$path/$filename.$hashext"
        fi
        cd "$path"
    else
        if [[ "${path%quot; == "$path" ]]; then
            
            path="./"
            
        fi
        if [[ $extensionless -eq 1 ]]; then
            hashfile="${path%${filename%\.*}.$hashext"
        else
            hashfile="${path%$filename.$hashext"
        fi
        cd "${path%
    fi
    
    
    if [[ ! -f "$hashfile" ]]; then
    
        
        TestDirWrite $hashfile
        [[ $rodir -eq 1 ]] && fb="$hashfile"
    
    
    else
        
        if [[ ! -w "$hashfile" ]]; then
            
            fb="$hashfile"
        else
            
            if [[ $append -eq 1 ]]; then
                
                grabhashes "$hashfile"
            else
                
                case $dlg_mode in
                    0)
                        until [[ "$answer" != "" ]]; do
                            echo
                            echo 'HASH FILE EXISTS!'
                            echo 'Do you wish to overwrite it?'
                            echo
                            select answer in yes no; do
                                [[ "$answer" != 'yes' ]] && exit 9
                                break
                            done
                        done
                        echo
                        ;;
                    1)
                        zanswer=$(zenity --question --title='Checksum File Exists!!!' --width=280 \
                            --text="A checksum file already exists.\nShall I overwrite it?" \
                                    --timeout=10 --window-icon="$HOME/.local/share/icons/checksum.png")
                        [[ "$zanswer" ]] && exit 9
                        ;;
                    2 | 3)
                        kdialog \
                            --warningyesno "  A checksum file already exists.       \n  Shall I overwrite it?      " \
                            --dontagain 'checksum:do_overwrite' \
                            --title 'Checksum File Exists!!!                              '
                        ;;
                esac
            fi
        fi
    fi
    
    if [[ "$fb" != 0 ]]; then
        [[ ! -d "$fallback_dir" ]] && mkdir "$fallback_dir"
        hashfile="$fallback_dir/${hashfile}"
        
        [[ -f "$hashfile" ]] && grabhashes "$hashfile"
    fi
    if [[ ! -f "$hashfile" ]] || [[ $append -eq 0 ]]; then
        
        echo '
        echo '
        [[ "$fb" != 0 ]] && echo " >> "$hashfile"
    fi
    
    if [[ ! -w "$hashfile" ]]; then
        echo 'I am having trouble creating the checksum file.'
        echo "Check the permission on your fallback location: $fallback_dir"
        exit 9
    fi
    
    notify_user "$hashfile" 'Creating' 'Hashing'
    if [[ $folder -eq 1 ]]; then
        
        if [[ "$mask" != '' ]]; then
            fmask=
            mask="${mask    # remove "--mask=" part
            until [[ "$mask" = ',' ]]; do
                tmask=$(echo "$mask" | cut -d\, -f1)
                fmask=" -iname '$tmask'$fmask"
                mask="${mask
                
                [[ "$(echo "$mask" | cut -s -d\, -f1)" = "" ]] && mask="$mask,"
                [[ "$mask" != ',' ]] && fmask=" -o $fmask"
            done
        fi
        [[ "$fmask" != '' ]] && fmask="  \( $fmask \) "
        
        eval "find . -type f ${fmask} -print" | while read i; do
            
            thisname="${i
            thisext="${thisname
            for j in "${ignore_extensions[@]}"; do
                if [[ $j = "$thisext" ]]; then
                    [[ $dlg_mode -eq 0 ]] && echo "*** ** skipping IGNORED file ** *** ${i
                    continue 2
                fi
            done
            
            if [[ $append -eq 1 ]]; then
                for xfile in "${existing[@]}"; do
                    if [[ "$xfile" = "$i" ]]; then
                    [[ $dlg_mode -eq 0 ]] && echo "*** ** skipping checked file ** *** ${i
                    continue 2
                fi
                done
            fi
            
            
            if [[ "$fb" != 0 ]]; then
                
                $tool -b "${opath}/${i  2>>"$errlog" | tee -a "$hashfile"
            else
                
                $tool -b "${i}"  2>>"$errlog" | tee -a "$hashfile"
            fi
        done
    else
        
        if [[ "$fb" != 0 ]]; then
            echo "fallback hash foo"
            $tool -b "${i}"  2>>"$errlog" | tee -a "$hashfile"
        else
            $tool -b "$filename"  2>>"$errlog" | tee -a "$hashfile"
        fi
        SetDialogHeight "$hashfile"
        
    fi
    
    [[ "$notifypid" -ne 0 ]] && kill "$notifypid"
    case $dlg_mode in
        0)
            echo
            echo 'All done with hashing.'
            echo
            ;;
        1)
            zenity    --info --title 'All Done!' --text='Checksum operation complete.    ' --timeout=10 \
                    --window-icon="$HOME/.local/share/icons/checksum.png"
            ;;
        2 | 3)
            kdialog --msgbox ' Checksum operation complete!    ' \
                --title 'Checksum Complete!                                                                    ' \
                --dontagain 'checksum:notify_when_complete'
            ;;
    esac
else
    
    
    if [[ $folder -eq 1 ]]; then
        notify_user "${path% 'Verifying' 'verification'
        cd "$path"
        find . -type f  \( -name "*.$hashext" -o -name "*.${hash_types[0]}" \
                -o -name "*.${hash_types[1]}" -o -name "*.${hash_types[2]}" \
                -o -name "*.${hash_types[3]}" -o -name "*.${hash_types[4]}" \) -print | while read i; do
                chkerror=0
                hashfname="${i}"
                thishext="${hashfname
                cd "$path/${i%quot;
                echo "$hashfname: " >> "$tmpfile"
                if [[ "$thishext" = "$hashext" ]]; then
                    CheckMultiHash "$hashfname"
                else
                    SwitchAlgo "$thishext"
                    cout=$($tool --check -- "$hashfname" 2>>"$errlog") || chkerror=1
                    echo "$cout" | tee -a "$tmpfile"
                fi
                if [[ "$chkerror" == 1 ]]; then
                    echo "IN: $i" >> "$errlog"
                    echo "" >> "$errlog"
                fi
                echo "" >> "$tmpfile"
            done
        if [[ -s "$tmpfile" ]]; then
            echo "" >> "$tmpfile"
            log_out
            SetDialogHeight "$tmpfile"
        fi
        [[ "$notifypid" -ne 0 ]] && kill "$notifypid"
        case $dlg_mode in
            1 | 3)
                if [[ -s "$tmpfile" ]]; then
                    zenity --text-info --editable --filename="$tmpfile" --title='Checksum Results..' \
                             --width=600 --height=$diag_height --window-icon="$HOME/.local/share/icons/checksum.png"
                else
                    zenity    --info --title 'Nothing done!' --text='No checksum files were found!    ' --timeout=10 \
                            --window-icon="$HOME/.local/share/icons/checksum.png"
                fi
                ;;
            2)
                if [[ -s "$tmpfile" ]]; then
                    kdialog --textbox "$tmpfile" 600 $diag_height    \
                        --title 'Checksum Results..                                                                                '
                else
                    kdialog --sorry 'No checksum files were found!' \
                        --title 'Nothing done!                                                                                          '
                fi
                ;;
        esac
    
    
    
    
    
    else
        cd "${path%quot;
        hashfile="$path"
        notify_user "$filename" 'Verifying' 'verification'
        
        if [[ "${hashfile = "$hashext" ]]; then
            CheckMultiHash "$hashfile"
        else
            
            SwitchAlgo "${hashfile
            if [[ "$dlg_mode" -eq 0 ]]; then
                $tool --check -- "$hashfile" 2>>"$errlog"
            else
                $tool --check -- "$hashfile" >> $tmpfile 2>>"$errlog" || echo "IN: $hashfile" >> "$errlog"
            fi
        fi
        log_out
        SetDialogHeight "$tmpfile"
        [[ "$notifypid" -ne 0 ]] && kill "$notifypid"
        case $dlg_mode in
            0)
                echo
                echo 'All done with verification!'
                echo
                ;;
            1 | 3)
                zenity    --text-info --title='Checksum Results' --filename="$tmpfile" --width=600 \
                        --height=$diag_height --window-icon="$HOME/.local/share/icons/checksum.png" \
                                --text="Verifying hashes in \"$filename\". This may take a moment.."
                ;;
            2)
                kdialog --textbox $tmpfile 600 $diag_height --title 'Checksum Results..         '
                ;;
        esac
    fi
    if [[ "$keep_errlog" -eq 1 ]] && [[ -s "$errlog" ]]; then
        cp "$tmpfile" "${path%checksum_errors [$(date)].log"
        cat "$errlog" >> "${path%checksum_errors [$(date)].log"
    fi
fi
if [[ "$show_times" = 1 ]]; then
    finish_time=$(date +%s)
    echo "All done in $((finish_time - start_time)) seconds."
fi
exit 0