[mod.sources] v09i012: ELM Mail System, Part12/19

sources-request@mirror.TMC.COM (03/10/87)

Submitted by: Dave Taylor <hplabs!taylor>
Mod.sources: Volume 9, Issue 12
Archive-name: elm2/Part12

#! /bin/sh
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
# If this archive is complete, you will see the message:
#		"End of archive 12 (of 19)."
# Contents:  src/curses.q src/editmsg.c src/leavembox.c utils/readmsg.c
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo shar: Extracting \"src/curses.q\" \(11832 characters\)
if test -f src/curses.q ; then 
  echo shar: Will not over-write existing file \"src/curses.q\"
else
sed "s/^X//" >src/curses.q <<'END_OF_src/curses.q'
X/** 			curses.c		**/
X
X/**  This library gives programs the ability to easily access the
X     termcap information and write screen oriented and raw input
X     programs.  The routines can be called as needed, except that
X     to use the cursor / screen routines there must be a call to
X     InitScreen() first.  The 'Raw' input routine can be used
X     independently, however.
X
X     Modified 2/86 to work (hopefully) on Berkeley systems.  If
X     there are any problems with BSD Unix, please report them to
X     the author at hpcnoe!dat@HPLABS (fixed, if possible!)
X
X     (C) Copyright 1985 Dave Taylor, HP Colorado Networks
X**/
X
X#include "headers.h"
X
X#ifdef RAWMODE
X# ifdef BSD
X#  include <sgtty.h>
X# else
X#  include <termio.h>
X# endif
X#endif
X
X#include <ctype.h>
X
X#ifdef BSD
X#undef tolower
X#endif
X#include "curses.h"
X
X#ifdef BSD
X# include "/usr/include/curses.h"  	/* don't ask! */
X#endif
X
X#ifdef RAWMODE
X# define TTYIN	0
X#endif
X
Xextern int debug;
X
X#ifdef RAWMODE
X#  ifndef BSD
X    struct termio _raw_tty, 
X                _original_tty;
X#  endif
X
Xstatic int _inraw = 0;                  /* are we IN rawmode?    */
Xstatic int _line  = -1,			/* initialize to "trash" */
X	   _col   = -1;
X
X#ifdef UTS
Xstatic int _clear_screen = 0;		/* Next i/o clear screen? */
Xstatic char _null_string[SLEN];		/* a string of nulls...   */
X#endif
X
X#endif
X
Xstatic int _intransmit;			/* are we transmitting keys? */
X
Xstatic
Xchar *_clearscreen, *_moveto, *_up, *_down, *_right, *_left,
X     *_setbold, *_clearbold, *_setunderline, *_clearunderline, 
X     *_sethalfbright, *_clearhalfbright, *_setinverse, *_clearinverse,
X     *_cleartoeoln, *_cleartoeos, *_transmit_on, *_transmit_off;
Xstatic
Xint
X     _lines, _columns;
X
Xstatic char _terminal[1024];              /* Storage for terminal entry */
Xstatic char _capabilities[256];           /* String for cursor motion */
X
Xstatic char *ptr = _capabilities;	/* for buffering         */
X
Xint    outchar();			/* char output for tputs */
X
XInitScreen()
X{
X   /* Set up all this fun stuff: returns zero if all okay, or;
X        -1 indicating no terminal name associated with this shell,
X        -2..-n  No termcap for this terminal type known
X   */
X
X   int  tgetent(),      /* get termcap entry */
X        error;
X   char *tgetstr(),     /* Get termcap capability */
X        termname[40];
X
X#ifdef SUN
X   if (getenv("TERM") == NULL)
X     return(-1);
X#endif
X
X#ifdef UTS
X
X    /* use _line for lack of a better variable, what the heck! */
X
X    for (_line = 0; _line < SLEN; _line++)
X	_null_string[_line] = '\0';
X#endif
X
X   if (strcpy(termname, getenv("TERM")) == NULL)
X     return(-1);
X
X   if ((error = tgetent(_terminal, termname)) != 1)
X     return(error-2);
X
X   _line  =  1;		/* where are we right now?? */
X   _col   =  1;		/* assume zero, zero...     */
X
X   /* load in all those pesky values */
X   _clearscreen       = tgetstr("cl", &ptr);
X   _moveto            = tgetstr("cm", &ptr);
X   _up                = tgetstr("up", &ptr);
X   _down              = tgetstr("do", &ptr);
X   _right             = tgetstr("nd", &ptr);
X   _left              = tgetstr("bs", &ptr); 
X   _setbold           = tgetstr("so", &ptr);
X   _clearbold         = tgetstr("se", &ptr);
X   _setunderline      = tgetstr("us", &ptr);
X   _clearunderline    = tgetstr("ue", &ptr);
X   _setinverse        = tgetstr("so", &ptr);
X   _clearinverse      = tgetstr("se", &ptr);
X   _sethalfbright     = tgetstr("hs", &ptr);
X   _clearhalfbright   = tgetstr("he", &ptr);
X   _cleartoeoln       = tgetstr("ce", &ptr);
X   _cleartoeos        = tgetstr("cd", &ptr);
X   _lines	      = tgetnum("li");
X   _columns	      = tgetnum("co");
X   _transmit_on	      = tgetstr("ks", &ptr);
X   _transmit_off      = tgetstr("ke", &ptr);
X
X
X   if (!_left) {
X      _left = ptr;
X      *ptr++ = '\b';
X      *ptr++ = '\0';
X   }
X
X#ifdef BSD
X	initscr();	/* initalize curses too! */
X#endif
X
X   return(0);
X}
X
Xchar *return_value_of(termcap_label)
Xchar *termcap_label;
X{
X	/** This will return the string kept by termcap for the 
X	    specified capability. Modified to ensure that if 
X	    tgetstr returns a pointer to a transient address	
X	    that we won't bomb out with a later segmentation
X	    fault (thanks to Dave@Infopro for this one!) **/
X
X	static char escape_sequence[20];	
X
X   	char *tgetstr();     		/* Get termcap capability */
X
X	strcpy(escape_sequence, tgetstr(termcap_label, &ptr));
X	return( (char *) escape_sequence);
X}
X
Xtransmit_functions(newstate)
Xint newstate;
X{
X	/** turn function key transmission to ON | OFF **/
X	
X	if (newstate != _intransmit) {
X	  _intransmit = ! _intransmit;
X	  if (newstate == ON)
X   	    tputs(_transmit_on, 1, outchar);
X	  else 
X   	    tputs(_transmit_off, 1, outchar);
X
X   	  fflush(stdout);      /* clear the output buffer */
X	}
X}
X
X/****** now into the 'meat' of the routines...the cursor stuff ******/
X
XScreenSize(lines, columns)
Xint *lines, *columns;
X{
X	/** returns the number of lines and columns on the display. **/
X
X	*lines = _lines - 1;		/* assume index from zero */
X	*columns = _columns;
X}
X
XClearScreen()
X{
X        /* clear the screen: returns -1 if not capable */
X
X#ifdef UTS
X   if (isatube) {
X     _clear_screen++;	/* queue up for clearing... */
X     return(0);
X   }
X#endif
X
X   if (!_clearscreen) 
X     return(-1);
X
X   tputs(_clearscreen, 1, outchar);
X   fflush(stdout);      /* clear the output buffer */
X   return(0);
X}
X
XMoveCursor(row, col)
Xint row, col;
X{
X        /** move cursor to the specified row column on the screen.
X            0,0 is the top left! **/
X
X           char *tgoto();
X	   char *stuff;
X
X	_line = row;		/* update current location... */
X	_col  = col;
X
X#ifdef UTS
X	if (isatube) {
X	  at row, col;
X	  return(0);
X	}
X#endif
X        if (!_moveto) 
X          return(-1);
X
X        stuff = (char *) tgoto(_moveto, col, row);
X	tputs(stuff, 1, outchar);
X        fflush(stdout);
X        return(0);
X}
X
X
XCursorUp()
X{
X        /** move the cursor up one line **/
X
X	_line = (_line> 0? _line - 1: _line);	/* up one line... */
X
X#ifdef UTS
X	if (isatube) {
X	  at _line, _col;
X	  return(0);
X	}
X#endif
X        if (!_up)
X           return(-1);
X
X   	tputs(_up, 1, outchar);
X	fflush(stdout);
X        return(0);
X}
X
X
XCursorDown()
X{
X        /** move the cursor down one line **/
X
X	_line = (_line< LINES? _line + 1: _line);	/* up one line... */
X
X#ifdef UTS
X	if (isatube) {
X	  at _line, _col ;
X	  return(0);
X	}
X#endif
X
X       if (!_down) 
X          return(-1);
X
X       tputs(_down, 1, outchar);
X       fflush(stdout);
X       return(0);
X}
X
X
XCursorLeft()
X{
X        /** move the cursor one character to the left **/
X
X	_col = (_col > 0? _col - 1: _col);	/* up one line... */
X
X#ifdef UTS
X	if (isatube) {
X	  at _line, _col;
X	  return(0);
X	}
X#endif
X
X       if (!_left) 
X          return(-1);
X
X       tputs(_left, 1, outchar);
X       fflush(stdout);
X       return(0);
X}
X
X
XCursorRight()
X{
X        /** move the cursor one character to the right (nondestructive) **/
X
X	_col = (_col < COLUMNS? _col + 1: _col);	/* up one line... */
X
X#ifdef UTS
X	if (isatube) {
X	  at _line, _col;
X	  return(0);
X	}
X#endif
X
X       if (!_right) 
X          return(-1);
X
X       tputs(_right, 1, outchar);
X       fflush(stdout);
X       return(0);
X}
X
X
XStartBold()
X{
X        /** start boldface/standout mode **/
X
X       if (!_setbold) 
X         return(-1);
X
X       tputs(_setbold, 1, outchar);
X       fflush(stdout);
X       return(0);
X}
X
X
XEndBold()
X{
X        /** compliment of startbold **/
X
X        if (!_clearbold) 
X           return(-1);
X
X       tputs(_clearbold, 1, outchar);
X       fflush(stdout);
X       return(0);
X}
X
X
XStartUnderline()
X{
X        /** start underline mode **/
X
X       if (!_setunderline) 
X          return(-1);
X
X       tputs(_setunderline, 1, outchar);
X       fflush(stdout);
X       return(0);
X}
X
X
XEndUnderline()
X{
X        /** the compliment of start underline mode **/
X
X       if (!_clearunderline) 
X          return(-1);
X
X       tputs(_clearunderline, 1, outchar);
X       fflush(stdout);
X       return(0);
X}
X
X
XStartHalfbright()
X{
X        /** start half intensity mode **/
X
X       if (!_sethalfbright) 
X         return(-1);
X
X       tputs(_sethalfbright, 1, outchar);
X       fflush(stdout);
X       return(0);
X}
X
XEndHalfbright()
X{
X        /** compliment of starthalfbright **/
X
X       if (!_clearhalfbright) 
X          return(-1);
X
X       tputs(_clearhalfbright, 1, outchar);
X       fflush(stdout);
X       return(0);
X}
X
XStartInverse()
X{
X        /** set inverse video mode **/
X
X       if (!_setinverse) 
X         return(-1);
X
X       tputs(_setinverse, 1, outchar);
X       fflush(stdout);
X       return(0);
X}
X
X
XEndInverse()
X{
X        /** compliment of startinverse **/
X
X       if (!_clearinverse) 
X         return(-1);
X
X       tputs(_clearinverse, 1, outchar);
X       fflush(stdout);
X       return(0);
X}
X
XPutLine0(x, y, line)
Xint x,y;
Xchar *line;
X{
X	/** Write a zero argument line at location x,y **/
X
X	_line = x;
X	_col  = y;
X
X#ifdef UTS
X	if (isatube) {
X	  panel (erase=_clear_screen, cursor=_line, _col) {
X	    #ON, line, strlen(line)-1#
X	  }
X	  _clear_screen = 0;
X	  _col += (printable_chars(line) - 1);
X	  return(0);
X	}
X#endif
X	MoveCursor(x,y);
X	printf("%s", line);	/* to avoid '%' problems */
X	fflush(stdout);
X	_col += (printable_chars(line) - 1);
X}
X
XPutLine1(x,y, line, arg1)
Xint x,y;
Xchar *line;
Xchar *arg1;
X{
X	/** write line at location x,y - one argument... **/
X
X	char buffer[SLEN];
X	
X	sprintf(buffer, line, arg1);
X
X	PutLine0(x, y, buffer);
X}
X
XPutLine2(x,y, line, arg1, arg2)
Xint x,y;
Xchar *line;
Xchar *arg1, *arg2;
X{
X	/** write line at location x,y - one argument... **/
X
X	char buffer[SLEN];
X	
X	sprintf(buffer, line, arg1, arg2);
X
X	PutLine0(x, y, buffer);
X}
X
XPutLine3(x,y, line, arg1, arg2, arg3)
Xint x,y;
Xchar *line;
Xchar *arg1, *arg2, *arg3;
X{
X	/** write line at location x,y - one argument... **/
X
X	char buffer[SLEN];
X	
X	sprintf(buffer, line, arg1, arg2, arg3);
X
X	PutLine0(x, y, buffer);
X}
X
XCleartoEOLN()
X{
X        /** clear to end of line **/
X#ifdef UTS
X	char buffer[SLEN];
X	register int cols, i = 0;
X
X	if (isatube) {
X
X	  for (cols = _col; cols < COLUMNS; cols++)
X	    buffer[i++] = ' ';
X
X	  buffer[i] = '\0';
X	
X	  panel (noerase, cursor=_line, _col) {
X	    #ON, buffer, strlen(buffer)-1#
X	  }
X	}
X#endif
X
X       if (!_cleartoeoln) 
X         return(-1);
X
X       tputs(_cleartoeoln, 1, outchar);
X       fflush(stdout);  /* clear the output buffer */
X       return(0);
X}
X
XCleartoEOS()
X{
X        /** clear to end of screen **/
X
X#ifdef UTS
X	register int line_at;
X
X	for (line_at = _line; line_at < LINES-1; line_at++) {
X	  panel (noerase) { 
X	    #ON, _null_string, COLUMNS# 
X	  }
X	}
X	return(0);
X
X#endif
X
X       if (!_cleartoeos) 
X         return(-1);
X
X       tputs(_cleartoeos, 1, outchar);
X       fflush(stdout);  /* clear the output buffer */
X       return(0);
X}
X
X#ifdef RAWMODE
X
XRaw(state)
Xint state;
X{
X	/** state is either ON or OFF, as indicated by call **/
X
X        if (state == OFF && _inraw) {
X#ifdef BSD
X	  echo();
X	  nocrmode();
X#else
X	  (void) ioctl(TTYIN, TCSETAW, &_original_tty);
X#endif
X          _inraw = 0;
X	}
X        else if (state == ON && ! _inraw) {
X#ifdef BSD
X	   noecho();
X	   crmode();
X#else
X	  (void) ioctl(TTYIN, TCGETA, &_original_tty);	/** current setting **/
X	  	
X	  (void) ioctl(TTYIN, TCGETA, &_raw_tty);    /** again! **/
X	  _raw_tty.c_iflag &= ~(INLCR | ICRNL |BRKINT);
X	  _raw_tty.c_iflag |= IXON;
X	  _raw_tty.c_oflag |= OPOST;
X	  _raw_tty.c_oflag &= ~(OLCUC | ONLCR | OCRNL | ONOCR | ONLRET);
X	  _raw_tty.c_lflag &= ~(ICANON | ECHO);
X	  _raw_tty.c_cc[VMIN] = '\01';
X	  _raw_tty.c_cc[VTIME] = '\0';
X	  (void) ioctl(TTYIN, TCSETAW, &_raw_tty);
X#endif
X
X          _inraw = 1;
X        }
X}
X
Xint
XReadCh()
X{
X        /** read a character with Raw mode set! **/
X
X        register int result;
X        char ch;
X
X        result = read(0, &ch, 1);
X	
X	return(result == 0? EOF : ch);
X}
X
X#endif
X
Xoutchar(c)
Xchar c;
X{
X	/** output the given character.  From tputs... **/
X	/** Note: this CANNOT be a macro!              **/
X
X	putc(c, stdout);
X}
END_OF_src/curses.q
if test 11832 -ne `wc -c <src/curses.q`; then
    echo shar: \"src/curses.q\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"src/editmsg.c\" \(12220 characters\)
