########################################################################
#                                                                      #
#               This software is part of the ast package               #
#          Copyright (c) 1982-2012 AT&T Intellectual Property          #
#          Copyright (c) 2020-2023 Contributors to ksh 93u+m           #
#                      and is licensed under the                       #
#                 Eclipse Public License, Version 2.0                  #
#                                                                      #
#                A copy of the License is available at                 #
#      https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html      #
#         (with md5 checksum 84283fa8859daf213bdda5a9f8d1be1d)         #
#                                                                      #
#                  David Korn <dgk@research.att.com>                   #
#                  Martijn Dekker <martijn@inlv.org>                   #
#               K. Eugene Carlson <kvngncrlsn@gmail.com>               #
#                                                                      #
########################################################################

# cd wrapper: make 'cd' work with 'dirs', 'mcd', 'pushd', 'popd'.
# Use 'cd n', where n is a number, to change to that directory on the stack.
# Use 'cd -n' to do the same, counting backwards from the top of the stack.

# Non-destructively initialize global parameters for directory stack.
# Default stack size is 32, or $CDSTACK if set before init.
integer _push_max=${_push_max:-${CDSTACK:-32}}
integer _push_top=${_push_top:-${CDSTACK:-32}}
typeset -a _push_stack

# Change directory and put directory on front of stack
function cd
{
	typeset dir=
	integer n=0 type=4
	case $1 in
	-|-1|2) # handle 'cd -'
		n=_push_top type=1
		set --
		;;
	-[1-9]*([0-9]))
		# handle 'cd -n'
		n=_push_top+${1#-}-1 type=2
		set --
		;;
	1)	# keep present directory
		print -r -- "$PWD"
		return
		;;
	[1-9]*([0-9]))
		# handle 'cd n'
		n=_push_top+${1}-2 type=2
		set --
		;;
	*) 	if	((_push_top <= 0))
		then	type=3
			n=_push_max
		fi
		;;
	esac
	if	((type<3))
	then	if	((n >= _push_max+1))
		then	print -u2 "$0: Directory stack not that deep."
			return 1
		else	dir=${_push_stack[n]}
		fi
	fi
	# change ~ to $HOME
	case $dir in
	\~*)	dir=$HOME${dir#\~} ;;
	esac
	# If there are no arguments, use $dir if non-empty.
	(($#)) || set -- ${dir:+"$dir"}
	# Call the cd built-in.
	\command cd "$@" >/dev/null || return
	# change $HOME to ~
	dir=${OLDPWD#"$HOME/"}
	case $dir in
	"$HOME")dir=\~ ;;
	/*)	;;
	*)	dir=\~/$dir ;;
	esac
	case $type in
	1)	# swap first two elements
		_push_stack[_push_top]=$dir
		;;
	2|3)	# put $dir on top and shift down by one until top
		integer i=_push_top
		for dir in "$dir" "${_push_stack[@]}"
		do	((i > n)) && break
			_push_stack[i]=$dir
			i=i+1
		done
		;;
	4)	# push name
		_push_stack[_push_top=_push_top-1]=$dir
		;;
	esac
	print -r -- "$PWD"
}
