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

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

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

This is the second 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 - "call.c" 2>&1
sed "s/^X//" >"call.c" <<'!The!End!'
X/* --------------------
X	vmail -- call.c
X
X	Routines that call MH equivalents, editor, shell.
X
X	Copyright (C) J. Zobel, University of Melbourne, October 1987.
X-------------------- */
X
X#include "defs.h"
X#include <signal.h>
X
X#define WARNING	"Warning -- mail headers may be out of date"
X
Xunion wait status;
X
X/* --------------------
X	Fork a call to `comp'.
X	Terminal type must be reset before call.
X-------------------- */
Xvoid
Xcomp()
X{
X	char	*tmp, *argv[20], str[LEN], s1[LEN], *next_token();
X	int		i, (*oldint)(), (*oldquit)(), (*signal())();
X
X	*s1 = '\0';
X	if(comp_args) {
X		sprintf(str, "(give options to)   comp ");
X		get_string(str, s1);
X	}
X	clear();
X	addstatus("composing mail ...", false);
X	move(STATUS+1, 0);
X	refresh();
X	top_level = false;			/* used by tstp() so that right thing is done
X								   when process is restarted */
X	if(! vfork()) {
X		argv[0] = COMP;
X		for(i=1, tmp=s1 ; *tmp != '\0' ; i++) {
X			argv[i] = tmp;
X			tmp = next_token(tmp);
X		}
X		argv[i] = 0;
X		no_control();
X		execv(COMP, argv);
X		printf("Warning: can't execute %s\n", COMP);
X		exit(0);
X	}
X	oldint = signal(SIGINT, SIG_IGN);
X	oldquit = signal(SIGQUIT, SIG_IGN);
X	wait(&status);
X	signal(SIGINT, oldint);
X	signal(SIGQUIT, oldquit);
X	top_level = true;
X	to_control();
X	hold_end();					/* wait for user to want to continue - may wish
X								   to read error messages */
X	display_page();
X	addstatus(WARNING, true);	/* vmail's data structures not updated */
X}
X
X
X/* --------------------
X	Fork a call to `forw'.
X	Terminal type must be reset before call.
X-------------------- */
Xvoid
Xforw()
X{
X	char	*tmp, *argv[20], str[LEN], s1[LEN], s2[10], *next_token();
X	int		i, (*oldint)(), (*oldquit)(), (*signal())();
X
X	sprintf(s2, "%d", curmail->number);
X	*s1 = '\0';
X	if(forw_args) {
X		sprintf(str, "(give options to)   forw +%s %s ", curflr->name, s2);
X		get_string(str, s1);
X	}
X	clear();
X	addstatus("forwarding mail ...", false);
X	move(STATUS+1, 0);
X	refresh();
X	top_level = false;			/* used by tstp() so that right thing is done
X								   when process is restarted */
X	if(! vfork()) {
X		sprintf(str, "+%s", curflr->name);
X		argv[0] = FORW; argv[1] = str; argv[2] = s2;
X		for(i=3, tmp=s1 ; *tmp != '\0' ; i++) {
X			argv[i] = tmp;
X			tmp = next_token(tmp);
X		}
X		argv[i] = 0;
X		no_control();
X		execv(FORW, argv);
X		printf("Warning: can't execute %s\n", FORW);
X		exit(0);
X	}
X	oldint = signal(SIGINT, SIG_IGN);
X	oldquit = signal(SIGQUIT, SIG_IGN);
X	wait(&status);
X	signal(SIGINT, oldint);
X	signal(SIGQUIT, oldquit);
X	top_level = true;
X	to_control();
X	hold_end();					/* wait for user to want to continue - may wish
X								   to read error messages */
X	display_page();
X	addstatus(WARNING, true);	/* vmail's data structures not updated */
X}
X
X
X/* --------------------
X	Fork a call to `repl'.
X	Terminal type must be reset before call.
X-------------------- */
Xvoid
Xrepl()
X{
X	char	*tmp, *argv[20], str[LEN], s1[LEN], s2[10], *next_token();
X	int		i, (*oldint)(), (*oldquit)(), (*signal())();
X
X	sprintf(s2, "%d", curmail->number);
X	*s1 = '\0';
X	if(repl_args) {
X		sprintf(str, "(give options to)   repl +%s %s ", curflr->name, s2);
X		get_string(str, s1);
X	}
X	clear();
X	addstatus("answering mail ...", false);
X	move(STATUS+1, 0);
X	refresh();
X	top_level = false;			/* used by tstp() so that right thing is done
X								   when process is restarted */
X	if(! vfork()) {
X		sprintf(str, "+%s", curflr->name);
X		argv[0] = REPL; argv[1] = str; argv[2] = s2;
X		for(i=3, tmp=s1 ; *tmp != '\0' ; i++) {
X			argv[i] = tmp;
X			tmp = next_token(tmp);
X		}
X		argv[i] = 0;
X		no_control();
X		execv(REPL, argv);
X		printf("Warning: can't execute %s\n", REPL);
X		exit(0);
X	}
X	oldint = signal(SIGINT, SIG_IGN);
X	oldquit = signal(SIGQUIT, SIG_IGN);
X	wait(&status);
X	signal(SIGINT, oldint);
X	signal(SIGQUIT, oldquit);
X	top_level = true;
X	to_control();
X	hold_end();					/* wait for user to want to continue - may wish
X								   to read error messages */
X	display_page();
X	addstatus(WARNING, true);	/* vmail's data structures not updated */
X}
X
X
X/* --------------------
X	Fork a call to editor.
X	Terminal type must be reset before call.
X-------------------- */
Xvoid
Xedit()
X{
X	char	str[LEN];
X	int		(*oldint)(), (*oldquit)(), (*signal())();
X
X	clear();
X	mvaddstr(TITLE, 0, "editing mail ...");
X	move(STATUS, 0);
X	refresh();
X	top_level = false;			/* used by tstp() so that right thing is done
X								   when process is restarted */
X	if(! vfork()) {
X		no_control();
X		sprintf(str, "%s/%s/%d", mail_dir, curflr->name, curmail->number);
X		execlp(editor, editor, str, 0);
X		printf("Warning: can't execute %s\n", editor);
X		exit(0);
X	}
X	oldint = signal(SIGINT, SIG_IGN);
X	oldquit = signal(SIGQUIT, SIG_IGN);
X	wait(&status);
X	signal(SIGINT, oldint);
X	signal(SIGQUIT, oldquit);
X	top_level = true;
X	to_control();
X	hold_end();					/* wait for user to want to continue - may wish
X								   to read error messages */
X	display_page();
X}
X
X
X/* --------------------
X	Fork a call to shell.
X	Terminal type must be reset before call.
X
X	This should perhaps be modified so that only a single command can be
X	issued, as in vi ... but this was simpler to do.
X-------------------- */
Xvoid
Xcall_shell()
X{
X	int		(*oldint)(), (*oldquit)(), (*signal())();
X
X	clear();
X	mvaddstr(TITLE, 0, "calling shell ...");
X	move(STATUS, 0);
X	refresh();
X	top_level = false;			/* used by tstp() so that right thing is done
X								   when process is restarted */
X	if(! vfork()) {
X		no_control();
X		fix_mh();
X		execlp(shell, shell, "-i", 0);
X		printf("Warning: can't execute %s\n", shell);
X		exit(0);
X	}
X	oldint = signal(SIGINT, SIG_IGN);
X	oldquit = signal(SIGQUIT, SIG_IGN);
X	wait(&status);
X	signal(SIGINT, oldint);
X	signal(SIGQUIT, oldquit);
X	top_level = true;
X	to_control();
X	hold_end();					/* wait for user to want to continue - may wish
X								   to read error messages */
X	display_page();
X}
X
X
X/* --------------------
X	Pipe current mail item into given command.
X-------------------- */
Xvoid
Xdo_pipe()
X{
X	char	str[LEN], s1[LEN];
X	int		(*oldint)(), (*oldquit)(), (*signal())();
X
X	*s1 = '\0';
X	sprintf(str, "(give command to)   show +%s %d | ", curflr->name,
X														curmail->number);
X	get_string(str, s1);
X	clear();
X	addstatus("piping mail ...", false);
X	move(STATUS+1, 0);
X	refresh();
X	sprintf(str, "%s %s/%s/%d | %s", CAT, mail_dir, curflr->name,
X														curmail->number, s1);
X	top_level = false;			/* used by tstp() so that right thing is done
X								   when process is restarted */
X	no_control();
X	oldint = signal(SIGINT, SIG_IGN);
X	oldquit = signal(SIGQUIT, SIG_IGN);
X	system(str);			/* exec needs full path of command => use system */
X	signal(SIGINT, oldint);
X	signal(SIGQUIT, oldquit);
X	top_level = true;
X	to_control();
X	hold_end();					/* wait for user to want to continue - may wish
X								   to read error messages */
X	display_page();
X}
!The!End!

