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