if test -f src/editmsg.c ; then 
  echo shar: Will not over-write existing file \"src/editmsg.c\"
else
sed "s/^X//" >src/editmsg.c <<'END_OF_src/editmsg.c'
X/**			editmsg.c			**/
X
X/** This contains routines to do with starting up and using an editor (or two)
X    from within Elm.  This stuff used to be in mailmsg2.c...
X
X    (C) Copyright 1986, Dave Taylor
X**/
X
X#include "headers.h"
X#include <errno.h>
X#include <setjmp.h>
X#include <signal.h>
X
Xextern int errno;
X
Xchar *error_name(), *error_description(), *strcpy();
Xunsigned long sleep();
X
Xint
Xedit_the_message(filename, already_has_text)
Xchar *filename;
Xint  already_has_text;
X{
X	/** Invoke the users editor on the filename.  Return when done. 
X	    If 'already has text' then we can't use the no-editor option
X	    and must use 'alternative editor' (e.g. $EDITOR or default_editor)
X	    instead... **/
X
X	char buffer[SLEN];
X	register int stat, return_value = 0;
X
X	buffer[0] = '\0';
X
X	if (strcmp(editor, "builtin") == 0 || strcmp(editor, "none") == 0) {
X	  if (already_has_text) 
X	    sprintf(buffer, "%s %s", alternative_editor, filename);
X	  else
X	    return(no_editor_edit_the_message(filename));
X	}
X
X	PutLine0(LINES, 0, "invoking editor...");  fflush(stdout);
X	if (strlen(buffer) == 0)
X	  sprintf(buffer,"%s %s", editor, filename);
X
X	Raw(OFF);
X
X	chown(filename, userid, groupid);	/* file was owned by root! */
X
X	if (cursor_control)
X	  transmit_functions(OFF);		/* function keys are local */
X
X	if ((stat = system_call(buffer, SH)) != 0) { 
X	  dprint1(1,"System call failed with stat %d (edit_the_message)\n", 
X		  stat);
X	  error1("Can't invoke editor '%s' for composition", editor);
X	  dprint2(1,"** %s - %s **\n", error_name(errno), 
X		error_description(errno));
X	  ClearLine(LINES-1);
X	  sleep(2);
X	  return_value = 1;
X	}
X
X	if (cursor_control)
X	  transmit_functions(ON);		/* function keys are local */
X	
X	Raw(ON);
X
X	return(return_value);
X}
X
Xextern char to[VERY_LONG_STRING], cc[VERY_LONG_STRING], 
X	    expanded_to[VERY_LONG_STRING], expanded_cc[VERY_LONG_STRING], 
X            subject[SLEN];
X
Xint      interrupts_while_editing;	/* keep track 'o dis stuff         */
Xjmp_buf  edit_location;		        /* for getting back from interrupt */
X
X#ifdef ALLOW_BCC
Xextern char bcc[VERY_LONG_STRING], expanded_bcc[VERY_LONG_STRING];
X#endif
X
Xchar *strip_commas();
X
Xint
Xno_editor_edit_the_message(filename)
Xchar *filename;
X{
X	/** If the current editor is set to either "builtin" or "none", then
X	    invoke this program instead.  As it turns out, this and the 
X	    routine above have a pretty incestuous relationship (!)...
X	**/
X
X	FILE *edit_fd;
X	char buffer[SLEN], editor_name[SLEN];
X	int      edit_interrupt(), (*oldint)(), (*oldquit)();
X
X	Raw(OFF);
X
X	if ((edit_fd = fopen(filename, "a")) == NULL) {
X	  error2("Couldn't open %s for appending [%s]", filename, 
X		  error_name(errno));
X	  sleep (2);
X	  dprint1(1,"Error encountered trying to open file %s;\n", filename);
X	  dprint2(1,"** %s - %s **\n", error_name(errno),
X		    error_description(errno));
X	  Raw(ON);
X	  return(1);
X	}
X
X	/** file open...let's start accepting input! **/
X
X	printf(
X          "\nPlease enter message, ^D to end, or ~? <RETURN> for help;\n\n");
X
X	oldint  = signal(SIGINT,  edit_interrupt);
X	oldquit = signal(SIGQUIT, edit_interrupt);
X
X	interrupts_while_editing = 0;
X
Xmore_input:
X
X	while (gets(buffer) != NULL) {
X
X	  if (setjmp(edit_location) != 0) {
X	    if (interrupts_while_editing > 1) {
X	      Raw(ON);
X
X	      (void) signal(SIGINT,  oldint);
X	      (void) signal(SIGQUIT, oldquit);
X
X	      if (edit_fd != NULL)	/* insurance... */
X	        fclose(edit_fd);
X	      return(1);
X	    }
X	    goto more_input;	/* read input again, please! */
X	  }
X	
X	  interrupts_while_editing = 0;	/* reset to zero... */
X
X	  if (strcmp(buffer, ".") == 0)
X	    break;	/* '.' is as good as a ^D to us dumb programs :-) */
X	  if (buffer[0] == TILDE) 
X	    switch (tolower(buffer[1])) {
X	      case '?' : tilde_help();	printf("(continue)\n"); goto more_input;
X	      case '~' : move_left(buffer, 1); goto tilde_input;	/*!!*/
X
X	      case 't' : get_with_expansion("To: ", to, expanded_to);	break;
X#ifdef ALLOW_BCC
X	      case 'b' : get_with_expansion("Bcc: ", bcc,expanded_bcc);	break;
X#endif
X	      case 'c' : get_with_expansion("Cc: ", cc, expanded_cc);	break;
X	      case 's' : get_with_expansion("Subject: ", subject,NULL);	break;
X
X	      case 'h' : get_with_expansion("To: ", to, expanded_to);	
X	                 get_with_expansion("Cc: ", cc, expanded_cc);
X#ifdef ALLOW_BCC
X	                 get_with_expansion("Bcc: ", bcc,expanded_bcc);
X#endif
X	                 get_with_expansion("Subject: ", subject,NULL);	break;
X
X	      case 'r' : read_in_file(edit_fd, (char *) buffer + 2);
X		  	 goto more_input;
X	      case 'e' : if (strlen(emacs_editor) > 0) 
X	                   if (access(emacs_editor, ACCESS_EXISTS) == 0) {
X	                     strcpy(buffer, editor);
X			     strcpy(editor, emacs_editor);
X	  		     fclose(edit_fd);
X			     (void) edit_the_message(filename,0);
X	   		     Raw(OFF);
X			     strcpy(editor, buffer);
X			     edit_fd = fopen(filename, "a");
X		          printf("(continue entering message, ^D to end)\n");
X	                     goto more_input;
X		           }
X		           else
X	                     printf(
X			  "(Can't find Emacs on this system!  continue)\n");
X			 else
X		            printf(
X			"(Don't know where Emacs would be...continue)\n");	
X			 goto more_input;
X
X	       case 'v' : strcpy(buffer, editor);
X			  strcpy(editor, default_editor);
X			  fclose(edit_fd);
X			  (void) edit_the_message(filename,0);
X	   		  Raw(OFF);
X			  strcpy(editor, buffer);
X			  edit_fd = fopen(filename, "a");
X		          printf("(continue entering message, ^D to end)\n");
X	                  goto more_input;
X
X	       case 'o' : printf("Please enter the name of the editor : ");
X			  gets(editor_name);
X	                  if (strlen(editor_name) > 0) {
X	                    strcpy(buffer, editor);
X			    strcpy(editor, editor_name);
X			    fclose(edit_fd);
X			    (void) edit_the_message(filename,0);
X	 		    Raw(OFF);
X			    strcpy(editor, buffer);
X			    edit_fd = fopen(filename, "a");
X		          printf("(continue entering message, ^D to end)\n");
X	                    goto more_input;
X		          }
X	  		  printf("(continue)\n");
X	                  goto more_input; 
X		case '!' : if (strlen(buffer) < 3) 
X			     (void) system_call(shell, USER_SHELL);
X			   else
X	                     (void) system_call((char *) buffer+2, USER_SHELL);
X	    		   printf("(continue)\n");
X			   goto more_input;
X		 case 'm' : /* same as 'f' but with leading prefix added */
X	         case 'f' : /* this can be directly translated into a
X			       'readmsg' call with the same params! */
X			    read_in_messages(edit_fd, (char *) buffer + 1);
X			    goto more_input;
X	         case 'p' : /* print out message so far.  Soooo simple! */
X			    print_message_so_far(edit_fd, filename);
X			    goto more_input;
X		 default  : printf(
X			     "(don't know what ~%c is.  Try ~? for help)\n",
X				    buffer[1]);
X	       }
X	     else
Xtilde_input:
X	       fprintf(edit_fd, "%s\n", buffer);
X	};
X
X	printf("<end-of-message>\n\n");
X
X	Raw(ON);
X
X	(void) signal(SIGINT,  oldint);
X	(void) signal(SIGQUIT, oldquit);
X
X	if (edit_fd != NULL)	/* insurance... */
X	  fclose(edit_fd);
X
X	return(0);
X}
X
Xtilde_help()
X{
X	/* a simple routine to print out what is available at this level */
X
X	printf(
X"(Available 'tilde' commands at this point are;\n\
X\n\
X\t~?\tPrint this help menu\n\
X\t~~\tAdd line prefixed by a single '~' character\n");
X
X#ifdef ALLOW_BCC
X	printf("\
X\t~b\tChange the addresses in the Blind-carbon-copy list\n");
X#endif
X
X	printf("\
X\t~c\tChange the addresses in the Carbon-copy list\n\
X\t~e\tInvoke the Emacs editor on the message, if possible\n\
X\t~f\tadd the specified list of messages, or current\n");
X#ifdef ALLOW_BCC
X	printf("\t~h\tchange all available headers (to,cc,bcc,subject)\n");
X#endif
X	printf("\
X\t~m\tsame as '~f', but with the current 'prefix'\n\
X\t~o\tInvoke a user specified editor on the message\n\
X\t~p\tprint out message as typed in so far\n\
X\t~r\tRead in the specified file\n\
X\t~s\tChange the subject of the message\n\
X\t~t\tChange the addresses in the To list\n\
X\t~v\tInvoke the Vi visual editor on the message\n\
X\t~!\texecute a unix command (or give a shell if no command)\n\
X\n");
X
X}
X
Xread_in_file(fd, filename)
XFILE *fd;
Xchar *filename;
X{
X	/** Open the specified file and stream it in to the already opened 
X	    file descriptor given to us.  When we're done output the number
X	    of lines we added, if any... **/
X
X	FILE *myfd;
X	char myfname[SLEN], buffer[LONG_SLEN];
X	register int lines = 0;
X
X	while (whitespace(filename[lines])) lines++;
X
X	strcpy(myfname, (char *) filename + lines);
X
X	if (strlen(myfname) == 0) {
X	  printf("(no filename specified for file read!  Continue...)\n");
X	  return;
X	}
X
X	if ((myfd = fopen(myfname,"r")) == NULL) {
X	  printf("(Couldn't open file '%s' for reading!  Continue...)\n",
X		 myfname);
X	  return;
X	}
X
X	lines = 0;
X
X	while (fgets(buffer, LONG_SLEN, myfd) != NULL) {
X	  lines++;
X  	  fputs(buffer, fd);
X	  fflush(stdout);
X	}
X
X	fclose(myfd);
X
X	printf("(added %d line%s from file %s.  Please continue...)\n",
X		lines, plural(lines), myfname);
X	return;
X}
X
Xprint_message_so_far(edit_fd, filename)
XFILE *edit_fd;
Xchar *filename;
X{
X	/** This prints out the message typed in so far.  We accomplish
X	    this in a cheap manner - close the file, reopen it for reading,
X	    stream it to the screen, then close the file, and reopen it
X	    for appending.  Simple, but effective!
X
X	    A nice enhancement would be for this to -> page <- the message
X	    if it's sufficiently long.  Too much work for now, though.
X	**/
X	
X	char buffer[LONG_SLEN];
X
X	fclose(edit_fd);
X
X	if ((edit_fd = fopen(filename, "r")) == NULL) {
X	  printf("\nMayday!  Mayday!  Mayday!\n");
X	  printf("\nPanic: Can't open file for reading!  Bail!\n");
X	  emergency_exit();
X	}
X
X	printf("To: %s\n", format_long(to, 4));
X	printf("Cc: %s\n", format_long(cc, 4));
X#ifdef ALLOW_BCC
X	printf("Bcc: %s\n", format_long(bcc, 5));
X#endif
X	printf("Subject: %s\n\n", subject);
X
X	while (fgets(buffer, LONG_SLEN, edit_fd) != NULL)
X	  printf("%s", buffer);
X
X	fclose(edit_fd);
X
X	if ((edit_fd = fopen(filename, "a")) == NULL) {
X	  printf("Mayday!  Mayday!  Abandon Ship!  Aiiieeeeee\n");
X	  printf("\nPanic: Can't reopen file for appending!\n");
X	  emergency_exit();
X	}
X
X	printf("(continue entering message, please)\n\n");
X}
X
Xread_in_messages(fd, buffer)
XFILE *fd;
Xchar *buffer;
X{
X	/** Read the specified messages into the open file.  If the
X	    first character of "buffer" is 'm' then prefix it, other-
X	    wise just stream it in straight...
X	**/
X
X	FILE *myfd, *popen();
X	char  local_buffer[LONG_SLEN];
X	register int lines = 0, add_prefix=0;
X
X	add_prefix = (tolower(buffer[0]) == 'm');
X
X	sprintf(local_buffer, "%s %s", readmsg, ++buffer);
X
X	if ((myfd = popen(local_buffer, "r")) == NULL) {
X	   printf("(can't get to 'readmsg' command.  Sorry...)\n");
X	   return;	
X	}
X
X	while (fgets(local_buffer, LONG_SLEN, myfd) != NULL) {
X	  lines++;
X	  if (add_prefix)
X	    fprintf(fd, "%s%s", prefixchars, local_buffer);
X	  else 
X	    fputs(local_buffer, fd);
X	}
X
X	pclose(myfd);
X
X	if (lines == 0)
X	  printf("(Couldn't add the requested message.   Continue)\n");
X	else
X	  printf("(added %d line%s to message...  Please continue)\n",
X		lines, plural(lines));
X
X	return;
X}
X
Xget_with_expansion(prompt, buffer, expanded_buffer)
Xchar *prompt, *buffer, *expanded_buffer;
X{
X	/** This is used to prompt for a new value of the specified field.
X	    If expanded_buffer == NULL then we won't bother trying to expand
X	    this puppy out!
X	**/
X
X	char mybuffer[VERY_LONG_STRING];
X
X	printf(prompt);	fflush(stdout);	/* output! */
X
X	strcpy(mybuffer, buffer);
X
X	Raw(ON);
X	optionally_enter(buffer, -1, -1, TRUE);	/* already data! */
X	Raw(OFF);
X	putchar('\n');
X
X	if (strcmp(buffer, mybuffer) != 0 && expanded_buffer != NULL) 
X	  build_address(strip_commas(buffer), expanded_buffer);
X
X	return;
X}
X
Xedit_interrupt()
X{
X	/** This routine is called when the user hits an interrupt key
X	    while in the builtin editor...it increments the number of 
X	    times an interrupt is hit and returns it.
X	**/
X
X	signal(SIGINT, edit_interrupt);
X	signal(SIGQUIT, edit_interrupt);
X
X	if (! interrupts_while_editing++)
X	  printf("(Interrupt.  One more to cancel this letter...)\n\r");
X
X	longjmp(edit_location);		/* get back */
X}
END_OF_src/editmsg.c
if test 12220 -ne `wc -c <src/editmsg.c`; then
    echo shar: \"src/editmsg.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"src/leavembox.c\" \(12961 characters\)
