[comp.sources.unix] v12i004: vmail - screen-based mail handler, Part01/03

jz@mulga.oz.au (Justin Zobel) (10/14/87)

Submitted by: jz@mulga.oz.au
Posting-number: Volume 12, Issue 4
Archive-name: vmail/Part01

This is the first of three parts of vmail, an interactive mail
handler that sits on top of MH.
    vmail has a number of advantages over raw MH.  It is screen-based and
faster (and more convenient) than the MH show-scan-rmm refile cycle.
vmail makes it feasible for users to organise and keep track of moderate
volumes of mail without wasting too much time, and is very simple to use.
It has been in use at Melbourne University Computer Science Department
for about six months without any problems, and has become the interface of
choice for many users.

: ---------------------------------------- cut here

echo x - "READ_ME" 2>&1
sed "s/^X//" >"READ_ME" <<'!The!End!'
XVMAIL is copyright (C) J. Zobel, University of Melbourne, October 1987.
XPermission to copy and modify vmail is given in full, but copies and
Xmodified copies must contain this copyright notice.
X
X    vmail is an interactive mail handler that sits on top of MH6.5.
XBecause vmail updates the MH environment by updating .mh_sequences,
Xit is not compatible with early versions of MH, although it should
Xwork with MH5 and possibly MH4.
X
X    vmail has a number of advantages over raw MH.  It is screen-based and
Xfaster (and more convenient) than the MH show-scan-rmm refile cycle.
XThe `scan' pages for the specified folders are loaded at start up time;
Xfurther folders may be loaded at any time.  `scan' is replaced by paging
Xbetween screens of stored information, `show' by a fork() to a pager,
X`rmm' and `refile' by calls to rename (2).  Other features, such as folder
Xpacking, are also faster, as most of the required information is already
Xknown to the process.  vmail makes it feasible for users to organise and
Xkeep track of moderate volumes of mail without wasting too much time, and
Xis very simple to use.  It has been in use at Melbourne University Computer
XScience Department for about six months without any problems, and has
Xbecome the interface of choice for many users.  Users who (being new to
XUNIX) were introduced to mail with vmail have never tried the MH commands
Xthemselves, being satisfied with the vmail interface and wanting to avoid
Xthe plethora of MH functions and usages.
X
X
XTechnical Notes
X
X    vmail is currently Berkeley specific, in particular use of signal(),
Xvfork(), ioctl() and union wait.  It should work with most versions of MH,
Xbut makes assumptions about the structure of folders, ie, that a folder is
Xa directory containing files whose names are numbers (other files are
Xignored).  By default, a .mh_profile is required to specify alternatives
Xto other defaults such as name of mail directory; the environment variable
XMH may be used to set an alternative profile.  The usual assumptions are
Xmade about the format of mail items - use of fields (if present) such as
X"To:", "From:" and so on, but these also should not cause any difficulties.
X    comp, repl and forw are all forked from vmail rather than imitated,
Xthere being no real time advantage to doing otherwise, but are given by
Xsingle-letter commands rather than longer commands to a shell.  Most MH
Xoptions as set in the profile are ignored (most are not relevant);
Xbecause comp, repl, etc are forks rather than built-ins, this is not
Xreally a problem.  The paths for these executables are in defs.h and
Xmay have to be changed.
X    The most unreliable feature is probably the parsing of inc output in
Xinc.c, used when `i' is given to incorporate new mail.  Because some people
Xlike to give options to inc in their profile, and because inc was too long
Xto consider rewriting, the simplest solution was to parse the inc output -
Xif the format changed this code would break.
X    Some may find the mixture of stdio, curses and ioctl ugly, but it is
Xreasonably orthogonal - stdio is used when tty is in normal mode, curses
Xwhen control is required, and ioctl and signal when processes are being
Xsuspended, woken or forked.
X
XMade with "make" (no arguments).  If cc does not recognize -k option,
Xvalue of make variable STACK must be changed.  The compiler option
X-DUSDATE must be set if dates are to be displayed in US format.
X
XFiles
X
X	READ_ME			This file.
X	vmail.1			Manual entry.
X	Makefile		Version number.
X
X	defs.h			Pathnames, defaults, structs, externs,
X				constants.  Some of these will need to be
X				edited, eg locations of MH routines.
X	macro.h			Simple functions.
X
X	call.c			Forks to MH functions, editor, shell.
X	choose.c		Folder chooser, folder display.
X	cmds.c			Assorted commands.
X	find.c			Searching through headers for strings.
X	inc.c			Handles fork to inc, parsing of inc output.
X	init.c			Startup, interrupt handling.
X	load.c			Loading of folders/mail from mail directory
X				into internal structures.
X	low.c			Some primitive functions.
X	main.c			High-level switch.
X	move.c			Commands for refile, delete, undo.
X	page.c			Commands associated with movement between
X				pages and movement within a page.
X
XNB: All source files are formatted with tabstop=4.
X
X===============================================================================
X
XI would appreciate hearing about bugs/bug fixes/amendments/improvements/
Xadditions; any correspondence should be sent to
X
XUUCP:	...!munnari!mulga!jz
XARPA:	jz%mulga.oz@uunet.uu.net
XCSNET:	jz%mulga.oz@australia
X
XAny modifications received will be considered for a future release of
Xvmail.  Of particular interest is SYS V compatibility and modifications
Xto suit versions of MH to which I have no access.
!The!End!

echo x - "vmail.1" 2>&1
sed "s/^X//" >"vmail.1" <<'!The!End!'
X.TH VMAIL 1 "30 September 1987"
X.UC Melb
X.SH NAME
Xvmail \- tty interface to MH
X.SH SYNOPSIS
X\fBvmail\fR [\-inc] [\-flush] [\-comp] [\-forw] [\-ans]
X[+cur_folder] folders ...
X.SH DESCRIPTION
X\fIvmail\fR is a tty interface to the MH mail system.
XIt combines most of the MH features into a single package, and performs
Xthe most frequently-used functions considerably faster than the
XMH equivalents.
XIn particular, a folder is only scanned once in a \fIvmail\fR
Xsession; the slow scan-show-rmm-scan cycle of MH is eliminated.
XAlso, \fIvmail\fR tends to be simpler for new users to learn than
Xthe MH system.
X.SH "STARTING UP"
X.LP
XWhen \fIvmail\fR is invoked, it loads mail headers for the folders
Xwhich will initially be active.
XThe default is for \fBinbox\fR (or the MH profile field Current-Folder)
Xto be the only active folder.
X(Other folders may be made active during the course of a session.)
XThere are a number of command-line flags.
X\fB\-inc\fR asks \fIvmail\fR to incorporate mail before loading any folders.
X\fB\-flush\fR instructs \fIvmail\fR not to flush typeahead.
X\fB\-comp\fR, \fB\-forw\fR and \fB\-ans\fR specify that \fIcomp\fR, \fIforw\fR
Xand \fIrepl\fR (`answer') respectively do not require that the user give
Xarguments; see the commands \fBc\fR, \fBf\fR and \fBa\fR.
XArguments may also be set by the profile component \fBvmail\fR.
XTypical usage of \fIvmail\fR might be:
X.sp
X	vmail +priv inbox outbox
X.sp
Xto start up \fIvmail\fR with folders \fBpriv\fR, \fBinbox\fR, and
X\fBoutbox\fR active and with \fBpriv\fR as the current folder.
X\fIvmail\fR might then print
X.nf
X.sp
X\f(as	Vmail 9/87 -- reading mail headers
X        	inbox: 122-279 (32 items)
X        	outbox: 1-95 (19 items)
X        	priv: 2-19 (5 items)\fR
X.sp
X.fi
Xas it loaded the named folders.
XWhen loading completed it would bring up the screen
Xrepresenting the mail items in the folder \fBpriv\fR.
X.nf
X.sp
X\f(as     priv (page 1 of 1)
X
X        2  17-06-86  fred@munnari    Just testing << Are you receiving
X        6   7-11-86  To: fred, nerk  A promise << The commitment: Dinn
X        7   9-05-87  bill            thanks for everything, but << wha
X       12  11-05-87 \-bill            Re: thanks for everything, but <<
X       19   9-05-87  To: bill        Re: thanks for everything, but <<\fR
X.sp
X.fi
X(Note that the dates in the example are in British format; a US installation
Xwould have the US form.)
XThe `\fB\-\fR' indicates that a reply has been sent to that piece of mail.
XText to the left of `\fB<<\fR' is the subject, other text is the
Xfirst part of the body.
XThe user could now move up and down between mail items with \fBk\fR and
X\fBj\fR, show the current mail item with \fB<space>\fR, delete the current
Xmail item with \fBd\fR, or forward or answer it with \fBf\fR or \fBa\fR.
X.LP
XIf the folder \fBpriv\fR contained  more mail items than could be
Xdisplayed on a screen, \fBpriv\fR would be broken into a number of pages.
X\fIvmail\fR has one or more pages for each active folder.
XThe user can move between pages, which are ordered alphabetically on
Xfolder name, by typing \fB<return>\fR and \fB<backspace>\fR 
X(forwards and backwards respectively).
X.SH "THE DETAILS"
X.LP
XFor those who like to mix use of MH and \fIvmail\fR, \fIvmail\fR
Xupdates the MH environment on \fBq\fR (quit), \fB!\fR (call shell)
Xand \fB^Z\fR (suspend).
XSpecifically, the fields `Current-Folder' in \fBcontext\fR and
X`cur' in \fBmh-profile\fR are updated to be the current folder and
Xcurrent mail item respectively.
X.LP
XThe following points are relevant to all \fIvmail\fR commands.
XFirst, type-ahead is flushed (as in \fIrn\fR), so any commands
Xtyped ahead of time will be ignored.
XSecond, \fIvmail\fR always remembers the name of an ``alternate
Xfolder'', the last folder other than the current folder with which
Xthere has been an interaction (such as the folder to which an item
Xwas refiled, or the previous current folder).
XThis alternate folder is used by a number of commands.
XThird, some commands may be preceded by a count.
X.LP
XThe following is a complete list of \fIvmail\fR commands.
X.TP 10
X.B <space>
XShow current mail item (like \fIshow\fR).
X.TP 10
X.B <return>
XGo to next active page of mail headers (uses count).
X.TP 10
X.B <backspace>
XGo to previous active page of mail headers (uses count).
X.TP 10
X.B ^,$
XGo to the first (or last) active page of mail headers.
X.TP 10
X.B /,?
XSearch forwards (or backwards) through mail headers for the given
Xregular expression.
X\fB/<return>\fR will repeat search forwards, \fB?<return>\fR will repeat
Xthe search backwards.
XIt is not possible to backspace over \fB/\fR or \fB?\fR; use interrupt
Xinstead.
X.TP 10
X.B .
XRe-execute last command (if one of acdDefirR).
X(On repeat, \fBr\fR will not prompt for a folder name.)
X.TP 10
X.B ^L
XRefresh screen.
X.TP 10
X.B |
XPipe current mail item into command.
X.TP 10
X.B !
XInvoke your favourite shell (\fIcsh\fR by default).
X.TP 10
X.B a
XAnswer current mail item (call to \fIrepl\fR).
X\fIvmail\fR will ask for arguments unless \fB\-ans\fR has been set.
X\fIvmail\fR data structures won't be updated.
X.TP 10
X.B c
XCompose new mail (call to \fIcomp\fR).
X\fIvmail\fR will ask for arguments unless \fB\-comp\fR has been set.
X\fIvmail\fR data structures won't be updated.
X.TP 10
X.B C
XInvoke the folder chooser.
XThis is a screen which permits users to select a current folder
Xby moving the cursor to a folder name with \fIvi\fR movement keys
Xand hitting \fB<space>\fR.
X.TP 10
X.B d
XDelete current mail item (uses count) (like \fIrmm\fR).
XActually, the mail item is moved from maildir/folder/num to maildir/folder/#num.
X\fIvi\fR users beware, \fBdd\fR deletes two items of mail.
X.TP 10
X.B D
XDelete current mail item, show next.
X.TP 10
X.B e
XEdit current mail item.
X.TP 10
X.B f
XForward current mail item (call to \fIforw\fR).
X\fIvmail\fR will ask for arguments unless \fB\-forw\fR has been set.
X\fIvmail\fR data structures won't be updated.
X.TP 10
X.B g,G
XGo to the named (or go to alternate) folder.
X.TP 10
X.B F
XShow all foldernames.
X.TP 10
X.B h
XDisplay the help screen.
X.TP 10
X.B H
XGo to top of page (or as offset by count).
X.TP 10
X.B i
XIncorporate mail (call to \fIinc\fR).
X\fIvmail\fR data structures are updated.
X.TP 10
X.B j
XMove cursor down (uses count).
XAt the bottom of the page, \fBj\fR will go to the top of the next page
Xof the current folder.
X.TP 10
X.B k
XMove cursor up (uses count).
XAt the top of the page, \fBk\fR will go to the bottom of the previous page
Xof the current folder.
X.TP 10
X.B L
XGo to bottom of page (or as offset by count).
X.TP 10
X.B M
XGo to middle of page.
X.TP 10
X.B n
XGo to the next folder, making it active if it is not so already (uses count).
X.TP 10
X.B p
XGo to the previous folder, making it active if it is not so
Xalready (uses count).
X.TP 10
X.B P
XPrint the name of the alternate folder.
X.TP 10
X.B q
XExit.
X.TP 10
X.B r
XRefile current item into the named folder.
X.TP 10
X.B R
XRefile current item into the last folder to which something was refiled.
X.TP 10
X.B s
XSave current mail item in the named file.
XMost filename expansions are not recognized, but `~/' and `~user/' are
Xreplaced by the appropriate paths.
X.TP 10
X.B u
XUndo most recent deletion.
X.TP 10
X.B v
XMake the current folder inactive.
X.TP 10
X.B z
XPack the current folder.
X.LP
XThe commands \fBa\fR, \fBc\fR and \fBf\fR update the physical folders,
Xbut the corresponding pages of mail headers in \fIvmail\fR are not updated.
X\fBv\fR and \fBg\fR can be used in conjunction to force the \fIvmail\fR
Xrepresentation of a folder to come up to date.
X.LP
X\fIvmail\fR recognizes the following environment variables.
XWhere they describe an executable, a full path name should be given.
X.TP 10
X.B SHELL
XSubshell invoked on `!'.
XDefault is \fI/bin/csh\fR.
X.TP 10
X.B PAGER
XDefault is \fI/usr/ucb/more\fR.
X.TP 10
X.B EDITOR
XUsed for editing stored mail.
XThe default is \fI/usr/ucb/vi\fR.
X.TP 10
X.B MH
XUsed to identify the MH profile.
XFrom the profile, \fIvmail\fR recognizes \fBPath\fR, \fBMsg-protect\fR
Xand \fBFolder-protect\fR.
XThe calls to MH utilities use the MH profile as appropriate.
XThe default is \fB~/.mh_profile\fR.
X.SH "PROFILE ENTRIES"
X.LP
XFolder-Protect (protection for new folders)
X.LP
XPath (of mail directory) 
X.LP
XCurrent-Folder (for startup folder)
X.LP
XVmail (list of arguments)
X.SH "SEE ALSO"
Xcomp(1), repl(1), forw(1), inc(1)
X.SH BUGS
X.LP
XCheck the alternate folder before using it unless absolutely sure
Xof its identity; a command may change it unexpectedly.
X.LP
XOnly a couple of formats of "Date:" lines are recognized; if the format is
Xdifferent, the date is replaced by a block of spaces.
X.LP
XStartup time is proportional to the number of items in the current folder.
XIt is therefore better to have a large number of folders each with fewer
Xmail items than a small number of large folders.
XMany first-time vmail users find that their inbox is large enough
X(> 400 items, say) that vmail takes a while to start up \- expect to
Xspend some time initially decluttering inbox and other large folders.
X.SH FILES
X.LP
X$HOME/.mh_profile
X.LP
X$HOME/Mail
X.LP
X$HOME/Mail/context
!The!End!

echo x - "Makefile" 2>&1
sed "s/^X//" >"Makefile" <<'!The!End!'
X#	vmail
X#
X#	Interactive screen-based mail handler that sits on top of MH.
X#
X#	Copyright (C) J. Zobel, University of Melbourne, October 1987.
X
XVERS=\"10/87\"
X
XDESTDIR=
XBIN=/usr/local
XOBJ=call.o choose.o cmds.o find.o inc.o init.o load.o low.o main.o move.o page.o
X
XLDFLAGS=
X#	this line is
X#LDFLAGS=-k 48k
X#	if -k option recognized
X#	-k sets required stack space on the PE C compiler
X
XCFLAGS=-DVERSION=$(VERS)
X#	CFLAGS are
X#CFLAGS=-DVERSION=$(VERS) -DUSDATE
X#	if dates required in US format
X
XLIBS=-lcurses -ltermcap
X
Xvmail: $(OBJ)
X	$(CC) $(LDFLAGS) -o vmail $(OBJ) $(LIBS)
X
X$(OBJ): defs.h macro.h Makefile
X
Xinstall: $(DESTDIR)$(BIN)/vmail
X
X$(DESTDIR)$(BIN)/vmail: vmail
X	mv vmail $(DESTDIR)$(BIN)/vmail
X
Xclean:
X	rm $(OBJ) core
!The!End!

echo x - "defs.h" 2>&1
sed "s/^X//" >"defs.h" <<'!The!End!'
X/* --------------------
X	vmail -- defs.h
X
X	Interactive screen-based mail handler that sits on top of MH.
X
X	Copyright (C) J. Zobel, University of Melbourne, October 1987.
X-------------------- */
X
X#include <stdio.h>
X#include <pwd.h>
X#include <setjmp.h>
X#include <sgtty.h>
X#include <sys/types.h>
X#include <sys/wait.h>
X#include <sys/dir.h>
X#include <sys/file.h>
X#include <sys/stat.h>
X#include <curses.h>
X
X#define COMP		"/usr/local/mh/comp"	/* MH utilities needed by vmail */
X#define FORW		"/usr/local/mh/forw"
X#define INC			"/usr/local/mh/inc"
X#define REPL		"/usr/local/mh/repl"
X#define PAGER		"/usr/ucb/more"			/* default pager */
X#define EDITOR		"/usr/ucb/vi"			/* default editor */
X#define SHELL		"/bin/csh"				/* default shell */
X#define CAT			"/bin/cat"				/* used as `show' for pipes */
X
X#define LEN			255						/* standard string length */
X#define MAXITEMS	4097					/* number of items in folder */
X#define FPROT		0755					/* standard file protection */
X#define TITLE		0						/* title line */
X#define STATUS		1						/* status line */
X#define FIRST		2						/* first line of info */
X#define PROFILE		".mh_profile"			/* default profile */
X#define CURFOL		"inbox"					/* default current folder */
X#define CONTEXT		"context"				/* default context file */
X#define SEQU		".mh_sequences"			/* default .mh_sequences file */
X#define MAILDIR		"Mail"					/* default mail directory */
X
X#define	true		1
X#define false		0
X#define EMPTY		2						/* used to mark empty folders */
X#define CTRL_L		'\014'
X#define ESC			'\033'
X#define DEL			'\177'
X
X/* --------------------
X	Data structures.
X
X	Each folder is divided into pages of (_lines_-2) mail items, _lines_
X	being the number of lines for the tty type.  Pages of folders are
X	stored in a doubly-linked list of pages (struct mail_folder), mail
X	items (struct mail_item) in doubly liked lists of mail.  Folders are
X	sorted alphabetically, and pages of mail items are sorted numerically.
X-------------------- */
Xstruct mail_item {
X	int		number;						/* number of item */
X	char	*title;						/* header */
X	struct mail_item *next, *prev;		/* links to other headers */
X};
Xtypedef struct mail_item *item;
X
Xstruct mail_folder {
X	char	*name;						/* folder name */
X	int		pages, pagenum;				/* no. of pages, no. of page */
X	bool	valid;						/* true if folder active */
X	item	mail, last;					/* first and last mail items for page
X										   - NULL if folder not active */
X	struct mail_folder *next, *prev;	/* linked list of folders */
X};
Xtypedef struct mail_folder *folder;
X
X
Xextern folder	folders,				/* list of all folders */
X				curflr,					/* current folder */
X				alternate;				/* alternate folder */
Xextern item		curmail;				/* current mail */
Xextern char		**environ,
X				*user,					/* user name */
X				*pager,					/* desired pager */
X				*editor,				/* desired editor */
X				*shell,					/* desired shell */
X				*mail_dir,				/* mail directory */
X				*context;				/* context file */
Xextern int		y,						/* current Y co-ordinate (for curses) */
X				folder_protect,			/* protection for folders */
X				lines,					/* lines per screen */
X				cols;					/* cols per screen */
Xextern bool		top_level,				/* flag used to see if process stopped
X										   from within subprocess */
X				do_flush,				/* true if input is to be flushed */
X				comp_args,				/* true if calls to comp needs args */
X				repl_args,				/* true if calls to repl needs args */
X				forw_args;				/* true if calls to forw needs args */
Xextern jmp_buf	env;
X
X#include "macro.h"
X
Xvoid	add_page_header(), addstatus(), choose(), comp(), create_mail_record(),
X		cursor_down(), cursor_up(), display_page(), delete_item(), edit(),
X		find_folders(), flatten(), forw(), get_env(), get_home(),
X		read_profile(), get_string(), get_title(), goto_folder(), call_shell(),
X		goto_incorp_folder(), goto_next_folder(), goto_prev_folder(),
X		hold_end(), inactive(), inc(), init(), list_folders(),
X		mark_valid_folders(), move_item(), next_page(), no_control(),
X		prev_page(), repl(), save_item(), search(), show_folder(), show_mail(),
X		show_title(), tint(), to_control(), to_normal(), tstp(), undo(), pack(),
X		cursor_first(), cursor_middle(), cursor_last(), goto_first_page(),
X		goto_last_page(), squash(), get_date(), process_args(), do_pipe();
!The!End!

echo x - "macro.h" 2>&1
sed "s/^X//" >"macro.h" <<'!The!End!'
X/* --------------------
X	macro.h -- assorted functions
X
X	Copyright (C) J. Zobel, University of Melbourne, October 1987.
X-------------------- */
X
X#define beep()		printf("%c", 007)
X#define NEWSTR(N)	(char *) malloc(N)
X#define NEW(S)		(struct S *) malloc(sizeof(struct S))
X
X/* --------------------
X	Moving between pages of mail items.
X-------------------- */
X#define PREV_VALID(F)	for(; F != (folder) NULL && ! F->valid ; F=F->prev)
X#define NEXT_VALID(F)	for(; F != (folder) NULL && ! F->valid ; F=F->next)
X#define FRST_OF_NAME(F)	for(; F->prev != (folder) NULL && \
X								F->name == F->prev->name ; F=F->prev)
X#define LAST_OF_NAME(F)	for(; F->next != (folder) NULL && \
X								F->name == F->next->name ; F=F->next)
X#define GOTO_NAME(F,S)	for(F=folders ; F != (folder) NULL && \
X								strcmp(S, F->name) != 0 ; F=F->next)
!The!End!
exit