[mod.sources] v09i005: ELM Mail System, Part05/19

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

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

#! /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 5 (of 19)."
# Contents:  filter/actions.c filter/rules.c hdrs/elm.h hdrs/headers.h
#   src/calendar.c src/hdrconfg.c src/help.c src/pattern.c src/utils.c
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo shar: Extracting \"filter/actions.c\" \(5857 characters\)
if test -f filter/actions.c ; then 
  echo shar: Will not over-write existing file \"filter/actions.c\"
else
sed "s/^X//" >filter/actions.c <<'END_OF_filter/actions.c'
X/**			actions.c			**/
X
X/** RESULT oriented routines *chuckle*.  These routines implement the
X    actions that result from either a specified rule being true or from
X    the default action being taken.
X
X    (C) Copyright 1986, Dave Taylor
X**/
X
X#include <stdio.h>
X#include <pwd.h>
X#include <ctype.h>
X#include <fcntl.h>
X
X#include "defs.h"
X#include "filter.h"
X
Xmail_message(address)
Xchar *address;
X{
X	/** Called with an address to send mail to.   For various reasons
X	    that are too disgusting to go into herein, we're going to actually
X	    open the users mailbox and by hand add this message.  Yech.
X	    NOTE, of course, that if we're going to MAIL the message to someone
X	    else, that we'll try to do nice things with it on the fly...
X	**/
X
X	FILE *pipefd, *tempfd, *mailfd;
X	int  attempts = 0, ret, in_header = TRUE, line_count = 0;
X	char tempfile[SLEN], mailbox[SLEN], lockfile[SLEN],
X	     buffer[VERY_LONG_STRING];
X
X	if (verbose && ! log_actions_only)
X	  printf("%sfilter (%s): Mailing message to %s\n", 
X		  BEEP, username, address);
X
X	if (! show_only) {
X	  sprintf(tempfile, "%s.%d", filter_temp, getpid());
X
X	  if ((tempfd = fopen(tempfile, "r")) < 0) {
X	    fprintf(stderr, "%sfilter (%s): Can't open temp file %s!!\n", 
X		    BEEP, username, tempfile);
X	    exit(1);
X	  }
X	 	
X	  if (strcmp(address, username) != 0) {	/* mailing to someone else */
X	    
X	    if (already_been_forwarded) {	/* potential looping! */
X	      if (contains(from, username)) {
X	        fprintf(stderr, 
X	"%sfilter (%s): Filter loop detected!  Message left in file %s.%d\n", 
X			BEEP, username, filter_temp, getpid());
X	        exit(0);
X	      }
X	    }
X
X	    sprintf(buffer, "%s %s %s", sendmail, smflags, address);
X
X	    if ((pipefd = popen(buffer, "w")) == NULL) {
X	      fprintf(stderr, "%sfilter (%s): popen %s failed!\n", 
X		      BEEP, buffer);
X	      sprintf(buffer, "((%s %s %s ; %s %s) & ) < %s &",
X		      sendmail , smflags, address, remove, tempfile, tempfile);
X	      system(buffer);
X	      return;
X	    }
X
X	    fprintf(pipefd, "Subject: \"%s\"\n", subject);
X	    fprintf(pipefd, "From: The Filter of %s@%s <%s>\n", 
X		    username, hostname, username);
X	    fprintf(pipefd, "To: %s\n", address);
X	    fprintf(pipefd, "X-Filtered-By: filter, version %s\n\n", VERSION);
X
X	    fprintf(pipefd, "-- Begin filtered message --\n\n");
X	
X	    while (fgets(buffer, LONG_SLEN, tempfd) != NULL)
X	      if (already_been_forwarded && in_header)
X	        in_header = (strlen(buffer) == 1? 0 : in_header);
X	      else
X	        fprintf(pipefd," %s", buffer);
X
X	    fprintf(pipefd, "\n-- End of filtered message --\n");
X	    fclose(pipefd);
X	    fclose(tempfd);
X	
X	    return;		/* YEAH!  Wot a slick program, eh? */
X	  
X	  }
X	  
X	  /** else it is to the current user... **/
X
X	  sprintf(mailbox,  "%s%s", mailhome, username);
X	  sprintf(lockfile,  "%s%s.lock", mailhome, username);
X
X	  while ((ret=creat(lockfile, 0777)) < 0  && attempts++ < 10) 
X	    sleep(2);	/* wait two seconds, okay?? */
X
X	  if (ret < 0) {
X	    fprintf(stderr, "%sfilter (%s): Couldn't create lockfile %s\n",
X		    BEEP, username, lockfile);
X	    strcpy(mailbox,"[due to lock not being allowed]");
X	    /* doing that copy will make sure the next 'open' fails... */
X	  }
X
X	  if (mailbox[0] == '[' || (mailfd=fopen(mailbox,"a")) == NULL) {
X
X	    fprintf(stderr, "%sfilter (%s): Can't open mailbox %s!\n",
X			BEEP, username, mailbox);
X
X	    sprintf(mailbox, "%s/%s", home, EMERGENCY_MAILBOX);
X	    if ((mailfd=fopen(mailbox, "a")) == NULL) {
X	      fprintf(stderr,"%sfilter (%s): Can't open %s either!!\n",
X		      BEEP, username, mailbox);
X
X	      sprintf(mailbox,"%s/%s", home, EMERG_MBOX); 
X	      if ((mailfd = fopen(mailbox, "a")) == NULL) {
X	         fprintf(stderr,"%sfilter (%s): Can't open %s either!!!!\n",
X		      BEEP, username, mailbox);
X	         fprintf(stderr, 
X		         "%sfilter (%s): I can't open ANY mailboxes!  Augh!!\n",
X			 BEEP, username);
X	         fclose(tempfd);
X		 unlink(lockfile);
X	         leave("Cannot open any mailbox");	/* DIE DIE DIE DIE!! */
X	      }
X	      else
X	        fprintf(stderr,"%sfilter (%s): Using %s as emergency mailbox\n",
X			BEEP, username, mailbox);
X	    }
X	    else
X	      fprintf(stderr,"%sfilter (%s): Using %s as emergency mailbox\n",
X		      BEEP, username, mailbox);
X	  }
X
X	  while (fgets(buffer, sizeof(buffer), tempfd) != NULL) {
X	    line_count++;
X	    if (the_same(buffer, "From ") && line_count > 1)
X	      fprintf(mailfd, ">%s", buffer);
X	    else
X	      fputs(buffer, mailfd);
X	  }
X
X	  fclose(mailfd);
X	  unlink(lockfile);	/* blamo! */
X	  fclose(tempfd);
X	}
X}
X
Xsave_message(foldername)
Xchar *foldername;
X{
X	/** Save the message in a folder.  Use full file buffering to
X	    make this work without contention problems **/
X
X	FILE  *fd, *tempfd;
X	char  filename[SLEN], buffer[LONG_SLEN];
X
X	if (verbose)
X	  printf("%sfilter (%s): Message saved in folder %s\n", 
X		  BEEP, username, foldername);
X	
X	if (!show_only) {
X	  sprintf(filename, "%s.%d", filter_temp, getpid());
X
X	  if ((fd = fopen(foldername, "a")) == NULL) {
X	    fprintf(stderr, 
X		 "%sfilter (%s): can't save message to requested folder %s!\n",
X		    BEEP, username, foldername);
X	    return(1);
X	  }
X
X	  if ((tempfd = fopen(filename, "r")) == NULL) {
X	     fprintf(stderr, 
X		     "%sfilter (%s): can't open temp file for reading!\n",
X		     BEEP, username);
X	     return(1);
X	  }
X
X	  while (fgets(buffer, sizeof(buffer), tempfd) != NULL)
X	    fputs(buffer, fd);
X	
X	  fclose(fd);
X	  fclose(tempfd);
X	}
X
X 	return(0);
X}
X
Xexecute(command)
Xchar *command;
X{
X	/** execute the indicated command, feeding as standard input the
X	    message we have.
X	**/
X
X	char buffer[LONG_SLEN];
X
X	if (verbose)
X	  printf("%sfilter (%s): Executing %s\n", 
X		  BEEP, username, command);
X
X	if (! show_only) {
X	  sprintf(buffer, "%s %s.%d | %s", cat, filter_temp, getpid(), command);
X	  system(buffer);
X	}
X}
END_OF_filter/actions.c
if test 5857 -ne `wc -c <filter/actions.c`; then
    echo shar: \"filter/actions.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"filter/rules.c\" \(6383 characters\)