if test -f src/leavembox.c ; then 
  echo shar: Will not over-write existing file \"src/leavembox.c\"
else
sed "s/^X//" >src/leavembox.c <<'END_OF_src/leavembox.c'
X/**			leavembox.c			**/
X
X/** leave current mailbox, updating etc. as needed...
X  
X    (C) Copyright 1985, Dave Taylor
X**/
X
X#include "headers.h"
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <errno.h>
X
X#define  ECHOIT 	1	/* echo on for prompting! */
X
X/** added due to a bug in the 2.1 OS **/
X
Xstruct utimbuf {
X	time_t	actime;		/** access time **/
X	time_t  modtime;	/** modification */
X       };
X
Xextern int errno;
X
Xchar *error_name(), *error_description(), *strcpy();
Xunsigned short getegid();
Xunsigned long sleep();
X
Xint
Xleave_mbox(quitting)
Xint quitting;
X{
X	/** Exit, saving files into mbox and deleting specified, or simply 
X	    delete specified mail... If "quitting" is true, then output status 
X            regardless of what happens.  Returns 1 iff mailfile was
X	    changed (ie messages deleted from file), 0 if not, and -1 if new
X	    mail has arrived in the meantime...
X	**/
X
X	FILE *temp;
X	char outfile[SLEN], buffer[SLEN];
X	struct stat buf;		/* stat command  */
X	struct utimbuf times;		/* utime command */
X	register int to_delete = 0, to_save = 0, i, mode = 00644,
X		     pending = 0, number_saved = 0, last_sortby;
X	char dflt;
X	long bytes();
X
X	dprint0(1,"\n\n-- leaving_mailbox --\n\n");
X
X	if (message_count == 0) 
X	  return(FALSE);	/* nothing changed */
X
X	for (i = 0; i < message_count; i++)
X	  if (ison(header_table[i].status, DELETED)) to_delete++;
X	  else                                       to_save++;
X
X	dprint2(2,"Count: %d to delete and %d to save\n", to_delete, to_save);
X
X	if (mbox_specified == 0) 
X	  update_mailtime();
X
X	if (hp_softkeys && question_me) {
X	  define_softkeys(YESNO);		/* YES or NO on softkeys */
X	  softkeys_on();
X	}
X
X	if (always_del) 	/* set up the default answer... */
X	  dflt = 'y';
X	else 
X	  dflt = 'n';
X
X	if (question_me && to_delete)
X	  if (to_save) {
X	    fflush(stdin);
X	    sprintf(buffer, "Delete message%s? (y/n) ", plural(to_delete));
X	    if (want_to(buffer, dflt, ECHOIT) != 'y') {
X	      if (mbox_specified == 0) unlock();	/* remove lock! */
X	      dprint1(3,"\tDelete message%s? - answer was NO\n", 
X			plural(to_delete));
X	      error("Nothing deleted");
X	      return(FALSE);	/* nothing was deleted! */
X	    }
X	  }
X	  else if (! to_save) {		/* nothing to save!! */
X	    fflush(stdin);
X	    if (want_to("Delete all mail? (y/n) ", dflt, ECHOIT)!='y') {
X	      if (mbox_specified == 0) unlock();	/* remove lock! */
X	      dprint0(3,"Delete all mail? - answer was NO\n");
X	      error("Nothing deleted");
X	      return(FALSE);   /* nothing was deleted */
X	    }
X	  }
X
X	if (always_leave) 
X	  dflt = 'y';
X	else
X	  dflt = 'n';
X	  
X	/** we have to check to see what the sorting order was...so that
X	    the order of saved messages is the same as the order of the
X	    messages originally (a subtle point...) **/
X
X	if (sortby != RECEIVED_DATE) {	/* what we want anyway! */
X	  last_sortby = sortby;
X	  sortby = RECEIVED_DATE;
X	  sort_mailbox(message_count, FALSE);
X	  sortby = last_sortby;
X	}
X
X	if (question_me && to_save && mbox_specified == 0) {
X	  fflush(stdin);
X	  if (want_to("Keep mail in incoming mailbox? (y/n) ",dflt, ECHOIT)
X	      == 'y') 
X	      if (to_delete)	    /* okay - keep undeleted as pending!    */
X	        pending++;
X	      else {	   	    /* gag! nothing to delete, don't save!  */	
X	        unlock();	    /* remove mailfile lock!	   	    */
X	        dprint0(3,"Keep mail in incoming mailbox? -- answer was YES\n");
X	        error("Mailbox unchanged");
X	        return(FALSE);	/* nothing changed! */
X	      }
X	}
X
X	/** okay...now lets do it! **/
X
X	if (to_save > 0) {
X	  if (to_delete > 0)
X	    sprintf(buffer ,"[%s %d message%s, and deleting %d]", 
X	          pending? "keeping" : "storing", 
X		  to_save, plural(to_save), to_delete);
X	  else if (quitting)
X	    sprintf(buffer,"[%s %s]",
X	          pending? "keeping" : "storing",
X		  to_save > 1? "all messages" : "message");
X	  else
X	    buffer[0] = '\0';	/* no string! */
X	}
X	else {
X	  if (to_delete > 0)
X	    sprintf(buffer, "[deleting all messages]");
X	  else if (quitting)
X	    sprintf(buffer, "[no messages to %s, and none to delete]",
X	            pending? "keep" : "save");
X	  else
X	    buffer[0] = '\0';
X	}
X
X	dprint1(2,"Action: %s\n", buffer);
X
X	error(buffer);
X
X	if (! mbox_specified) {
X	  if (pending) {                /* keep some messages pending! */
X	    sprintf(outfile,"%s%d", temp_mbox, getpid());
X	    unlink(outfile);
X	  }
X	  else if (mailbox_defined)	/* save to specified mailbox */
X	    strcpy(outfile, mailbox);
X	  else				/* save to $home/mbox */
X	    sprintf(outfile,"%s/mbox", home);
X	}
X	else {
X	  if (! to_delete) return(FALSE);	/* no work to do! */
X          sprintf(outfile, "%s%d", temp_file, getpid());
X	  unlink(outfile); /* ensure it's empty! */
X	}
X
X	if (to_save) {
X	  if ((errno = can_open(outfile, "a"))) {
X	    error1(
X	         "Permission to append to %s denied!  Leaving mailbox intact\n",
X		 outfile);
X	    dprint2(1,
X		 "Error: Permission to append to outfile %s denied!! (%s)\n",
X		 outfile, "leavembox");
X	    dprint2(1,"** %s - %s **\n", error_name(errno),
X		 error_description(errno));
X	    unlock();
X	    return(0);
X	  }
X	  if ((temp = fopen(outfile,"a")) == NULL) {
X	    if (mbox_specified == 0)
X	      unlock();		/* remove mailfile lock! */
X	    dprint1(1,"Error: could not append to file %s\n", 
X		    outfile);
X	    dprint2(1,"** %s - %s **\n", error_name(errno),
X		 error_description(errno));
X	    sprintf(buffer, "           Could not append to file %s!          ",
X		    outfile);
X	    Centerline(LINES-1, buffer);
X	    emergency_exit();
X	  }
X  
X	  for (i = 0; i < message_count; i++)
X	    if (! (header_table[i].status & DELETED)) {
X	      current = i+1;
X	      if (! number_saved++) {
X	        dprint2(2,"Saving message%s #%d, ", plural(to_save), current);
X	      }
X	      else {
X		dprint1(2,"#%d, ", current);
X	      }
X	      copy_message("", temp, FALSE, FALSE);
X	    }
X	  fclose(temp);
X	  dprint0(2,"\n\n");
X	}
X
X	/* remove source file...either default mailbox or original copy of 
X           specified one! */
X
X	/** let's grab the original mode and date/time of the mailfile 
X	    before removing it **/
X
X        if (stat(infile, &buf) == 0)
X	  mode = buf.st_mode & 00777;
X	else {
X	  dprint2(1,"Error: errno %s attempting to stat file %s\n", 
X		     error_name(errno), infile);
X          error3("Error %s (%s) on stat(%s)", error_name(errno), 
X		error_description(errno), infile);
X	}
X
X	fclose(mailfile);	/* close the baby... */
X	
X	if (mailfile_size != bytes(infile)) {
X	  sort_mailbox(message_count, FALSE);	/* display sorting order! */
X	  unlock();
X	  error("New mail has just arrived - resyncing...");
X	  return(-1);
X	}
X	unlink(infile); 	/* and BLAMO!        */
X
X	if (to_save && (mbox_specified || pending)) {
X	  if (link(outfile, infile) != 0) 
X	    if (errno == EXDEV) { /** different file devices!  Use copy! **/
X	      if (copy(outfile, infile) != 0) {
X	        dprint2(1,"leavembox: copy(%s, %s) failed;",
X			outfile, infile);
X	        dprint2(1,"** %s - %s **\n", error_name(errno),
X		     error_description(errno));
X	        error("couldn't modify mail file!");
X	        sleep(1);
X	        sprintf(infile,"%s/%s", home, unedited_mail);
X		if (copy(outfile, infile) != 0) {
X	          dprint1(1,"leavembox: couldn't copy to %s either!!  Help;", 
X			  infile);
X	          dprint2(1,"** %s - %s **\n", error_name(errno),
X		          error_description(errno));
X	          error("something godawful is happening to me!!!");
X		  emergency_exit();
X	        }
X		else {
X	          dprint1(1,"\nWoah! Confused - Saved mail in %s (leavembox)\n", 
X			  infile);
X	          error1("saved mail in %s", infile);
X	        }
X	      }	
X	    }
X	    else {
X	      dprint2(1,"link(%s, %s) failed (leavembox)\n", outfile, infile);
X	      dprint2(1,"** %s - %s **\n", error_name(errno),
X			error_description(errno));
X	      error2("link failed! %s - %s", error_name(errno),
X		error_description(errno));
X	      emergency_exit();
X	    }
X	  unlink(outfile);
X	}
X	else if (keep_empty_files) {
X	  sleep(1);
X	  error1("..keeping empty mail file '%s'..", infile);
X	  temp = fopen(infile, "w");
X	  fclose(temp);
X	  chmod(infile, mode);
X	  chown(infile, userid, groupid);
X	}
X
X	if (mbox_specified == 0) {
X	  if (mode != 00644) { /* if not the default mail access mode... */
X	    if (! pending) { /* if none still being saved */
X	      temp = fopen(infile, "w");
X	      fclose(temp);
X	    }
X	    chmod(infile,mode);
X
X	    /* let's set the access times of the new mail file to be
X	       the same as the OLD one (still sitting in 'buf') ! */
X
X	    times.actime = buf.st_atime;
X	    times.modtime= buf.st_mtime;
X
X	    if (utime(infile, &times) != 0) {
X	      dprint0(1,"Error: encountered error doing utime (leavmbox)\n");
X	      dprint2(1,"** %s - %s **\n", error_name(errno), 
X		     error_description(errno));
X	      error2("Error %s trying to change file %s access time", 
X		     error_name(errno), infile);
X	    }
X	  }
X	  unlock();	/* remove the lock on the file ASAP! */
X
X	  /** finally, let's change the ownership of the default
X	      outgoing mailbox, if needed **/
X
X	  if (to_save) 
X	    chown(outfile, userid, groupid);
X	}
X
X#ifdef SAVE_GROUP_MAILBOX_ID
X	chown(infile, userid, getegid());	/** see the Config Guide **/
X#else
X	chown(infile, userid, groupid);		/**  file owned by user  **/
X#endif
X
X	return(to_delete);	
X}
X
Xchar lock_name[SLEN];
X
Xlock(direction)
Xint direction;
X{
X	/** Create lock file to ensure that we don't get any mail 
X	    while altering the mailbox contents!
X	    If it already exists sit and spin until 
X               either the lock file is removed...indicating new mail
X	    or
X	       we have iterated MAX_ATTEMPTS times, in which case we
X	       either fail or remove it and make our own (determined
X	       by if REMOVE_AT_LAST is defined in header file
X
X	    If direction == INCOMING then DON'T remove the lock file
X	    on the way out!  (It'd mess up whatever created it!).
X	**/
X
X	register int iteration = 0, access_val, lock_fd;
X
X	sprintf(lock_name,"%s%s.lock", mailhome, username);
X
X	access_val = access(lock_name, ACCESS_EXISTS);
X
X	while (access_val != -1 && iteration++ < MAX_ATTEMPTS) {
X	  dprint1(2,"File '%s' currently exists!  Waiting...(lock)\n", 
X		  lock_name);
X	  if (direction == INCOMING)
X	    PutLine0(LINES, 0, "\nMail being received!\twaiting...");
X	  else
X	    error1("Attempt %d: Mail being received...waiting", 
X                   iteration);
X	  sleep(5);
X	  access_val = access(lock_name, ACCESS_EXISTS);
X	}
X	
X	if (access_val != -1) {
X
X#ifdef REMOVE_AT_LAST
X
X	  /** time to waste the lock file!  Must be there in error! **/
X
X	  dprint0(2,
X	     "Warning: I'm giving up waiting - removing lock file(lock)\n");
X	  if (direction == INCOMING)
X	    PutLine0(LINES, 0,"\nTimed out - removing current lock file...");
X	  else
X	    error("Throwing away the current lock file!");
X
X	  if (unlink(lock_name) != 0) {
X	    dprint3(1,"Error %s (%s)\n\ttrying to unlink file %s (%s)\n", 
X		     error_name(errno), error_description(errno), lock_name);
X	    PutLine1(LINES, 0, 
X		   "\n\rI couldn't remove the current lock file %s\n\r", 
X		   lock_name);
X	    PutLine2(LINES, 0, "** %s - %s **\n\r", error_name(errno),
X		   error_description(errno));
X	    if (direction == INCOMING)
X	      leave();
X	    else
X	      emergency_exit();
X	  }
X	  
X	  /* everything is okay, so lets act as if nothing had happened... */
X
X#else
X
X	  /* okay...we die and leave, not updating the mailfile mbox or
X	     any of those! */
X	  if (direction == INCOMING) {
X	    PutLine1(LINES, 0, "\nGiving up after %d iterations...", iteration);
X	    PutLine0(LINES, 0, 
X		"Please try to read your mail again in a few minutes.\n");
X	    dprint1(2,"Warning:bailing out after %d iterations...(lock)\n", 
X		    iteration);
X	    leave_locked(0);
X	  }
X	  else {
X	    dprint1(2,"Warning: after %d iterations, timed out! (lock)\n", 
X		    iteration);
X	    leave(error("Timed out on lock file reads.  Leaving program"));
X	  }
X
X#endif
X	}
X
X	/* if we get here we can create the lock file, so lets do it! */
X
X	if ((lock_fd = creat(lock_name, 0)) == -1) {
X	  dprint2(1,"Can't create lock file: creat(%s) raises error %s (lock)\n", 
X		  lock_name, error_name(errno));
X	  if (errno == EACCES)
X	    leave(error1(
X                 "Can't create lock file!  I need write permission in %s!\n\r",
X		  mailhome));
X	  else {
X	    dprint1(1,"Error encountered attempting to create lock %s\n", 
X		  lock_name);
X	    dprint2(1,"** %s - %s **\n", error_name(errno),
X		  error_description(errno));
X	    PutLine1(LINES, 0,
X         "\n\rError encountered while attempting to create lock file %s;\n\r", 
X		  lock_name);
X	    PutLine2(LINES, 0, "** %s - %s **\n\r", error_name(errno),
X		  error_description(errno));
X	    leave();
X	  }
X	}
X	close(lock_fd);	/* close it.  We don't want to KEEP the thing! */
X}
X
Xunlock()
X{
X	/** Remove the lock file!    This must be part of the interrupt
X	    processing routine to ensure that the lock file is NEVER
X	    left sitting in the mailhome directory! **/
X
X	(void) unlink(lock_name);
X}
END_OF_src/leavembox.c
if test 12961 -ne `wc -c <src/leavembox.c`; then
    echo shar: \"src/leavembox.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"utils/readmsg.c\" \(13084 characters\)
