#!/bin/sh
# TODO: Currently, this file isn't entirely POSIX compatible,
# it will run fine with bash or zsh, however some functions may cause
# issues with pure POSIX. The fill will however run fine, the errors
# would only occur if the incompatible functions would be started.

# Show application listening on given port
port() {
    sudo netstat -pln | grep "$1" | awk '{print $NF}'
}

# Create a new directory and enter it
mkd() {
    # shellcheck disable=SC2164
    mkdir -p "$1" && cd "$1";
}

# `o` with no arguments opens the current directory, otherwise opens the given
# location
o() {
    if [ $# -eq 0 ]; then
        open .;
    else
        open "$@";
    fi;
}

# Use bat for nicer git diffs
batdiff() {
    git diff --name-only --diff-filter=d | xargs bat --diff
}

# Determine size of a file or total size of a directory
dirsize() {
    if du -b /dev/null > /dev/null 2>&1; then
        arg=-sbh;
    else
        arg=-sh;
    fi
    if [[ -n "$*" ]]; then
        \du $arg -- "$@";
    else
        \du $arg .[^.]* ./*;
    fi;
}

randmac() {
    sudo ip link set dev "$1" down
    sudo macchanger -A "$1"
    sudo ip link set dev "$1" up
}

# Go to the root of a git tree
cdgit () {
    if [ "$(git rev-parse --is-inside-work-tree > /dev/null 2>&1)" -eq 0 ]; then
        while ! [ -d .git ]; do
            cd ..
        done
        return 0
    else
        echo "Not a git directory"
        return 1
    fi
}

# Create a data URL from a file
dataurl() {
    mimeType="$(file -b --mime-type "$1")"
    if echo "$mimeType" | grep -e "^text/.*$" >/dev/null; then
        mimeType="${mimeType};charset=utf-8"
    fi
    echo "data:${mimeType};base64,$(openssl base64 -in "$1" | tr -d '\n')";
}

# `tre` is a shorthand for `tree` with hidden files and color enabled, ignoring
# the `.git` directory, listing directories first. The output gets piped into
# `less` with options to preserve color and line numbers, unless the output is
# small enough for one screen.
tre() {
    tree -I '.git|node_modules|bower_components' --group-directories-first "$@" | less -FRNX;
}



# Show all the names (CNs and SANs) listed in the SSL certificate
# for a given domain
getcertnames() {
    if [ -z "${1}" ]; then
        echo "ERROR: No domain specified.";
        return 1;
    fi;

    domain="${1}";
    echo "Testing ${domain}…";
    echo ""; # newline

    tmp=$(echo -e "GET / HTTP/1.0\nEOT" \
        | openssl s_client -connect "${domain}:443" -servername "${domain}" 2>&1);

    if [[ "${tmp}" = *"-----BEGIN CERTIFICATE-----"* ]]; then
        certText=$(echo "${tmp}" \
            | openssl x509 -text -certopt "no_aux, no_header, no_issuer, no_pubkey, \
            no_serial, no_sigdump, no_signame, no_validity, no_version");
        echo "Common Name:";
        echo ""; # newline
        echo "${certText}" | grep "Subject:" | sed -e "s/^.*CN=//" | sed -e "s/\/emailAddress=.*//";
        echo ""; # newline
        echo "Subject Alternative Name(s):";
        echo ""; # newline
        echo "${certText}" | grep -A 1 "Subject Alternative Name:" \
            | sed -e "2s/DNS://g" -e "s/ //g" | tr "," "\n" | tail -n +2;
        return 0;
    else
        echo "ERROR: Certificate not found.";
        return 1;
    fi;
}

# Compare original and gzipped file size
gz_compare() {
    origsize=$(wc -c < "$1");
    gzipsize=$(gzip -c "$1" | wc -c);
    ratio=$(echo "$gzipsize * 100 / $origsize" | bc -l);
    printf "orig: %d bytes\n" "$origsize";
    printf "gzip: %d bytes (%2.2f%%)\n" "$gzipsize" "$ratio";
}

# Extract almost any archive
extract() {
 if [ -z "$1" ]; then
    # display usage if no parameters given
    echo "Usage: extract <path/file_name>.<zip|rar|bz2|gz|tar|tbz2|tgz|Z|7z|xz|ex|tar.bz2|tar.gz|tar.xz>"
    echo "       extract <path/file_name_1.ext> [path/file_name_2.ext] [path/file_name_3.ext]"
    return 1
 else
    for n in "$@"
    do
      if [ -f "$n" ] ; then
          case "${n%,}" in
            *.tar.bz2|*.tar.gz|*.tar.xz|*.tbz2|*.tgz|*.txz|*.tar)
                         tar xvf "$n"       ;;
            *.lzma)      unlzma ./"$n"      ;;
            *.bz2)       bunzip2 ./"$n"     ;;
            *.rar)       unrar x -ad ./"$n" ;;
            *.gz)        gunzip ./"$n"      ;;
            *.zip)       unzip ./"$n"       ;;
            *.z)         uncompress ./"$n"  ;;
            *.7z|*.arj|*.cab|*.chm|*.deb|*.dmg|*.iso|*.lzh|*.msi|*.rpm|*.udf|*.wim|*.xar)
                         7z x ./"$n"        ;;
            *.xz)        unxz ./"$n"        ;;
            *.exe)       cabextract ./"$n"  ;;
            *)
                         echo "extract: '$n' - unknown archive method"
                         return 1
                         ;;
          esac
      else
          echo "'$n' - file does not exist"
          return 1
      fi
    done