if test -f filter/rules.c ; then 
  echo shar: Will not over-write existing file \"filter/rules.c\"
else
sed "s/^X//" >filter/rules.c <<'END_OF_filter/rules.c'
X/**			rules.c			**/
X
X/** This file contains all the rule routines, including those that apply the
X    specified rules and the routine to print the rules out.
X
X    (C) Copyright 1986, Dave Taylor
X**/
X
X#include <stdio.h>
X#include <pwd.h>
X#include <ctype.h>
X#ifdef BSD
X# include <sys/time.h>
X#else
X# include <time.h>
X#endif
X#include <fcntl.h>
X
X#include "defs.h"
X#include "filter.h"
X
Xint
Xaction_from_ruleset()
X{
X	/** Given the set of rules we've read in and the current to, from, 
X	    and subject, try to match one.  Return the ACTION of the match
X            or LEAVE if none found that apply.
X	**/
X
X	register int index = 0, not, relation, try_next_rule, x;
X	struct condition_rec *cond;
X
X	while (index < total_rules) {
X	  cond = rules[index].condition;
X	  try_next_rule = 0;
X
X	  while (cond != NULL && ! try_next_rule) {
X	    
X	    not = (cond->relation < 0);
X	    relation = abs(cond->relation);
X	
X	    switch (cond->matchwhat) {
X
X	      case TO     : x = contains(to, cond->argument1); 		break;
X	      case FROM   : x = contains(from, cond->argument1); 	break;
X	      case SUBJECT: x = contains(subject, cond->argument1);	break;
X	      case LINES  : x = compare(lines, relation, cond->argument1);break;
X		       
X	      case CONTAINS: fprintf(stderr,
X       "%sfilter (%s): Error: rules based on 'contains' are not implemented!\n",
X			     BEEP, username);
X			     exit(0);
X	    }
X
X	    if ((not && x) || ((! not) && (! x))) /* this test failed (LISP?) */
X	      try_next_rule++;
X	    else
X	      cond = cond->next;		  /* next condition, if any?  */
X	  }
X
X	  if (! try_next_rule) {
X	    rule_choosen = index;
X 	    return(rules[rule_choosen].action);
X	  }
X	  index++;
X	}
X
X	rule_choosen = -1;
X	return(LEAVE);
X}
X
X#define get_the_time()	if (!gotten_time) { 		  \
X			   thetime = time( (long *) 0);   \
X			   timerec = localtime(&thetime); \
X			   gotten_time++; 		  \
X			}
X
Xexpand_macros(word, buffer, line)
Xchar *word, *buffer;
Xint  line;
X{
X	/** expand the allowable macros in the word;
X		%d	= day of the month  
X		%D	= day of the week  
X	        %h	= hour (0-23)	 
X		%m	= month of the year
X		%r	= return address of sender
X	   	%s	= subject of message
X	   	%S	= "Re: subject of message"  (only add Re: if not there)
X		%t	= hour:minute 	
X		%y	= year		  
X	    or simply copies word into buffer.
X	**/
X
X	struct tm *localtime(), *timerec;
X	long     time(), thetime;
X	register int i, j=0, gotten_time = 0, reading_a_percent_sign = 0;
X
X	for (i = 0; i < strlen(word); i++) {
X	  if (reading_a_percent_sign) {
X	    reading_a_percent_sign = 0;
X	    switch (word[i]) {
X
X	      case 'r' : buffer[j] = '\0';
X			 strcat(buffer, from);
X	                 j = strlen(buffer);
X			 break;
X
X	      case 's' : buffer[j] = '\0';
X			 strcat(buffer, subject);
X	                 j = strlen(buffer);
X			 break;
X
X	      case 'S' : buffer[j] = '\0';
X			 if (! the_same(subject, "Re:")) 
X			   strcat(buffer, subject);
X			 strcat(buffer, subject);
X	                 j = strlen(buffer);
X			 break;
X
X	      case 'd' : get_the_time(); buffer[j] = '\0';
X			 strcat(buffer, itoa(timerec->tm_mday,FALSE));
X	                 j = strlen(buffer);
X			 break;
X
X	      case 'D' : get_the_time(); buffer[j] = '\0';
X			 strcat(buffer, itoa(timerec->tm_wday,FALSE));
X	                 j = strlen(buffer);
X			 break;
X
X	      case 'm' : get_the_time(); buffer[j] = '\0';
X			 strcat(buffer, itoa(timerec->tm_mon,FALSE));
X	                 j = strlen(buffer);
X			 break;
X
X	      case 'y' : get_the_time(); buffer[j] = '\0';
X			 strcat(buffer, itoa(timerec->tm_year,FALSE));
X	                 j = strlen(buffer);
X			 break;
X
X	      case 'h' : get_the_time(); buffer[j] = '\0';
X			 strcat(buffer, itoa(timerec->tm_hour,FALSE));
X	                 j = strlen(buffer);
X			 break;
X
X	      case 't' : get_the_time(); buffer[j] = '\0';
X			 strcat(buffer, itoa(timerec->tm_hour,FALSE));
X			 strcat(buffer, ":");
X			 strcat(buffer, itoa(timerec->tm_min,TRUE));
X	                 j = strlen(buffer);
X			 break;
X
X	      default  : fprintf(stderr,
X   "%sfilter (%s): Error on line %d translating %%%c macro in word \"%s\"!\n",
X			         BEEP, username, line, word[i], word);
X			 exit(1);
X	    }
X	  }
X	  else if (word[i] == '%') 
X	    reading_a_percent_sign++;
X	  else 
X	    buffer[j++] = word[i];
X	}
X	buffer[j] = '\0';
X}
X
Xprint_rules()
X{
X	/** print the rules out.  A double check, of course! **/
X
X	register int i = -1;
X	char     *whatname(), *actionname();
X	struct   condition_rec *cond;
X
X	while (++i < total_rules) {
X	  printf("\nRule %d:  if (", i+1);
X
X	  cond = rules[i].condition;
X
X	  while (cond != NULL) {
X	    if (cond->relation < 0)
X	      printf("not %s %s %s%s%s", 
X		      whatname(cond->matchwhat),
X		      relationname(- (cond->relation)),
X		      quoteit(cond->matchwhat),
X		      cond->argument1,
X		      quoteit(cond->matchwhat));
X	    else
X	      printf("%s %s %s%s%s",
X		      whatname(cond->matchwhat),
X		      relationname(cond->relation),
X		      quoteit(cond->matchwhat),
X		      cond->argument1,
X		      quoteit(cond->matchwhat));
X
X	    cond = cond->next;
X
X	    if (cond != NULL) printf(" and ");
X	  }
X	    
X	  printf(") then\n\t  %s %s\n", 
X		 actionname(rules[i].action), 
X		 rules[i].argument2);
X	}
X	printf("\n");
X}
X
Xchar *whatname(n)
Xint n;
X{
X	static char buffer[10];
X
X	switch(n) {
X	  case FROM   : return("from");
X	  case TO     : return("to");
X	  case SUBJECT: return("subject");
X	  case LINES  : return ("lines");
X	  case CONTAINS: return("contains");
X	  default     : sprintf(buffer, "?%d?", n); return((char *)buffer);
X	}
X}
X
Xchar *actionname(n)
Xint n;
X{
X	switch(n) {
X	  case DELETE  : return("Delete");
X	  case SAVE    : return("Save");
X	  case SAVECC  : return("Copy and Save");
X	  case FORWARD : return("Forward");
X	  case LEAVE   : return("Leave"); 
X	  case EXEC    : return("Execute");
X	  default      : return("?action?");
X	}
X}
X
Xint
Xcompare(line, relop, arg)
Xint line, relop;
Xchar *arg;
X{
X	/** Given the actual number of lines in the message, the relop
X	    relation, and the number of lines in the rule, as a string (!),
X   	    return TRUE or FALSE according to which is correct.
X	**/
X
X	int rule_lines;
X
X	rule_lines = atoi(arg);
X
X	switch (relop) {
X	  case LE: return(line <= rule_lines);
X	  case LT: return(line <  rule_lines);
X	  case GE: return(line >= rule_lines);
X	  case GT: return(line >  rule_lines);
X	  case NE: return(line != rule_lines);
X	  case EQ: return(line == rule_lines);
X	}
X	return(-1);
X}
END_OF_filter/rules.c
if test 6383 -ne `wc -c <filter/rules.c`; then
    echo shar: \"filter/rules.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"hdrs/elm.h\" \(5581 characters\)
