[comp.unix.shell] Is there a smart pwd display program?

jbn35564@uxa.cso.uiuc.edu (J.B. Nicholson) (04/16/91)

I'm writing a function for KSH that shows you your current working directory
using tilde notation:

What you type:                    What you get from the function:
---------------------------------------------------------------------------
cd                                ~/
cd ~joeuser                       ~joeuser
cd /home/dir/of/joeuser           ~joeuser
cd ..                             /home/dir/of
cd /                              /
cd /home/dir/of/me/1/2/3          ~/1/2/3

So you get the idea.  It runs pretty fast, but I was wondering if there's a
program (written in some compiled language, or maybe even Perl or something)
that'll give you the same thing, that would probably be better to run than
my function because it would be faster.

I'm currently trying to add some stuff that'll make it seem faster and trying
to use only the quickest way of addressing things like if-then's and so
forth (I posted a note about this before).  I'll be glad to share my
function once I've finished it.

Thanks.

Jeff
--
+----------------------------------------------------------------------------+
| "If you hear an onion ring - answer it."                    J.B. Nicholson |
+----------------------------------------------------------------------------+
| jeffo@uiuc.edu (Internet)              These opinions are mine, that's all.|
+----------------------------------------------------------------------------+

dws@margay.cs.wisc.edu (DaviD W. Sanderson) (04/16/91)

In article <1991Apr15.170910.28160@ux1.cso.uiuc.edu> jeffo@uiuc.edu writes:
>I'm writing a function for KSH that shows you your current working directory
>using tilde notation:
[description deleted]

This is what I use as part of my setup.  It doesn't decide how to print
things out; it just sets the variable PSD so you can use it elsewhere
however you like.  It is quite fast, since it only uses "case".

