[comp.sources.misc] v07i109: kashe - change directory by menu

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (07/29/89)

Posting-number: Volume 7, Issue 109
Submitted-by: jack@cs.glasgow.ac.uk
Archive-name: kashe

The following stuff saves me a LOT of typing directory names.  Needs the
Korn shell.  I've been using it unchanged for months, but it's only been
tested on Sun 3 machines.  Much easier to use than to describe.

RTFM before trying to use it - it helps to have 'lc', and your environment
needs to be set up right.  The details on using it if ksh isn't your login
shell are there to help proselytize for ksh among users of other shells.

best wishes - jack

#--------------------------------CUT HERE-------------------------------------
#! /bin/sh
#
# This is a shell archive.  Save this into a file, edit it
# and delete all lines above this comment.  Then give this
# file to sh by executing the command "sh file".  The files
# will be extracted into the current directory owned by
# you with default permissions.
#
# The files contained herein are:
#
# -rw-r-----  1 jack         9439 Jul 28 19:10 kashe.1
# -rw-r-----  1 jack         6819 Jul 28 19:24 .kshenv
#
echo 'x - kashe.1'
if test -f kashe.1; then echo 'shar: not overwriting kashe.1'; else
sed 's/^X//' << '________This_Is_The_END________' > kashe.1
X.TH KASHE 1 "7 January 1989" "Jack Campin" "KSH(1) FUNCTIONS"
X
X.SH NAME
X
Xu, d, c, g, p, csort, cdrop, cwipe, csave, cload, cgrep, chelp \- quicker ways
Xto change directory
X
X.SH SYNOPSIS
X
X.B u
X.LP
X.B d
X[
X.I directory
X]
X.LP
X.B c
X[
X.I directory
X]
X.LP
X.B g
X.I number
X.LP
X.B p
X.I pattern
X.LP
X.B csort
X.LP
X.B cdrop
X.LP
X.B cwipe
X[
X.I pattern
X]
X.LP
X.B csave
X[
X.I file
X]
X.LP
X.B cload
X[
X.I file
X]
X.LP
X.B cgrep
X.I pattern
X.LP
X.B chelp
X
X.SH DESCRIPTION
X
XThis is a collection of Korn shell (
X.I ksh (1)
X) functions I wrote after having to
Xswitch between different directories a lot.  I am a pretty hopeless typist,
Xand even with
X.I ksh's
Xcommand-edit-and-retry features, it was getting boring
Xentering something like
X
X.br
X	cd /bakcuppisa/users/jack/oscar/parse
X
Xand then having to change the "kc" to "ck".
X
XSo these functions give you a zippy menu interface to
X.I cd.
XTheir rationale is that most people most of the time only switch between a
Xrelatively small "working set" of directories; these functions are intended to
Xmake this set easily accessible.  By far the most frequent commands in Unix are
X.I cd, pwd,
Xand
X.I ls;
Xthese functions improve the interface to all of these.  I use them together
Xwith a prompt line that displays my current directory, hence the "<pathname>\ $
X" prompts below.  The functions fall into two groups; the first maintains a
Xcache of "interesting" directories, which you might use like this (assuming
Xthe 3 directories below are in the cache already):
X
X.br
X	/pisa/users/jack $ c
X.br
X	1) /usr/spool/news/comp/binaries/mac
X.br
X	2) /usr/lib/tex
X.br
X	3) /backuppisa/users/jack
X.br
X	4) New...
X.br
X	number? 1
X.br
X	/usr/spool/news/comp/binaries/mac $
X
XThose directories might have got there by the following session, starting from
Xempty:
X
X.br
X	/pisa/users/jack $ c
X.br
X	directory? /usr/spool/news/comp/binaries/mac
X.br
X	/usr/spool/news/comp/binaries/mac $ cd /usr/lib
X.br
X	/usr/lib $ c tex
X.br
X	/usr/lib/tex $ cd /backuppisa/users/jack
X.br
X	/backuppisa/users/jack $ c
X.br
X	1) /usr/spool/news/comp/binaries/mac
X.br
X	2) /usr/lib/tex
X.br
X	3) New...
X.br
X	number? 3
X.br
X	directory? .
X.br
X	/backuppisa/users/jack $ cd
X.br
X	/pisa/users/jack $
X
XExtra items can be added at any time.  Directories can be specified any way
Xyou like, including tilde aliases.  Directories in the cache are represented
Xby absolute pathname.
X.PP
XThus
X.B c
Xhas three modes of operation -
X.I cd
Xto a cached directory by menu; add a new one to the cache when prompted and
X.I cd
Xto it; or add
X.I directory
Xto the cache and
X.I cd
Xto it, sidestepping the menu.  Only valid directories can be added this way.
XThus you can use it where you would previously have used
X.I cd,
Xto load your cache.
X.PP
XThere is also a "goto" for use when you know the menu item number you want
X(typically, just after you've typed the wrong number to
X.B c
Xand still have the menu in front of you):
X
X.br
X	/pisa/users/jack $ g 2
X.br
X	/usr/lib/tex $
X
XAnd a "goto by pattern" which scans the cache looking for a match:
X
X.br
X	/usr/lib/tex $ p mac
X.br
X	/usr/spool/news/comp/binaries/mac $
X
X.B  p
Xwill give you a menu if more than one directory in the cache matches the
Xpattern.  The pattern syntax is as for
X.I egrep (1).
XYou will usually be able to find a very short string that uniquely identifies
Xyour target.  I find this more useful than
X.B g.
X.PP
X.B csort
Xsorts the cache, eliminating duplicated entries.
X.B cdrop
Xdeletes directories from it; enter the numbers of the items to delete when
Xprompted.
X.B cwipe
Xwill either clear it completely, or, if given a pattern argument, will just
Xremove items matching that pattern.
X.B csave
Xsaves the cache to a file.
X.B cload
Xreloads it, appending the file to your current cache.
X.PP
XA less generally useful function in the same group,
X.B cgrep,
Xtakes a string and adds to the
Xcache all accessible directories below your current directory whose pathnames
Xhave a last component containing that string.  It then gives you a menu
Xlike
X.B c.
XIt's intended for finding your way around pieces of software
Xthat scatter themselves in undocumented places all over the file system but use
Xrelated names for all those places.  You won't use this often, and it's slow
X(because of the
X.I ls -R
Xembedded in it) but it will save you much frustration with
X.I find
Xand the like on the rare occasions when you want
Xit.  The string is an
X.I egrep
Xpattern.  Since
X.B cgrep
Xembeds the string in a larger search pattern, some patterns will not work, in
Xparticular those containing "^" or "$" as unescaped metacharacters, or "/".
X.B cgrep
X"" puts the entire tree below the current directory into the cache.
X.PP
XYou can ^C out of a menu selection leaving your directory unchanged.  The
Xcache is kept in a shell variable, so changes to it will not persist when
Xreturning from a subshell unless you
X.B csave
Xbefore exiting and then
X.B cload
Xin the outer shell.
X.PP
XThe second group is a pair of functions that tries to emulate the way you move
Xaround the file system on the Mac - a function
X.B u
Xto go up and
X.B d
Xto go down.  (Originally inspired by my infuriatingly frequent typing of "cd.."
Xfor "cd ..").  It's usually a lot more convenient to use
X.B d
Xin "unknown territory" than to manually list subdirectories and then do
X.I cd;
Xfor one thing,
X.B d
Xfilters out non-directories from its display.  If the target directory has no
Xsubdirectories,
X.B d
Xacts like
X.I cd.
XThere are two alternative forms of
X.B d
Xprovided depending on whether the fast file lister
X.I lc,
X(written by gamiddleton@watmath.uucp and posted to comp.sources.unix in
X1987) is available; comment out the one you don't want.
X.PP
XFinally,
X.B chelp
Xprints out a command synopsis.
X.PP
XBy using both groups of functions, you can move to any point in the file
Xsystem without ever typing a directory name, and get back to it from anywhere
Xwith four or five keystrokes.
X
X.SH ENVIRONMENT
X
XTo get all these into your environment, put them into a file called, say,
X.I .kshenv.
XThe Korn shell finds this file from the variable
X.B ENV.
X(
X.B chelp
Xuses
X.B ENV
Xto find the text it displays.)  The file read or written by
X.B cload
Xand
X.B csave
Xis specified by the filename parameter if one is given; if not, by
X.B KSHDIRFILE
Xif that is set; and as a default, the file
X.I .kshenvdirs
Xin the home directory (this is OK if you always use the same domain).  These
Xfunctions use a shell variable
X.B NL
Xwhich should always contain a newline.  This is simply to make the code easier
Xto read.
X.PP
XHow you set up this environment depends on what
Xyour login shell is.
X
X.PP
X.B Korn shell:
Xadd the lines
X
X.br
X	ENV=.kshenv \ \ export ENV
X.br
X	KSHDIRFILE=~/.`domainname`-dirs \ \ export KSHDIRFILE
X
Xto your
X.I .profile.
XTo get the prompt line, also add
X
X.br
X	PS1='${PWD} $' \ \ export PS1
X.PP
XTo preload the cache at login time, also add to your
X.I .profile
Xthe lines
X
X.br
X	.\ \ $ENV
X.br
X	cload
X
X.PP
X.B C shell:
Xadd the lines
X
X.br
X	setenv ENV .kshenv
X.br
X	setenv KSHDIRFILE \ \  ~/.`domainname`-dirs
X
Xto your
X.I .login
Xand add
X
X.br
X	PS1='${PWD} $' \ \ export PS1
X
Xto your
X.I .kshenv.
X
X.PP
X.B Bourne shell:
Xadd the lines
X
X.br
X	ENV=.kshenv \ \ export ENV
X.br
X	KSHDIRFILE=~/.`domainname`-dirs \ \ export KSHDIRFILE
X
Xto your
X.I .profile
Xand add
X
X.br
X	PS1='${PWD} $' \ \ export PS1
X
Xto your
X.I .kshenv.
X
X.SH FILE FORMAT
X
XA cache file is simply a list of full pathnames of directories terminated by
Xnewlines.
X
X.SH DIAGNOSTICS
X
XError messages are printed if these functions are called with the wrong number
Xof parameters, if files can't be read or written, or if commands used by the
Xfunctions fail.  The exit code returned is 1 when a failure or a signal is
Xdetected, otherwise 0.
X
X.SH BUGS
X
XIf you have more directories in the cache than will fit on the screen
X(this might happen after using
X.B cgrep
X) you'll have problems seeing them with
X.B c
Xor
X.B cdrop.
XThere is no way to avoid this as far as I know.  The workaround is to
X.B csave,
Xedit the file, and then
X.B cload
Xagain.
X.PP
XYou will sometimes get insane error messages if you enter random
Xstrings instead of numbers.  This is
X.I ksh's
Xfault and nothing can be done about it.
X.PP
XThese functions don't handle directory names with embedded newlines.  Embedded
Xspaces are OK, though; embedded tabs are invisible in the menus but otherwise
Xseem to work (and if you embed tabs in directory names you deserve to have
Xproblems, anyway).
X.PP
XIf anyone has bug reports or ideas for improvement, let me know.  I suspect
Xsymbolic links may not be handled quite right, but haven't found a definite
Xbug there yet.  The same ideas can be used for maintaining caches of strings
Xin general; it would not be trivial to modify these into a general-purpose
Xstring cache manager but might be worth doing someday.
X.PP
XAn undo facility would be easy enough to add; anyone want it?
X
X.SH KLUDGE
X
XYou need multiple cache files if you log in from more than one server, given
Xthe inconsistencies in Glasgow's present NFS naming scheme - the same
Xdirectory has pathname
X.I /pisa
Xfrom a login on vanuata and
X.I /vanuata.pisa
Xfrom a login on hawaii.  Hence the need for the
X.B KSHDIRFILE
Xvariable, to say which is the right cache file for the domain you're logged
Xin to.  If you only ever use one domain you can ignore this.
X
X.SH SECTARIAN BRAG
X
XYou don't have a prayer of adapting these to the antediluvian C or Bourne
Xshells while still getting useful speed.
X
X.SH AUTHOR
X
XJack Campin, Glasgow University Computing Science Department, 17 Lilybank
XGardens, Glasgow G12 8QQ, Scotland (jack@cs.glasgow.ac.uk)
________This_Is_The_END________
if test `wc -c < kashe.1` -ne 9439; then
	echo 'shar: kashe.1 was damaged during transit (should have been 9439 bytes)'