if test -f utils/readmsg.c ; then 
  echo shar: Will not over-write existing file \"utils/readmsg.c\"
else
sed "s/^X//" >utils/readmsg.c <<'END_OF_utils/readmsg.c'
X/**			readmsg.c			**/
X
X/** This routine adds the functionality of the "~r" command to the Elm mail 
X    system while still allowing the user to use the editor of their choice.
X
X    The program, without any arguments, tries to read a file in the users home 
X    directory called ".readmsg" (actually defined in the sysdefs.h system 
X    defines file) and if it finds it reads the current message.  If it doesn't 
X    find it, it will return a usage error.
X
X    The program can also be called with an explicit message number, list of 
X    message numbers, or a string to match in the message (including the header).
X    NOTE that when you use the string matching option it will match the first 
X    message containing that EXACT (case sensitive) string and then exit.
X
X    Changed 5/86 to SORT the input list of message numbers to ensure that
X    they're in first-to-last order...
X	
X    Added the "weed" option as the default.  This is inspired by the mail
X    system used at NASA RIACS.  If THEY can do it, so can we!!
X
X    Also added '*' as a flag - indicating ALL messages in the mailbox.
X
X    (C) Copyright 1985, Dave Taylor
X
X**/
X
X#include <stdio.h>
X#include <ctype.h>
X
X#include "defs.h"
X
X/** three defines for what level of headers to display **/
X
X#define ALL		1
X#define WEED		2
X#define NONE		3
X
X#define metachar(c)	(c == '=' || c == '+' || c == '%')
X
Xstatic char ident[] = { WHAT_STRING };
X
X#define  MAX_LIST	25		/* largest single list of arguments */
X
X#define  LAST_MESSAGE	9999		/* last message in list ('$' char)  */
X#define  LAST_CHAR	'$'		/* char to delimit last message..   */
X#define  STAR		'*'		/* char to delimit all messages...  */
X
Xint read_message[MAX_LIST]; 		/* list of messages to read	    */
Xint messages = 0;			/* index into list of messages      */
X
Xint numcmp();				/* strcmp, but for numbers          */
Xchar *words(),				/* function defined below...        */
X     *expand_define();			/*     ditto...                     */
X
X#define DONE	0			/* for use with the getopt	    */
X#define ERROR   -1			/*   library call...		    */
X
Xextern char *optional_arg;		/* for parsing the ... 		    */
Xextern int   opt_index;			/*  .. starting arguments           */
X
Xchar *getenv();				/* keep lint happy */
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	FILE *file;			        /* generic file descriptor! */
X	char filename[SLEN], 			/* filename buffer          */
X	     infile[SLEN],			/* input filename	    */
X	     buffer[SLEN], 			/* file reading buffer      */
X	     string[SLEN];			/* string match buffer      */
X
X	int current_in_queue = 0, 		/* these are used for...     */
X	    current = 0,			/* ...going through msgs     */
X	    list_all_messages = 0,		/* just list 'em all??       */
X	    num, 				/* for argument parsing      */
X	    page_breaks = 0,			/* use "^L" breaks??         */
X            total,				/* number of msgs current    */
X	    include_headers = WEED, 		/* flag: include msg header? */
X	    last_message = 0, 			/* flag: read last message?  */
X	    not_in_header = 0,			/* flag: in msg header?      */
X	    string_match = 0;			/* flag: using string match? */
X
X	/**** start of the actual program ****/
X
X	while ((num = get_options(argc, argv, "nhf:p")) > 0) {
X	  switch (num) {
X	    case 'n' : include_headers = NONE;		break;
X	    case 'h' : include_headers = ALL;		break;
X	    case 'f' : strcpy(infile, optional_arg);	
X		       if (metachar(infile[0]))
X	                 if (expand(infile) == 0)
X	                   printf("%s: couldn't expand filename %s!\n", 
X			          argv[0], infile);
X		       break;
X	    case 'p' : page_breaks++;			break;
X	  }
X	}
X	
X	if (num == ERROR) {
X	  printf("Usage: %s [-n|-h] [-f filename] [-p] <message list>\n",
X		  argv[0]);
X	  exit(1);
X	}
X
X	/** whip past the starting arguments so that we're pointing
X	    to the right stuff... **/
X
X	*argv++;	/* past the program name... */
X
X	while (opt_index-- > 1) {
X	  *argv++;
X	  argc--;
X	}
X
X	/** now let's figure out the parameters to the program... **/
X
X	if (argc == 1) {	/* no arguments... called from 'Elm'? */
X	  sprintf(filename, "%s/%s", getenv("HOME"), readmsg_file);
X	  if ((file = fopen(filename, "r")) != NULL) {
X	    fscanf(file, "%d", &(read_message[messages++]));
X	    fclose(file);
X	  }
X	  else {	/* no arguments AND no .readmsg file!! */
X	    fprintf(stderr,
X	        "Usage: readmsg [-n|-h] [-f filename] [-p] <message list>\n");
X	    exit(1);
X	  }
X	}
X	else if (! isdigit(*argv[0]) && *argv[0] != LAST_CHAR && 
X	         *argv[0] != STAR) {  
X	  string_match++;
X	 
X	  while (*argv)
X	    sprintf(string, "%s%s%s", string, string[0] == '\0'? "" : " ",
X		    *argv++);
X	}
X	else if (*argv[0] == STAR) 		/* all messages....   */
X	  list_all_messages++;
X	else { 					  /* list of nums   */
X
X	  while (--argc > 0) {
X	    num = -1;
X
X	    sscanf(*argv,"%d", &num);
X
X	    if (num < 0) {
X	      if (*argv[0] == LAST_CHAR) {
X	        last_message++;
X		num = LAST_MESSAGE;
X	      }
X	      else {
X	        fprintf(stderr,"I don't understand what '%s' means...\n",
X			*argv); 
X	       	exit(1); 
X	      }
X	    }
X	    else if (num == 0) {	/* another way to say "last" */
X	      last_message++;
X	      num = LAST_MESSAGE;
X	    }
X
X	    *argv++; 
X
X	    read_message[messages++] = num;
X	  }
X
X	  /** and now sort 'em to ensure they're in a reasonable order... **/
X
X	  qsort(read_message, messages, sizeof(int), numcmp);
X	}
X
X	/** Now let's get to the mail file... **/
X
X	if (strlen(infile) == 0) 
X	  sprintf(infile, "%s/%s", mailhome, getenv("LOGNAME"));
X
X	if ((file = fopen(infile, "r")) == NULL) {
X	  printf("But you have no mail! [ file = %d ]\n", infile);
X	  exit(0);
X	}
X
X	/** Now it's open, let's display some 'ole messages!! **/
X
X	if (string_match || last_message) {   /* pass through it once */
X
X	  if (last_message) {
X	    total = count_messages(file);	/* instantiate count */
X	    for (num=0; num < messages; num++)
X	      if (read_message[num] == LAST_MESSAGE)
X		read_message[num] = total;
X	  }
X	  else if (string_match)
X	    match_string(file, string);		/* stick msg# in list */
X
X	  if (total == 0 && ! string_match) {
X	    printf("There aren't any messages to read!\n");
X	    exit(0);
X	  }
X	}
X
X 	/** now let's have some fun! **/
X	
X	while (fgets(buffer, SLEN, file) != NULL) {
X	  if (strncmp(buffer, "From ", 5) == 0) {
X	    if (! list_all_messages) {
X	      if (current == read_message[current_in_queue])
X	        current_in_queue++;
X	      if (current_in_queue >= messages) 
X	        exit(0);
X	    }
X	    current++;
X	    not_in_header = 0;	/* we're in the header! */
X	  }
X	  if (current == read_message[current_in_queue] || list_all_messages)
X	    if (include_headers==ALL || not_in_header)
X	      printf("%s", buffer);
X	    else if (strlen(buffer) < 2) {
X	      not_in_header++;
X	      if (include_headers==WEED) 
X		list_saved_headers(page_breaks);
X	    }
X	    else if (include_headers==WEED)
X	      possibly_save(buffer); 	/* check to see if we want this */ 
X	}
X	
X	exit(0);
X}
X
Xint
Xcount_messages(file)
XFILE *file;
X{
X	/** Returns the number of messages in the file **/
X
X	char buffer[SLEN];
X	int  count = 0;
X
X	while (fgets(buffer, SLEN, file) != NULL)
X	  if (strncmp(buffer, "From ", 5) == 0)
X	    count++;
X
X	rewind( file );
X	return( count );
X}
X
Xmatch_string(mailfile, string)
XFILE *mailfile;
Xchar *string;
X{
X	/** Increment "messages" and put the number of the message
X	    in the message_count[] buffer until we match the specified 
X	    string... **/
X
X	char buffer[SLEN];
X	int  message_count;
X
X	while (fgets(buffer, SLEN, mailfile) != NULL) {
X	  if (strncmp(buffer, "From ", 5) == 0)
X	    message_count++;
X
X	  if (in_string(buffer, string)) {
X	    read_message[messages++] = message_count;
X	    rewind(mailfile);	
X	    return;
X	  }
X	}
X
X	fprintf(stderr,"Couldn't find message containing '%s'\n", string);
X	exit(1);
X}
X
Xint 
Xnumcmp(a, b)
Xint *a, *b;
X{
X	/** compare 'a' to 'b' returning less than, equal, or greater
X	    than, accordingly.
X	 **/
X
X	return(*a - *b);
X}
X
Xstatic char from[SLEN], subject[SLEN], date[SLEN], to[SLEN];
X
Xpossibly_save(buffer)
Xchar *buffer;
X{
X	/** Check to see what "buffer" is...save it if it looks 
X	    interesting... We'll always try to get SOMETHING
X	    by tearing apart the "From " line...  **/
X
X	if (strncmp(buffer, "Date:", 5) == 0)
X	  strcpy(date, buffer);
X	else if (strncmp(buffer, "Subject:", 8) == 0)
X	  strcpy(subject,buffer);
X	else if (strncmp(buffer,"From:", 5) == 0)
X	  strcpy(from, buffer);
X	else if (strncmp(buffer,"To: ", 3) == 0)
X	  strncpy(to, buffer, SLEN);
X	else if (strncmp(buffer,"From ", 5) == 0) {
X	  sprintf(from, "From: %s\n", words(2,1, buffer));	
X	  sprintf(date,"Date: %s",    words(3,7, buffer));
X	  to[0] = '\0';
X	}
X}
X
Xlist_saved_headers(page_break)
Xint page_break;
X{
X	/** This routine will display the information saved from the
X	    message being listed...If it displays anything it'll end
X	    with a blank line... **/
X
X	register int displayed_line = FALSE;
X	static int messages_listed = 0;
X
X	if (page_break && messages_listed++) putchar(FORMFEED);	
X
X	if (strlen(from)    > 0) { printf("%s", from);    displayed_line++;}
X	if (strlen(subject) > 0) { printf("%s", subject); displayed_line++;}
X	if (strlen(to)      > 0) { printf("%s", to);      displayed_line++;}
X	if (strlen(date)    > 0) { printf("%s", date);    displayed_line++;}
X	
X	if (displayed_line)
X	   putchar('\n');
X}
X
Xchar *words(word, num_words, buffer)
Xint word, num_words;
Xchar *buffer;
X{
X	/** Return a buffer starting at 'word' and containing 'num_words'
X	    words from buffer.  Assume white space will delimit each word.
X	**/
X
X	static char internal_buffer[SLEN];
X	char   *wordptr, *bufptr, mybuffer[SLEN], *strtok();
X	int    wordnumber = 0, copying_words = 0;
X
X	internal_buffer[0] = '\0';	/* initialize */
X
X	strcpy(mybuffer, buffer);
X	bufptr = (char *) mybuffer;	/* and setup */
X
X	while ((wordptr = strtok(bufptr, " \t")) != NULL) {
X	  if (++wordnumber == word) {
X	    strcpy(internal_buffer, wordptr);
X	    copying_words++;
X	    num_words--;
X	  }
X	  else if (copying_words) {
X	    strcat(internal_buffer, " ");
X	    strcat(internal_buffer, wordptr);
X	    num_words--;
X	  }
X
X	  if (num_words < 1) 
X	    return((char *) internal_buffer);
X
X	  bufptr = NULL;
X	}
X
X	return( (char *) internal_buffer);
X}
X
Xint
Xexpand(infile)
Xchar *infile;
X{
X	/** Expand the filename since the first character is a meta-
X	    character that should expand to the "maildir" variable
X	    in the users ".elmrc" file...
X
X	    Note: this is a brute force way of getting the entry out 
X	    of the .elmrc file, and isn't recommended for the faint 
X	    of heart!
X	**/
X
X	FILE *rcfile;
X	char  buffer[SLEN], *expanded_dir, *home, *getenv(), *bufptr;
X	int   foundit = 0;
X
X	bufptr = (char *) buffer;		/* same address */
X	
X	if ((home = getenv("HOME")) == NULL) {
X	  printf(
X	     "Can't expand environment variable $HOME to find .elmrc file!\n");
X	  exit(1);
X	}
X
X	sprintf(buffer, "%s/%s", home, elmrcfile);
X
X	if ((rcfile = fopen(buffer, "r")) == NULL) {
X	  printf("Can't open your \".elmrc\" file (%s) for reading!\n",
X		 buffer);
X	  exit(1);
X	}
X
X	while (fgets(buffer, SLEN, rcfile) != NULL && ! foundit) {
X	  if (strncmp(buffer, "maildir", 7) == 0 ||
X	      strncmp(buffer, "folders", 7) == 0) {
X	    while (*bufptr != '=' && *bufptr) 
X	      bufptr++;
X	    bufptr++;			/* skip the equals sign */
X	    while (whitespace(*bufptr) && *bufptr)
X	      bufptr++; 
X	    home = bufptr;		/* remember this address */
X
X	    while (! whitespace(*bufptr) && *bufptr != '\n')
X	      bufptr++;
X
X	    *bufptr = '\0';		/* remove trailing space */
X	     foundit++;
X	  }
X	}
X
X	fclose(rcfile);			/* be nice... */
X
X	if (! foundit) {
X	  printf("Couldn't find \"maildir\" in your .elmrc file!\n");
X	  exit(1);
X	}
X
X	/** Home now points to the string containing your maildir, with
X	    no leading or trailing white space...
X	**/
X
X	expanded_dir = expand_define(home);
X
X	sprintf(buffer, "%s%s%s", expanded_dir, 
X		(expanded_dir[strlen(expanded_dir)-1] == '/' ||
X		infile[0] == '/') ? "" : "/", (char *) infile+1);
X
X	strcpy(infile, buffer);
X}
X
Xchar *expand_define(maildir)
Xchar *maildir;
X{
X	/** This routine expands any occurances of "~" or "$var" in
X	    the users definition of their maildir directory out of
X	    their .elmrc file.
X
X	    Again, another routine not for the weak of heart or staunch
X	    of will!
X	**/
X
X	static char buffer[SLEN];	/* static buffer AIEE!! */
X	char   name[SLEN],		/* dynamic buffer!! (?) */
X	       *nameptr,	       /*  pointer to name??     */
X	       *value;		      /* char pointer for munging */
X
X	if (*maildir == '~') 
X	  sprintf(buffer, "%s%s", getenv("HOME"), ++maildir);
X	else if (*maildir == '$') { 	/* shell variable */
X
X	  /** break it into a single word - the variable name **/
X
X	  strcpy(name, (char *) maildir + 1);	/* hurl the '$' */
X	  nameptr = (char *) name;
X	  while (*nameptr != '/' && *nameptr) nameptr++;
X	  *nameptr = '\0';	/* null terminate */
X	  
X	  /** got word "name" for expansion **/
X
X	  if ((value = getenv(name)) == NULL) {
X	    printf("Couldn't expand shell variable $%s in .elmrc!\n", name);
X	    exit(1);
X	  }
X	  sprintf(buffer, "%s%s", value, maildir + strlen(name) + 1);
X	}
X	else
X	   strcpy(buffer, maildir);
X
X	return( ( char *) buffer);
X}
X
END_OF_utils/readmsg.c
if test 13084 -ne `wc -c <utils/readmsg.c`; then
    echo shar: \"utils/readmsg.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: End of archive 12 \(of 19\).
cp /dev/null ark12isdone
DONE=true
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
    if test ! -f ark${I}isdone ; then
	echo shar: You still need to run archive ${I}.
	DONE=false
    fi
done
if test "$DONE" = "true" ; then
	echo You have unpacked all 19 archives.
	echo "See the Instructions file"
	rm -f ark[1-9]isdone ark[1-9][0-9]isdone
fi
##  End of shell archive.
exit 0