[comp.mail.mush] listing mail folders

loverso@Xylogics.COM (John Robert LoVerso) (09/19/89)

As a carry-over from my attempts to use MH over the years, I've always
had multi-level mail folders.  As a result, the `folders' command is
all but useless for me.  This is even more so with the advent of edit_hdrs.
I have the tendency to start writing a message, and then get bogged down
and have to leave it.  So, I just write it into +draft/xxx, to be resumed later
on (to resume, I start a new message, then ~r +draft/xxx and then delete the
stub headers).

So, how to list the contents of +draft, or any other folder for that matter?
Answer: a way-too-long-and-complicated shell script, which I use for listing
mail folders.  Among other things, it provides a form of file completion;
this is dependent upon the workings of EOF and sh read.  I'm come back
to that.

As a disclaimer, let me note that I work on a fairly unloaded, 8 processor
Multimax.  I.e., I've got cycles to burn.  This script relies upon that:
woe to the person who runs it on a 750.  The functionality the script
provides is of the kind that would be nice if a simpler version were built
into mush (I saw some mention of file completion in 7.0); but I like the
flexibility my script gives me.

I use this script anytime I want to access a mail folder.  My active ~/Mail
is 13Mb and growing (call me a pack-rat).  If I get a peice of mail, I
usually have long since forgotten where to file it.  For a long time, I
had multiple folders of similar items in different subfolders.  And I'd
end up saving half the messages in one, half in the other.  No more.  A
simple `fa' gets me a complete listing of folders.  Even better, the `file
completion' mechanism lets me poke and prod to find the right folder.
When I want to save a letter, I usually `fq' first to find the right
folder, and then just issue the write command.

How to use the psuedo "file completion":  At the "folder:" prompt, you
can just hit ^D (really your EOF char).  This will list all top level
folders.  Partial text can be typed followed by two EOFs (to force a "short
read" and EOF notificiation back to sh), which will list all folders (and
subfolders) matching the given text.  If there is only 1 possible match,
the text is expanded to that value.  Finally, if "typein" is available
(using TIOCSTI), the possibly expanded string is stuffed back to input.
If "typein" is not available, then the possibly expanded value is saved
such that if you hit return with no input, it will be the accepted input.

(It should be obvious that using typein gives a feeling like 4.3csh filec,
etc.  I've included it below; it's part of an input-line-editting package
- yes, redo.c is the source for typein.  I'm sorry, but you probably won't
get typein/redo to work under SysV.)
(BTW, I never got used to tcsh's way of input line editting)

The move-cursor-up-line-and-clear is only there to make calling from mush
look nicer.  Rip it out if it bothers you.

The script can be called many ways: there are pre-defined output formats and
the do-it-yourself variety.  I.e., `listfolder -quick' with list just top-level
folders with their size.  By adding in the building-block option `expand'
(listfolder -q x) it will give expanded folder names.  I normally use the
"-q x" and "-l x" options.  The "p" modifier is useful.  "-q x p" and "-q p"
are two ways of listing all folders with different output styles.  Only one
pre-defined (-) option can be given.  Because of the weird argument parsing,
it's not possible to give folder names on the command line (when calling from
within mush this isn't desireable, anyway).

Oh, I make heavy use of "rs".  This is a "reshape array" program that's been
part of user-contributed src to 4BSD since 4.2.  I guess any such program
would work.  The "-teg0" options say: "e": consider each line of input one
array element, "g0": use no gutter-width (inter-column spaces), and "t":
transpose rows/cols.  For the last, this causes the rs output to have the
sorted input be layed out such that its sorted going down rows instead of
across columns.  I use rs because its output shaping alg is better than ls'.

John
-- 
John Robert LoVerso			Xylogics, Inc.  617/272-8140
loverso@Xylogics.COM			Annex Terminal Server Development Group

enc
-- 

# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# listfolder redo.c

echo x - listfolder
sed -e 's/^X//' > "listfolder" << '//E*O*F listfolder//'
X#!/bin/sh
X#
X# List mail folders
X#
X# sample mush bindings:
X# (make sure you "unbind f" once and only once!)
X#	bind-macro f? '[shell-escape]listfolder\n'
X#	bind-macro fq '[shell-escape]listfolder -q x\n'
X#	bind-macro fl '[shell-escape]listfolder -l x\n'
X#	bind-macro fa '[shell-escape]listfolder -all\n'
X#	bind-macro ff '[shell-escape]listfolder'
X#
X# last change: Mon Sep 18 23:44:12 EDT 1989 jlv
X#
X
X# WARNING: spurious floating exceptions may be gotten from the equivalent of:
X#		rs -eg0 < /dev/null
X# I haven't looked into fixing rs.
X
X# this shouldn't be here (sigh)
Xcd ~/Mail
X
X# move up 1 line and erase it 	(should use `tput'; not all machines have it)
Xcase ${TERM-dumb} in
Xvt1*|vt2*|wy*|sun*|ansi|dmd*)
X	echo -n '[A[K' ;;
Xesac
X
X#
X# Map command line `options' to internal `commands'
X#
Xcase ${1--help} in
X-quick|-q)	set recurse $@ ;;
X-long|-l)	set recurse long $@ ;;
X-ls)		set recurse expand col $@ ;;
X-all)		set all noprompt nodir total $@ ;;
X-alllong|-al)	set recurse long noprompt nodir total $@ ;;
X-help|-*)	echo 'usage: listfolder [-switch] [keyword]...'
X		echo 'switches:	-Quick -Long -ls -all -ALllong'
X		set help ;;
Xesac
X
Xls="ls -F"
Xlsformat="-s"
Xlsargs=""
Xnototal="| grep -v '^total'"		# ok as long as -l or -s
Xnodir=""
Xrs="| rs -teg0"
X# my `sh' has true but not false builtin, so debug= is now nodebug=
X
Xfor select in ${@-help}
Xdo
X	case $select in
X	-*)		;;
X	debug)		nodebug=false echo select: "$@" ;;
X	short|s)	lsformat="-s" rs="| rs -teg0";;
X	long|l)		lsformat="-l" rs="" ;;
X	col|c)		lsformat="-C" rs="" ;;
X	expand|x)	expand=true default="*" ;;
X	recurse|r)	lsargs="$lsargs -R" ;;
X	all|a)		lsargs="$lsargs -d" default="* */*" ;;
X	nodir|d)	nodir="| grep -v /$" ;;
X	noprompt|p)	prompt=false ;;
X	total|t)	nototal="" ;;
X
X	help|*)
X		echo -n 'keywords:	eXpand {Short|Long|Col} {All|Recurse}'
X		echo ' noPrompt noDir Total'
X		exit 0
X		;;
X	esac
Xdone
X
X#
X# prompt for folder name, if required
X# simulate "file completion"
X# (if you don't have `typein', then comment calls to it)
X#
Xif ${prompt-true}
Xthen
X	while true
X	do
X		echo -n 'folder: '
X		# read returns EOF! (for ^D)
X		if read folder
X		then
X			if [ X"$folder" = X ]
X			then
X				folder=$old
X			fi
X			# should exist
X			[ "X`ls -d $folder`" != X ] && break
X		else
X			echo ""
X			if [ X"$folder" = X ]
X			then
X				showfolder=*
X			else
X				showfolder=`echo $folder |
X					tr ' ' '\012' |
X					sed -e 's;$;*;'`
X			fi
X			exp=`ls -dF $showfolder`
X			if [ `echo "$exp" | wc -w` -eq 1 ]
X			then
X				echo `echo "$exp"*`
X				typein "$exp"
X				old="$exp"
X			else
X				echo `echo "$exp"`
X				typein "$folder"
X				old="$folder"
X			fi
X		fi
X	done
Xfi
X
X$nodebug || echo I "<$folder>"
X
Xif [ X"$folder" = X -a X"$default" != X ]
Xthen
X	folder="$default"
Xfi
X
X$nodebug || echo F "<$folder>"
X
X#
X# expand folder names that are directories
X#
Xif ${expand-false}
Xthen
X	if [ X"$folder" != X ]
X	then
X		#folder=`echo "$folder" | tr ' ' '\012' | sed -e 's;$;/*;'`
X		folder=`ls -dF $folder |
X			# the two 'd's are ls-cleanup
X			sed -e '/\/@$/d' -e '/\*$/d' -e 's;/$;/*;' |
X			tr '\012' ' '`
X	else
X		folder=*
X	fi
Xfi
X
X$nodebug || echo XF "<$folder>"
X
Xeval $ls $lsformat $lsargs $folder $nototal $nodir $rs | ${PAGER-more}
X
Xexit 0
//E*O*F listfolder//