if test -f hdrs/elm.h ; then 
  echo shar: Will not over-write existing file \"hdrs/elm.h\"
else
sed "s/^X//" >hdrs/elm.h <<'END_OF_hdrs/elm.h'
X/**			elm.h				**/
X
X/**  Main header file for ELM mail system.  **/
X
X/**  (C) Copyright 1986, Dave Taylor   **/
X
X#include <stdio.h>
X#include <fcntl.h>
X
X#include "../hdrs/curses.h"
X#include "../hdrs/defs.h"
X
X/******** static character string containing the version number  *******/
X
Xstatic char ident[] = { WHAT_STRING };
X
X/******** and another string for the copyright notice            ********/
X
Xstatic char copyright[] = { "@(#)          (C) Copyright 1986, Dave Taylor" };
X
X/******** global variables accessable by all pieces of the program *******/
X
Xint current = 0;		/* current message number  */
Xint header_page = 0;     	/* current header page     */
Xint last_header_page = -1;     	/* last header page        */
Xint message_count = 0;		/* max message number      */
Xint headers_per_page;		/* number of headers/page  */
Xchar infile[SLEN];		/* name of current mailbox */
Xchar hostname[SLEN];		/* name of machine we're on*/
Xchar username[SLEN];		/* return address name!    */
Xchar full_username[SLEN];	/* Full username - gecos   */
Xchar home[SLEN];		/* home directory of user  */
Xchar folders[SLEN];		/* folder home directory   */
Xchar mailbox[SLEN];		/* mailbox name if defined */
Xchar editor[SLEN];		/* editor for outgoing mail*/
Xchar alternative_editor[SLEN];	/* alternative editor...   */
Xchar printout[SLEN];		/* how to print messages   */
Xchar savefile[SLEN];		/* name of file to save to */
Xchar calendar_file[SLEN];	/* name of file for clndr  */
Xchar prefixchars[SLEN];		/* prefix char(s) for msgs */
Xchar shell[SLEN];		/* current system shell    */
Xchar pager[SLEN];		/* what pager to use       */
Xchar batch_subject[SLEN];	/* subject buffer for batchmail */
Xchar local_signature[SLEN];	/* local msg signature file     */
Xchar remote_signature[SLEN];	/* remote msg signature file    */
X
Xchar backspace,			/* the current backspace char */
X     kill_line;			/* the current kill-line char */
X
Xchar up[SHORT], down[SHORT];	/* cursor control seq's    */
Xint  cursor_control = FALSE;	/* cursor control avail?   */
X
Xchar start_highlight[SHORT],
X     end_highlight[SHORT];	/* stand out mode...       */
X
Xint  has_highlighting = FALSE;	/* highlighting available? */
X
Xchar *weedlist[MAX_IN_WEEDLIST];
Xint  weedcount;
X
Xint allow_forms = NO;		/* flag: are AT&T Mail forms okay?  */
Xint file_changed = 0;		/* flag: true if infile changed     */
Xint mini_menu = 1;		/* flag: menu specified?	    */
Xint mbox_specified = 0;		/* flag: specified alternate mbox?  */
Xint check_first = 1;		/* flag: verify mail to be sent!    */
Xint auto_copy = 0;		/* flag: automatically copy source? */
Xint filter = 1;			/* flag: weed out header lines?	    */
Xint resolve_mode = 1;		/* flag: delete saved mail?	    */
Xint auto_cc = 0;		/* flag: mail copy to user?	    */
Xint noheader = 1;		/* flag: copy + header to file?     */
Xint title_messages = 1;		/* flag: title message display?     */
Xint edit_outbound = 0;		/* flag: edit outbound headers?	    */
Xint hp_terminal = 0;		/* flag: are we on HP term?	    */
Xint hp_softkeys = 0;		/* flag: are there softkeys?        */
Xint save_by_name = 1;		/* flag: save mail by login name?   */
Xint mail_only = 0;		/* flag: send mail then leave?      */
Xint check_only = 0;		/* flag: check aliases then leave?  */
Xint move_when_paged = 0;	/* flag: move when '+' or '-' used? */
Xint point_to_new = 1;		/* flag: start pointing at new msg? */
Xint bounceback = 0;		/* flag: bounce copy off remote?    */
Xint signature = 0;		/* flag: include $home/.signature?  */
Xint always_leave = 0;		/* flag: always leave msgs pending? */
Xint always_del = 0;		/* flag: always delete marked msgs? */
Xint arrow_cursor = 0;		/* flag: use "->" cursor regardless?*/
Xint debug = 0; 			/* flag: default is no debug!       */
Xint read_in_aliases = 0;	/* flag: have we read in aliases??  */
Xint warnings = 1;		/* flag: output connection warnings?*/
Xint user_level = 0;		/* flag: how good is the user?      */
Xint selected = 0;		/* flag: used for select stuff      */
Xint names_only = 1;		/* flag: display user names only?   */
Xint question_me = 1;		/* flag: ask questions as we leave? */
Xint keep_empty_files = 0;	/* flag: leave empty mailbox files? */
X
X#ifdef UTS
X int isatube = 0;		/* flag: are we on an IBM 3270?     */
X#endif
X
Xint sortby = REVERSE SENT_DATE;	/* how to sort incoming mail...     */
X
Xlong timeout = 600L;		/* timeout (secs) on main prompt    */
X
Xint mailbox_defined = 0;	/** mailbox specified?    **/
X
X/** set up some default values for a 'typical' terminal *snicker* **/
X
Xint LINES=23;			/** lines per screen      **/
Xint COLUMNS=80;			/** columns per page      **/
X
Xlong size_of_pathfd;		/** size of pathfile, 0 if none **/
X
XFILE *mailfile;			/* current mailbox file     */
XFILE *debugfile;		/* file for debug output    */
XFILE *pathfd;			/* path alias file          */
XFILE *domainfd;			/* domain file		    */
X
Xlong mailfile_size;		/* size of current mailfile */
X
Xint   max_headers;		/* number of headers allocated */
X
Xstruct header_rec *header_table;
X
Xstruct alias_rec user_hash_table[MAX_UALIASES];
Xstruct alias_rec system_hash_table[MAX_SALIASES];
X
Xstruct date_rec last_read_mail; /* last time we read mailbox  */
X
Xstruct lsys_rec *talk_to_sys;   /* what machines do we talk to? */
X
Xstruct addr_rec *alternative_addresses;	/* how else do we get mail? */
X
Xint system_files = 0;		/* do we have system aliases? */
Xint user_files = 0;		/* do we have user aliases?   */
X
Xint system_data;		/* fileno of system data file */
Xint user_data;			/* fileno of user data file   */
X
Xint userid;			/* uid for current user	      */
Xint groupid;			/* groupid for current user   */
END_OF_hdrs/elm.h
if test 5581 -ne `wc -c <hdrs/elm.h`; then
    echo shar: \"hdrs/elm.h\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"hdrs/headers.h\" \(5735 characters\)
if test -f hdrs/headers.h ; then 
  echo shar: Will not over-write existing file \"hdrs/headers.h\"