#-------
# setPSD - sets PSD, the part of my current directory path, to put
# into my prompt.
#
# There is a small point about testing for directories under $HOME.
# One should NOT simply check whether $PWD matches $HOME*, as this
# will not tell you if $PWD is your home directory or a directory
# under $HOME.  (If HOME=/var/home/dws, and PWD=/var/home/dwsalterego,
# this heuristic would tell you that you were in the dws account
# when you were not.)  Instead, one should check whether $PWD
# matches $HOME or $HOME/*, which is what this routine does.
#
# The number of "/*" in _setPSD_strip determines the maximum
# number of elements in the directory to be displayed after
# it is trimmed.
#
# DaviD W. Sanderson (dws@cs.wisc.edu)
#-------
_setPSD_strip="/*/*/*"	# tailor as desired.
setPSD()
{
	_wd="${1:-$PWD}"
	case $_wd
	{
	$HOME)
		#-------
		# An exact match of $HOME is the simplest case.
		#-------
		PSD="~"
		;;
	$HOME/*)
		#-------
		# A directory under $HOME.
		#-------
		_wd="${_wd#$HOME}"
		PSD0="${_wd%$_setPSD_strip/*}"
		PSD="${_wd#$PSD0/*/}"
		PSD="~${PSD}"
		;;
	*)	
		#-------
		# Show up to the last N directories in PATH, where
		# N is the number of "/*" in _setPSD_strip.
		# PSD begins with / for directories up to
		# N levels deep, no / for directories further down.
		#-------
		PSD0="${_wd%$_setPSD_strip/*}"
		PSD="${_wd#$PSD0/*/}"
		case $PSD
		{
		/*)	;;
		*)	PSD=" $PSD";;
		}
		;;
	}
	unset _wd PSD0
}
-- 
       ___
      / __\  U N S H I N E	           DaviD W. Sanderson
     |  |  | I N E			    dws@cs.wisc.edu
_____|  |  |_____  ________
\      / \   |__/ /////__	Fusion Powered Locomotives Made to Order
 \____/   \__|_/  \\\\\______	 (TARDIS model available at extra cost)

bjaspan@athena.mit.edu (Barr3y Jaspan) (04/18/91)

In article <1991Apr15.170910.28160@ux1.cso.uiuc.edu>, jbn35564@uxa.cso.uiuc.edu (J.B. Nicholson) writes:
|> I'm writing a function for KSH that shows you your current working directory
|> using tilde notation:
|>
|> [ examples deleted ]

You might be interested in a hack I wrote called (surprise!) 'prompt'. 
Essentially, it takes a list of path/replacement pairs and replaces the
matching part of any path with its replacement.  It also truncates the final
path to a specified number of components.  Here is how I use it (with csh or
tcsh):

alias pcmd '~bjaspan/${bindir}/prompt 4 $cwd $home 0 ~ 
  /afs/athena.mit.edu/user/b/bjaspan 0 ~ /afs/athena.mit.edu/user 3 ~
  /afs/sipb.mit.edu/project/sipbsrc 1 "sipbsrc: "'
alias cd_magic 'set noglob && set prompt = "<%m> `pcmd`% " && unset
  noglob
alias cd 'cd \!* && cd_magic'
alias pushd 'pushd \!* && cd_magic'
alias popd 'popd \!* && cd_magic'

(Obviously, we use AFS here.)  The arguments to prompt mean:
	o only display a total of four components of the final path, after
	  replacement
	o $cwd is in the alias literally, so it gets expanded at each 
	  invokation, thus passing the current working dir to the program
	
The rest are actually triples, "path-offset-replacement".  Any current path
beginning with "path" is replaced by "replacement", and "offset" characters
are deleted from the remaining componenets (if there aren't that many
characters left, nothing happens).  Here is a transcript demonstrating the
program's behavior (with commentary):

~% cd
# $home is replaced by ~ with 0 chars deleted
~% cd src
# only the specified components are removed from the path
~/src% cd /afs/sipb.mit.edu/project/sipbsrc/
# offset==1 for sipbsrc, and since there aren't that many more characters,
# nothing happens here
/afs/sipb.mit.edu/project/sipbsrc% cd src
# Now there are more characters, so 1 (the leading /) is deleted.
sipbsrc: src% cd lib/elisp/gnus-dist/attic
# Only four components are shown
sipbsrc: .../lib/elisp/gnus-dist/attic% cd /afs/athena.mit.edu/user
# /afs/athena.mit.edu/user deletes three characters, and there aren't that many
/afs/athena.mit.edu/user% cd t
# Still not enough characters
/afs/athena.mit.edu/user/t% cd tytso
# Now there are, so replace the text with ~ and delete the 3 chars
~tytso%

(Note for the sharp eyed:  yes, there is a <%m> in the prompt to display the
hostname, and yes, it works.  I deleted it so everything would fit on one
line.)

----------------------------------------------------------------------

Anyway, the best thing about this program is its size -- about 3K on a vax
running bsd (and similarly small on other machines).  It uses one syscall
(write) and one library function (strlen).  It provides its own bcopy() in
case yours doesn't do overlapping regions (although, clearly, any bcopy
should, and could probably be done better in assembly).

Note: your mileage may vary.  If you find problems, feel free to tell me but I
may or may not care to fix them.  It works for me.  :-)  Oh, and I don't
usually read this newsgroup...

Barr3y Jaspan, bjaspan@mit.edu
Watchmaker Computing

---- snip snip ----

#define MHT	10
#define USAGE	"Usage: prompt max_dirs cwd [ hometop offset text ... ]\n"

int left_bcopy(src, dest, len)
   char	*src, *dest;
   int	len;
{
     while (len--)
	  *dest++ = *src++;
}

main(argc, argv)
   int	argc;
   char	**argv;
{
     char	*cwd, *hometop[MHT], *hometop_text[MHT];
     int	cwd_len, hometop_len[MHT], hometop_offset[MHT];
     int	hometop_num, i, max_dirs, count;

     if (argc < 3) {
	  write(1, USAGE, strlen(USAGE));
	  exit(1);
     }

     hometop_num = i = 0;
     max_dirs = atoi(*++argv);
     cwd = *++argv;
     cwd_len = strlen(cwd);

     while ((hometop_num < MHT) && (hometop[hometop_num] = *++argv)) {
	  hometop_offset[hometop_num] = atoi(*++argv);
	  hometop_text[hometop_num] = *++argv;
	  hometop_len[hometop_num] = strlen(hometop[hometop_num]);
	  ++hometop_num;
     }

     while (i < hometop_num) {
	  if (strncmp(hometop[i], cwd, hometop_len[i])==0 &&
	      (cwd_len >= hometop_len[i]+hometop_offset[i])) {
	       left_bcopy(cwd+hometop_len[i]+hometop_offset[i], cwd,
			  cwd_len-hometop_len[i]);
	       cwd[cwd_len-hometop_len[i]] = '\0';
	       cwd_len = strlen(cwd);
	       write(1, hometop_text[i], strlen(hometop_text[i]));
	       break; }
	  ++i;
     }

     if (max_dirs) {
	  for (i=cwd_len, count=0; i>0; i--) {
	       if (*(cwd+i) == '/' && (++count == max_dirs)) {
		    write(1, "...", 3);
		    write(1, cwd+i, cwd_len-i);
		    return 0; }
	  }
     }

     write(1, cwd, cwd_len);
     return 0;
}





-- 
Barr3y Jaspan, bjaspan@mit.edu