echo x - "choose.c" 2>&1
sed "s/^X//" >"choose.c" <<'!The!End!'
X/* --------------------
X	vmail -- choose.c
X
X	Tty-based folder selection.
X
X	Copyright (C) J. Zobel, University of Melbourne, October 1987.
X-------------------- */
X
X#include "defs.h"
X
X#define START			10				/* offset from margin of screen */
X#define RESET()			toggle=false,	/* left- or right- hand indicator */ \
X						i=FIRST+1,		/* initial row */					 \
X						j=START,		/* initial column */				 \
X						f=first			/* first folder */
X
XWINDOW	*chooser = (WINDOW *) NULL;
X
X/* --------------------
X	Display folder selection page, read commands to move around page and
X	select folders.
X-------------------- */
Xvoid
Xchoose()
X{
X	int		n, i, j;
X	bool	toggle;
X	folder	next, f, showing(),
X			first = folders,	/* first folder on screen */
X			last;				/* folder after last folder on screen */
X	char	c, str[LEN], flushin();
X
X	RESET();
X	if(chooser == (WINDOW *) NULL)
X		chooser = newwin(0, 0, 0, 0);
X	last = showing(first);
Xrestart:
X	while((c = flushin()) != ' ' && c != 'q') switch(c) {
X		case '\r':		/* go to next screen of folder names */
X		case '\n':
X			if(last == (folder) NULL)
X				beep();
X			else {
X				first = last;
X				last = showing(first);
X				RESET();
X			}
X			break;
X		case '\b':		/* go back one page of folders */
X			if(first == folders)
X				beep();
X			else {
X				for(i=(lines-FIRST)*2 ; i > FIRST && first->prev != (folder) NULL ; i--, first=first->prev)
X					;
X				last = showing(first);
X				RESET();
X			}
X			break;
X		case CTRL_L:	/* redraw */
X			last = showing(first);
X			break;
X		case 'h':		/* move left */
X			if(! toggle)	/* already at left */
X				beep();
X			else {
X				next = f;
X				FRST_OF_NAME(next);
X				next = next->prev;
X				if(next == (folder) NULL)
X					beep();
X				else {
X					f = next; j = START; toggle = false;
X					wmove(chooser, i, j);
X					wrefresh(chooser);
X				}
X			}
X			break;
X		case 'j':		/* move down */
X			if(i+1 >= lines)	/* at bottom */
X				beep();
X			else {
X				next = f;
X				for(n=0 ; n < 2 && next != (folder) NULL ; n++) {
X					LAST_OF_NAME(next);
X					next = next->next;
X				}
X				if(next == (folder) NULL)
X					beep();
X				else {
X					i++; f = next;
X					wmove(chooser, i, j);
X					wrefresh(chooser);
X				}
X			}
X			break;
X		case 'k':		/* move up */
X			if(i <= FIRST+1)	/* at top */
X				beep();
X			else {
X				next = f;
X				for(n=0 ; n < 2 && next != (folder) NULL ; n++) {
X					FRST_OF_NAME(next);
X					next = next->prev;
X				}
X				if(next == (folder) NULL)
X					beep();
X				else {
X					i--; f = next;
X					wmove(chooser, i, j);
X					wrefresh(chooser);
X				}
X			}
X			break;
X		case 'l':		/* move right */
X			if(toggle)		/* at right */
X				beep();
X			else {
X				next = f;
X				LAST_OF_NAME(next);
X				next = next->next;
X				if(next == (folder) NULL)
X					beep();
X				else {
X					f = next; j = cols/2; toggle = true;
X					wmove(chooser, i, j);
X					wrefresh(chooser);
X				}
X			}
X			break;
X		default:
X			beep();
X			break;
X	}
X	if(c == ' ') {		/* attempt to go to folder */
X		if(! f->valid) {	/* load folder */
X			mvwaddstr(chooser, FIRST, 0, "Reading mail headers ...");
X			wmove(chooser, i, j);
X			wrefresh(chooser);
X			f->valid = true;
X			find_mail(f, false);
X		}
X		if(f->valid == EMPTY) {		/* folder is empty */
X				/* Go back to chooser, display "empty" message */
X			sprintf(str, "%s -- folder empty", f->name);
X			if(first == f)		/* find new first folder for page */
X				if(f->next != (folder) NULL)
X					first = f->next;
X				else
X					first = folders;
X			last = showing(first);
X			RESET();
X			mvwaddstr(chooser, FIRST, 0, str);
X			wmove(chooser, i, j);
X			wrefresh(chooser);
X			goto restart;
X		} 
X		curflr = f;
X		curmail = f->mail;
X		y = FIRST;
X	}
X	display_page();
X}
X
X
X/* --------------------
X	Show all folder names starting with "start" ending with last folder or
X	when page is full.
X-------------------- */
Xfolder
Xshowing(start)
X	folder	start;
X{
X	int		count, i = FIRST+1, j = START;
X	bool	toggle = false;
X	folder	p, f;
X	item	m;
X	char	str[LEN];
X
X	wclear(chooser);
X	mvwaddstr(chooser, TITLE, 0,
X"h,j,k,l -- left,down,up,right        <return>,<bs> -- next/prev page of folders");
X	mvwaddstr(chooser, STATUS, 0,
X"<space> -- select folder             q -- quit, no change         ^L -- refresh");
X	for(p=f=start ; i < lines && f != (folder) NULL ; p=f=p->next) {
X		if(f->valid) {
X				/* find last page of folder, count items in folders */
X			for(count=0, p=f ; p->next != (folder) NULL &&
X										p->next->name == f->name ; p=p->next)
X				for(m=p->mail ; m != (item) NULL ; m=m->next, count++)
X					;
X			for(m=p->mail ; m != (item) NULL ; m=m->next, count++)
X				;
X			if(count == 1)
X				sprintf(str, "%s: %d, 1 item", f->name, f->mail->number);
X			else
X				sprintf(str, "%s: %d-%d, %d items", f->name, f->mail->number,
X													p->last->number, count);
X		} else
X			sprintf(str, "%-15s(inactive)", f->name);
X		if(f == curflr) standout();
X		mvwaddstr(chooser, i, j, str);
X		if(f == curflr) standend();
X		if(toggle)
X			j = START, i++;
X		else
X			j = cols/2;
X		toggle = ! toggle;
X	}
X	wmove(chooser, FIRST+1, START);
X	wrefresh(chooser);
X	return(f);
X}
X
X
XWINDOW *folwin = (WINDOW *) NULL;
X
X/* --------------------
X	List all folders, active or otherwise.
X-------------------- */
Xvoid
Xlist_folders()
X{
X	folder	f = folders, p;
X	int		count, i = FIRST, half = cols/2;
X	bool	toggle = false;
X	char	str[LEN];
X	WINDOW	*newwin();
X	item	m;
X
X	if(folwin == (WINDOW *) NULL)
X		folwin = newwin(0, 0, 0, 0);
X	wclear(folwin);
X	for(p=f ; ; p=f=p->next) {
X		if((i+2) % lines == 0) {
X			if(use_prompt(folwin) == 'q')
X				break;
X			i = FIRST;
X			wclear(folwin);
X		}
X		if(f == (folder) NULL) {
X			use_prompt(folwin);
X			break;
X		}
X		if(f->valid) {
X				/* find last page of folder, count items in folders */
X			for(count=0, p=f ; p->next != (folder) NULL &&
X										p->next->name == f->name ; p=p->next)
X				for(m=p->mail ; m != (item) NULL ; m=m->next, count++)
X					;
X			for(m=p->mail ; m != (item) NULL ; m=m->next, count++)
X				;
X			if(count == 1)
X				sprintf(str, "%s: %d, 1 item", f->name, f->mail->number);
X			else
X				sprintf(str, "%s: %d-%d, %d items", f->name, f->mail->number,
X													p->last->number, count);
X		} else
X			sprintf(str, "%-15s(inactive)", f->name);
X		if(f == curflr)
X			wstandout(folwin);
X		if(toggle) {
X			mvwaddstr(folwin, i, half, str);
X			i++;
X		} else
X			mvwaddstr(folwin, i, 10, str);
X		if(f == curflr)
X			wstandend(folwin);
X		toggle = !toggle;
X	}
X	display_page();
X}
!The!End!