else
sed "s/^X//" >hdrs/headers.h <<'END_OF_hdrs/headers.h'
X/**		headers.h		**/
X
X/**  header file for ELM mail system.  **/
X
X/**  (C) Copyright 1985, Dave Taylor   **/
X
X#include <stdio.h>
X#include <fcntl.h>
X
X#include "curses.h"
X#include "defs.h"
X
X/******** global variables accessable by all pieces of the program *******/
X
Xextern int current;		/* current message number  */
Xextern int header_page;         /* current header page     */
Xextern int last_header_page;    /* last header page        */
Xextern int message_count;	/* max message number      */
Xextern int headers_per_page;	/* number of headers/page  */
Xextern char infile[SLEN];	/* name of current mailbox */
Xextern char hostname[SLEN];	/* name of machine we're on*/
Xextern char username[SLEN];	/* return address name!    */
Xextern char full_username[SLEN];/* Full username - gecos   */
Xextern char home[SLEN];		/* home directory of user  */
Xextern char folders[SLEN];	/* folder home directory   */
Xextern char mailbox[SLEN];	/* mailbox name if defined */
Xextern char editor[SLEN];	/* default editor for mail */
Xextern char alternative_editor[SLEN];/* the 'other' editor */
Xextern char printout[SLEN];	/* how to print messages   */
Xextern char savefile[SLEN];	/* name of file to save to */
Xextern char calendar_file[SLEN];/* name of file for clndr  */
Xextern char prefixchars[SLEN];	/* prefix char(s) for msgs */
Xextern char shell[SLEN];	/* default system shell    */
Xextern char pager[SLEN];	/* what pager to use...    */
Xextern char batch_subject[SLEN];/* subject buffer for batchmail */
Xextern char local_signature[SLEN];/* local msg signature file   */
Xextern char remote_signature[SLEN];/* remote msg signature file */
X
Xextern char backspace,		/* the current backspace char  */
X	    kill_line;		/* the current kill_line char  */
X
Xextern char up[SHORT], 
X	    down[SHORT];	/* cursor control seq's    */
Xextern int  cursor_control;	/* cursor control avail?   */
X
Xextern char start_highlight[SHORT],
X	    end_highlight[SHORT];  /* standout mode... */
X
Xextern int  has_highlighting;	/* highlighting available? */
X
X/** the following two are for arbitrary weedout lists.. **/
X
Xextern char *weedlist[MAX_IN_WEEDLIST];
Xextern int  weedcount;		/* how many headers to check?        */
X
Xextern int  allow_forms;	/* flag: are AT&T Mail forms okay?    */
Xextern int  file_changed;	/* flag: true iff infile changed      */
Xextern int  mini_menu;		/* flag: display menu?     	      */
Xextern int  mbox_specified;     /* flag: specified alternate mailbox? */
Xextern int  check_first;	/* flag: verify mail to be sent!      */
Xextern int  auto_copy;		/* flag: auto copy source into reply? */
Xextern int  filter;		/* flag: weed out header lines?	      */
Xextern int  resolve_mode;	/* flag: resolve before moving mode?  */
Xextern int  auto_cc;		/* flag: mail copy to yourself?       */
Xextern int  noheader;		/* flag: copy + header to file?       */
Xextern int  title_messages;	/* flag: title message display?       */
Xextern int  edit_outbound;	/* flag: edit outbound headers?       */
Xextern int  hp_terminal;	/* flag: are we on an hp terminal?    */
Xextern int  hp_softkeys;	/* flag: are there softkeys?          */
Xextern int  save_by_name;  	/* flag: save mail by login name?     */
Xextern int  mail_only;		/* flag: send mail then leave?        */
Xextern int  check_only;		/* flag: check aliases and leave?     */
Xextern int  move_when_paged;	/* flag: move when '+' or '-' used?   */
Xextern int  point_to_new;	/* flag: start pointing at new msgs?  */
Xextern int  bounceback;		/* flag: bounce copy off remote?      */
Xextern int  signature;		/* flag: include $home/.signature?    */
Xextern int  always_leave;	/* flag: always leave mail pending?   */
Xextern int  always_del;		/* flag: always delete marked msgs?   */
Xextern int  arrow_cursor;	/* flag: use "->" regardless?	      */
Xextern int  debug;		/* flag: debugging mode on?           */
Xextern int  read_in_aliases;	/* flag: have we read in aliases yet? */
Xextern int  warnings;		/* flag: output connection warnings?  */
Xextern int  user_level;		/* flag: how knowledgable is user?    */
Xextern int  selected;		/* flag: used for select stuff        */
Xextern int  names_only;		/* flag: display names but no addrs?  */
Xextern int  question_me;	/* flag: ask questions as we leave?   */
Xextern int  keep_empty_files;	/* flag: keep empty files??	      */
X
X#ifdef UTS
Xextern int  isatube;		/* flag: are we on an IBM 3270 tube?  */
X#endif
X
Xextern int  sortby;		/* how to sort mailboxes	      */
X
Xextern long timeout;		/* seconds for main level timeout     */
X
Xextern int mailbox_defined;	/** specified mailbox?  **/
X
Xextern int LINES;		/** lines per screen    **/
Xextern int COLUMNS;		/** columns per line    **/
X
Xextern long size_of_pathfd;	/** size of pathfile, 0 if none **/
X
Xextern FILE *mailfile;		/* current mailbox file     */
Xextern FILE *debugfile;		/* file for debut output    */
Xextern FILE *pathfd;		/* path alias file          */
Xextern FILE *domainfd;		/* domains file 	    */
X
Xextern long mailfile_size;	/* size of current mailfile */
X
Xextern int  max_headers;	/* number of headers currently allocated */
X
Xextern struct header_rec *header_table;
X
Xextern struct alias_rec user_hash_table  [MAX_UALIASES];
Xextern struct alias_rec system_hash_table[MAX_SALIASES];
X
Xextern struct date_rec last_read_mail;
X
Xextern struct lsys_rec *talk_to_sys;	/* who do we talk to? */
X
Xextern struct addr_rec *alternative_addresses;	/* how else do we get mail? */
X
Xextern int system_files;	/* do we have system aliases? */
Xextern int user_files;		/* do we have user aliases?   */
X
Xextern int system_data;		/* fileno of system data file */
Xextern int user_data;		/* fileno of user data file   */
X
Xextern int userid;		/* uid for current user	      */
Xextern int groupid;		/* groupid for current user   */
END_OF_hdrs/headers.h
if test 5735 -ne `wc -c <hdrs/headers.h`; then
    echo shar: \"hdrs/headers.h\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"src/calendar.c\" \(5495 characters\)
if test -f src/calendar.c ; then 
  echo shar: Will not over-write existing file \"src/calendar.c\"