echo x - redo.c
sed -e 's/^X//' > "redo.c" << '//E*O*F redo.c//'
X/*
X * CSH Command Editting Package - C chunk
X *
X * redo tmpfile [cmd]
X * typein [args]...
X *
X * (redo and typein are hard links to the same file)
X *
X * "redo" outputs a shell script used to edit and re-exec a shell command
X * described by the history command "cmd" - if cmd absent, !! is assumed.
X * The shell script created looks like this:
X *      % redo ~/tmp/redo fg
X *	echo "!fg:q" >! /usr2/loverso/tmp/redo
X *	( ex +open /usr2/loverso/tmp/redo ; typein < /usr2/loverso/tmp/redo )
X *
X * "typein" uses TIOCSTI to stuff either its stdin or its arguments back into
X * the tty input stream.
X *
X * NOTE: if you use a non-standard history character, i.e. not !
X * then you must `setenv HISTCHARS $histchars' in your ".login".
X *
X * To setup the editting package, in your .cshrc:
X *
X *        set TMP=~/tmp/redo
X *        alias ! redo $TMP \!\* \&\& source $TMP
X *
X * If you don't have a ~/tmp, use
X *
X *        set TMP=/tmp/redo_$user
X *
X * then:
X *
X *        ! 25      to edit & re-execute !25
X *        ! v       to edit & re-execute !v
X *        ! !       to edit & re-execute !!
X *   (or) !         to edit & re-execute !!
X *
X * Bugs:
X *     This does not work with !?xx? substitutions, although I tried.
X *     Use:   % !?xx?:p
X *            % !
X *  
X * Originally by William P. Malloy <ittral!malloy>, 12/2/83
X * Worked over by John Robert LoVerso <loverso@Encore.COM>, 9/6/88
X */
X
X#include <stdio.h>
X#include <sgtty.h>
X#include <strings.h>
X
Xvoid
Xredo(argc, argv)
X	int argc;
X	char *argv[];
X{
X	char *histchar, *histstr, *getenv();
X	FILE *fp;
X
X	if (argc < 2 || (fp = fopen(argv[1], "w")) == NULL)
X		exit(2);
X	if ((histchar = getenv("HISTCHARS")) == NULL)
X		histchar = "!";
X	histstr = (argc < 3) || (strcmp(argv[2], histchar) == 0)
X			? "-2"
X			: argv[2];
X	fprintf(fp, "echo \"%c%s%s:q\" >! %s\n",
X		*histchar,
X		histstr,
X#ifdef QMARK
X		*histstr == '?' && histstr[strlen(histstr)] != '?'
X			? "?"
X			:
X#endif
X			"",
X		argv[1]);
X	fprintf(fp, "( ex +open %s ; ", argv[1]);
X	fprintf(fp, "typein < %s )\n", argv[1]);
X}
X
Xvoid
Xtypein(argc, argv)
X	int argc;
X	char **argv;
X{
X	struct sgttyb stb, stb2;
X	int pendin = LPENDIN;
X	char c;
X
X	(void)ioctl(1, (int)TIOCGETP, (char *)&stb);
X	stb2 = stb;
X	stb.sg_flags &= ~ECHO;
X	stb.sg_flags |= CBREAK;				/* to allow LPENDIN */
X	(void)ioctl(1, (int)TIOCSETN, (char *)&stb);
X	if (argc == 1)					/* from stdin? */
X		while ((c=getchar()) != EOF)
X			(void)ioctl(1, (int)TIOCSTI, (char *)&c);
X	else						/* from args? */
X		while (++argv, --argc)
X			while (**argv)
X				(void)ioctl(1, (int)TIOCSTI, (*argv)++);
X	(void)ioctl(1, (int)TIOCSETN, (char *)&stb2);
X	(void)ioctl(1, (int)TIOCLBIS, (char *)&pendin);
X}
X
Xmain(argc, argv)
X	int argc;
X	char *argv[];
X{
X	register char *cp;
X
X	cp = (cp = rindex(*argv, '/')) ? ++cp : *argv;
X	switch (*cp) {
X	case 'r':
X		redo(argc,argv);
X		break;
X	case 't':
X		typein(argc,argv);
X		break;
X	default:
X		exit(1);
X	}
X	exit(0);
X}
//E*O*F redo.c//

exit 0