v22i068: ELM mail syste, release 2.3, Part09/26

Rich Salz rsalz at bbn.com
Fri Jun 1 00:12:06 AEST 1990


Submitted-by: Syd Weinstein <syd at dsinc.dsi.com>
Posting-number: Volume 22, Issue 68
Archive-name: elm2.3/part09

#!/bin/sh
# this is part 9 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file filter/filter.c continued
#
CurArch=9
if test ! -r s2_seq_.tmp
then echo "Please unpack part 1 first!"
     exit 1; fi
( read Scheck
  if test "$Scheck" != $CurArch
  then echo "Please unpack part $Scheck next!"
       exit 1;
  else exit 0; fi
) < s2_seq_.tmp || exit 1
echo "x - Continuing file filter/filter.c"
sed 's/^X//' << 'SHAR_EOF' >> filter/filter.c
X#include "defs.h"
X#ifdef I_TIME
X#  include <time.h>
X#endif
X#ifdef I_SYSTIME
X#  include <sys/time.h>
X#endif
X#include <fcntl.h>
X
X#define  MAIN_ROUTINE			/* for the filter.h file, of course! */
X#include "filter.h"
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	extern char *optarg;
X	FILE   *fd;				/* for output to temp file! */
X	struct passwd  *passwd_entry;
X#ifndef	_POSIX_SOURCE
X	struct passwd  *getpwuid();		/* for /etc/passwd          */
X#endif
X	char filename[SLEN],			/* name of the temp file    */
X	     buffer[MAX_LINE_LEN];		/* input buffer space       */
X	int  in_header = TRUE,			/* for header parsing       */
X	     in_to     = FALSE,			/* are we on 'n' line To: ? */
X	     summary   = FALSE,			/* a summary is requested?  */
X	     c;					/* var for getopt routine   */
X
X	/* first off, let's get the info from /etc/passwd */ 
X	
X	if ((passwd_entry = getpwuid(getuid())) == NULL) 
X	  leave("Cannot get password entry for this uid!");
X
X	strcpy(home, passwd_entry->pw_dir);
X	strcpy(username, passwd_entry->pw_name);
X	outfname[0] = to[0] = '\0';	/* nothing read in yet, right? */
X
X#ifdef HOSTCOMPILED
X	strncpy(hostname, HOSTNAME, sizeof(hostname));
X#else
X	gethostname(hostname, sizeof(hostname));
X#endif
X
X	/* now parse the starting arguments... */
X
X	while ((c = getopt(argc, argv, "clno:rSsv")) != EOF) {
X	  switch (c) {
X	    case 'c' : clear_logs = TRUE;			break;
X	    case 'l' : log_actions_only = TRUE;			break;
X	    case 'o' : strcpy(outfname, optarg);		break;
X	    case 'r' : printing_rules = TRUE;			break;
X	
X	    case 's' : summary = TRUE;				break;
X	    case 'S' : long_summary = TRUE;			break;
X
X	    case 'n' : show_only = TRUE;			break;
X	    case 'v' : verbose = TRUE;				break;
X	    case '?' : fprintf(stderr, 
X		       "Usage: | filter [-nrv]\n   or: filter [-c] -[s|S]\n");
X          	       exit(1);
X	  }
X	}
X
X	if (c < 0) {
X	}
X
X	/* let's open our outfd logfile as needed... */
X
X	if (outfname[0] == '\0') 	/* default is stdout */
X	  outfd = stdout;
X	else 
X	  if ((outfd = fopen(outfname, "a")) == NULL) {
X	    if (isatty(fileno(stderr)))
X	      fprintf(stderr,"filter (%s): couldn't open log file %s\n",
X		      username, outfname);
X	  }
X
X	if (summary || long_summary) {
X          if (get_filter_rules() == -1) {
X	    exit(1);
X	    if (outfd != NULL) fclose(outfd);
X	  }
X	  show_summary();
X	  if (outfd != NULL) fclose(outfd);
X	  exit(0);
X	}
X
X	if (printing_rules) {
X          if (get_filter_rules() == -1)
X	    fprintf(outfd,"filter (%s): Couldn't get rules!\n", username);
X          else
X	    print_rules();
X	  if (outfd != NULL) fclose(outfd);
X          exit(0);
X	}
X
X	/* next, create the tempfile and save the incoming message */
X
X	sprintf(filename, "%s.%d", filter_temp, getpid());
X
X	if ((fd = fopen(filename,"w")) == NULL)
X	  leave("Cannot open temporary file!");
X
X	while (fgets(buffer, MAX_LINE_LEN, stdin) != NULL) {
X
X	  remove_return(buffer);
X
X	  if (in_header) {
X
X	    if (! whitespace(buffer[0])) 
X		in_to = FALSE;
X
X	    if (the_same(buffer, "From ")) 
X	      save_from(buffer);
X	    else if (the_same(buffer, "Subject:")) 
X	      save_subject(buffer);
X	    else if (the_same(buffer, "To:") || the_same(buffer, "Cc:")) {
X	      in_to++;
X	      save_to(buffer);
X	    }
X	    else if (the_same(buffer, "X-Filtered-By:")) 
X	      already_been_forwarded++;	/* could be a loop here! */
X#ifdef USE_EMBEDDED_ADDRESSES
X	    else if (the_same(buffer, "From:"))
X	      save_embedded_address(buffer, "From:");
X	    else if (the_same(buffer, "Reply-To:"))
X	      save_embedded_address(buffer, "Reply-To:");
X#endif
X	    else if (strlen(buffer) < 2) 
X	      in_header = 0;
X	    else if (whitespace(buffer[0]) && in_to)
X	      strcat(to, buffer);
X	  }
X	
X          fprintf(fd, "%s\n", buffer);	/* and save it regardless! */
X	  fflush(fd);
X	  lines++;
X	}
X
X	fclose(fd);
X
X	/** next let's see if the user HAS a filter file, and if so what's in
X            it (and so on) **/
X
X	if (get_filter_rules() == -1)
X	  mail_message(username);
X	else {
X	  switch (action_from_ruleset()) {
X
X	    case DELETE_MSG : if (verbose && outfd != NULL)
X			    fprintf(outfd, "filter (%s): Message deleted\n",
X				    username);
X			  log(DELETE_MSG);				break;
X
X	    case SAVE   : if (save_message(rules[rule_choosen].argument2)) {
X			    mail_message(username);
X			    log(FAILED_SAVE);
X			  }
X			  else
X		 	    log(SAVE);					break;
X
X	    case SAVECC : if (save_message(rules[rule_choosen].argument2))
X			    log(FAILED_SAVE);
X			  else
X		            log(SAVECC);					
X			  mail_message(username);			break;
X
X	    case FORWARD: mail_message(rules[rule_choosen].argument2);
X			  log(FORWARD);					break;
X
X	    case EXEC   : execute(rules[rule_choosen].argument2);
X			  log(EXEC);					break;
X
X	    case LEAVE  : mail_message(username);
X			  log(LEAVE);					break;
X	  }
X	}
X
X	(void) unlink(filename);	/* remove the temp file, please! */
X	if (outfd != NULL) fclose(outfd);
X	exit(0);
X}
X
Xsave_from(buffer)
Xchar *buffer;
X{
X	/** save the SECOND word of this string as FROM **/
X
X	register char *f = from;
X
X	while (*buffer != ' ')
X	  buffer++;				/* get to word     */
X
X	for (buffer++; *buffer != ' ' && *buffer; buffer++, f++) 
X	  *f = *buffer;				/* copy it and     */
X
X	*f = '\0';				/* Null terminate! */
X}
X
Xsave_subject(buffer)
Xchar *buffer;
X{
X	/** save all but the word "Subject:" for the subject **/
X
X	register int skip = 8;  /* skip "Subject:" initially */
X
X	while (buffer[skip] == ' ') skip++;
X
X	strcpy(subject, (char *) buffer + skip);
X}
X
Xsave_to(buffer)
Xchar *buffer;
X{
X	/** save all but the word "To:" or "Cc:" for the to list **/
X
X	register int skip = 3;	/* skip "To:" or "Cc:" initially */
X
X	while (buffer[skip] == ' ') skip++;
X
X	strcat(to, (char *) buffer + skip);
X}
X
X#ifdef USE_EMBEDDED_ADDRESSES
X
Xsave_embedded_address(buffer, fieldname)
Xchar *buffer, *fieldname;
X{
X	/** this will replace the 'from' address with the one given, 
X	    unless the address is from a 'reply-to' field (which overrides 
X	    the From: field).  The buffer given to this routine can have one 
X            of three forms:
X		fieldname: username <address>
X		fieldname: address (username)
X		fieldname: address
X	**/
X	
X	static int processed_a_reply_to = 0;
X	char address[LONG_STRING];
X	register int i, j = 0;
X
X	/** first let's extract the address from this line.. **/
X
X	if (buffer[strlen(buffer)-1] == '>') {	/* case #1 */
X	  for (i=strlen(buffer)-1; buffer[i] != '<' && i > 0; i--)
X		/* nothing - just move backwards .. */ ;
X	  i++;	/* skip the leading '<' symbol */
X	  while (buffer[i] != '>')
X	    address[j++] = buffer[i++];
X	  address[j] = '\0';
X	}
X	else {	/* get past "from:" and copy until white space or paren hit */
X	  for (i=strlen(fieldname); whitespace(buffer[i]); i++)
X	     /* skip past that... */ ;
X	  while (buffer[i] != '(' && ! whitespace(buffer[i]) && buffer[i]!='\0')
X	    address[j++] = buffer[i++];
X	  address[j] = '\0';
X	}
X
X	/** now let's see if we should overwrite the existing from address
X	    with this one or not.. **/
X
X	if (processed_a_reply_to)
X	  return;	/* forget it! */
X
X	strcpy(from, address);			/* replaced!! */
X
X	if (strcmp(fieldname, "Reply-To:") == 0)
X	  processed_a_reply_to++;
X}
X#endif
SHAR_EOF
echo "File filter/filter.c is complete"
chmod 0444 filter/filter.c || echo "restore of filter/filter.c fails"
echo "x - extracting filter/lock.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > filter/lock.c &&
X
Xstatic char rcsid[] ="@(#)$Id: lock.c,v 4.1 90/04/28 22:41:57 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein - elm at DSI.COM
X *			dsinc!elm
X *
X *******************************************************************************
X * $Log:	lock.c,v $
X * Revision 4.1  90/04/28  22:41:57  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
X
X
X/** The lock() and unlock() routines herein duplicate exactly the
X    equivalent routines in the Elm Mail System, and should also be
X    compatible with sendmail, rmail, etc etc.
X
X  
X**/
X
X#include <stdio.h>
X#include <fcntl.h>
X#include <errno.h>
X#include "defs.h"
X#include "filter.h"
X
Xstatic  int  we_locked_it;
Xstatic  char lockfile[SLEN];
X
X#ifdef	LOCK_BY_FLOCK
X#include <sys/types.h>
X#include <sys/file.h>
Xstatic	flock_fd = -1;
Xstatic	char flock_name[SLEN];
X#endif
X
Xextern  int  errno;
X
Xint
Xlock()
X{
X	/** This routine will return 1 if we could lock the mailfile,
X	    zero otherwise.
X	**/
X
X	int attempts = 0, ret;
X
X#ifndef	LOCK_FLOCK_ONLY			/* { !LOCK_FLOCK_ONLY	*/
X	sprintf(lockfile,  "%s%s.lock", mailhome, username);
X#ifdef PIDCHECK
X	/** first, try to read the lock file, and if possible, check the pid.
X	    If we can validate that the pid is no longer active, then remove
X	    the lock file.
X	**/
X	if((ret=open(lockfile,O_RDONLY)) != -1) {
X	  char pid_buffer[SHORT];
X	  if (read(ret, pid_buffer, SHORT) > 0) {
X	    attempts = atoi(pid_buffer);
X	    if (attempts) {
X	      if (kill(attempts, 0)) {
X	        close(ret);
X	        if (unlink(lockfile) != 0)
X		  return(1);
X	      }
X	    }
X	  }
X	  attempts = 0;
X        }
X#endif
X
X	while ((ret = open(lockfile, O_WRONLY | O_CREAT | O_EXCL, 0444)) < 0 
X	       && attempts++ < 10) {
X	  sleep(3);	/* wait three seconds each pass, okay?? */
X	}
X
X	if (ret >= 0) {
X	  we_locked_it++;
X	  close(ret);			/* no need to keep it open! */
X	  ret = 1;
X	} else {
X	  ret = 0;
X	}
X	  
X#endif					/* } !LOCK_FLOCK_ONLY	*/
X#ifdef	LOCK_BY_FLOCK			/* { LOCK_BY_FLOCK	*/
X	(void)sprintf(flock_name,"%s%s",mailhome,username);
X	flock_fd = open(flock_name,O_RDONLY);
X	if ( flock_fd >= 0 )
X	  for (attempts = 0; attempts < 10; attempts++) {
X	    if ( (ret = flock(flock_fd,LOCK_NB|LOCK_EX)) != -1 )
X	        break;
X	    if ( errno != EWOULDBLOCK && errno != EAGAIN )
X	        break;
X	    (void)sleep((unsigned)3);
X	  }
X	if ( flock_fd >= 0 && ret == 0 ) {
X	    we_locked_it++;
X	    ret = 1;
X	} else {
X	    we_locked_it = 0;
X	    if ( lockfile[0] ) {
X	    	(void)unlink(lockfile);
X		lockfile[0] = 0;
X	    }
X	    if ( flock_fd >= 0 ) {
X	    	(void)close(flock_fd);
X	    	flock_fd = -1;
X	    }
X	    ret = 0;
X	}
X#endif
X	return(ret);
X}
X
Xunlock()
X{
X	/** this routine will remove the lock file, but only if we were
X	    the people that locked it in the first place... **/
X
X#ifndef	LOCK_FLOCK_ONLY
X	if (we_locked_it && lockfile[0]) {
X	  unlink(lockfile);	/* blamo! */
X	  lockfile[0] = 0;
X	}
X#endif
X#ifdef	LOCK_BY_FLOCK
X	if (we_locked_it && flock_fd >= 0) {
X	  (void)close(flock_fd);
X	  flock_fd = -1;
X	}
X#endif
X	we_locked_it = 0;
X}
SHAR_EOF
chmod 0444 filter/lock.c || echo "restore of filter/lock.c fails"
echo "x - extracting filter/parse.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > filter/parse.c &&
X
Xstatic char rcsid[] ="@(#)$Id: parse.c,v 4.1 90/04/28 22:41:58 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein - elm at DSI.COM
X *			dsinc!elm
X *
X *******************************************************************************
X * $Log:	parse.c,v $
X * Revision 4.1  90/04/28  22:41:58  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
X
X
X/** This is the parser for the filter program.  It accepts a wide variety of
X    constructs, building the ruleset table as it goes along.  Check the 
X    data structure in filter.h for more information on how the rules are
X    stored.  The parser is a cunning state-table based program.
X
X**/
X
X#include <stdio.h>
X#include <ctype.h>
X
X#include "defs.h"
X#include "filter.h"
X
X#define NONE			0
X#define AND			10
X
X#define NEXT_CONDITION		0
X#define GETTING_OP		1
X#define READING_ARGUMENT	2
X#define READING_ACTION		3
X#define ACTION_ARGUMENT		4
X
Xchar *strtok(), *whatname(), *actionname();
X
Xint
Xget_filter_rules()
X{
X	/** Given the users home directory, open and parse their rules table,
X	    building the data structure as we go along.
X	    returns -1 if we hit an error of any sort...
X	**/
X
X	FILE *fd;				/* the file descriptor     */
X	char  buffer[SLEN], 			/* fd reading buffer       */
X	      *str, 				/* ptr to read string      */
X	      *word,				/* ptr to 'token'          */
X	      filename[SLEN], 			/* the name of the ruleset */
X	      action_argument[SLEN], 		/* action arg, per rule    */
X	      cond_argument[SLEN];		/* cond arg, per condition */
X	int   not_condition = FALSE, 		/* are we in a "not" ??    */
X	      type=NONE, 			/* what TYPE of condition? */
X	      lasttype, 			/* and the previous TYPE?  */
X	      state = NEXT_CONDITION,		/* the current state       */
X	      in_single, in_double, 		/* for handling spaces.    */
X	      i, 				/* misc integer for loops  */
X	      relop = NONE,			/* relational operator     */
X	      action, 				/* the current action type */
X	      buflen,				/* the length of buffer    */
X	      line = 0;				/* line number we're on    */
X
X	struct condition_rec	*cond, *newcond;
X
X	sprintf(filename,"%s/%s", home, filterfile);
X
X	if ((fd = fopen(filename,"r")) == NULL) {
X	  if (outfd != NULL)
X	   fprintf(outfd,"filter (%s): Couldn't read user filter rules file!\n",
X		  username);
X	  return(-1);
X	}
X
X	cond_argument[0] = action_argument[0] = '\0';
X
X	/* Now, for each line... **/
X
X	if ((cond = (struct condition_rec *) 
X		     malloc(sizeof(struct condition_rec))) == NULL) {
X	  if (outfd != NULL)
X	    fprintf(outfd,"filter (%s): couldn't malloc first condition rec!\n",
X		    username);
X	  return(-1);
X	}
X	
X	rules[total_rules].condition = cond;	/* hooked in! */
X
X	while (fgets(buffer, SLEN, fd) != NULL) {
X	  line++;
X
X	  if (buffer[0] == '#' || (buflen = strlen(buffer)) < 2)
X	    continue;		/* nothing to look at! */
X
X	  in_single = in_double = 0;
X
X	  for (i=0; i < buflen; i++) {
X	    if (buffer[i] == '"') 
X	      in_double = ! in_double;
X	    else if (buffer[i] == '\'')
X	      in_single = ! in_single;
X	    if ((in_double || in_single) && buffer[i] == ' ')
X	      buffer[i] = '_';
X	  }
X
X	  lasttype = type;
X	  type = NONE;
X	  str = (char *) buffer;
X
X	  /** Three pieces to this loop - get the `field', the 'relop' (if
X	      there) then, if needed, get the argument to check against (not 
X	      needed for errors or the AND, of course)
X	  **/
X
X	  while ((word = strtok(str, " ()[]:\t\n")) != NULL) {
X
X	    str = (char *) NULL;		/* we can start stomping! */
X	  
X	    lowercase(word);
X
X	    if (strcmp(word, "if") == 0) {	/* only ONE 'if' allowed */
X	      if ((word = strtok(str, " ()[]:\t\n")) == NULL)	/* NEXT! */
X	        continue;
X	      lowercase(word);
X	    }
X	
X	    if (state == NEXT_CONDITION) {
X	      lasttype = type;
X	      type = NONE;
X
X	      if (the_same(word, "not") || the_same(word, "!")) {
X	        not_condition = TRUE;
X	        if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
X	          continue;
X	      }
X
X	           if (the_same(word, "from")) 	    type = FROM;
X	      else if (the_same(word, "to")) 	    type = TO;
X	      else if (the_same(word, "subject"))   type = SUBJECT;
X	      else if (the_same(word, "lines"))     type = LINES;
X	      else if (the_same(word, "contains"))  type = CONTAINS;
X	      else if (the_same(word, "and") || 
X	               the_same(word, "&&")) 	    type = AND;
X
X	      else if (the_same(word,"?") || the_same(word, "then") || 
X		       the_same(word, "always")) {
X
X		/** shove THIS puppy into the structure and let's continue! **/
X
X	        if (lasttype == AND) {
X		  if (outfd != NULL)
X	            fprintf(outfd,
X         "filter (%s): Error reading line %d of rules - badly placed \"and\"\n",
X		    username, line);
X		  return(-1);
X	        }
X
X	        if (the_same(word, "always"))
X		  cond->matchwhat = ALWAYS;	/* so it's a hack... */
X		else
X		  cond->matchwhat = lasttype;
X
X	        if (relop == NONE) relop = EQ;	/* otherwise can't do -relop */
X	        cond->relation  = (not_condition? - (relop) : relop);
X
X		for (i=strlen(cond_argument); --i >= 0;)
X	          if (cond_argument[i] == '_') cond_argument[i] = ' ';
X
X		strcpy(cond->argument1, cond_argument);
X	        if ((newcond = (struct condition_rec *)
X		     malloc(sizeof(struct condition_rec))) == NULL) {
X		  if (outfd != NULL)
X	            fprintf(outfd,
X		     	    "filter (%s): Couldn't malloc new cond rec!!\n",
X		            username);
X		  return(-1);
X	        }
X	        cond->next = NULL;
X
X	        relop = EQ;	/* default relational condition */
X
X	        state = READING_ACTION;
X	        if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
X	          continue;
X	        goto get_outta_loop;
X	      }
X
X	      if (type == NONE) {
X		if (outfd != NULL)
X	          fprintf(outfd,
X      "filter (%s): Error reading line %d of rules - field \"%s\" unknown!\n",
X		     username, line, word);
X		return(-1);
X	      }
X
X	      if (type == AND) {
X
X		/** shove THIS puppy into the structure and let's continue! **/
X
X		cond->matchwhat = lasttype;
X	        cond->relation  = (not_condition? - (relop) : relop);
X		strcpy(cond->argument1, cond_argument);
X	        if ((newcond = (struct condition_rec *)
X	             malloc(sizeof(struct condition_rec))) == NULL) {
X		  if (outfd != NULL)
X	            fprintf(outfd,
X			"filter (%s): Couldn't malloc new cond rec!!\n",
X			username);
X		  return(-1);
X	        }
X	        cond->next = newcond;
X		cond = newcond;
X		cond->next = NULL;
X
X	        not_condition = FALSE;
X	        state = NEXT_CONDITION;
X	      }
X	      else {
X	        state = GETTING_OP;
X	      }
X	    }
X
Xget_outta_loop: 	/* jump out when we change state, if needed */
X
X	    if (state == GETTING_OP) {
X
X	       if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
X	         continue;
X
X	       lowercase(word);
X
X	       relop = NONE;
X
X	       if (the_same(word, "=") || the_same(word, "in") || 
X                   the_same(word, "contains")) {
X                 state = READING_ARGUMENT;
X	         relop = EQ;
X	       }
X	       else {
X	         if (the_same(word, "<=")) 	relop = LE;
X	         else if (the_same(word, ">="))	relop = GE;
X	         else if (the_same(word, ">"))	relop = GT;
X	         else if (the_same(word, "<>")||
X		          the_same(word, "!="))	relop = NE;
X	         else if (the_same(word, "<"))	relop = LT;
X
X		 /* maybe there isn't a relop at all!! */
X
X		 state=READING_ARGUMENT;
X
X	       }
X	    }
X		 
X	    if (state == READING_ARGUMENT) {
X	      if (relop != NONE) {
X	        if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
X	          continue;
X	      }
X	      for (i=strlen(word); --i>=0;)
X	        if (word[i] == '_') word[i] = ' ';
X
X	      strcpy(cond_argument, word);
X	      state = NEXT_CONDITION;
X	    }
X
X	    if (state == READING_ACTION) {
X	      action = NONE;
X
X	      not_condition = FALSE;
X
X	      if (the_same(word, "delete"))       action = DELETE_MSG;
X	      else if (the_same(word, "savec"))   action = SAVECC;
X	      else if (the_same(word, "save"))    action = SAVE;
X	      else if (the_same(word, "forward")) action = FORWARD;
X	      else if (the_same(word, "exec"))    action = EXEC;
X	      else if (the_same(word, "leave"))   action = LEAVE;
X	      else {
X		if (outfd != NULL)
X	          fprintf(outfd,
X	"filter (%s): Error on line %d of rules - action \"%s\" unknown\n",
X			username, line, word);
X	      }
X
X	      if (action == DELETE_MSG || action == LEAVE) {
X	        /** add this to the rules section and alloc next... **/
X
X	        rules[total_rules].action = action;
X		rules[total_rules].argument2[0] = '\0';	/* nothing! */
X	        total_rules++;
X	         
X	        if ((cond = (struct condition_rec *)
X		     malloc(sizeof(struct condition_rec))) == NULL) {
X		  if (outfd != NULL)
X	            fprintf(outfd,
X			"filter (%s): couldn't malloc first condition rec!\n",
X			username);
X	          return(-1);
X	        }
X	
X	        rules[total_rules].condition = cond;	/* hooked in! */
X	        state = NEXT_CONDITION;	
X	      }
X	      else {
X	        state = ACTION_ARGUMENT;
X	      }
X
X	      if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
X	        continue;
X
X	    }
X	
X	    if (state == ACTION_ARGUMENT) {
X	      strcpy(action_argument, word);
X
X	      /** add this to the rules section and alloc next... **/
X
X	      rules[total_rules].action = action;
X	      expand_macros(action_argument, rules[total_rules].argument2,line,
X			    printing_rules);
X	      total_rules++;
X	         
X	      if ((cond = (struct condition_rec *)
X		     malloc(sizeof(struct condition_rec))) == NULL) {
X		if (outfd != NULL)
X	          fprintf(outfd,
X			  "filter (%s): couldn't malloc first condition rec!\n",
X			  username);
X	        return(-1);
X	      }
X	
X	      rules[total_rules].condition = cond;	/* hooked in! */
X
X	      state = NEXT_CONDITION;
X	      if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
X	        continue;
X	    }
X	  }
X	}
X
X	return(0);
X}
SHAR_EOF
chmod 0444 filter/parse.c || echo "restore of filter/parse.c fails"
echo "x - extracting filter/rules.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > filter/rules.c &&
X
Xstatic char rcsid[] ="@(#)$Id: rules.c,v 4.1 90/04/28 22:42:00 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein - elm at DSI.COM
X *			dsinc!elm
X *
X *******************************************************************************
X * $Log:	rules.c,v $
X * Revision 4.1  90/04/28  22:42:00  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
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**/
X
X#include <stdio.h>
X#include <pwd.h>
X#include <ctype.h>
X#include "defs.h"
X#ifdef I_TIME
X#  include <time.h>
X#endif
X#ifdef I_SYSTIME
X#  include <sys/time.h>
X#endif
X#include <fcntl.h>
X
X#include "filter.h"
X
Xchar *listrule();
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 iindex = 0, not, relation, try_next_rule, x;
X	struct condition_rec *cond;
X
X	while (iindex < total_rules) {
X	  cond = rules[iindex].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: if (outfd != NULL) fprintf(outfd,
X       "filter (%s): Error: rules based on 'contains' are not implemented!\n",
X			    username);
X			    if (outfd != NULL) fclose(outfd);
X			    exit(0); 		
X
X	      case ALWAYS: not = FALSE; x = TRUE;			break;
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 = iindex;
X 	    return(rules[rule_choosen].action);
X	  }
X	  iindex++;
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, display)
Xchar *word, *buffer;
Xint  line, display;
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. If "display" is set then
X	    instead it puts "<day-of-month>" etc. etc. in the output.
X	**/
X
X#ifndef	_POSIX_SOURCE
X	struct tm *localtime();
X	long    time();
X#endif
X	struct tm *timerec;
X	long	thetime;
X	register int i, j=0, gotten_time = 0, reading_a_percent_sign = 0, len;
X
X	for (i = 0, len = strlen(word); i < len; 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			 if (display)
X	 		   strcat(buffer, "<return-address>");
X			 else
X			   strcat(buffer, from);
X	                 j = strlen(buffer);
X			 break;
X
X	      case 's' : buffer[j] = '\0';
X			 if (display)
X	 		   strcat(buffer, "<subject>");
X			 else {
X			   strcat(buffer, "\"");
X			   strcat(buffer, subject);
X			   strcat(buffer, "\"");
X			 }
X	                 j = strlen(buffer);
X			 break;
X
X	      case 'S' : buffer[j] = '\0';
X			 if (display)
X	 		   strcat(buffer, "<Re: subject>");
X			 else {
X			   if (! the_same(subject, "Re:")) 
X			     strcat(buffer, "\"Re: ");
X			   strcat(buffer, subject);
X			   strcat(buffer, "\"");
X			 }
X	                 j = strlen(buffer);
X			 break;
X
X	      case 'd' : get_the_time(); buffer[j] = '\0';
X			 if (display)
X			   strcat(buffer, "<day-of-month>");
X			 else
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			 if (display)
X			   strcat(buffer, "<day-of-week>");
X			 else
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			 if (display)
X			   strcat(buffer, "<month>");
X			 else
X			   strcat(buffer, itoa(timerec->tm_mon+1,FALSE));
X	                 j = strlen(buffer);
X			 break;
X
X	      case 'y' : get_the_time(); buffer[j] = '\0';
X			 if (display)
X			   strcat(buffer, "<year>");
X			 else
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			 if (display)
X			   strcat(buffer, "<hour>");
X			 else
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			 if (display)
X			   strcat(buffer, "<time>");
X		         else {
X			   strcat(buffer, itoa(timerec->tm_hour,FALSE));
X			   strcat(buffer, ":");
X			   strcat(buffer, itoa(timerec->tm_min,TRUE));
X			 }
X	                 j = strlen(buffer);
X			 break;
X
X	      default  : if (outfd != NULL) fprintf(outfd,
X   "filter (%s): Error on line %d translating %%%c macro in word \"%s\"!\n",
X			         username, line, word[i], word);
X			 if (outfd != NULL) fclose(outfd);
X			 exit(1);
X	    }
X	  }
X	  else if (word[i] == '%') 
X	    reading_a_percent_sign++;
X	  else 
X	    buffer[j++] = (word[i] == '_' ? ' ' : 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	if (outfd == NULL) return;	/* why are we here, then? */
X
X	while (++i < total_rules) {
X	  if (rules[i].condition->matchwhat == ALWAYS) {
X	    fprintf(outfd, "\nRule %d:  ** always ** \n\t%s %s\n", i+1,
X		 actionname(rules[i].action), listrule(rules[i].argument2));
X	    continue;
X	  }
X
X	  fprintf(outfd, "\nRule %d:  if (", i+1);
X
X	  cond = rules[i].condition;
X
X	  while (cond != NULL) {
X	    if (cond->relation < 0)
X	      fprintf(outfd, "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	      fprintf(outfd, "%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) fprintf(outfd, " and ");
X	  }
X	    
X	  fprintf(outfd, ") then\n\t  %s %s\n", 
X		 actionname(rules[i].action), 
X		 listrule(rules[i].argument2));
X	}
X	fprintf(outfd, "\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_MSG : 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}
X
Xchar *listrule(rule)
Xchar *rule;
X{
X	/** simply translates all underscores into spaces again on the
X	    way past... **/
X
X	static char buffer[SLEN];
X	register int i;
X
X	i = strlen(rule);
X	buffer[i] = '\0';
X	while (--i >= 0)
X	  buffer[i] = (rule[i] == '_' ? ' ' : rule[i]);
X
X	return( (char *) buffer);
X}
SHAR_EOF
chmod 0444 filter/rules.c || echo "restore of filter/rules.c fails"
echo "x - extracting filter/summarize.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > filter/summarize.c &&
X
Xstatic char rcsid[] ="@(#)$Id: summarize.c,v 4.1 90/04/28 22:42:02 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein - elm at DSI.COM
X *			dsinc!elm
X *
X *******************************************************************************
X * $Log:	summarize.c,v $
X * Revision 4.1  90/04/28  22:42:02  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
X
X/** This routine is called from the filter program (or can be called
X    directly with the correct arguments) and summarizes the users filterlog
X    file.  To be honest, there are two sorts of summaries that are
X    available - either the '.filterlog' file can be output (filter -S) 
X    or a summary by rule and times acted upon can be output (filter -s).
X    Either way, this program will delete the two associated files each
X    time ($HOME/.filterlog and $HOME/.filtersum) *if* the -c option is
X    used to the program (e.g. clear_logs is set to TRUE).
X
X**/
X
X#include <stdio.h>
X
X#include "defs.h"
X
X#include "filter.h"
X
Xshow_summary()
X{
X	/* Summarize usage of the program... */
X
X	FILE   *fd;				/* for output to temp file! */
X	char filename[SLEN],			/* name of the temp file    */
X	     buffer[SLEN];			/* input buffer space       */
X	int  erroneous_rules = 0,
X	     default_rules   = 0,
X	     messages_filtered = 0,		/* how many have we touched? */
X	     rule,
X	     applied[MAXRULES];
X
X	sprintf(filename, "%s/%s", home, filtersum);
X
X	if ((fd = fopen(filename, "r")) == NULL) {
X	  if (outfd != NULL)
X	    fprintf(outfd,"filter (%s): Can't open filtersum file %s!\n",
X
X		    username, filename);
X	  if (outfd != NULL) fclose(outfd);
X	  exit(1);
X	}
X
X	for (rule=0;rule < MAXRULES; rule++)
X	  applied[rule] = 0;			/* initialize it all! */
X
X	/** Next we need to read it all in, incrementing by which rule
X	    was used.  The format is simple - each line represents a 
X	    single application of a rule, or '-1' if the default action
X	    was taken.  Simple stuff, eh?  But oftentimes the best.  
X	**/
X
X	while (fgets(buffer, SLEN, fd) != NULL) {
X	  if ((rule = atoi(buffer)) > total_rules || rule < -1) {
X	    if (outfd != NULL)
X	      fprintf(outfd,
X      "filter (%s): Warning - rule #%d is invalid data for short summary!!\n",
X	            username, rule);
X	    erroneous_rules++;
X	  }
X	  else if (rule == -1)
X	    default_rules++;
X	  else
X	    applied[rule]++;
X	  messages_filtered++;
X	}
X	
X	fclose(fd);
X
X	/** now let's summarize the data... **/
X
X	if (outfd == NULL) return;		/* no reason to go further */
X
X	fprintf(outfd, 
X		"\n\t\t\tA Summary of Filter Activity\n");
X	fprintf(outfd, 
X		  "\t\t\t----------------------------\n\n");
X
X	fprintf(outfd,"A total of %d message%s %s filtered:\n\n",
X		messages_filtered, plural(messages_filtered),
X		messages_filtered > 1 ? "were" : "was");
X
X	if (erroneous_rules)
X	  fprintf(outfd, 
X	          "[Warning: %d erroneous rule%s logged and ignored!]\n\n",
X		   erroneous_rules, erroneous_rules > 1? "s were" : " was");
X	
X	if (default_rules) {
X	   fprintf(outfd,
X "The default rule of putting mail into your mailbox\n");
X	   fprintf(outfd, "\tapplied %d time%s (%d%%)\n\n",
X		   default_rules, plural(default_rules),
X		   (default_rules*100+(messages_filtered>>1))/messages_filtered
X	  	  );
X	}
X
X	 /** and now for each rule we used... **/
X
X	 for (rule = 0; rule < total_rules; rule++) {
X	   if (applied[rule]) {
X	      fprintf(outfd, "Rule #%d: ", rule+1);
X	      switch (rules[rule].action) {
X		  case LEAVE:	    fprintf(outfd, "(leave mail in mailbox)");
X				    break;
X		  case DELETE_MSG:  fprintf(outfd, "(delete message)");
X				    break;
X		  case SAVE  :      fprintf(outfd, "(save in \"%s\")",
X					    rules[rule].argument2);		break;
X		  case SAVECC:      fprintf(outfd, 
X					    "(left in mailbox and saved in \"%s\")",
X					    rules[rule].argument2);		break;
X		  case FORWARD:     fprintf(outfd, "(forwarded to \"%s\")",
X					    rules[rule].argument2);		break;
X		  case EXEC  :      fprintf(outfd, "(given to command \"%s\")",
X					    rules[rule].argument2);		break;
X	     }
X	     fprintf(outfd, "\n\tapplied %d time%s (%d%%)\n\n", 
X		     applied[rule], plural(applied[rule]),
X	            (applied[rule]*100+(messages_filtered>>1))/messages_filtered
X		    );
X	  }
X	}
X
X	if (long_summary) {
X
X	  /* next, after a ^L, include the actual log file... */
X
X	  sprintf(filename, "%s/%s", home, filterlog);
X
X	  if ((fd = fopen(filename, "r")) == NULL) {
X	    fprintf(outfd,"filter (%s): Can't open filterlog file %s!\n",
X		      username, filename);
X	  }
X	  else {
X	    fprintf(outfd, "\n\n\n%c\n\nExplicit log of each action;\n\n", 
X		    (char) 12);
X	    while (fgets(buffer, SLEN, fd) != NULL)
X	      fprintf(outfd, "%s", buffer);
X	    fprintf(outfd, "\n-----\n");
X	    fclose(fd);
X	  }
X	}
X
X	/* now remove the log files, please! */
X
X	if (clear_logs) {
X	  sprintf(filename, "%s/%s", home, filterlog);
X	  unlink(filename);
X	  sprintf(filename, "%s/%s", home, filtersum);
X	  unlink(filename);
X	}
X
X	return;
X}
SHAR_EOF
chmod 0444 filter/summarize.c || echo "restore of filter/summarize.c fails"
echo "x - extracting filter/utils.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > filter/utils.c &&
X
Xstatic char rcsid[] ="@(#)$Id: utils.c,v 4.1 90/04/28 22:42:03 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein - elm at DSI.COM
X *			dsinc!elm
X *
X *******************************************************************************
X * $Log:	utils.c,v $
X * Revision 4.1  90/04/28  22:42:03  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
X
X/** Utility routines for the filter program...
X
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
Xleave(reason)
Xchar *reason;
X{
X	if (outfd != NULL)
X	  fprintf(outfd,"filter (%s): LEAVE %s\n", username, reason);
X	if (outfd != NULL) fclose(outfd);
X	exit(1);
X}
X
Xlog(what)
Xint what;
X{
X	/** make an entry in the log files for the specified entry **/
X
X	FILE *fd;
X	char filename[SLEN];
X
X	if (! show_only) {
X	  sprintf(filename, "%s/%s", home, filtersum);	/* log action once! */
X	  if ((fd = fopen(filename, "a")) == NULL) {
X	    if (outfd != NULL)
X	      fprintf(outfd, "filter (%s): Couldn't open log file %s\n", 
X		    filename);
X	    fd = stdout;
X	  }
X	  fprintf(fd, "%d\n", rule_choosen);
X	  fclose(fd);
X	}
X
X	sprintf(filename, "%s/%s", home, filterlog);
X
X	if (show_only)
X	  fd = stdout;
X	else if ((fd = fopen(filename, "a")) == NULL) {
X	  if (outfd != NULL)
X	    fprintf(outfd, "filter (%s): Couldn't open log file %s\n", 
X		  filename);
X	  fd = stdout;
X	}
X	
X#ifdef _IOFBF
X	setvbuf(fd, NULL, _IOFBF, BUFSIZ);
X#endif
X
X	if (strlen(from) + strlen(subject) > 60)
X	  fprintf(fd, "\nMail from %s\n\tabout %s\n", from, subject);
X	else
X	  fprintf(fd, "\nMail from %s about %s\n", from, subject);
X
X	if (rule_choosen != -1)
X	  if (rules[rule_choosen].condition->matchwhat == TO)
X	    fprintf(fd, "\t(addressed to %s)\n", to);
X
X	switch (what) {
X	  case DELETE_MSG : fprintf(fd, "\tDELETED");			break;
X	  case SAVE       : fprintf(fd, "\tSAVED in file \"%s\"", 
X				rules[rule_choosen].argument2);		break;
X	  case SAVECC     : fprintf(fd,"\tSAVED in file \"%s\" AND PUT in mailbox", 
X				rules[rule_choosen].argument2);  	break;
X	  case FORWARD    : fprintf(fd, "\tFORWARDED to \"%s\"", 
X				rules[rule_choosen].argument2);		break;
X	  case EXEC       : fprintf(fd, "\tEXECUTED \"%s\"",
X				rules[rule_choosen].argument2);		break;
X	  case LEAVE      : fprintf(fd, "\tPUT in mailbox");		break;
X	}
X
X	if (rule_choosen != -1)
X	  fprintf(fd, " by rule #%d\n", rule_choosen+1);
X	else
X	  fprintf(fd, ": the default action\n");
X
X	fflush(fd);
X	fclose(fd);
X}
X
Xint
Xcontains(string, pattern)
Xchar *string, *pattern;
X{
X	/** Returns TRUE iff pattern occurs IN IT'S ENTIRETY in buffer. **/ 
X
X	register int i = 0, j = 0;
X
X	while (string[i] != '\0') {
X	  while (tolower(string[i++]) == tolower(pattern[j++])) 
X	    if (pattern[j] == '\0') 
X	      return(TRUE);
X	  i = i - j + 1;
X	  j = 0;
X	}
X	return(FALSE);
X}
X
Xchar *itoa(i, two_digit)
Xint i, two_digit;
X{	
X	/** return 'i' as a null-terminated string.  If two-digit use that
X	    size field explicitly!  **/
X
X	static char value[10];
X	
X	if (two_digit)
X	  sprintf(value, "%02d", i);
X	else
X	  sprintf(value, "%d", i);
X
X	return( (char *) value);
X}
X
Xlowercase(string)
Xchar *string;
X{
X	/** translate string into all lower case **/
X
X	register int i;
X
X	for (i= strlen(string); --i >= 0; )
X	  if (isupper(string[i]))
X	    string[i] = tolower(string[i]);
X}
SHAR_EOF
chmod 0444 filter/utils.c || echo "restore of filter/utils.c fails"
echo "x - extracting hdrs/curses.h (Text)"
sed 's/^X//' << 'SHAR_EOF' > hdrs/curses.h &&
X
X/* $Id: curses.h,v 4.1 90/04/28 22:42:05 syd Exp $ */
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein, Elm Coordinator
X *	elm at DSI.COM			dsinc!elm
X *
X *******************************************************************************
X * $Log:	curses.h,v $
X * Revision 4.1  90/04/28  22:42:05  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
X
X     /*** Include file for seperate compilation.  ***/
X
X#define OFF		0
X#define ON 		1
X
Xint  InitScreen(),      /* This must be called before anything else!! */
X
X     ClearScreen(), 	 CleartoEOLN(),
X
X     MoveCursor(),
X
X     StartBold(),        EndBold(), 
X     StartUnderline(),   EndUnderline(), 
X     StartHalfbright(),  EndHalfbright(),
X     StartInverse(),     EndInverse(),
X	
X     transmit_functions(),
X
X     Raw(),              RawState(),
X     ReadCh();
X
Xchar *return_value_of();
SHAR_EOF
chmod 0444 hdrs/curses.h || echo "restore of hdrs/curses.h fails"
echo "x - extracting hdrs/defs.h (Text)"
sed 's/^X//' << 'SHAR_EOF' > hdrs/defs.h &&
X
X/* $Id: defs.h,v 4.1 90/04/28 22:42:06 syd Exp $ */
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein, Elm Coordinator
X *	elm at DSI.COM			dsinc!elm
X *
X *******************************************************************************
X * $Log:	defs.h,v $
X * Revision 4.1  90/04/28  22:42:06  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
X
X/**  define file for ELM mail system.  **/
X
X
X#include "../config.h"
X#include "sysdefs.h"	/* system/configurable defines */
X
X
X# define VERSION 		"2.3"		/* Version number... */
X# define VERS_DATE	"May 1, 1990"		/* for elm -v option */
X# define WHAT_STRING	\
X	"@(#) Version 2.3, USENET supported version, released May 1990"
X
X#define KLICK		25
X
X#define SLEN		256	    /* long for ensuring no overwrites... */
X#define SHORT		10	    /* super short strings!		  */
X#define NLEN		48	    /* name length for aliases            */
X#define WLEN		20
X#define STRING		128	/* reasonable string length for most..      */
X#define LONG_STRING	512	/* even longer string for group expansion   */
X#define VERY_LONG_STRING 2560	/* huge string for group alias expansion    */
X#define MAX_LINE_LEN	5120	/* even bigger string for "filter" prog..   */
X
X#define BREAK		'\0'  		/* default interrupt    */
X#define BACKSPACE	'\b'     	/* backspace character  */
X#define TAB		'\t'            /* tab character        */
X#define RETURN		'\r'     	/* carriage return char */
X#define LINE_FEED	'\n'     	/* line feed character  */
X#define FORMFEED	'\f'     	/* form feed (^L) char  */
X#define COMMA		','		/* comma character      */
X#define SPACE		' '		/* space character      */
X#define DOT		'.'		/* period/dot character */
X#define BANG		'!'		/* exclaimation mark!   */
X#define AT_SIGN		'@'		/* at-sign character    */
X#define PERCENT		'%'		/* percent sign char.   */
X#define COLON		':'		/* the colon ..		*/
X#define BACKQUOTE	'`'		/* backquote character  */
X#define TILDE_ESCAPE	'~'		/* escape character~    */
X#define ESCAPE		'\033'		/* the escape		*/
X
X#define NO_OP_COMMAND	'\0'		/* no-op for timeouts   */
X
X#define STANDARD_INPUT  0		/* file number of stdin */
X
X#ifndef TRUE
X#define TRUE		1
X#define FALSE		0
X#endif
X
X#define NO		0
X#define YES		1
X#define MAYBE		2		/* a definite define, eh?  */
X#define FORM		3		/*      <nevermind>        */
X#define PREFORMATTED	4		/* forwarded form...       */
X
X#define SAME_PAGE	1		/* redraw current only     */
X#define NEW_PAGE	2		/* redraw message list     */
X#define ILLEGAL_PAGE	0		/* error in page list, punt */
X
X#define PAD		0		/* for printing name of    */
X#define FULL		1		/*   the sort we're using  */
X
X#define OUTGOING	0		/* defines for lock file   */
X#define INCOMING	1		/* creation..see lock()    */
X
X#define SH		0		/* defines for system_call */
X#define USER_SHELL	1		/* to work correctly!      */
X
X#define EXECUTE_ACCESS	01		/* These five are 	   */
X#define WRITE_ACCESS	02		/*    for the calls	   */
X#define READ_ACCESS	04		/*       to access()       */
X#define ACCESS_EXISTS	00		/*           <etc>         */
X#define EDIT_ACCESS	06		/*  (this is r+w access)   */
X
X#define BIG_NUM		999999		/* big number!             */
X#define BIGGER_NUM	9999999 	/* bigger number!          */
X
X#define START_ENCODE	"[encode]"
X#define END_ENCODE	"[clear]"
X
X#define DONT_SAVE	"[no save]"
X#define DONT_SAVE2	"[nosave]"
X
X#define alias_file	".aliases"
X#define group_file	".groups"
X#define system_file	".systems"
X
X#define default_folders		"Mail"
X#define default_recvdmail	"=received"
X#define default_sentmail	"=sent"
X
X/** some defines for the 'userlevel' variable... **/
X
X#define RANK_AMATEUR	0
X#define AMATEUR		1
X#define OKAY_AT_IT	2
X#define GOOD_AT_IT	3
X#define EXPERT		4
X#define SUPER_AT_IT	5
X
X/** some defines for the "status" field of the header record **/
X
X#define ACTION		1		/* bit masks, of course */
X#define CONFIDENTIAL	2
X#define DELETED		4
X#define EXPIRED		8
X#define FORM_LETTER	16
X#define NEW		32
X#define PRIVATE		64
X#define TAGGED		128
X#define URGENT		256
X#define VISIBLE		512
X#define UNREAD		1024
X#define STATUS_CHANGED	2048
X
X#define UNDELETE	0		/* purely for ^U function... */
X
X/** values for headers exit_disposition field */
X#define UNSET	0
X#define KEEP	1
X#define	STORE	2
X#define DELETE	3
X
X/** some months... **/
X
X#define JANUARY		0			/* months of the year */
X#define FEBRUARY	1
X#define MARCH		2
X#define APRIL		3
X#define MAY		4
X#define JUNE		5
X#define JULY		6
X#define AUGUST		7
X#define SEPTEMBER	8
X#define OCTOBER		9
X#define NOVEMBER	10
X#define DECEMBER	11
X
X#define equal(s,w)	(strcmp(s,w) == 0)
X#define min(a,b)	a < b? a : b
X#define ctrl(c)	        c - 'A' + 1	/* control character mapping */
X#define plural(n)	n == 1 ? "" : "s"
X#define lastch(s)	s[strlen(s)-1]
X
X/* find tab stops preceding or following a given column position 'a', where
X * the column position starts counting from 1, NOT 0!
X * The external integer "tabspacing" must be declared to use this. */
X#define prev_tab(a)	(((((a-1)/tabspacing))*tabspacing)+1)
X#define next_tab(a)	(((((a-1)/tabspacing)+1)*tabspacing)+1)
X
X#define movement_command(c)	(c == 'j' || c == 'k' || c == ' ' || 	      \
X				 c == BACKSPACE || c == ESCAPE || c == '*' || \
X				 c == '-' || c == '+' || c == '=' ||          \
X				 c == '#' || c == '@' || c == 'x' || 	      \
X				 c == 'a' || c == 'q')
X
X#define no_ret(s)	{ register int xyz; /* varname is for lint */	      \
X		          for (xyz=strlen(s)-1; xyz >= 0 && 		      \
X				(s[xyz] == '\r' || s[xyz] == '\n'); )	      \
X			     s[xyz--] = '\0';                                 \
X			}
X			  
X#define first_word(s,w) (strncmp(s,w, strlen(w)) == 0)
X#define ClearLine(n)	MoveCursor(n,0); CleartoEOLN()
X#define whitespace(c)	(c == ' ' || c == '\t')
X#define ok_rc_char(c)	(isalnum(c) || c == '-' || c == '_')
X#define ok_alias_char(c) (isalnum(c) || c == '-' || c == '_' || c == '.')
X#define quote(c)	(c == '"' || c == '\'') 
X#define onoff(n)	(n == 0 ? "OFF" : "ON")
X
X/** The next function is so certain commands can be processed from the showmsg
X    routine without rewriting the main menu in between... **/
X
X#define special(c)	(c == 'j' || c == 'k')
X
X/** and a couple for dealing with status flags... **/
X
X#define ison(n,mask)	(n & mask)
X#define isoff(n,mask)	(!ison(n, mask))
X
X#define setit(n,mask)		n |= mask
X#define clearit(n, mask)	n &= ~mask
X
SHAR_EOF
echo "End of part 9"
echo "File hdrs/defs.h is continued in part 10"
echo "10" > s2_seq_.tmp
exit 0
-- 
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.



More information about the Comp.sources.unix mailing list