else
sed "s/^X//" >src/calendar.c <<'END_OF_src/calendar.c'
X/**			calendar.c		**/
X
X/** This routine implements a rather snazzy idea suggested by Warren
X    Carithers of the Rochester Institute of Technology that allows
X    mail to contain entries formatted in a manner that will allow direct
X    copying into a users calendar program.
X
X    Never able to leave good-enough alone, I've extended this idea to a
X    further one - the knowledge of a different type of calendar program
X    too.  Specifically, the current message can contain either of;
X
X	-> Mon 04/21 1:00p meet with chairman candidate
X
X    or 
X
X	- >April 21
X	-   
X	-     1:00 pm: Meet with Chairman Candidate
X	-
X
X    The first type will have the leading '->' removed and all subsequent
X    white space, creating a simple one-line entry in the users calendar
X    file.   The second type will remove the '-' and the leading white
X    spaces and leave everything else intact (that is, the file in the
X    second example would be appended with ">April 21" followed by a
X    blank line, the 1:00 pm meeting info, and another blank line.
X	
X    The file to include this in is either the default as defined in the
X    sysdefs.h file (see CALENDAR_FILE) or a filename contained in the
X    users ".elmrc" file, "calendar= <filename>".
X
X    (C) Copyright 1986 Dave Taylor
X**/
X
X#include "headers.h"
X
X#ifdef ENABLE_CALENDAR		/* if not defined, this will be an empty file */
X
X#include <errno.h>
X
Xextern int errno;
X
Xchar *error_name(), *error_description(), *strcpy();
X
Xscan_calendar()
X{
X	FILE *calendar;
X	int  count;
X
X	/* First step is to open the celendar file for appending... **/
X
X	if (can_open(calendar_file, "a") != 0) {
X	  dprint1(2, "Error: wrong permissions to append to calendar %s\n",
X		  calendar_file);
X	  dprint2(2, "** %s - %s **\n",
X		  error_name(errno), error_description(errno));
X	  error1("Not able to append to file %s!", calendar_file);
X	  return; 
X	}
X
X	if ((calendar = fopen(calendar_file,"a")) == NULL) {
X	  dprint1(2, "Error: couldn't append to calendar file %s (save)\n", 
X		   calendar_file);
X	  dprint2(2, "** %s - %s **\n",
X		  error_name(errno), error_description(errno));
X	  error1("Couldn't append to file %s!", calendar_file);
X	  return; 
X	}
X	
X	count = extract_info(calendar);
X
X	fclose(calendar);
X
X	chown(calendar_file, userid, groupid);	/* ensure owned by user */
X
X	if (count > 0)
X	  error2("%d entr%s saved in calendar file", 
X		 count, count > 1 ? "ies" : "y");
X	else 
X	  error("No calendar entries found in that message");
X
X	return;
X}
X
Xint
Xextract_info(save_to_fd)
XFILE *save_to_fd;
X{
X	/** Save the relevant parts of the current message to the given
X	    calendar file.  The only parameter is an opened file
X	    descriptor, positioned at the end of the existing file **/
X	    
X	register int entries = 0, ok = 1, lines, index, in_entry = FALSE;
X	char buffer[SLEN];
X
X    	/** get to the first line of the message desired **/
X
X    	if (fseek(mailfile, header_table[current-1].offset, 0) == -1) {
X       	  dprint2(1,"ERROR: Attempt to seek %d bytes into file failed (%s)",
X		header_table[current-1].offset, "extract_info");
X       	  error1("ELM [seek] failed trying to read %d bytes into file",
X	     	header_table[current-1].offset);
X       	  return(0);
X    	}
X
X        /* how many lines in message? */
X
X        lines = header_table[current-1].lines;
X
X        /* now while not EOF & still in message... scan it! */
X
X        while (ok && lines--) {
X          ok = (int) (fgets(buffer, LONG_SLEN, mailfile) != NULL);
X	  
X	  /* now let's see if it matches the basic pattern... */
X
X	  if ((index = calendar_line(buffer)) > -1) {
X
X	    if (buffer[index] == '>') {	/* single line entry */
X	      if (remove_through_ch(buffer, '>')) {
X	        fprintf(save_to_fd,"%s", buffer);
X	        entries++;
X	      }
X	    }
X	    else {				/* multi-line entry  */
X	        fprintf(save_to_fd, "%s", (char *) (buffer + index + 1));
X	        in_entry = TRUE;	
X	      }
X           }
X	   else if (in_entry) {
X	     in_entry = FALSE;
X	     entries++;
X	   }
X	}
X
X	dprint2(4,"Got %d calender entr%s.\n", entries, entries > 1? "ies":"y");
X
X	return(entries);
X}
X
Xint
Xcalendar_line(string)
Xchar *string;
X{
X	/** Iff the input line is of the form;
X
X	      {white space} <one or more '-'> 
X	
X	    this routine will return the index of the NEXT character
X	    after the dashed sequence...If this pattern doesn't occur, 
X	    or if any other problems are encountered, it'll return "-1"
X	**/
X
X	register int loc = 0;
X
X	if (chloc(string,'-') == -1) 	  /* no dash??? */
X	  return(-1);			/* that was easy! */
X
X	/** skip leading white space... **/
X
X	while (whitespace(string[loc])) loc++;	   /* MUST have '-' too! */
X
X	if (string[loc] != '-')	return(-1);	   /* nice try, sleazo! */
X
X	while (string[loc] == '-') loc++;
X
X	if (loc >= strlen(string)) return(-1);	/* Empty line... */
X
X	/* otherwise.... */  
X
X	return(loc);
X}
X    
X
Xint
Xremove_through_ch(string, ch)
Xchar *string;
Xchar  ch;
X{
X	/** removes all characters from zero to ch in the string, and 
X	    any 'white-space' following the 'n'th char... if it hits a
X    	    NULL string, it returns FALSE, otherwise it'll return TRUE!
X	**/
X
X	char buffer[SLEN];
X	register int index = 0, i = 0;
X
X	while (string[index] != ch && string[index] != '\0')
X	  index++;
X	
X	if (index >= strlen(string)) 
X	  return(FALSE);	/* crash! burn! */
X
X	index++;	/* get past the 'ch' character... */
X
X	while (whitespace(string[index])) index++;
X
X	while (index < strlen(string))
X	  buffer[i++] = string[index++];
X
X	buffer[i] = '\0';
X
X	strcpy(string, buffer);
X	
X	return(TRUE);
X}
X
X#endif
END_OF_src/calendar.c
if test 5495 -ne `wc -c <src/calendar.c`; then
    echo shar: \"src/calendar.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"src/hdrconfg.c\" \(5434 characters\)
if test -f src/hdrconfg.c ; then 
  echo shar: Will not over-write existing file \"src/hdrconfg.c\"
else
sed "s/^X//" >src/hdrconfg.c <<'END_OF_src/hdrconfg.c'
X/**			hdrconfg.c			**/
X
X/**   This file contains the routines necessary to be able to modify
X      the mail headers of messages on the way off the machine.  The
X      headers currently supported for modification are:
X
X	Subject:
X	To:
X	Cc:
X	Bcc:
X	Reply-To:
X
X	Expiration-Date:
X	Priority:
X        In-Reply-To:
X	Action:
X
X	<user defined>
X	
X      (C) Copyright 1985, Dave Taylor
X**/
X
X#include <stdio.h>
X#include "headers.h"
X
X#include <ctype.h>
X
X#ifdef BSD
X#undef toupper
X#endif
X
X/* these are all defined in the mailmsg file! */
X
Xextern char subject[SLEN], action[SLEN], in_reply_to[SLEN], expires[SLEN], 
X            priority[SLEN], reply_to[SLEN], to[VERY_LONG_STRING], 
X	    cc[VERY_LONG_STRING], expanded_to[VERY_LONG_STRING], 
X	    expanded_cc[VERY_LONG_STRING], user_defined_header[SLEN];
X
X#ifdef ALLOW_BCC
Xextern char bcc[VERY_LONG_STRING], expanded_bcc[VERY_LONG_STRING];
X#endif
X
Xchar *strip_commas(), *strcpy();
X
Xedit_headers()
X{
X	/** Edit headers.  **/
X
X	int unexpanded_to = TRUE, unexpanded_cc = TRUE;
X#ifdef ALLOW_BCC
X	int unexpanded_bcc = TRUE;
X#endif
X	char c;
X	
X	if (mail_only) goto outta_here;		/* how did we get HERE??? */
X
X	display_headers();
X
X	while (TRUE) {	/* forever */
X	  PutLine0(LINES-1,0,"Choice: ");
X	  CleartoEOLN();
X	  c = toupper(getchar());
X	  clear_error();
X	  switch (c) {
X	    case RETURN:
X	    case LINE_FEED:
X	    case 'Q' : goto outta_here;
X	    case ctrl('L') : display_headers();
X		       break;
X	    case 'T' : if (optionally_enter(to, 2, 5, TRUE) == -1)
X	                 goto outta_here;
X	    	       build_address(strip_commas(to), expanded_to);
X		       unexpanded_to = FALSE; 
X		       break;
X	    case 'S' : if (optionally_enter(subject, 7, 9, FALSE) == -1)
X			 goto outta_here;
X		       break;
X#ifdef ALLOW_BCC
X	    case 'B' : if (optionally_enter(bcc, 4, 5, TRUE) == -1)
X			 goto outta_here;
X	  	       build_address(strip_commas(bcc), expanded_bcc);
X		       unexpanded_bcc = FALSE;
X		       break;
X#endif
X	    case 'C' : if (optionally_enter(cc, 3, 5, TRUE) == -1)
X			 goto outta_here;
X	  	       build_address(strip_commas(cc), expanded_cc);
X		       unexpanded_cc = FALSE;
X		       break;
X	    case 'R' : if (optionally_enter(reply_to, 5, 10, FALSE) == -1)
X			 goto outta_here;
X		       break;
X	    case 'A' : if (optionally_enter(action, 9, 9, FALSE) == -1)
X			 goto outta_here;
X		       break;
X	    case 'E' : enter_date(10, 17, expires);
X		       break;
X	    case 'P' : if (optionally_enter(priority, 11,10, FALSE) == -1)
X			 goto outta_here;
X		       break;
X	    case 'U' : if (optionally_enter(user_defined_header,14,0,FALSE)==-1)
X			 goto outta_here;
X		       else
X		         check_user_header(user_defined_header);
X		       break;
X	    case 'I' : if (strlen(in_reply_to) > 0) {
X	                 if (optionally_enter(in_reply_to, 12,13, FALSE) == -1)
X			   goto outta_here;
X			 break;		
X		       }
X		       /** else fall through as an error **/
X	    default  : error("Unknown header being specified!");
X	  }
X	} 
X
Xoutta_here:	/* this section re-expands aliases before we leave... */
X
X	if (unexpanded_to)
X	  build_address(strip_commas(to), expanded_to);
X	if (unexpanded_cc)
X	  build_address(strip_commas(cc), expanded_cc);
X#ifdef ALLOW_BCC
X	if (unexpanded_bcc)
X	  build_address(strip_commas(bcc), expanded_bcc);
X#endif
X}
X
Xdisplay_headers()
X{
X	ClearScreen();
X
X	Centerline(0,"Message Header Edit Screen");
X
X	PutLine1(2,0,"To : %s", to);
X	PutLine1(3,0,"Cc : %s", cc); CleartoEOLN();
X#ifdef ALLOW_BCC
X	PutLine1(4,0,"Bcc: %s", bcc); CleartoEOLN();
X#endif
X	PutLine1(5,0,"Reply-To: %s", reply_to); CleartoEOS();
X
X	PutLine1(7,0,"Subject: %s", subject);
X	PutLine1(9,0,"Action : %s", action);
X	PutLine1(10,0,"Expiration-Date: %s", expires);
X	PutLine1(11,0,"Priority: %s", priority);
X	if (strlen(in_reply_to) > 0)
X	  PutLine1(12,0,"In-Reply-To: %s", in_reply_to);
X
X	if (strlen(user_defined_header) > 0)
X	  PutLine1(14,0, "%s", user_defined_header);
X
X	Centerline(LINES-5, 
X"Choose first letter of existing header, U)ser defined header, or <return>");
X}
X
Xenter_date(x, y, buffer)
Xint x, y;
Xchar *buffer;
X{
X	/** Enter the number of days this message is valid for, then
X	    display at (x,y) the actual date of expiration.  This 
X	    routine relies heavily on the routine 'days_ahead()' in
X	    the file date.c
X	**/
X
X	int days;
X
X	PutLine0(LINES-1,0, "How many days in the future should it expire? ");
X	CleartoEOLN();
X	Raw(OFF);
X	gets(buffer);
X	Raw(ON);
X	sscanf(buffer, "%d", &days);
X	if (days < 1)
X	  error("That doesn't make sense!");
X	else if (days > 14)
X	  error("Expiration date must be within two weeks of today");
X	else {
X	  days_ahead(days, buffer);
X	  PutLine0(x, y, buffer);
X	}
X}
X
Xcheck_user_header(header)
Xchar *header;
X{
X	/** check the header format...if bad print error and erase! **/
X
X	register int i = -1;
X
X	if (strlen(header) == 0)
X	   return;
X
X	if (whitespace(header[0])) {
X	  error ("you can't have leading white space in a header!");
X	  header[0] = '\0';
X	  ClearLine(14);
X	  return;
X	}
X
X	if (header[0] == ':') {
X	  error ("you can't have a colon as the first character!");
X	  header[0] = '\0';
X	  ClearLine(14);
X	  return;
X	}
X
X	while (header[++i] != ':') {
X	  if (header[i] == '\0') {
X	    error("you need a colon ending the field!");
X	    header[0] = '\0';
X	    ClearLine(14);
X	    return;
X	  }
X	  else if (whitespace(header[i])) {
X	    error("You can't have white space imbedded in the header name!");
X	    header[0] = '\0';
X	    ClearLine(14);
X	    return;
X	  }
X	}
X	
X	return;
X}
END_OF_src/hdrconfg.c
if test 5434 -ne `wc -c <src/hdrconfg.c`; then
    echo shar: \"src/hdrconfg.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"src/help.c\" \(5969 characters\)
