1#! /bin/bash
2#
3# cdhist - cd replacement with a directory stack like pushd/popd
4#
5# usage: cd [-l] [-n] [-] [dir]
6#
7# options:
8#	-l	print the cd directory stack, one entry per line
9#	-	equivalent to $OLDPWD
10#	-n	cd to nth directory in cd directory stack
11#	-s	cd to first directory in stack matching (substring) `s'
12#
13# arguments:
14#	dir	cd to dir and push dir onto the cd directory stack
15#
16# If the new directory is a directory in the stack and the options selected
17# it (-n, -s), the new working directory is printed
18#
19# If the variable CDHISTFILE is set, the cd directory stack is loaded from
20# and written to $CDHISTFILE every time `cd' is executed.
21#
22# Note: I got this off the net somewhere; I don't know the original author
23#
24# Chet Ramey
25# chet@po.cwru.edu	
26
27_cd_print()
28{
29	echo -e "$@"
30}
31
32cd()
33{
34	typeset -i cdlen i
35	typeset t
36	
37	if [ $# -eq 0 ]
38	then
39		set -- $HOME
40	fi
41	
42	if [ "$CDHISTFILE" ] && [ -r "$CDHISTFILE" ] # if directory history exists
43	then
44		typeset CDHIST
45		i=-1
46		while read -r t			# read directory history file
47		do
48			CDHIST[i=i+1]=$t
49		done <$CDHISTFILE
50	fi
51	
52	if [ "${CDHIST[0]}" != "$PWD" ] && [ -n "$PWD" ]
53	then
54		_cdins				# insert $PWD into cd history
55	fi
56	
57	cdlen=${#CDHIST[*]}			# number of elements in history
58	
59	case "$@" in
60	-)					# cd to new dir
61		if [ "$OLDPWD" = "" ] && ((cdlen>1))
62		then
63			'_cdprint' ${CDHIST[1]}
64			builtin cd ${CDHIST[1]}
65			pwd
66		else
67			builtin cd "$@"
68			# pwd
69		fi
70		;;
71	-l)					# _cdprint directory list
72		((i=cdlen))
73		while (((i=i-1)>=0))
74		do
75			num=$i
76			'_cdprint' "$num ${CDHIST[i]}"
77		done
78		return
79		;;
80	-[0-9]|-[0-9][0-9])			# cd to dir in list
81		if (((i=${1#-})<cdlen))
82		then
83			'_cdprint' ${CDHIST[i]}
84			builtin cd ${CDHIST[i]}
85			pwd
86		else
87			builtin cd $@
88			# pwd
89		fi
90		;;
91	-*)					# cd to matched dir in list
92		t=${1#-}
93		i=1
94		while ((i<cdlen))
95		do
96			case ${CDHIST[i]} in
97			*$t*)
98				'_cdprint' ${CDHIST[i]}
99				builtin cd ${CDHIST[i]}
100				pwd
101				break
102				;;
103			esac
104			((i=i+1))
105		done
106		if ((i>=cdlen))
107		then
108			builtin cd $@
109			# pwd
110		fi
111		;;
112	*)					# cd to new dir
113		builtin cd $@
114		# pwd
115		;;
116	esac
117
118	_cdins					# insert $PWD into cd history
119	
120	if [ "$CDHISTFILE" ]
121	then
122		cdlen=${#CDHIST[*]}		# number of elements in history
123
124		i=0
125		while ((i<cdlen))
126		do
127			echo ${CDHIST[i]}	# update directory history
128			((i=i+1))
129		done >$CDHISTFILE
130	fi
131}
132	
133_cdins()					# insert $PWD into cd history
134{						# meant to be called only by cd
135	typeset -i i
136
137	i=0
138
139	while (( i < ${#CDHIST[*]} ))		# see if dir is already in list
140	do
141		if [ "${CDHIST[$i]}" = "$PWD" ]
142		then
143			break
144		fi
145		((i=i+1))
146	done
147
148	if (( i>22 ))				# limit max size of list
149	then
150		i=22
151	fi
152
153	while (((i=i-1)>=0))			# bump old dirs in list
154	do
155		CDHIST[i+1]=${CDHIST[i]}
156	done
157
158	CDHIST[0]=$PWD				# insert new directory in list
159}
160	
161# examples
162shopt -s expand_aliases
163
164# go to known place before doing anything
165cd /
166
167echo CDHIST: "${CDHIST[@]}"
168for dir in /tmp /bin - -2 -l
169do
170	cd $dir
171	echo CDHIST: "${CDHIST[@]}"
172	echo PWD: $PWD
173
174done
175
176exit 0
177