echo x - "cmds.c" 2>&1
sed "s/^X//" >"cmds.c" <<'!The!End!'
X/* --------------------
X	vmail -- cmds.c
X
X	Commands not included in page.c or move.c.
X
X	Copyright (C) J. Zobel, University of Melbourne, October 1987.
X-------------------- */
X
X#include "defs.h"
X#include <signal.h>
X
X/* --------------------
X	Print the name of the alternate folder.
X-------------------- */
Xvoid
Xshow_folder()
X{
X	char	str[LEN];
X
X	if(alternate == (folder) NULL)
X		addstatus("No alternate folder", false);
X	else {
X		sprintf(str, "alternate folder is %s\n", alternate->name);
X		addstatus(str, false);
X	}
X}
X
X
X/* --------------------
X	Make current folder inactive - remove list of mail headers, set valid
X	to false.
X-------------------- */
Xvoid
Xinactive()
X{
X	folder	f, p;
X
X		/* find first page of folder */
X	p = curflr; FRST_OF_NAME(p);
X		/* find last page of folder */
X	f = curflr; LAST_OF_NAME(f);
X	p->next = f->next;
X	p->valid = false;
X/* should free records */
X	p->mail = p->last = (item) NULL;
X	p->pagenum = p->pages = 1;
X	if(p->next != (folder) NULL)
X		p->next->prev = p;
X	curflr = p;
X	NEXT_VALID(curflr);
X	if(curflr == (folder) NULL) {
X		curflr = p;
X		PREV_VALID(curflr);
X	}
X	if(curflr == (folder) NULL) {
X		addstatus("Making last active folder inactive", true);
X		to_normal();
X		exit(0);
X	} else {
X		curmail = curflr->mail;
X		y = FIRST;
X		display_page();
X	}
X}
X
X
X/* --------------------
X	Show current mail item with fork to pager.
X-------------------- */
Xvoid
Xshow_mail()
X{
X	int		(*oldint)(), (*oldquit)(), (*signal())();
X	char	str[LEN];
X	union wait status;
X
X	clear();
X	refresh();
X	top_level = false;			/* used by tstp() so that right thing is done
X								   when process is restarted */
X	if(! vfork()) {
X		no_control();
X		sprintf(str, "%s/%s/%d", mail_dir, curflr->name, curmail->number);
X		execlp(pager, pager, str, 0);
X		printf("Warning: can't execute %s\n", pager);
X		exit(0);
X	}
X	oldint = signal(SIGINT, SIG_IGN);
X	oldquit = signal(SIGQUIT, SIG_IGN);
X	wait(&status);
X	signal(SIGINT, oldint);
X	signal(SIGQUIT, oldquit);
X	top_level = true;
X	to_control();
X	hold_end();					/* wait for user to want to continue - may wish
X								   to read error messages */
X	display_page();
X}
X
X
X/* --------------------
X	Save current mail item to named file.  If first non-space character
X	is ~, expand it.
X-------------------- */
Xvoid
Xsave_item()
X{
X	struct passwd *pwent, *getpwnam();
X	char	*tmp, buf[LEN], *str = buf, save[LEN];
X	FILE	*fi, *fo, *fopen();
X
X	sprintf(save, "%s.%d", curflr->name, curmail->number);
X	sprintf(str, "file (%s)? ", save);
X	get_string(str, str);
X	for(; *str == ' ' ; str++)
X		;
X	for(tmp=str ; *tmp != '\0' && *tmp != ' ' ; tmp++)
X		;
X	*tmp = '\0';
X	if(*str != '\0')		/* don't take default */
X		if(*str == '~')	{	/* expand home directory name */
X			tmp = str + 2;
X			if(*(str+1) == '/')
X				pwent = getpwnam(user);
X			else {	
X				for( ; *tmp != '\0' && *tmp != '/' ; tmp++)
X					;
X				*(tmp++) = '\0';
X				if((pwent = getpwnam(str+1)) == (struct passwd *) NULL) {
X					sprintf(save, "%s: no such user", str+1);
X					addstatus(save, true);
X					return;
X				}
X			}
X			sprintf(save, "%s/%s", pwent->pw_dir, tmp);
X		} else				/* take str as given */
X			strcpy(save, str);
X	if((fo = fopen(save, "w")) == (FILE *) NULL) {
X		sprintf(str, "%s: no write permission", save);
X		addstatus(str, true);
X		return;
X	}
X	addstatus("saving ...", false);
X	sprintf(str, "%s/%s/%d", mail_dir, curflr->name, curmail->number);
X	fi = fopen(str, "r");
X	while(fgets(str, LEN, fi) != (char *) NULL)
X		fprintf(fo, "%s", str);
X	fclose(fi); fclose(fo);
X	addstatus("saved", false);
X}
!The!End!