if test -f src/help.c ; then 
  echo shar: Will not over-write existing file \"src/help.c\"
else
sed "s/^X//" >src/help.c <<'END_OF_src/help.c'
X/**			help.c			**/
X
X/*** help routine for ELM program 
X
X     (C) Copyright 1985, Dave Taylor
X
X***/
X
X#include <ctype.h>
X
X#ifdef BSD
X# undef tolower
X#endif
X
X#include "headers.h"
X
Xhelp()
X{
X	/** Process the request for help [section] from the user **/
X
X	char ch;		/* character buffer for input */
X	char *s;		/* string pointer...	      */
X
X	MoveCursor(LINES-7,0);
X	CleartoEOS();
X
X	Centerline(LINES-7, "ELM Help System");
X	Centerline(LINES-5,
X           "Press keys you want help for, '?' for a list, or '.' to end");
X
X	PutLine0(LINES-3, 0, "Help on key: ");
X
X	do {
X	  MoveCursor(LINES-3, strlen("Help on key: "));
X	  ch = tolower(ReadCh());
X	  
X	  if (ch == '.') return(0);	/* zero means footer rewrite only */
X
X	  s = "Unknown command.  Use '?' for a list of commands...";
X
X	  switch (ch) {
X
X	    case '?': display_helpfile(MAIN_HELP);	return(1);
X
X	    case '$': s =
X  "$ = Force a resync of the current mailbox.  This will 'purge' deleted mail";
X		break;
X
X	    case '!': s = 
X   "! = Escape to the Unix shell of your choice, or just to enter commands";
X	       break;
X	    case '@': s = 
X       "@ = Debug - display a summary of the messages on the header page";
X	       break;
X	    case '|': s = 
X "| = Pipe the current message or tagged messages to the command specified";
X	       break;
X	    case '#': s = 
X      "# = Debug - display all information known about current message";
X	      break;
X	    case '%': s = 
X   "% = Debug - display the computed return address of the current message";
X	       break;
X	    case '*': s = "* = Go to the last message in the current mailbox";
X	       break;
X	    case '-': s = 
X	       "- = Go to the previous page of messages in the current mailbox";
X	       break;
X	    case '=': s = 
X                  "'=' = Go to the first message in the current mailbox";
X	       break;
X	    case ' ': 
X	    case '+': s = 
X		"+ = Go to the next page of messages in the current mailbox";
X	       break;
X	    case '/': s = "/ = Search for specified pattern in mailbox";
X	       break;
X	    case '<': s = 
X	       "< = Scan current message for calendar entries (if enabled)";
X	       break;
X	    case '>': s = 
X	       "> = Save current message or tagged messages to specified file";
X	       break;
X	    case '^': s = 
X	       "^ = Toggle the Delete/Undelete status of the current message";
X	       break;
X	    case 'a': s = 
X       "a = Enter the alias sub-menu section.  Create and display aliases";
X	       break;
X	    case 'b': s = 
X    "b = Bounce (remail) a message to someone as if you have never seen it";
X	       break;
X	    case 'c': s = 
X       "c = Change mailboxes, leaving the current mailbox as if 'quitting'";
X	       break;
X	    case 'd': s = "d = Mark the current message for future deletion";
X	       break;
X	    case ctrl('D') :
X	s = "^D = Mark for deletion all messages with the specified pattern";
X		break;
X	    case 'e': s = 
X       "e = Invoke the editor on the entire mailbox, resync'ing when done";
X	       break;
X	    case 'f': s = 
X      "f = Forward the current message to someone, return address is yours";
X	       break;
X	    case 'g': s = 
X "g = Group reply not only to the sender, but to everyone who received msg";
X	       break;
X	    case 'h': s = 
X	       "h = Display message with all Headers (ignore weedout list)";
X	       break;
X	    case 'j': s = 
X       "j = Go to the next message.  This is the same as the DOWN arrow";
X	       break;
X	    case 'k': s = 
X       "k = Go to the previous message.  This is the same as the UP arrow";
X	       break;
X	    case 'm': s = 
X               "m = Create and send mail to the specified person or persons";
X	       break;
X	    case 'n': s = 
X               "n = Read the current message, then move current to next messge";
X	       break;
X	    case 'o': s = "o = Go to the options submenu";
X	       break;
X	    case 'p': s = 
X		"p = Print the current message or the tagged messages";
X	       break;
X	    case 'q': s = 
X		"q = Quit the mailer, asking about deletion, saving, etc";
X	       break;
X	    case 'r': s = 
X  "r = Reply to the message.  This only sends to the originator of the message";
X	       break;
X	    case 's': s = 
X               "s = Save current message or tagged messages to specified file";
X	       break;
X	    case 't': s = 
X               "t = Tag a message for further operations (or untag if tagged)";
X	       break;
X	    case ctrl('T') :
X		s = "^T = tag all messages with the specified pattern";
X		break;
X	    case 'u': 
X		s = "u = Undelete - remove the deletion mark on the message";
X	       break;
X	    case 'x': s = "x = Exit the mail system quickly";
X	       break;
X    	
X	    case '\n':
X	    case '\r': s = "<return> = Read the current message";
X	       break;
X    
X	    case ctrl('L'): s = "^L = Rewrite the screen";	
X	       break;
X            case ctrl('?'):					    /* DEL */
X	    case ctrl('Q'): s = "Exit the mail system quickly";
X	       break;
X	    default : if (isdigit(ch)) 
X	            s = "<number> = Make specified number the current message";
X	  }
X
X	  ClearLine(LINES-1);
X	  Centerline(LINES-1, s);
X
X	} while (ch != '.');
X	
X	/** we'll never actually get here, but that's okay... **/
X
X	return(0);
X}
X
Xdisplay_helpfile(section)
Xint section;
X{
X	/*** Help me!  Read file 'helpfile.<section>' and echo to screen ***/
X
X	FILE *hfile;
X	char buffer[SLEN];
X	int  lines=0;
X
X	sprintf(buffer, "%s/%s.%d", helphome, helpfile, section);
X	if ((hfile = fopen(buffer,"r")) == NULL) {
X	  dprint1(1,"Error: Couldn't open helpfile %s (help)\n", buffer);
X	  error1("couldn't open helpfile %s",buffer);
X	  return(FALSE);
X	}
X	
X	ClearScreen();
X
X	while (fgets(buffer, SLEN, hfile) != NULL) {
X	  if (lines > LINES-3) {
X	    PutLine0(LINES,0,"Press any key to continue: ");
X	    (void) ReadCh();
X	    lines = 0;
X	    ClearScreen();
X	    Write_to_screen("%s\r", 1, buffer);
X	  }
X	  else 
X	    Write_to_screen("%s\r", 1, buffer);
X
X	  lines++;
X	}
X
X        PutLine0(LINES,0,"Press any key to return: ");
X
X	(void) ReadCh();
X	clear_error();
X
X	return(TRUE);
X}
END_OF_src/help.c
if test 5969 -ne `wc -c <src/help.c`; then
    echo shar: \"src/help.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"src/pattern.c\" \(5598 characters\)
