[comp.unix.questions] Functions for C-shell style directory stack in Korn shell

rwl@uvacs.CS.VIRGINIA.EDU (Ray Lubinsky) (07/10/88)

I promised to post these a while ago and the recent set of postings about ksh
functions reminded me.

My apologies; this is a fairly large posting, but I do get tired of people
going on about how ksh is not quite able to match some of the C-shell's tricks.

Analogs to C-shell's "pushd", "popd", and "dirs" functions are included plus
the function "roll" which is easier than "pushd +count" for rolling the
directory stack (though that too is implemented).  "Cd" is aliased to the
function "chdir" which handles the directory stack and prints it out after
changing directory.  Naturally, you can modify "chdir" so that it also
does an ls(1), prints the date, or plays rogue every time you change
directories.

Enjoy!  Feel free to point out bugs &c.

-------------------------------- CUT HERE ------------------------------------
#
#	Directory stack support for ksh.
#
#	Author:	Ray Lubinsky, rwl@uvacs.cs.virginia.edu
#	Site:	Computer Science Department, University of Virginia
#
#	This file contains functions and aliases which implement a
#	directory stack for the Korn shell very similar to that built
#	into the C-shell.
#
alias cd='chdir'				# THIS IS NOT OPTIONAL
alias pop='popd'				# optional
alias push='pushd'				# optional
#
#	pushd -- push directory stack
#
#	Usage: pushd [<directory> | +<position>]
#
#	You may either change directories by pushing a new directory or
#	by reordering the directory stack.  The directory on the top of
#	the stack (first token in the $DIRSTACK variable) becomes the
#	current working directory.  Giving no arguments is the same as
#	"pushd +1"; see function roll() defined below.
#
function pushd
{
	if [ $# -gt 1 ] ; then
		echo 'pushd: Too many arguments.'
		return 1
	fi
	integer OK=1 FLIP=0 POS=0
	case $1 in
		'')	FLIP=1 ;;
		+*)	POS=${1#+}
			if [ 0 -eq $POS -o $POS -ne "$POS" ] ; then
				echo 'pushd: Bad directory.'
				return 1
			fi ;;
		*)	typeset +x NEWDIR=$1
	esac
	set -- ${DIRSTACK:=$PWD}
	if [ $FLIP -ne 0 ] ; then
		if [ $# -lt 2 ] ; then
			echo 'pushd: No other directory.'
			return 1
		else
			typeset +x SECOND=$1 FIRST=$2
			shift 2
			set -- $FIRST $SECOND $*
		fi
	elif [ $POS -eq 0 ] ; then
		set -- $NEWDIR $DIRSTACK
	elif [ $POS -ge $# ] ; then
		echo 'pushd: Directory stack not that deep.'
		return 1
	else
		typeset +x STACK=''
		integer i=0
		while [ $i -lt $POS ]
		do
			STACK="$STACK $1"
			shift
			let i=i+1
		done
		set -- $* $STACK
	fi
	'cd' $1 1>/dev/null
	OK=$?
	shift
	if [ $OK -eq 0 ] ; then
		DIRSTACK="$PWD $*"
		dirs
	else
		shift
		DIRSTACK=$*
	fi
	return $OK
}
#
#	popd -- pop directory stack
#
#	Usage: popd [+<position>]
#
#	Pop a directory off the directory stack.  With no arguments this
#	pops the top (current working) directory and changes directory to
#	the new top-of-stack directory.  With the position argument, the
#	directory at the given ordinal position from the top of the stack
#	is removed from $DIRSTACK; this never results in a change of current
#	working directory.
#
function popd
{
	if [ $# -gt 1 ] ; then
		echo 'popd: Too many arguments.'
		return 1
	fi
	integer OK=0 POS=0
	case $1 in
		'')	OK=1 ;;
		+*)	POS=${1#+}
			if [ 0 -lt $POS -a $POS -eq "$POS" ] ; then
				OK=1
			fi
	esac
	if [ $OK -eq 0 ] ; then
		echo 'popd: Bad directory.'
		return 1
	fi
	set -- $DIRSTACK
	if [ $# -le 1 ] ; then
		echo 'popd: Directory stack empty.'
		return 1
	elif [ $POS -eq 0 ] ; then
		shift
	elif [ $POS -ge $# ] ; then
		echo 'popd: Directory stack not that deep.'
		return 1
	else
		typeset +x STACK=''
		integer i=0
		while [ $i -ne $POS ]
		do
			STACK="$STACK $1"
			shift
			let i=i+1
		done
		shift
		set -- $STACK $*
	fi
	'cd' $1 1>/dev/null
	OK=$?
	if [ $OK -eq 0 ] ; then
		DIRSTACK=$*
		dirs
	fi
	return $OK
}
#
#	chdir -- wrapper function for cd which adjusts DIRSTACK
#
#	Usage: chdir [<directory>]
#
#	This function is used as the change-directory function.  To have cd
#	work right, you must alias cd to chdir.  Note that instances of cd
#	in this function are quoted so that you get the real builtin rather
#	than the alias.
#
function chdir
{
	typeset +x DIR
	case $# in
		0)	DIR=$HOME ;;
		1)	DIR=$1 ;;
		2)	DIR=`echo $PWD | /bin/sed "s$1$2"` ;;
		*)	echo 'cd: too many arguments' ; return 1
	esac
	if 'cd' $DIR 1>/dev/null ; then
		set -- $DIRSTACK
		DIRSTACK=$PWD
		if [ $# -gt 1 ] ; then
			shift
			DIRSTACK="$DIRSTACK $*"
		fi
		dirs
	else
		return 1
	fi
}
#
#	roll -- roll directory stack
#
#	Usage: roll [<count>]
#
#	I use this function when I want to roll the directory stack rather
#	than using "pushd +<count>" to avoid typing extra characters.
#	With no arguments, the function is equivalent to "pushd +1".
#
function roll
{
	typeset +x ROLL
	case $# in
		0)	ROLL=1 ;;
		1)	case $1 in
				[0-9]*) ROLL=$1 ;;
				*)	echo 'roll: number expected' ; return 1
			esac ;;
		*)	echo 'roll: too many arguments' ; return 1 ;;
	esac
	pushd +$ROLL
}
#
#	dirs -- print directory stack
#
#	Usage: dirs
#
#	Like the C-shell builtin, this prints the directory stack.
#
function dirs
{
	if [ "$HOME" = '/' ] ; then
		echo ${DIRSTACK:=$PWD}
	else
		echo ${DIRSTACK:=$PWD} | /bin/sed "s$HOME~g"
	fi
}
-------------------------------- CUT HERE ------------------------------------

-- 
| Ray Lubinsky,                    UUCP:      ...!uunet!virginia!uvacs!rwl    |
| Department of                    BITNET:    rwl8y@virginia                  |
| Computer Science,                CSNET:     rwl@cs.virginia.edu  -OR-       |
| University of Virginia                      rwl%uvacs@uvaarpa.virginia.edu  |