echo x - "find.c" 2>&1
sed "s/^X//" >"find.c" <<'!The!End!'
X/* --------------------
X	vmail -- find.c
X
X	Searching titles for strings.
X
X	Copyright (C) J. Zobel, University of Melbourne, October 1987.
X-------------------- */
X
X#include "defs.h"
X
Xstatic char str[LEN] = "^.*";
X
X/* --------------------
X	Get string for searching, either forwards (forwards = true) or backwards
X	(forwards = false).  Then scan mail headers using regex().
X-------------------- */
Xvoid
Xsearch(forwards)
X	int		forwards;
X{
X	char	s1[LEN], *s2, *re_comp();
X	int		i;
X	bool	found = false;
X	folder	f;
X	item	m;
X
X	get_string((forwards) ? "/" : "?", s1);
X	if((i = strlen(s1)) > 0) {
X			/* new search string */
X		strcpy(str+3, s1);
X		str[i+=3] = '.', str[++i] = '*', str[++i] = '\0';
X	} else if(strlen(str+3) == 0) {
X		addstatus("", false);
X		return;
X	}
X	if((s2 = re_comp(str)) != (char *) NULL) {
X		addstatus(s2, false);
X		return;
X	}
X	addstatus("searching ...", false);
X	if(forwards)
X		for(i=y+1, f=curflr, m=curmail->next ; ! found && m != curmail ; ) {
X			if(m == (item) NULL) {
X				f = f->next; NEXT_VALID(f);
X				if(f == (folder) NULL) {
X					f = folders;
X					NEXT_VALID(f);
X				}
X				m = f->mail;
X				i = FIRST;
X			}
X			if(! (found = re_exec(m->title)))
X				m = m->next, i++;
X		}
X	else
X		for(i=y-1, f=curflr, m=curmail->prev ; ! found && m != curmail ; ) {
X			if(m == (item) NULL) {
X				f = f->prev; PREV_VALID(f);
X				if(f == (folder) NULL) {
X						/* find last folder */
X					for(f=folders ; f->next != (folder) NULL ; f=f->next)
X						;
X					PREV_VALID(f);
X				}
X					/* find last mail item with count */
X				for(i=FIRST, m=f->mail ; m->next != (item) NULL ; i++,m=m->next)
X					;
X			}
X			if(! (found = re_exec(m->title)))
X				m = m->prev, i--;
X		}
X	curmail = m, y = i;
X	if(f != curflr) {
X		curflr = f;
X		display_page();
X	} else if(! found)	/* come back to current folder (known to be valid) */
X		addstatus("pattern not found", true);
X	else
X		addstatus("", false);
X}
!The!End!