if test -f src/pattern.c ; then 
  echo shar: Will not over-write existing file \"src/pattern.c\"
else
sed "s/^X//" >src/pattern.c <<'END_OF_src/pattern.c'
X/**			pattern.c			**/
X
X/**    General pattern matching for the ELM mailer.     
X
X       (C) Copyright 1986 Dave Taylor
X**/
X
X#include <errno.h>
X
X#include "headers.h"
X
Xstatic char pattern[SLEN] = { "" };
Xstatic char alt_pattern[SLEN] = { "" };
X
Xextern int errno;
X
Xchar *error_name(), *shift_lower(), *strcpy();
X
Xmeta_match(function)
Xint function;
X{
X	/** Perform specific function based on whether an entered string 
X	    matches either the From or Subject lines.. 
X	**/
X
X	register int i, tagged=0, count=0;
X	static char     meta_pattern[SLEN];
X
X	PutLine1(LINES-3, strlen("Command: "), 
X	     "%s messages that match pattern...", 
X	     function==TAGGED?"Tag": function==DELETED?"Delete":"Undelete");
X
X	if (function == TAGGED) {	/* are messages already tagged??? */
X	  for (i=0; i < message_count; i++)
X	    if (ison(header_table[i].status,TAGGED))
X	      tagged++;
X
X	  if (tagged) {
X	    if (tagged > 2) 
X	      PutLine0(LINES-2,0, "Some messages are already tagged");
X	    else
X	      PutLine0(LINES-2,0, "A message is already tagged");
X	
X	    Write_to_screen("- Remove tag%s? y%c", 2, plural(tagged),BACKSPACE);
X
X	    if (tolower(ReadCh()) != 'n') {	/* remove tags... */
X	      for (i=0; i < message_count; i++) {
X	        clearit(header_table[i].status,TAGGED);
X		show_new_status(i);
X	      }
X	    }
X	  }
X	}
X	
X	PutLine0(LINES-2,0, "Enter pattern: "); CleartoEOLN();
X
X	optionally_enter(meta_pattern, LINES-2,strlen("Enter pattern: "),FALSE);
X
X	if (strlen(meta_pattern) == 0) {
X	  ClearLine(LINES-2);
X	  return(0);
X	}
X
X	strcpy(meta_pattern, shift_lower(meta_pattern));   /* lowercase it */
X
X	for (i = 0; i < message_count; i++) {
X	  if (from_matches(i, meta_pattern)) {
X	    if ((selected && header_table[i].status & VISIBLE) || ! selected) {
X	      if (function == UNDELETE)
X	        clearit(header_table[i].status, DELETED);
X	      else
X	        setit(header_table[i].status, function);
X	      show_new_status(i);
X	      count++;
X	    }
X	  }
X	  else if (subject_matches(i, meta_pattern)) {
X	    if ((selected && header_table[i].status & VISIBLE) || ! selected) {
X	      if (function == UNDELETE)
X	        clearit(header_table[i].status, DELETED);
X	      else
X	        setit(header_table[i].status, function);
X	      show_new_status(i);
X	      count++;
X	    }
X	  }
X	}
X
X	ClearLine(LINES-2);	/* remove "pattern: " prompt */
X	
X	if (count > 0)
X	  error3("%s %d messsage%s", 
X	         function==TAGGED? "tagged" : 
X		   function==DELETED? "marked for deletion" : "undeleted",
X		 count, plural(count));
X	else
X	  error1("no matches - no messages %s",
X		 function==TAGGED? "tagged" : 
X		   function==DELETED? "marked for deletion": "undeleted");
X
X	return(0);
X}
X	  
Xint
Xpattern_match()
X{
X	/** Get a pattern from the user and try to match it with the
X	    from/subject lines being displayed.  If matched (ignoring
X	    case), move current message pointer to that message, if
X	    not, error and return ZERO **/
X
X	register int i;
X
X	PutLine0(LINES-3,40,"/ = match anywhere in messages");
X	
X	PutLine0(LINES-1,0, "Match Pattern:");
X
X	if (pattern_enter(pattern, alt_pattern, LINES-1, 16, 
X	    "Match Pattern (in entire mailbox):"))
X	  if (strlen(alt_pattern) > 0) {
X	    strcpy(alt_pattern, shift_lower(alt_pattern));
X	    return(match_in_message(alt_pattern));
X	  }
X	  else
X	    return(1);
X	  
X	if (strlen(pattern) == 0) 
X	  return(0);
X	else
X	  strcpy(pattern, shift_lower(pattern));
X
X	for (i = current; i < message_count; i++) {
X	  if (from_matches(i, pattern)) {
X	    if (!selected || (selected && header_table[i].status & VISIBLE)) {
X	      current = ++i;
X	      return(1);
X	    }
X	  }
X	  else if (subject_matches(i, pattern)) {
X	    if (!selected || (selected && header_table[i].status & VISIBLE)) {
X	      current = ++i;
X	      return(1);
X	    }
X	  }
X	}
X
X	return(0);
X}
X
Xint
Xfrom_matches(message_number, pat)
Xint message_number;
Xchar *pat;
X{
X	/** Returns true iff the pattern occurs in it's entirety
X	    in the from line of the indicated message **/
X
X	return( in_string(shift_lower(header_table[message_number].from), 
X		pat) );
X}
X
Xint
Xsubject_matches(message_number, pat)
Xint message_number;
Xchar *pat;
X{
X	/** Returns true iff the pattern occurs in it's entirety
X	    in the subject line of the indicated message **/
X
X	return( in_string(shift_lower(header_table[message_number].subject), 
X		pat) );
X}
X
Xmatch_in_message(pat)
Xchar *pat;
X{
X	/** Match a string INSIDE a message...starting at the current 
X	    message read each line and try to find the pattern.  As
X	    soon as we do, set current and leave! 
X	    Returns 1 if found, 0 if not
X	**/
X
X	char buffer[LONG_STRING];
X	int  message_number, lines, line;
X
X	message_number = current-1;
X
X	error("searching mailbox for pattern...");
X
X	while (message_number < message_count) {
X
X	  if (fseek(mailfile, header_table[message_number].offset, 0L) != 0) {
X
X	    dprint3(1,"Error: seek %ld bytes into file failed. errno %d (%s)\n",
X		      header_table[message_number].offset, errno, 
X		     "match_in_message");
X	    error2("ELM [match] failed looking %ld bytes into file (%s)",
X		   header_table[message_number].offset, error_name(errno));
X	    return(1);	/* fake it out to avoid replacing error message */
X	  }
X
X	  line = 0;
X	  lines = header_table[message_number].lines;
X
X	  while (fgets(buffer, LONG_STRING, mailfile) != NULL && line < lines) {
X	
X	    line++;
X
X	    if (in_string(shift_lower(buffer), pat)) {
X	      current = message_number+1; 
X	      clear_error();
X	      return(1);
X	    }
X	  }
X
X	  /** now we've passed the end of THIS message...increment and 
X	      continue the search with the next message! **/
X
X	  message_number++;
X	}
X
X	return(0);
X}
END_OF_src/pattern.c
if test 5598 -ne `wc -c <src/pattern.c`; then
    echo shar: \"src/pattern.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"src/utils.c\" \(5852 characters\)
