collection of standard snippets from mksh/dot.mkshrc
[shellsnippets/shellsnippets.git] / mksh / pushd-popd-dirs
1 # $MirOS: src/bin/mksh/dot.mkshrc,v 1.59 2011/02/09 19:32:35 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         typeset 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         typeset d dwidth
47         typeset -i isnoglob=0 fl=0 fv=0 fn=0 cpos=0
48
49         [[ $(set +o) == *@(-o noglob)@(| *) ]] && isnoglob=1
50         set -o noglob
51         while getopts ":lvn" d; do
52                 case $d {
53                 (l)     fl=1 ;;
54                 (v)     fv=1 ;;
55                 (n)     fn=1 ;;
56                 (*)     print -u2 'Usage: dirs [-lvn].'
57                         return 1 ;;
58                 }
59         done
60         shift $((OPTIND - 1))
61         if (( $# > 0 )); then
62                 print -u2 'Usage: dirs [-lvn].'
63                 return 1
64         fi
65         if (( fv )); then
66                 fv=0
67                 while (( fv < ${#DIRSTACK[*]} )); do
68                         d=${DIRSTACK[fv]}
69                         (( fl )) && d=${d/#~/$DIRSTACKBASE}
70                         print -r -- "$fv        $d"
71                         let fv++
72                 done
73         else
74                 fv=0
75                 while (( fv < ${#DIRSTACK[*]} )); do
76                         d=${DIRSTACK[fv]}
77                         (( fl )) && d=${d/#~/$DIRSTACKBASE}
78                         (( dwidth = (${%d} > 0 ? ${%d} : ${#d}) ))
79                         if (( fn && (cpos += dwidth + 1) >= 79 && \
80                             dwidth < 80 )); then
81                                 print
82                                 (( cpos = dwidth + 1 ))
83                         fi
84                         print -nr -- "$d "
85                         let fv++
86                 done
87                 print
88         fi
89         (( isnoglob )) || set +o noglob
90         return 0
91 }
92 function popd {
93         typeset d fa
94         typeset -i isnoglob=0 n=1
95
96         [[ $(set +o) == *@(-o noglob)@(| *) ]] && isnoglob=1
97         set -o noglob
98         while getopts ":0123456789lvn" d; do
99                 case $d {
100                 (l|v|n) fa="$fa -$d" ;;
101                 (+*)    n=2
102                         break ;;
103                 (*)     print -u2 'Usage: popd [-lvn] [+<n>].'
104                         return 1 ;;
105                 }
106         done
107         shift $((OPTIND - n))
108         n=0
109         if (( $# > 1 )); then
110                 print -u2 popd: Too many arguments.
111                 return 1
112         elif [[ $1 = ++([0-9]) && $1 != +0 ]]; then
113                 if (( (n = ${1#+}) >= ${#DIRSTACK[*]} )); then
114                         print -u2 popd: Directory stack not that deep.
115                         return 1
116                 fi
117         elif [[ -n $1 ]]; then
118                 print -u2 popd: Bad directory.
119                 return 1
120         fi
121         if (( ${#DIRSTACK[*]} < 2 )); then
122                 print -u2 popd: Directory stack empty.
123                 return 1
124         fi
125         unset DIRSTACK[n]
126         set -A DIRSTACK -- "${DIRSTACK[@]}"
127         cd_csh "${DIRSTACK[0]}" || return 1
128         (( isnoglob )) || set +o noglob
129         dirs $fa
130 }
131 function pushd {
132         typeset d fa
133         typeset -i isnoglob=0 n=1
134
135         [[ $(set +o) == *@(-o noglob)@(| *) ]] && isnoglob=1
136         set -o noglob
137         while getopts ":0123456789lvn" d; do
138                 case $d {
139                 (l|v|n) fa="$fa -$d" ;;
140                 (+*)    n=2
141                         break ;;
142                 (*)     print -u2 'Usage: pushd [-lvn] [<dir>|+<n>].'
143                         return 1 ;;
144                 }
145         done
146         shift $((OPTIND - n))
147         if (( $# == 0 )); then
148                 if (( ${#DIRSTACK[*]} < 2 )); then
149                         print -u2 pushd: No other directory.
150                         return 1
151                 fi
152                 d=${DIRSTACK[1]}
153                 DIRSTACK[1]=${DIRSTACK[0]}
154                 cd_csh "$d" || return 1
155         elif (( $# > 1 )); then
156                 print -u2 pushd: Too many arguments.
157                 return 1
158         elif [[ $1 = ++([0-9]) && $1 != +0 ]]; then
159                 if (( (n = ${1#+}) >= ${#DIRSTACK[*]} )); then
160                         print -u2 pushd: Directory stack not that deep.
161                         return 1
162                 fi
163                 while (( n-- )); do
164                         d=${DIRSTACK[0]}
165                         unset DIRSTACK[0]
166                         set -A DIRSTACK -- "${DIRSTACK[@]}" "$d"
167                 done
168                 cd_csh "${DIRSTACK[0]}" || return 1
169         else
170                 set -A DIRSTACK -- placeholder "${DIRSTACK[@]}"
171                 cd_csh "$1" || return 1
172         fi
173         (( isnoglob )) || set +o noglob
174         dirs $fa
175 }