echo x - "inc.c" 2>&1
sed "s/^X//" >"inc.c" <<'!The!End!'
X/* --------------------
X	vmail -- inc.c
X
X	Incorporate new mail with "inc", parse output and update appropriate
X	folder-pages.
X
X	Copyright (C) J. Zobel, University of Melbourne, October 1987.
X-------------------- */
X
X#include "defs.h"
X
X#define INCORP		"Incorporating new mail into "
X#define TAIL		"...\n"
X
Xstatic folder isfol;		/* folder into which mail is being incorporated */
Xstatic item ismail;			/* last item of mail on isfol page */
Xstatic int count;			/* number of items on isfol page */
X
X/* --------------------
X	Call, inc, parse output to work out what items have been read and into
X	what folders.
X-------------------- */
Xvoid
Xinc()
X{
X	char	str[LEN], line[LEN], *tmp, *fgets();
X	FILE	*pp, *fdopen();
X	int		num, pd[2], len = strlen(INCORP), numbers();
X	bool	redraw = true;
X	union wait status;
X
X	addstatus("incorporating mail ... ", true);
X	pipe(pd);
X	if(! vfork()) {
X		close(pd[0]);
X		dup2(pd[1], 1);
X		close(pd[1]);
X		dup2(1, 2);
X		execl(INC, "inc", 0);
X	}
X	close(pd[1]);
X	if((pp = fdopen(pd[0], "r")) == (FILE *) NULL) {
X		addstatus("panic, can't incorporate mail", true);
X		return;
X	}
X	while((tmp = fgets(str, LEN, pp)) != (char *) NULL)	/* read "inc" output */
X		if(strncmp(tmp, INCORP, len) == 0) {	/* reading into new folder */
X			tmp += len;
X			tmp[strlen(tmp)-strlen(TAIL)] = '\0';
X			goto_incorp_folder(tmp);
X			if(isfol == (folder) NULL) {
X				sprintf(line, "can't go to folder %s", tmp);
X				addstatus(line, true);
X				redraw = false;
X				break;
X			} else {
X				sprintf(line, "incorporating mail into folder %s", tmp);
X				addstatus(line, true);
X			}
X		} else if(num = numbers(tmp))	/* continuing to read into folder */
X			create_mail_record(num);
X		else if(strlen(str) > 2) {		/* vmail is confused - break */
X			str[strlen(str)-1] = '\0';
X			addstatus(str, true);
X			redraw = false;
X			break;
X		}
X	if(redraw) {
X		if(isfol != curflr)
X			alternate = curflr;
X		curflr = isfol;
X		curmail = ismail;
X		y = count;
X		display_page();
X	}
X	fclose(pp);
X	wait(&status);
X}
X
X
X/* --------------------
X	Extract the first number from str.
X-------------------- */
Xint
Xnumbers(str)
X	char	*str;
X{
X	char	save, *tmp;
X	int		i;
X
X	for(; *str == ' ' ; str++)
X		;
X	for(tmp=str ; *str >= '0' && *str <= '9' ; str++)
X		;
X	save = *str, *str = '\0', i = atoi(tmp), *str = save;
X	return(i);
X}
X
X
X/* --------------------
X	Find the folder into which mail is being incorporated, set isfol,
X	ismail, count.
X-------------------- */
Xvoid
Xgoto_incorp_folder(str)
X	char	*str;
X{
X	folder	create_folder();
X
X	GOTO_NAME(isfol, str);
X	if(isfol == (folder) NULL) {	/* new folder */
X		isfol = create_folder(str);
X		isfol->valid = true;
X		ismail = (item) NULL;
X		count = FIRST-1;
X	} else {
X		if(! isfol->valid) {
X			isfol->valid = true;
X			find_mail(isfol, false);
X		}
X			/* find last appropriate folder record */
X		LAST_OF_NAME(isfol);
X		for(count=FIRST, ismail=isfol->mail ; ismail->next != (item) NULL ;
X												count++, ismail=ismail->next)
X			;
X	}
X}
X
X
X/* --------------------
X	Create and insert new item record into isfol, may need to create new page.
X-------------------- */
Xvoid
Xcreate_mail_record(num)
X	int		num;
X{
X	item	m;
X	folder	new_folder();
X
X	m = NEW(mail_item);
X	get_title(isfol, m, num);
X	m->next = (item) NULL;
X	if(count > lines) {
X			/* create new folder record */
X		isfol = new_folder(isfol);
X		ismail = (item) NULL;
X		count = FIRST;
X	} else
X		count++;
X	if(ismail == (item) NULL) {
X			/* new record, possibly new folder */
X		m->prev = (item) NULL;
X		isfol->mail = isfol->last = m;
X	} else {
X			/* insert at end of list of mail items */
X		m->prev = ismail;
X		ismail->next = isfol->last = m;
X	}
X	ismail = m;
X}
!The!End!