fi
}

# Create a .tar.gz archive, using `zopfli`, `pigz` or `gzip` for compression
targz() {
    # Combine given names spearated with spaces as the filename
    tmpFile="${*%/}.tar"

    tar -cvf "${tmpFile}" "${@}" || return 1

    size=$(
        stat -f"%z" "${tmpFile}" 2> /dev/null; # macOS `stat`
        stat -c"%s" "${tmpFile}" 2> /dev/null;  # GNU `stat`
    );

    cmd="";
    if (( size < 52428800 )) && hash zopfli 2> /dev/null; then
        # the .tar file is smaller than 50 MB and Zopfli is available; use it
        cmd="zopfli";
    else
        if hash pigz 2> /dev/null; then
            cmd="pigz";
        else
            cmd="gzip";
        fi;
    fi;

    echo "Compressing .tar ($((size / 1000)) kB) using \`${cmd}\`…";
    "${cmd}" -v "${tmpFile}" || return 1;
    [ -f "${tmpFile}" ] && rm "${tmpFile}";

    zippedSize=$(
        stat -f"%z" "${tmpFile}.gz" 2> /dev/null; # macOS `stat`
        stat -c"%s" "${tmpFile}.gz" 2> /dev/null; # GNU `stat`
    );

    echo "${tmpFile}.gz ($((zippedSize / 1000)) kB) created successfully.";
}

anonymize() {
    # Reset the prompt on initial run to allow this script
    # to be ran multiple times without user having to reload
    # PS1 manually
    # shellcheck source=/home/itsdrike/.config/shell/prompt
    . "${XDG_CONFIG_DIR:-$HOME/.config}/shell/prompt"

    # Regular expression to match 0-255 numbers (color)
    color_int_re='^(0+)?([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'

    # Defaults
    NAME="%n"
    MACHINE=""
    #NAME_COLOR="%F{047}"
    NAME_COLOR="%F{172}"
    #DIR_COLOR="%F{027}"
    DIR_COLOR="%F{158}"
    AT_COLOR="%F{004}"
    MACHINE_COLOR="%F{070}"

    while [ $# -gt 0 ]; do
        key=$1

        case $key in
            -n|--name)
                NAME=$2
                shift
                shift
                ;;
            -m|--machine)
                MACHINE=$2
                shift
                shift
                ;;
            -nc|--name-color)
                if [[ $2 =~ $color_int_re ]]; then
                    NAME_COLOR="%F{$2}"
                else
                    NAME_COLOR=$2
                fi
                shift
                shift
                ;;
            -dc|--dir-color)
                if [[ $2 =~ $color_int_re ]]; then
                    DIR_COLOR="%F{$2}"
                else
                    DIR_COLOR=$2
                fi
                shift
                shift
                ;;
            -mc|--machine-color)
                if [[ $2 =~ $color_int_re ]]; then
                    MACHINE_COLOR="%F{$2}"
                else
                    MACHINE_COLOR=$2
                fi
                shift
                shift
                ;;
            -ac|--at-color)
                if [[ $2 =~ $color_int_re ]]; then
                    AT_COLOR="%F{$2}"
                else
                    AT_COLOR=$2
                fi
                shift
                shift
                ;;
            *)
                echo "Unrecognized argument: $key"
                echo "Arguments: -n|--name, -m|--machine, -nc|--name-color, -dc|--dir-color, -mc|--machine-color, -ac|--at-color"
                return 1
                ;;
        esac
    done


    OLD_LINE="%F{047}%n%f %F{027}"

    [ -n "$NAME" ] && NEW_LINE="${NAME_COLOR}${NAME}"
    [ -n "$NAME" ] && [ -n "$MACHINE" ] && NEW_LINE="${NEW_LINE}${AT_COLOR}@"
    [ -n "$MACHINE" ] && NEW_LINE="${NEW_LINE}${MACHINE_COLOR}${MACHINE}"
    [ -n "$NAME" ] || [ -n "$MACHINE" ] && NEW_LINE="${NEW_LINE}%f "
    NEW_LINE="${NEW_LINE}${DIR_COLOR}"

    # Use new anonymized name, machine and colors in PS1
    PS1=${PS1/"$OLD_LINE"/"$NEW_LINE"}
}