update from MirBSD CVS
[shellsnippets/shellsnippets.git] / mksh / pushd-popd-dirs
1 # $MirOS: src/bin/mksh/dot.mkshrc,v 1.88 2014/01/11 18:09:39 tg Exp $
2 #-
3 # Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010,
4 #               2011, 2012, 2013, 2014
5 #       Thorsten Glaser <tg@mirbsd.org>
6 #
7 # Provided that these terms and disclaimer and all copyright notices
8 # are retained or reproduced in an accompanying document, permission
9 # is granted to deal in this work without restriction, including un-
10 # limited rights to use, publicly perform, distribute, sell, modify,
11 # merge, give away, or sublicence.
12 #
13 # This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
14 # the utmost extent permitted by applicable law, neither express nor
15 # implied; without malicious intent or gross negligence. In no event
16 # may a licensor, author or contributor be held liable for indirect,
17 # direct, other damage, loss, or other issues arising in any way out
18 # of dealing in the work, even if advised of the possibility of such
19 # damage or existence of a defect, except proven that it results out
20 # of said person's immediate fault when using the work as intended.
21
22 # Berkeley C shell compatible dirs, popd, and pushd functions
23 # Z shell compatible chpwd() hook, used to update DIRSTACK[0]
24 DIRSTACKBASE=$(realpath ~/. 2>/dev/null || print -nr -- "${HOME:-/}")
25 set -A DIRSTACK
26 function chpwd {
27         DIRSTACK[0]=$(realpath . 2>/dev/null || print -r -- "$PWD")
28         [[ $DIRSTACKBASE = ?(*/) ]] || \
29             DIRSTACK[0]=${DIRSTACK[0]/#$DIRSTACKBASE/~}
30         :
31 }
32 chpwd .
33 function cd {
34         builtin cd "$@" || return $?
35         chpwd "$@"
36 }
37 function cd_csh {
38         local d t=${1/#~/$DIRSTACKBASE}
39
40         if ! d=$(builtin cd "$t" 2>&1); then
41                 print -u2 "${1}: ${d##*cd: $t: }."
42                 return 1
43         fi
44         cd "$t"
45 }
46 function dirs {
47         local d dwidth
48         local -i fl=0 fv=0 fn=0 cpos=0
49
50         while getopts ":lvn" d; do
51                 case $d {
52                 (l)     fl=1 ;;
53                 (v)     fv=1 ;;
54                 (n)     fn=1 ;;
55                 (*)     print -u2 'Usage: dirs [-lvn].'
56                         return 1 ;;
57                 }
58         done
59         shift $((OPTIND - 1))
60         if (( $# > 0 )); then
61                 print -u2 'Usage: dirs [-lvn].'
62                 return 1
63         fi
64         if (( fv )); then
65                 fv=0
66                 while (( fv < ${#DIRSTACK[*]} )); do
67                         d=${DIRSTACK[fv]}
68                         (( fl )) && d=${d/#~/$DIRSTACKBASE}
69                         print -r -- "$fv        $d"
70                         let fv++
71                 done
72         else
73                 fv=0
74                 while (( fv < ${#DIRSTACK[*]} )); do
75                         d=${DIRSTACK[fv]}
76                         (( fl )) && d=${d/#~/$DIRSTACKBASE}
77                         (( dwidth = (${%d} > 0 ? ${%d} : ${#d}) ))
78                         if (( fn && (cpos += dwidth + 1) >= 79 && \
79                             dwidth < 80 )); then
80                                 print
81                                 (( cpos = dwidth + 1 ))
82                         fi
83                         print -nr -- "$d "
84                         let fv++
85                 done
86                 print
87         fi
88         return 0
89 }
90 function popd {
91         local d fa
92         local -i n=1
93
94         while getopts ":0123456789lvn" d; do
95                 case $d {
96                 (l|v|n) fa+=" -$d" ;;
97                 (+*)    n=2
98                         break ;;
99                 (*)     print -u2 'Usage: popd [-lvn] [+<n>].'
100                         return 1 ;;
101                 }
102         done
103         shift $((OPTIND - n))
104         n=0
105         if (( $# > 1 )); then
106                 print -u2 popd: Too many arguments.
107                 return 1
108         elif [[ $1 = ++([0-9]) && $1 != +0 ]]; then
109                 if (( (n = ${1#+}) >= ${#DIRSTACK[*]} )); then
110                         print -u2 popd: Directory stack not that deep.
111                         return 1
112                 fi
113         elif [[ -n $1 ]]; then
114                 print -u2 popd: Bad directory.
115                 return 1
116         fi
117         if (( ${#DIRSTACK[*]} < 2 )); then
118                 print -u2 popd: Directory stack empty.
119                 return 1
120         fi
121         unset DIRSTACK[n]
122         set -A DIRSTACK -- "${DIRSTACK[@]}"
123         cd_csh "${DIRSTACK[0]}" || return 1
124         dirs $fa
125 }
126 function pushd {
127         local d fa
128         local -i n=1
129
130         while getopts ":0123456789lvn" d; do
131                 case $d {
132                 (l|v|n) fa+=" -$d" ;;
133                 (+*)    n=2
134                         break ;;
135                 (*)     print -u2 'Usage: pushd [-lvn] [<dir>|+<n>].'
136                         return 1 ;;
137                 }
138         done
139         shift $((OPTIND - n))
140         if (( $# == 0 )); then
141                 if (( ${#DIRSTACK[*]} < 2 )); then
142                         print -u2 pushd: No other directory.
143                         return 1
144                 fi
145                 d=${DIRSTACK[1]}
146                 DIRSTACK[1]=${DIRSTACK[0]}
147                 cd_csh "$d" || return 1
148         elif (( $# > 1 )); then
149                 print -u2 pushd: Too many arguments.
150                 return 1
151         elif [[ $1 = ++([0-9]) && $1 != +0 ]]; then
152                 if (( (n = ${1#+}) >= ${#DIRSTACK[*]} )); then
153                         print -u2 pushd: Directory stack not that deep.
154                         return 1
155                 fi
156                 while (( n-- )); do
157                         d=${DIRSTACK[0]}
158                         unset DIRSTACK[0]
159                         set -A DIRSTACK -- "${DIRSTACK[@]}" "$d"
160                 done
161                 cd_csh "${DIRSTACK[0]}" || return 1
162         else
163                 set -A DIRSTACK -- placeholder "${DIRSTACK[@]}"
164                 cd_csh "$1" || return 1
165         fi
166         dirs $fa
167 }