fi
fi		; : end of overwriting check
echo 'x - .kshenv'
if test -f .kshenv; then echo 'shar: not overwriting .kshenv'; else
sed 's/^X//' << '________This_Is_The_END________' > .kshenv
X# ".kshenv" (or rename to whatever ENV is set to).
X
X# An assortment of functions for moving around the file
X# system with fewer keystrokes, using a menu interface.
X
X# Jack Campin, 1988
X
X##  u             - up
X##  d [directory] - down [from "directory"]
X##  c [directory] - cd to a cached (or new) directory by menu,
X##                  or add "directory" to the cache and cd to it
X##  g number      - cd to a cached directory by number rather than menu
X##  p pattern     - cd to a cached directory containing "pattern"
X##  csort           - sort directory cache, removing duplicate entries
X##  cdrop           - remove entries from cache; enter number(s) when prompted
X##  cwipe [pattern] - empty the cache, or remove the items matching "pattern"
X##  csave [file]    - write cache to a file, default $KSHDIRFILE or .kshenvdirs
X##  cload [file]    - read cache from a file, default $KSHDIRFILE or .kshenvdirs
X##  cgrep pattern   - cd to a menu-given directory below the current directory
X##                    whose last component contains "pattern", adding all such
X##                    directories to the cache
X##  chelp           - print this synopsis
X
X# These use a cache of newline-separated directories in a variable KSHENVDIRS.
X
Xalias -t awk cat egrep lc ls sed sort tr  # speeds things up a bit
X
XNL="
X"  export NL	# newline, used for IFS; variable used for readability
X
Xfunction c {
X	typeset ifs="$IFS"  ps3="$PS3"
X	trap 'IFS="$ifs" ;  PS3="$ps3"  ;  return 1'  1 2 3 4 5 6 7 8 14 15
X	typeset j  i  val=1  ;  IFS="$NL"
X	if	[ $# != 0 ]
X	then	j=$1
X	else	if	[ "$KSHENVDIRS" != "" ]
X		then	PS3="number? "
X			select i in $KSHENVDIRS New...
X			do	[ "$i" = "New..." ]        &&  break
X				test -n "$i"  &&   cd $i   ||  continue
X				IFS="$ifs"  ;  PS3="$ps3"  ;   return 0
X			done
X		fi
X		read j?"directory? "
X	fi
X	if	[ "$j" != "" ]	# not interrupted while reading
X	then	if	eval cd $j	# makes tilde substitution work
X		then	# avoid introducing a duplicate
X			typeset x="$PWD"
X			for i in $KSHENVDIRS
X			do	[ "$x" = "$i" ]  &&  x=""  &&  break
X			done
X			KSHENVDIRS="$KSHENVDIRS$x$NL"  export KSHENVDIRS
X			val=0
X		fi
X	fi
X	IFS="$ifs"  ;  PS3="$ps3"  ;  return $val
X}
X
Xfunction g {
X	typeset ifs="$IFS"
X	trap 'IFS="$ifs"  ;  return 1'  1 2 3 4 5 6 7 8 14 15
X	IFS="$NL"
X	if	[ $# != 1 ]
X	then	print "Usage: g <number>" 1>&2
X	else	typeset -i n=0  ;  typeset j
X		for j in $KSHENVDIRS
X		do	(( n = $n + 1 ))
X			[ $n = $1 ]  ||  continue
X			cd $j  &&  IFS="$ifs"  &&  return 0
X			IFS="$ifs"  ;  return 1
X		done
X		print "g: no directory number $1" 1>&2
X	fi
X	IFS="$ifs"  ;  return 1
X}
X
Xfunction p {
X	[ $# != 1 ]    &&  print "Usage: p pattern" 1>&2  &&  return 1
X	[ "$1" = "" ]  &&  print "p: null argument" 1>&2  &&  return 1
X	typeset ifs="$IFS"  ps3=$PS3
X	trap 'IFS="$ifs"  ;  PS3="$ps3"  ;  return 1'  1 2 3 4 5 6 7 8 14 15
X	typeset i val=1 dirs="`print "$KSHENVDIRS" | egrep "$1"`"  ;  IFS="$NL"
X	if	[ "$dirs" = "" ]
X	then	print "p: no match found for '""$1""'" 1>&2
X	else	set $dirs
X		if	[ ${#*} = 1 ]
X		then	cd $dirs  ;  val=0
X		else	PS3="number? "
X			select i in $dirs
X			do	cd $i  &&  val=0  &&  break
X			done
X		fi
X	fi
X	IFS="$ifs"  ;  PS3=$ps3
X	return $val
X}
X
Xfunction csort {
X	typeset ifs="$IFS"
X	trap 'IFS=" "  ;  return 1'  1 2 3 4 5 6 7 8 14 15
X	IFS="$NL"
X	KSHENVDIRS=`print "$KSHENVDIRS" | sort -u`$NL   export KSHENVDIRS
X	IFS="$ifs"
X}
X
Xfunction cdrop {
X	[ "$KSHENVDIRS" = "" ]   &&  print "cdrop: nothing to drop" 1>&2  &&  return 0
X	typeset ifs="$IFS"  ps3="$PS3"  i
X	trap 'IFS="$ifs"  ;  PS3="$ps3"  ;  return 1'  1 2 3 4 5 6 7 8 14 15
X	PS3="numbers to drop? "  ;  IFS="$NL"
X	select i in $KSHENVDIRS
X	do	typeset -i m=0  ;  typeset y="$KSHENVDIRS"
X		for m in `print $REPLY | tr ' \011' '\012'`  # sidestep IFS
X		do	typeset x=""  ;  typeset j  ;  typeset -i n=0
X			for j in $y
X			do	(( n = $n + 1 ))
X				if	[ $m = $n ]
X				then	x="$x@$NL"	# tombstone value
X				else	x="$x$j$NL"
X				fi
X			done
X			y="$x"
X		done
X		typeset tmp=""
X		for i in $y	# edit out the tombstones
X		do	[ $i != "@" ]  &&  tmp="$tmp$i$NL"
X		done
X		KSHENVDIRS="$tmp"  export KSHENVDIRS
X		IFS="$ifs"  ;  PS3="$ps3"  ;  break
X	done
X}
X
Xfunction csave {
X	typeset x
X	case $# in
X	0)	if	[ "$KSHDIRFILE" != "" ]
X		then	x=$KSHDIRFILE
X		else	x=~/.kshenvdirs
X		fi  ;;
X	1)	x=$1  ;;
X	*)	print "Usage: csave [filename]" 1>&2  ;  return 1  ;;
X	esac
X	print "$KSHENVDIRS" > $x
X}
X
Xfunction cload {
X	typeset x
X	case $# in
X	0)	if	[ "$KSHDIRFILE" != "" ]
X		then	x=$KSHDIRFILE
X		else	x=~/.kshenvdirs
X		fi  ;;
X	1)	x=$1  ;;
X	*)	print "Usage: cload [filename]" 1>&2  ;  return 1  ;;
X	esac
X	if	test -r $x
X	then	typeset i  here="$PWD"  tmp="$KSHENVDIRS"  ifs="$IFS"
X		trap 'IFS="$ifs"  ;  return 1'  1 2 3 4 5 6 7 8 14 15
X		IFS="$NL"
X		for i in `cat $x`
X		do	cd "$i"  &&  tmp="$tmp$i$NL"
X			cd "$here"
X		done
X		KSHENVDIRS="$tmp"  export KSHENVDIRS
X	else	print "cload: $x unreadable" 1>&2  ;  IFS="$ifs"  ;  return 1
X	fi
X	IFS="$ifs"
X}
X
Xfunction cwipe {
X	case $# in
X	0)	KSHENVDIRS="" export KSHENVDIRS  ;  return 0   ;;
X	1)	typeset ifs="$IFS"  ;  IFS="$NL"
X		trap 'IFS="$ifs"  ;  return 1'  1 2 3 4 5 6 7 8 14 15
X		typeset i  j  y="$KSHENVDIRS"  tmp=""
X		for j in `print "$KSHENVDIRS" | egrep $1`
X		do	typeset x=""
X			for i in $y
X			do	if	[ "$i" = "$j" ]
X				then	x="$x@$NL"
X				else	x="$x$i$NL"
X				fi
X			done
X			y="$x"
X		done
X		for i in $y	# edit out tombstones
X		do	[ "$i" = "@" ]  ||  tmp="$tmp$i$NL"
X		done
X		KSHENVDIRS="$tmp" export KSHENVDIRS
X		IFS="$ifs"  ;;
X	*)	print "Usage: cwipe [pattern]" 1>&2  ;  return 1  ;;
X	esac
X}
X
Xfunction cgrep {
X	typeset ifs="$IFS"  ps3="$PS3"
X	trap 'IFS="$ifs"  ;  PS3="$ps3"  ;  return 1'  1 2 3 4 5 6 7 8 14 15
X	if	[ $# != 1 ]
X	then	print "Usage: cgrep pattern" 1>&2  ;  return 1
X	fi
X	typeset x="`ls -R		|
X		sed -n '/:$/s/://p'	|
X		egrep '(.*/)*.*'"${1}"'.*([~/])*$'`"
X	typeset i  y=""  here="$PWD"  ;  PS3="number? "  IFS="$NL"
X	for i in $x
X	do	cd $i  &&  y="$y$PWD$NL"  &&  cd $here
X	done
X	KSHENVDIRS="$KSHENVDIRS$y"  export KSHENVDIRS
X	select i in $KSHENVDIRS
X	do	cd $i  &&  break
X	done
X	IFS="$ifs"  ;  PS3=$ps3
X}
X
Xfunction u {
X	cd ..
X}
X
Xfunction d {
X	typeset ifs="$IFS"  ps3="$PS3"  tmp="$PWD"  val=1  i
X	trap 'IFS="$ifs" ; PS3="$ps3" ; cd "$tmp" ; return 1' 1 2 3 4 5 6 7 8 14 15
X	case $# in
X	0)	;;
X	1)	cd "$1"  ||  return 1  ;;	# test if target is a directory
X	*)	print "Usage: d [directory]" 1>&2  ;  return 1  ;;
X	esac
X	PS3="number? "  ;  IFS="$NL"
X	select i in `lc -d1`
X# slower alternative if "lc" is not available
X# the gross pattern is (empirically) faster than the obvious one
X#	select i in `ls -al |
X#		sed -n '/^d.*/{
X#			s/[^ ]*[ ]*//
X#			s/[^ ]*[ ]*//
X#			s/[^ ]*[ ]*//
X#			s/[^ ]*[ ]*//
X#			s/[^ ]*[ ]*//
X#			s/[^ ]*[ ]*//
X#			s/[^ ]*[ ]*//
X#			s/^\.*$//
X#			p
X#			}'`
X	do	[ "$i" != "" ]  &&  cd $i  &&  val=0  &&  break
X	done
X	IFS="$ifs"  ;  PS3=$ps3  ;  return $val
X}
X
Xfunction chelp {
X	sed -n '/^##/s/^##//p' < $ENV
X}
________This_Is_The_END________
if test `wc -c < .kshenv` -ne 6819; then
	echo 'shar: .kshenv was damaged during transit (should have been 6819 bytes)'
fi
fi		; : end of overwriting check
exit 0