echo x - "init.c" 2>&1
sed "s/^X//" >"init.c" <<'!The!End!'
X/* --------------------
X	vmail -- init.c
X
X	Initialisation routines - setting ttystate, finding valid folders,
X	trapping signals.
X
X	Ttystate is controlled by a mix of curses and ioctl.  For simplicity,
X	initial setups are done with curses.  Curses is also used for basic
X	screen manipulation.  However, for speed ioctl is used in switching
X	in and out of normal terminal state.
X
X	Copyright (C) J. Zobel, University of Melbourne, October 1987.
X-------------------- */
X
X#include "defs.h"
X#include <signal.h>
X
Xstatic struct sgttyb tty, t_tty;		/* for holding tty state */
Xstatic struct tchars chrs, t_chrs;
Xstatic struct ltchars lchrs, t_lchrs;
X
Xstatic char	termcap[1024],				/* termcap entry */
X			*cur_folder;				/* initial current folder */
X
X/* --------------------
X	Start-up routine - set terminal control, signals, etc.
X-------------------- */
Xvoid
Xinit(argc, argv)
X	int		argc;
X	char	**argv;
X{
X	folder	ftmp, find_mail();
X	char	*pargv[20],					/* argv from profile */
X			*profile = (char *) NULL,	/* location of MH profile */
X			*home = (char *) NULL,		/* home directory */
X			*term = (char *) NULL;		/* terminal type */
X	int		pargc = 0;					/* argc from profile */
X
X	get_home(&home);
X	get_env(&term, &profile, home);
X	tgetent(termcap, term);
X	cols = tgetnum("co");
X		/* lines holds no. of lines for headers, ie "li" less STATUS, TITLE */
X	lines = tgetnum("li") - 2;
X	ioctl(0, TIOCGETP, &tty);
X	ioctl(0, TIOCGETP, &chrs);
X	ioctl(0, TIOCGETP, &lchrs);
X
X	read_profile(&pargc, pargv, profile, home);
X	process_args(pargc, pargv);
X	find_folders();
X	mark_valid_folders(pargc, pargv);
X		/* give precedence to command line args => process second */
X	mark_valid_folders(argc, argv);
X	for(ftmp=folders ; ftmp != (folder) NULL ;)
X		if(ftmp->valid)
X			ftmp = find_mail(ftmp, true);
X		else
X			ftmp = ftmp->next;
X	if(curflr->valid == EMPTY) {
X		printf("%s: folder empty.\n", curflr->name);
X		exit(1);
X	}
X		/* find last instance of initial folder */
X	LAST_OF_NAME(curflr);
X	curmail = curflr->mail;
X
X	initscr();
X	crmode();
X	noecho();
X	nonl();
X
X	signal(SIGTSTP, tstp);
X	signal(SIGINT, tint);
X
X	ioctl(0, TIOCGETP, &t_tty);
X	ioctl(0, TIOCGETP, &t_chrs);
X	ioctl(0, TIOCGETP, &t_lchrs);
X
X	y = FIRST;
X	display_page();
X}
X
X
X/* --------------------
X	Find user name, home directory.
X-------------------- */
Xvoid
Xget_home(home)
X	char **home;
X{
X	struct passwd *pwent, *getpwuid();
X
X	pwent = getpwuid(getuid());
X	*home = NEWSTR(strlen(pwent->pw_dir)+1);
X	strcpy(*home, pwent->pw_dir);
X	if(access(*home, R_OK | W_OK | X_OK)) {
X		printf("%s: no permissions.\n", *home);
X		exit(1);
X	}
X	user = NEWSTR(strlen(pwent->pw_name)+1);
X	strcpy(user, pwent->pw_name);
X}
X
X
X/* --------------------
X	Find pager, editor, shell, terminal type, MH profile - defaults are PAGER,
X	EDITOR, SHELL, none, PROFILE.  Set by PAGER, EDITOR, SHELL, TERM, MH
X	environment variables.
X-------------------- */
Xvoid
Xget_env(term, profile, home)
X	char **term, **profile, *home;
X{
X	char	**tmp;
X
X	for(tmp = environ ; *tmp != (char *) NULL ; tmp++)
X		if(!strncmp("PAGER=", *tmp, 6)) {
X			pager = NEWSTR(strlen(*tmp)-4);
X			strcpy(pager, *tmp+6);
X		} else if(!strncmp("EDITOR=", *tmp, 7)) {
X			editor = NEWSTR(strlen(*tmp)-5);
X			strcpy(editor, *tmp+7);
X		} else if(!strncmp("SHELL=", *tmp, 6)) {
X			shell = NEWSTR(strlen(*tmp)-4);
X			strcpy(shell, *tmp+6);
X		} else if(!strncmp("TERM=", *tmp, 5)) {
X			*term = NEWSTR(strlen(*tmp)-3);
X			strcpy(*term, *tmp+5);
X		} else if(!strncmp("MH=", *tmp, 3)) {
X			*profile = NEWSTR(strlen(*tmp)-1);
X			strcpy(*profile, *tmp+3);
X		}
X	if(*term == (char *) NULL) {
X		printf("Terminal type unknown\n");
X		exit(1);
X	}
X	if(*profile == (char *) NULL) {
X		*profile = NEWSTR(strlen(home)+strlen(PROFILE)+2);
X		sprintf(*profile, "%s/%s", home, PROFILE);
X	}
X	if(pager == (char *) NULL) {
X		pager = NEWSTR(strlen(PAGER)+1);
X		strcpy(pager, PAGER);
X	}
X	if(shell == (char *) NULL) {
X		shell = NEWSTR(strlen(SHELL)+1);
X		strcpy(shell, SHELL);
X	}
X	if(editor == (char *) NULL) {
X		editor = NEWSTR(strlen(EDITOR)+1);
X		strcpy(editor, EDITOR);
X	}
X}
X
X
Xstatic char argkeep[LEN];			/* storage for args from profile */
X
X/* --------------------
X	Find mail directory, current-folder, context, default options.
X-------------------- */
Xvoid
Xread_profile(pargc, pargv, profile, home)
X	int *pargc;
X	char **pargv, *profile, *home;
X{
X	FILE	*fp, *fopen();
X	char	str[LEN], *ptr, iscontext[LEN], *index(), *next_token();
X
X	if((fp = fopen(profile, "r")) == (FILE *) NULL) {
X		printf("Profile: %s: cannot open.\n", profile);
X		exit(1);
X	}
X	*iscontext = '\0';
X	while(fgets(str, LEN, fp) != (char *) NULL) {
X			/* get entries from profile */
X		if(lstrncmp("context:", str, 8) == 0 && *(ptr=str+8) != '\0') {
X			squash(str);
X			strcpy(iscontext, str+8);
X		} else if(lstrncmp("vmail:", str, 6) == 0 && *(ptr=str+6) != '\0') {
X			for( ; *ptr == ' ' || *ptr == '\t' ; ptr++)
X				;
X			*index(ptr, '\n') = '\0';
X			strcpy(argkeep, ptr);
X			for(ptr=argkeep ; *ptr != '\0' ; ) {
X				pargv[(*pargc)++] = ptr;
X				ptr = next_token(ptr);
X			}
X		} else if(lstrncmp("path:", str, 5) == 0 && *(ptr=str+5) != '\0') {
X			squash(str);
X			if(*ptr == '/') {		/* full pathname */
X				mail_dir = NEWSTR(strlen(ptr)+1);
X				strcpy(mail_dir, ptr);
X			} else {
X				mail_dir = NEWSTR(strlen(home)+strlen(ptr)+1);
X				sprintf(mail_dir, "%s/%s", home, ptr);
X			}
X		} else if(lstrncmp("folder-protect:", str, 15) == 0 &&
X														*(str+15) != '\0') {
X			squash(str);
X			folder_protect = atoo(str+15);
X		} else if(lstrncmp("current-folder:", str, 15) == 0 &&
X														*(str+15) != '\0') {
X			squash(str);
X			cur_folder = NEWSTR(strlen(str+15)+1);
X			strcpy(cur_folder, str);
X		}
X	}
X	fclose(fp);
X	if(mail_dir == (char *) NULL) {
X		mail_dir = NEWSTR(strlen(home)+strlen(MAILDIR)+2);
X		sprintf(mail_dir, "%s/%s", home, str+6);
X	}
X	if(access(mail_dir, R_OK | W_OK | X_OK)) {
X		printf("%s: no permissions.\n", mail_dir);
X		exit(1);
X	}
X	if(*iscontext == '\0')
X		strcpy(iscontext, CONTEXT);
X	context = NEWSTR(strlen(mail_dir)+strlen(iscontext)+2);
X	sprintf(context, "%s/%s", mail_dir, iscontext);
X	if(access(context, R_OK | W_OK)) {
X		printf("%s: no permissions.\n", context);
X		exit(1);
X	}
X	if(cur_folder == (char *) NULL)
X		cur_folder = CURFOL;
X}
X
X
X/* --------------------
X	Squash spaces, tabs, newlines out of given string.
X-------------------- */
Xvoid
Xsquash(str)
X	char	*str;
X{
X	int		i, j;
X
X	for(i=0, j=0 ; (str[j] = str[i]) != '\0' ; i++)
X		if(str[j] != ' ' && str[j] != '\t' && str[j] != '\n')
X			j++;
X}
X
X
X/* --------------------
X	Mark folders as specified by setenv, command line.  At startup, default
X	is for only active folder to be cur_folder.
X-------------------- */
Xvoid
Xmark_valid_folders(argc, argv)
X	int		argc;
X	char	**argv;
X{
X	char	*name;
X	folder	f;
X
X	name = cur_folder;
X		/* find valid folders - mark all folders from argv as valid */
X	for(; argc > 0 ; argc--, argv++)
X		if(**argv == '+')				/* startup folder */
X			name = (*argv) + 1;
X		else if(**argv != '-') {		/* not a flag */
X			GOTO_NAME(f, *argv);
X			if(f == (folder) NULL)
X				printf("Warning: no such folder as %s\n", *argv);
X			else
X				f->valid = true;
X		}
X	GOTO_NAME(f, name);
X	if(f == (folder) NULL) {
X		printf("%s does not exist\n", name);
X		exit(1);
X	}
X	f->valid = true;
X	curflr = f;
X}
X
X
X/* --------------------
X	Reset terminal, clean up.
X-------------------- */
Xvoid
Xto_normal()
X{
X	move(lines+FIRST-1, 0);
X	refresh();
X	no_control();
X	printf("\n");
X}
X
X
X/* --------------------
X	Reset terminal.
X-------------------- */
Xvoid
Xno_control()
X{
X	ioctl(0, TIOCSETP, &tty);
X	ioctl(0, TIOCSETP, &chrs);
X	ioctl(0, TIOCSETP, &lchrs);
X}
X
X
X/* --------------------
X	Set terminal.
X-------------------- */
Xvoid
Xto_control()
X{
X	ioctl(0, TIOCSETP, &t_tty);
X	ioctl(0, TIOCSETP, &t_chrs);
X	ioctl(0, TIOCSETP, &t_lchrs);
X}
X
X
X#define	mask(s)	(1 << ((s)-1))
X
X/* --------------------
X	Trap for ^Z.
X-------------------- */
Xvoid
Xtstp()
X{
X	int		x, y;
X
X	getyx(curscr, y, x);
X	to_normal();
X	fix_mh();
X
X	signal(SIGTSTP, SIG_DFL);
X	sigsetmask(sigblock(0) &~ mask(SIGTSTP));
X	kill(0, SIGTSTP);
X	sigblock(mask(SIGTSTP));
X	signal(SIGTSTP, tstp);
X
X	if(top_level) {
X		to_control();
X		touchwin(curscr);
X		wmove(curscr, y, x);
X		wrefresh(curscr);
X	}
X}
X
X
X/* --------------------
X	Trap for ^?.
X-------------------- */
Xvoid
Xtint()
X{
X	touchwin(stdscr);
X	addstatus("-- interrupt --", true);
X	longjmp(env, 0);	/* jump to main */
X}
X
X
X/* --------------------
X	Convert an ascii string to octal.
X-------------------- */
Xint
Xatoo(str)
X	char *str;
X{
X	int		i;
X
X	for(; *str < '0' && *str > '7' ; str++)
X		;
X	for(i=0 ; *str >= '0' && *str <= '7' ; str++)
X		i = i*8 + *str - '0';
X	return(i);
X}
X
X
X/* --------------------
X	Update MH environment - context and current mail item of current folder.
X-------------------- */
Xfix_mh()
X{
X	char str[LEN], buf[20];
X
X	update(context, "Current-Folder:", curflr->name, 15);
X	sprintf(str, "%s/%s/%s", mail_dir, curflr->name, SEQU);
X	sprintf(buf, "%d", curmail->number);
X	update(str, "cur:", buf, 4);
X}
X
X
X/* --------------------
X	Update file, replacing line beginning with match of len by "match new".
X-------------------- */
Xupdate(file, match, new, len)
X	char	*file, *match, *new;
X	int		len;
X{
X	FILE	*fp, *tmp, *fopen();
X	bool	change = false;
X	char	*mktemp(), *fgets();
X	char	str[LEN], *name = mktemp("/tmp/vmail.XXXXXX");
X
X	if((fp = fopen(file, "r")) == (FILE *) NULL) {
X		if((fp = fopen(file, "w+")) == (FILE *) NULL)
X			printf("Can't open %s for writing\n", file);
X		else {
X			fprintf(fp, "%s %s\n", match, new);
X			close(fp);
X		}
X	} else {
X		if((tmp = fopen(name, "w+")) == (FILE *) NULL)
X			printf("Can't open %s\n", file);
X		else {
X			while(fgets(str, LEN, fp) != (char *) NULL)
X				if(lstrncmp(str, match, len) == 0) {
X					change = true;
X					fprintf(tmp, "%s %s\n", match, new);
X				} else
X					fprintf(tmp, "%s", str);
X			if(! change)
X				fprintf(tmp, "%s %s\n", match, new);
X			fclose(fp);
X			fclose(tmp);
X			if((fp = fopen(file, "w+")) == (FILE *) NULL)
X				printf("Can't open %s for writing\n", file);
X			else {
X				tmp = fopen(name, "r");
X				while(fgets(str, LEN, tmp) != (char *) NULL)
X					fprintf(fp, "%s", str);
X				fclose(fp);
X				fclose(tmp);
X				unlink(name);
X			}
X		}
X	}
X}
!The!End!
exit