#!/usr/bin/env ksh
# Change directory and put directory on front of stack
# shellcheck disable=SC2102  # .sh.var refs are legal as an array index
# shellcheck disable=SC2154  # .sh._push_stack[@] is referenced but not assigned
# shellcheck disable=SC2211  # .sh.var assignments are not "glob used as a command name"

function pushd {
    typeset dir=''
    typeset type=0
    integer i
    case $1 in
    "") # pushd
        # shellcheck disable=SC2154
        if ((.sh._push_top >= .sh._push_max))
        then
            print pushd: No other directory.
            return 1
        fi
        type=1
        dir="${.sh._push_stack[.sh._push_top]}"
        ;;
    +[1-9]|+[1-9][0-9]) # pushd +n
        integer i=$((.sh._push_top - $1))
        if ((i >= .sh._push_max))
        then
            print pushd: Directory stack not that deep.
            return 1
        fi
        type=2
        dir="${.sh._push_stack[i]}"
        ;;
    *)  if ((.sh._push_top <= 0))
        then
            print pushd: Directory stack overflow.
            return 1
        fi
    esac
    case $dir in
    \~*)   dir="$HOME${dir#\~}"
    esac
    cd "${dir:-$1}" > /dev/null || return 1
    dir="${OLDPWD#$HOME/}"
    case $dir in
    $HOME)
        dir=\~
        ;;
    /*) ;;
    *)  dir=\~/$dir
    esac
    case $type in
    0)  # pushd name
        .sh._push_top=$((.sh._push_top - 1))
        .sh._push_stack[.sh._push_top]="$dir"
        ;;
    1)  # pushd
        .sh._push_stack[.sh._push_top]="$dir"
        ;;
    2)  # push +n
        type=${1#+}
        set -- "${.sh._push_stack[@]}" "$dir" "${.sh._push_stack[@]}"
        shift "$type"
        integer i=$(( .sh._push_top - 1 ))
        for dir
        do
            (( (i = i + 1) < .sh._push_max )) || break
            .sh._push_stack[i]="$dir"
        done
    esac
    dirs
}