if test -f src/utils.c ; then 
  echo shar: Will not over-write existing file \"src/utils.c\"
else
sed "s/^X//" >src/utils.c <<'END_OF_src/utils.c'
X/**		utils.c		**/
X
X/** Utility routines for ELM 
X
X    All routines herein: (C) Copyright 1985 Dave Taylor
X**/
X
X#include "headers.h"
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <ctype.h>
X#include <errno.h>
X
X#ifdef BSD
X#undef tolower
X#endif
X
X#include <signal.h>
X
Xextern int errno;
X
Xchar *error_name();
Xvoid   exit();
X
Xshow_mailfile_stats()
X{
X	/** when we're about to die, let's try to dump lots of good stuff
X	    to the debug file... **/
X
X	struct stat buffer;
X
X	if (debug == 0) return;		/* Damn!  Can't do it! */
X
X	if (fstat(fileno(mailfile), &buffer) == 0) {
X	  dprint1(1,"\nDump of stats for mailfile %s;\n", infile);
X
X	  dprint3(1, "\tinode: %d, mode: %o, uid: %d, ",
X			buffer.st_ino, buffer.st_mode, buffer.st_uid);
X	  dprint2(1,"gid: %d, size: %d\n\n", buffer.st_gid, buffer.st_size);
X
X	  dprint1(1,"\toffset into file = %l\n", ftell(mailfile));
X	}
X	else
X	  dprint2(1,"\nfstat on mailfile '%s' failed with error %s!!\n\n",
X			infile, error_name(errno));
X}
X	
Xemergency_exit()
X{
X	/** used in dramatic cases when we must leave without altering
X	    ANYTHING about the system... **/
X
X	dprint0(1,
X     "\nERROR: Something dreadful is happening!  Taking emergency exit!!\n\n");
X	dprint0(1,"  possibly leaving behind the following files;\n");
X	dprint2(1,"     The mailbox tempfile : %s%s\n", temp_mbox, username);
X	dprint2(1,"     The mailbox lock file: %s%s.lock\n", mailhome, username);
X	dprint2(1,"     The composition file : %s%d\n", temp_file, getpid());
X	dprint2(1,"     The header comp file : %s%d\n", temp_file, getpid()+1);
X	dprint2(1,"     The readmsg data file: %s/%s\n", home, readmsg_file);
X
X	Raw(OFF);
X	if (cursor_control)  transmit_functions(OFF);
X	if (hp_terminal)     softkeys_off();
X
X	if (cursor_control)
X	  MoveCursor(LINES, 0);
X
X	PutLine0(LINES,0, "\nEmergency Exit taken!  All temp files intact!\n\n");
X
X	exit(1);
X}
X
X/*ARGSUSED*/
X/*VARARGS0*/
X
Xleave(val)
Xint val;	/* not used, placeholder for signal catching! */
X{
X	char buffer[SLEN];
X
X	dprint0(2,"\nLeaving mailer normally (leave)\n");
X
X	Raw(OFF);
X	if (cursor_control)  transmit_functions(OFF);
X	if (hp_terminal)     softkeys_off();
X
X	sprintf(buffer,"%s%d",temp_file, getpid());  /* editor buffer */
X	(void) unlink(buffer);
X
X	sprintf(buffer,"%s%d",temp_file, getpid()+1);  /* editor buffer */
X	(void) unlink(buffer);
X
X	sprintf(buffer,"%s%s",temp_mbox, username);  /* temp mailbox */
X	(void) unlink(buffer);
X
X	sprintf(buffer,"%s/%s", home, readmsg_file);  /* readmsg temp */
X	(void) unlink(buffer);
X
X	sprintf(buffer,"%s%s.lock",mailhome, username); /* lock file */
X	(void) unlink(buffer);
X
X	if (! mail_only) {
X	  MoveCursor(LINES,0);
X	  Writechar('\n');
X	}
X
X	exit(0);
X}
X
Xsilently_exit()
X{
X	/** This is the same as 'leave', but it doesn't remove any non-pid
X	    files.  It's used when we notice that we're trying to create a
X	    temp mail file and one already exists!!
X	**/
X	char buffer[SLEN];
X
X	dprint0(2,"\nLeaving mailer quietly (silently_exit)\n");
X
X	Raw(OFF);
X	if (cursor_control)  transmit_functions(OFF);
X	if (hp_terminal)     softkeys_off();
X
X	sprintf(buffer,"%s%d",temp_file, getpid());  /* editor buffer */
X	(void) unlink(buffer);
X
X	sprintf(buffer,"%s%d",temp_file, getpid()+1);  /* editor buffer */
X	(void) unlink(buffer);
X
X	if (! mail_only) {
X	  MoveCursor(LINES,0);
X	  Writechar('\n');
X	}
X
X	exit(0);
X}
X
X/*ARGSUSED0*/
X
Xleave_locked(val)
Xint val;	/* not used, placeholder for signal catching! */
X{
X	/** same as leave routine, but don't disturb lock file **/
X
X	char buffer[SLEN];
X
X        dprint0(3,
X	    "\nLeaving mailer due to presence of lock file (leave_locked)\n");
X
X	Raw(OFF);
X	if (cursor_control)  transmit_functions(OFF);
X	if (hp_terminal)     softkeys_off();
X
X	sprintf(buffer,"%s%d",temp_file, getpid());  /* editor buffer */
X	(void) unlink(buffer);
X
X	sprintf(buffer,"%s%d",temp_file, getpid()+1);  /* editor buffer */
X	(void) unlink(buffer);
X
X	sprintf(buffer,"%s%s",temp_mbox, username);  /* temp mailbox */
X	(void) unlink(buffer);
X
X	MoveCursor(LINES,0);
X	Writechar('\n');
X
X	exit(0);
X}
X
Xint
Xget_page(msg_pointer)
Xint msg_pointer;
X{
X	/** Ensure that 'current' is on the displayed page,
X	    returning non-zero iff the page changed! **/
X
X	register int first_on_page, last_on_page;
X
X	dprint1(6,"* get_page(%d) returns...", msg_pointer);
X
X	first_on_page = (header_page * headers_per_page) + 1;
X
X	last_on_page = first_on_page + headers_per_page - 1;
X
X	dprint2(8,"[first-on-page=%d, last-on-page=%d]",
X		first_on_page, last_on_page);
X
X	if (selected)	/* but what is it on the SCREEN??? */
X	  msg_pointer = compute_visible(msg_pointer-1);
X
X	if (selected && msg_pointer > selected) {
X	  dprint0(6,"FALSE - too far!\n");
X	  return(FALSE);	/* too far - page can't change! */
X	}
X
X	if (msg_pointer > last_on_page) {
X	  header_page = (int) (msg_pointer-(selected? 0:1)) / headers_per_page;
X	  dprint3(6,"TRUE (%d > %d  New hp=%d)!\n",
X		msg_pointer, last_on_page, header_page);
X	  return(1);
X	}
X	else if (msg_pointer < first_on_page) {
X	  header_page = (int) (msg_pointer-1) / headers_per_page;
X	  dprint3(6,"TRUE (%d < %d   New hp=%d)!\n",
X		msg_pointer, first_on_page, header_page);
X	  return(1);
X	}
X	else {
X	  dprint2(6,"FALSE [first=%d last=%d]\n",
X		  first_on_page, last_on_page);
X	  return(0);
X	}
X}
X
Xchar *nameof(filename)
Xchar *filename;
X{
X	/** checks to see if 'filename' has any common prefixes, if
X	    so it returns a string that is the same filename, but 
X	    with '=' as the folder directory, or '~' as the home
X	    directory..
X	**/
X
X	static char buffer[STRING];
X	register int i = 0, index = 0;
X
X	if (strncmp(filename, folders, strlen(folders)) == 0) {
X	  buffer[i++] = '=';
X	  index = strlen(folders);
X	}
X	else if (strncmp(filename, home, strlen(home)) == 0) {
X	  buffer[i++] = '~';
X	  index = strlen(home);
X	}
X	else index = 0;
X
X	while (filename[index] != '\0')
X	  buffer[i++] = filename[index++];
X	buffer[i] = '\0';
X	
X	return( (char *) buffer);
X}
END_OF_src/utils.c
if test 5852 -ne `wc -c <src/utils.c`; then
    echo shar: \"src/utils.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: End of archive 5 \(of 19\).
cp /dev/null ark5isdone
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 archivenlCopyrp_