From: sources@anomaly.sbs.risc.net
Subject: v01i107 - ELM v2.3.11, Electronic Mail Reader - Part 07/20
Reply-To: sources@anomaly.sbs.risc.net
Organization: The Rhode Island Internet Systems Cooperative Network
Date: Sun, 13 Sep 1992 17:40:24 GMT
Followup-To: biz.sco.general


#!/bin/sh
# this is part.07 (part 7 of a multipart archive)
# do not concatenate these parts, unpack them in order with /bin/sh
# file doc/readmsg.1 continued
#
if test ! -r shar3_seq_.tmp; then
	echo "Please unpack part 1 first!"
	exit 1
fi
(read Scheck
 if test "$Scheck" != 7; then
	echo "Please unpack part $Scheck next!"
	exit 1
 else
	exit 0
 fi
) < shar3_seq_.tmp || exit 1
echo "x - Continuing file doc/readmsg.1"
sed 's/^X//' << 'SHAR_EOF' >> doc/readmsg.1 &&
Xarguments will include a summary of the headers and the body 
Xof the message being replied
Xto.  If you aren't currently editing a message the program will
Xreturn an error.
X.P
XSecondly, if you want to include certain messages, you can
Xspecify them by listing their ordinal locations in the 
Xmail file (that is, their "message numbers")
Xup to 25 at a time.  The \fImeta-\fRnumber '$' is understood to mean
Xthe last message in the mailfile.  Similarly, '*' is understood to
Xrepresent every message in the file (that is, 1-$)
X.P
XFinally, you can also specify a pattern that occurs in one of
Xthe messages as a way of including it.  This pattern can be
Xtyped in directly (no quotes) if the words are separated by a 
Xsingle space in the actual message.  The pattern matching is case
Xsensitive, so "Hello" and "hello" are NOT the same thing!!
X.sp
X.P
XThe \fB-f\fR flag indicates that you'd rather use the file specified
Xfor the operations specified rather than the default mailbox.
X.P
XThe \fB-h\fR flag instructs the program to include the entire header
Xof the matched message or messages when displaying their
Xtext.  (default is to display the From: Date: and Subject: lines
Xonly)
X.P
XThe \fB-n\fR flag instructs the program to exclude \fIall\fR
Xheaders.  This is used mostly for extracting files mailed and
Xsuch.
X.P
XFinally, the \fB-p\fR flag indicates that the program should
Xput form-feeds (control-L) between message headers.
X.sp
X.SH "EXAMPLES"
XFirst off, to use this from within \fBvi\fR to include the text of the 
Xcurrent message, you could use the command:
X.nf
X
X	:r !readmsg
X
X.fi
X(as you hit the ':' the editor will put you at the bottom of the screen
Xwith the ':' prompt).  The space following ':r' is required.
X.sp 2
XLet's look at something more interesting, however;
X.sp
XSuppose you have the mailfile;
X.nf
X
X   From joe Jun 3 1986 4:45:30 MST
X   Subject: hello
X   
X   Hey Guy!  Wanta go out and have a milk this evening?
X   
X   Joe
X   
X   From john Jun 3 1986 4:48:20 MST
X   Subject: Dinner at Eight
X   From: John Dinley <xyz!john>
X
X   Remember you should show up about eight, okay?
X
X		   - John D -
X
X   From xxzyz!cron Jun 3 1986 5:02:43 MST
X
X   Cannot connect to server: blob
X   Job 43243 deleted from queue.
X   
X.fi
XThe following commands will result in;
X.nf
X
X  $ readmsg 2		
X  [ display the second message, from John ]
X
X  $ readmsg 		
X  [ an error, unless we're calling from \fIelm\fR ]
X
X  $ readmsg BLOB	
X  [ no match - case sensitive! ]
X
X  $ readmsg -h connect to server 
X  [ displays third message, including headers ]
X
X.fi
X.SH FILES
X/usr/mail/<username>   		The incoming mail
X.br
X$HOME/.readmsg			The temp file from \fIelm\fR
X.SH AUTHOR
XDave Taylor, Hewlett-Packard Laboratories
X.SH SEE\ ALSO
Xnewmail(1L), elm(1L)
X.SH BUGS
XThe '*' metacharacter doesn't always work as expected!
X.br
XPerhaps the pattern matching should be case insensitive?
X.SH BUG REPORTS TO
XSyd Weinstein	elm@DSI.COM	(dsinc!elm)
X.SH COPYRIGHTS
X.ps 18
X\fB\(co\fR\s12 Copyright 1986, 1987 by Dave Taylor
X.br
X.ps 18
X\fB\(co\fR\s12 Copyright 1988, 1989, 1990 by The USENET Community Trust
SHAR_EOF
echo "File doc/readmsg.1 is complete" &&
# ============= doc/wnewmail.1 ==============
echo "x - extracting doc/wnewmail.1 (Text)"
sed 's/^X//' << 'SHAR_EOF' > doc/wnewmail.1 &&
X.TH WNEWMAIL 1L "Elm Version 2.3" "USENET Community Trust"
X.SH NAME
Xwnewmail - daemon to asynchronously notify of new mail
X.SH SYNOPSIS
X.B wnewmail
X.br
X.B wnewmail
Xfilename
X.PP
X.SH DESCRIPTION
X.I Wnewmail\^
Xis a daemon designed to run in \fBa window\fR on a windowing
Xsystem (such as an HP or Sun system) and check every 10 seconds
Xto see if there is any new mail for the user that
Xstarted it up.
X.P
XIf there is new mail, the program will "beep", and write to
Xthe window for each of the new messages;
X.nf
X
X   Mail from <name> -- <subject>
X
X.fi
Xwhere <name> is either the name of the person sending it,
Xif available (the ARPA 'From:' line) or machine!login where
Xmachine is the machine the mail was sent from.  If there
Xis no subject, the message "<no subject>" will appear on
Xthe screen.
X.P
XIf the message is a \fIpriority\fR message (that is, the
Xheader contains a line "Priority:"), then the line output
Xwill be "PRIORITY mail from ..." rather than just "Mail from".
X.P
XThis program will run forever, and can internally reset 
Xitself if mail is deleted from the incoming mailbox while
Xtrying to monitor it.
X.P
XIf \fBwnewmail\fR is started up with a filename, it will
Xperform exactly the same, but with the specified file as
Xthe one to check rather than the default users mailbox.
X.SH AUTHOR
XDave Taylor, Hewlett-Packard Laboratories.
X.SH SEE ALSO
Xnotify in sh(1) or csh(1), newmail(1L)
X.SH NOTE
XThis is almost identical to the program \fBnewmail\fR...
X.SH BUG REPORTS TO
XSyd Weinstein	elm@DSI.COM	(dsinc!elm)
X.SH COPYRIGHTS
X.ps 18
X\fB\(co\fR\s12 Copyright 1986, 1987 by Dave Taylor
X.br
X.ps 18
X\fB\(co\fR\s12 Copyright 1988, 1989, 1990 by The USENET Community Trust
SHAR_EOF
# ============= filter/Makefile.SH ==============
if test ! -d 'filter'; then
    echo "x - creating directory filter"
    mkdir 'filter'
fi
echo "x - extracting filter/Makefile.SH (Text)"
sed 's/^X//' << 'SHAR_EOF' > filter/Makefile.SH &&
Xcase $CONFIG in
X'')
X    if test ! -f config.sh; then
X	ln ../config.sh . || \
X	ln ../../config.sh . || \
X	ln ../../../config.sh . || \
X	(echo "Can't find config.sh."; exit 1)
X    fi
X    . ./config.sh
X    ;;
Xesac
Xcase "$0" in
X*/*) cd `expr X$0 : 'X\(.*\)/'` ;;
Xesac
X
Xecho "Extracting filter/Makefile (with variable substitutions)"
Xcat >Makefile <<!GROK!THIS!
X#
X# @(#)$Id: Makefile.SH,v 4.1 90/04/28 22:41:51 syd Exp $
X#  Makefile for the Elm system filter program
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@DSI.COM
X#			dsinc!elm
X#
X#  $Log:	Makefile.SH,v $
X# Revision 4.1  90/04/28  22:41:51  syd
X# checkin of Elm 2.3 as of Release PL0
X# 
X#
X# Variables
X#	Variables established by Configure
XCC		=	$cc
XCCFLAGS		=	$ccflags $xencf
XCHGRP		=	$chgrp
XCHMOD		=	$chmod
XCP		=	$cp
XDEST		=	$bin
XECHO		=	$echo
XLFLAGS		=	$ldflags $xenlf
XLIB		=	$lib
XLIB2		= 	$libs
XLIBS		=	$termlib $dbm
XLINT		=	$lint
XMAILGRP		=	$mailgrp
XMAILERMODE	=	$mailermode
XMAKE		=	$make
XMV		=	$mv
XOPTIMIZE	=	$optimize
XRM		= 	$rm -f
XTOUCH		=	$touch
X
X!GROK!THIS!
X
Xcat >>Makefile <<'!NO!SUBS!'
X#	Variables you may want to manually edit
X#		If you want debug logging then you'll
X#		want to uncomment the following.
X#DEBUG		=	-DDEBUG
X
X#	Other variables
XBIN		=	../bin
XINCLDIR		=	../hdrs
XCFLAGS		=	$(CCFLAGS) $(OPTIMIZE) -I$(INCLDIR) $(DEBUG) $(DACSNET) 
XLINTFLAGS	=	-I$(INCLDIR)
XSHELL		=	/bin/sh
X
X# Definitions of variables
XFILTER_SRC	=	actions.c	\
X			buffer.c	\
X			filter.c	\
X			lock.c		\
X			parse.c		\
X			rules.c		\
X			summarize.c	\
X			utils.c		\
X			../src/opt_utils.c
X
XFILTER_OBJ	=	actions.o	\
X			buffer.o	\
X			filter.o	\
X			lock.o		\
X			parse.o		\
X			rules.o		\
X			summarize.o	\
X			utils.o		\
X			../src/opt_utils.o
X
X# Standard targets
Xall:		$(BIN)/filter
X		
Xinstall:	$(DEST)/filter
X
Xuninstall:	
X		$(RM) $(DEST)/filter
X
X#	This is the only target that gets installed even if not out-of-date
X#	with respect the files from which it is installed.
Xrmt-install:	rmt-defined
X		-$(MV) $(DEST)/filter $(DEST)/filter.old
X		-$(RM) $(DEST)/filter.old
X		$(CP) $(REMOTE)$(DEST)/filter $(DEST)/filter
X		$(CHGRP) $(MAILGRP) $(DEST)/filter
X		$(CHMOD) $(MAILERMODE) $(DEST)/filter
X
Xrmt-defined:
X	@(if [ "$(REMOTE)" = "" ];\
X	  then\
X	    $(ECHO) "You need to define 'REMOTE' as the remote file system";\
X	    $(ECHO) "for this particular command. The easiest way to do this";\
X	    $(ECHO) "to type:";\
X	    $(ECHO) "        make REMOTE=<remote file system> rmt-install";\
X	    exit 1;\
X	  fi);
X
Xlint:		
X		$(LINT) $(LINTFLAGS) $(FILTER_SRC) > LINT.OUT
X
Xclean:		
X		$(RM) $(FILTER_OBJ) $(BIN)/filter
X
X# Dependencies and rules
X#	Dependencies of header files upon other header files they include
X.PRECIOUS:		$(INCLDIR)/defs.h $(INCLDIR)/elm.h $(INCLDIR)/headers.h
X
X$(INCLDIR)/defs.h:	$(INCLDIR)/../config.h $(INCLDIR)/sysdefs.h
X			$(CHMOD) u+w $@
X			$(TOUCH) $@
X
X$(INCLDIR)/elm.h:	$(INCLDIR)/curses.h $(INCLDIR)/defs.h
X			$(CHMOD) u+w $@
X			$(TOUCH) $@
X
X$(INCLDIR)/headers.h:	$(INCLDIR)/curses.h $(INCLDIR)/defs.h
X			$(CHMOD) u+w $@
X			$(TOUCH) $@
X
X#	Dependencies and rules for C object files
Xactions.o:		$(INCLDIR)/defs.h $(INCLDIR)/filter.h
Xfilter.o:		$(INCLDIR)/defs.h $(INCLDIR)/filter.h
Xlock.o:			$(INCLDIR)/defs.h $(INCLDIR)/filter.h
Xparse.o:		$(INCLDIR)/defs.h $(INCLDIR)/filter.h
Xrules.o:		$(INCLDIR)/defs.h $(INCLDIR)/filter.h
Xsummarize.o:		$(INCLDIR)/defs.h $(INCLDIR)/filter.h
Xutils.o:		$(INCLDIR)/defs.h $(INCLDIR)/filter.h
X../src/opt_utils.o:
X			cd ../src; $(MAKE) -$(MAKEFLAGS) $(@F)
X
X#	Dependencies and rules for compiling programs
X$(BIN)/filter:	$& $(FILTER_OBJ)
X		$(CC) $(LFLAGS) -o $@ $(FILTER_OBJ) $(LIB2)
X
X#	Dependencies and rules for installing programs from bin directory
X$(DEST)/filter:		$(BIN)/filter
X			-$(MV) $(DEST)/filter $(DEST)/filter.old
X			-$(RM) $(DEST)/filter.old
X			$(CP) $? $@
X			$(CHGRP) $(MAILGRP) $@
X			$(CHMOD) $(MAILERMODE) $@
X!NO!SUBS!
SHAR_EOF
# ============= filter/actions.c ==============
echo "x - extracting filter/actions.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > filter/actions.c &&
X
Xstatic char rcsid[] ="@(#)$Id: actions.c,v 4.1.1.2 90/10/07 20:36:41 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1.1.2 $   $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@DSI.COM
X *			dsinc!elm
X *
X *******************************************************************************
X * $Log:	actions.c,v $
X * Revision 4.1.1.2  90/10/07  20:36:41  syd
X * allow non-elm mailers to correctly parse filter's folders.
X * From: sane!genmri!doug@crdgw1.ge.com (Doug Becker)
X * 
X * Revision 4.1.1.1  90/06/05  20:28:51  syd
X * The open system call in actions.c for EMERGENCY_MAILBOX and EMER_MBOX
X * were tested with the inequality >= 0 exactly backwards.
X * If the user's system mail box (/usr/spool/mail/user_id) is
X * removed the attempt of filter to flock it fails.  If it does not exist then
X * it should create it and then lock it.
X * From: john@hopf.math.nwu.edu (John Franks)
X * 
X * Revision 4.1  90/04/28  22:41:53  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
X
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
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
XFILE *emergency_local_delivery();
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  in_header = TRUE, line_count = 0, mailunit;
X	char tempfile[SLEN], mailbox[SLEN], lockfile[SLEN],
X	     buffer[VERY_LONG_STRING];
X
X	if (verbose && ! log_actions_only && outfd != NULL)
X	  fprintf(outfd, "filter (%s): Mailing message to %s\n", 
X		   username, address);
X
X	if (! show_only) {
X	  sprintf(tempfile, "%s.%d", filter_temp, getpid());
X
X	  if ((tempfd = fopen(tempfile, "r")) == NULL) {
X	    if (outfd != NULL)
X	      fprintf(outfd, "filter (%s): Can't open temp file %s!!\n", 
X		    username, tempfile);
X	    if (outfd != NULL) fclose(outfd);
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		if (outfd != NULL)
X	          fprintf(outfd, 
X	"filter (%s): Filter loop detected!  Message left in file %s.%d\n", 
X			username, filter_temp, getpid());
X	        if (outfd != NULL) fclose(outfd);
X	        exit(0);
X	      }
X	    }
X
X	    if (strcmp(sendmail, mailer) == 0)
X	      sprintf(buffer, "%s %s %s", sendmail, smflags, address);
X	    else
X	      sprintf(buffer, "%s %s", mailer, address);
X
X	    if ((pipefd = popen(buffer, "w")) == NULL) {
X	      if (outfd != NULL)
X	        fprintf(outfd, "filter (%s): popen %s failed!\n", username, buffer);
X	      sprintf(buffer, "((%s %s %s ; %s %s) & ) < %s &",
X		      sendmail , smflags, address, remove_cmd, 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, 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	  /** OTHERWISE it is to the current user... **/
X
X	  sprintf(mailbox,  "%s%s", mailhome, username);
X	  
X	  if (! lock()) {
X	    if (outfd != NULL) {
X	      fprintf(outfd, "filter (%s): Couldn't create lockfile %s\n",
X		    username, lockfile);
X	      fprintf(outfd, "filter (%s): Can't open mailbox %s!\n",
X			username, mailbox);
X	    }
X	    if ((mailfd = emergency_local_delivery()) == NULL)
X	      exit(1);
X	  }
X	  else if ((mailunit = open(mailbox, O_APPEND | O_WRONLY | O_CREAT, 0600)) >= 0)
X	    mailfd = fdopen(mailunit, "a");
X	  else if ((mailfd = emergency_local_delivery()) == NULL)
X	    exit(1);
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	  fputs("\n", mailfd);
X
X	  fclose(mailfd);
X	  unlock();		/* blamo or not?  Let it decide! */
X	  fclose(tempfd);
X	} /* end if show only */
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[SLEN];
X	int   fdunit;
X
X	if (verbose && outfd != NULL)
X	  fprintf(outfd, "filter (%s): Message saved in folder %s\n", 
X		  username, foldername);
X	
X	if (!show_only) {
X	  sprintf(filename, "%s.%d", filter_temp, getpid());
X
X	  if ((fdunit = open(foldername, O_APPEND | O_WRONLY | O_CREAT, 0600)) < 0) {
X	    if (outfd != NULL)
X	      fprintf(outfd, 
X		 "filter (%s): can't save message to requested folder %s!\n",
X		    username, foldername);
X	    return(1);
X	  }
X	  fd = fdopen(fdunit,"a");
X
X	  if ((tempfd = fopen(filename, "r")) == NULL) {
X	    if (outfd != NULL)
X	      fprintf(outfd, 
X		     "filter (%s): can't open temp file for reading!\n",
X		     username);
X	     return(1);
X	  }
X
X	  while (fgets(buffer, sizeof(buffer), tempfd) != NULL)
X	    fputs(buffer, fd);
X
X	  /*
X	   * Add two newlines, to ensure that other mailers (which, unlike
X	   * elm, may only look for \n\nFrom_ as the start-of-message
X	   * indicator).
X	   */
X	  fprintf(fd, "%s", "\n\n");
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[SLEN];
X
X	if (verbose && outfd != NULL)
X	  fprintf(outfd, "filter (%s): Executing %s\n", username, command);
X
X	if (! show_only) {
X	  sprintf(buffer, "%s %s.%d | %s", cat, filter_temp, getpid(), command);
X	  system(buffer);
X	}
X}
X
XFILE *
Xemergency_local_delivery()
X{
X	/** This is called when we can't deliver the mail to the usual
X	    mailbox in the usual way ...
X	**/
X
X	FILE *tempfd;
X	char  mailbox[SLEN];
X	int   mailunit;
X
X	sprintf(mailbox, "%s/%s", home, EMERGENCY_MAILBOX);
X
X	if ((mailunit = open(mailbox, O_APPEND | O_WRONLY | O_CREAT, 0600)) < 0) {
X	  if (outfd != NULL)
X	    fprintf(outfd, "filter (%s): Can't open %s either!!\n",
X		    username, mailbox);
X
X	  sprintf(mailbox,"%s/%s", home, EMERG_MBOX); 
X
X	  if ((mailunit = open(mailbox, O_APPEND | O_WRONLY | O_CREAT, 0600)) < 0) {
X
X	    if (outfd != NULL) {
X	      fprintf(outfd,"filter (%s): Can't open %s either!!!!\n",
X		      username, mailbox);
X	      fprintf(outfd, 
X		      "filter (%s): I can't open ANY mailboxes!  Augh!!\n",
X		       username);
X	     }
X
X	     leave("Cannot open any mailbox");		/* DIE DIE DIE DIE!! */
X	   }
X	   else
X	     if (outfd != NULL)
X	       fprintf(outfd, "filter (%s): Using %s as emergency mailbox\n",
X		       username, mailbox);
X	  }
X	  else
X	    if (outfd != NULL)
X	      fprintf(outfd, "filter (%s): Using %s as emergency mailbox\n",
X		      username, mailbox);
X
X	tempfd = fdopen(mailunit, "a");
X	return((FILE *) tempfd);
X}
SHAR_EOF
# ============= filter/buffer.c ==============
echo "x - extracting filter/buffer.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > filter/buffer.c &&
X
Xstatic char rcsid[] ="@(#)$Id: buffer.c,v 4.1 90/04/28 22:41:54 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@DSI.COM
X *			dsinc!elm
X *
X *******************************************************************************
X * $Log:	buffer.c,v $
X * Revision 4.1  90/04/28  22:41:54  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
X
X#include <stdio.h>
X
Xchar  _vbuf[5*BUFSIZ];		/* space for file buffering */
SHAR_EOF
# ============= filter/filter.c ==============
echo "x - extracting filter/filter.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > filter/filter.c &&
X
Xstatic char rcsid[] ="@(#)$Id: filter.c,v 4.1.1.1 90/10/24 16:11:44 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1.1.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@DSI.COM
X *			dsinc!elm
X *
X *******************************************************************************
X * $Log:	filter.c,v $
X * Revision 4.1.1.1  90/10/24  16:11:44  syd
X * Fix out of order lines
X * From: Steve Campbell
X * 
X * Revision 4.1  90/04/28  22:41:55  syd
X * checkin of Elm 2.3 as of Release PL0
X * 
X *
X ******************************************************************************/
X
X
X/** This program is used as a filter within the users ``.forward'' file
X    and allows intelligent preprocessing of mail at the point between
X    when it shows up on the machine and when it is actually put in the
X    mailbox.
X
X    The program allows selection based on who the message is FROM, TO, or
X    what the subject is.  Acceptable actions are for the program to DELETE_MSG
X    the message, SAVE the message in a specified folder, FORWARD the message
X    to a specified user, SAVE the message in a folder, but add a copy to the
X    users mailbox anyway, or simply add the message to the incoming mail.
X
X    Filter also keeps a log of what it does as it goes along, and at the
X    end of each `quantum' mails a summary of actions, if any, to the user.
X
X    Uses the files: $HOME/.filter for instructions to this program, and
X    $HOME/.filterlog for a list of what has been done since last summary.
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#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	    if (outfd != NULL) fclose(outfd);
X	    exit(1);
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			  logf(DELETE_MSG);				break;
X
X	    case SAVE   : if (save_message(rules[rule_choosen].argument2)) {
X			    mail_message(username);
X			    logf(FAILED_SAVE);
X			  }
X			  else
X		 	    logf(SAVE);					break;
X
X	    case SAVECC : if (save_message(rules[rule_choosen].argument2))
X			    logf(FAILED_SAVE);
X			  else
X		            logf(SAVECC);					
X			  mail_message(username);			break;
X
X	    case FORWARD: mail_message(rules[rule_choosen].argument2);
X			  logf(FORWARD);					break;
X
X	    case EXEC   : execute(rules[rule_choosen].argument2);
X			  logf(EXEC);					break;
X
X	    case LEAVE  : mail_message(username);
X			  logf(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
# ============= filter/lock.c ==============
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.1.3 90/06/21 23:51:34 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1.1.3 $   $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@DSI.COM
X *			dsinc!elm
X *
X *******************************************************************************
X * $Log:	lock.c,v $
X * Revision 4.1.1.3  90/06/21  23:51:34  syd
X * Fix typo
X * 
X * Revision 4.1.1.2  90/06/21  23:02:37  syd
X * Make lock use same name as elm
X * From: Edwin WIles
X * 
X * Revision 4.1.1.1  90/06/05  20:28:53  syd
X * The open system call in actions.c for EMERGENCY_MAILBOX and EMER_MBOX
X * were tested with the inequality >= 0 exactly backwards.
X * If the user's system mail box (/usr/spool/mail/user_id) is
X * removed the attempt of filter to flock it fails.  If it does not exist then
X * it should create it and then lock it.
X * From: john@hopf.math.nwu.edu (John Franks)
X * 
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
Xchar *
Xmk_lockname(home, user)
Xchar *home, *user;
X{
X	/** Create the proper name of the lock file for file_to_lock.
X	    Return lock_name for informational purposes.
X	 **/
X
X#ifdef XENIX
X	/* lock is /tmp/[basename of file_to_lock].mlk */
X	sprintf(lockfile, "/tmp/%.10s.mlk", user);
X#else
X	/* lock is [file_to_lock].lock */
X	sprintf(lockfile, "%s%s.lock", home, user);
X#endif
X	return(lockfile);
X}
X
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	mk_lockname(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 | O_CREAT, 0600);
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
# ============= filter/parse.c ==============
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@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
# ============= filter/rules.c ==============
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@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
# ============= filter/summarize.c ==============
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@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
# ============= filter/utils.c ==============
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.1.2 90/10/24 16:08:29 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 4.1.1.2 $   $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@DSI.COM
X *			dsinc!elm
X *
X *******************************************************************************
X * $Log:	utils.c,v $
X * Revision 4.1.1.2  90/10/24  16:08:29  syd
X * Add FAILED_SAVE case to log
X * From: Steve Cambell
X * 
X * Revision 4.1.1.1  90/07/12  20:23:19  syd
X * patch fixes some minor typing mistakes in error messages
X * From: hz247bi@duc220.uni-duisburg.de (Bieniek)
X * 
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
Xlogf(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		    username, 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		  username, 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 FAILED_SAVE: fprintf(fd, "\tSAVE FAILED for file \"%s\"", 
X				rules[rule_choosen].argument2);		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
# ============= hdrs/curses.h ==============
if test ! -d 'hdrs'; then
    echo "x - creating directory hdrs"
    mkdir 'hdrs'
fi
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@DSI.COM			dsinc!elm
X *
X *******************************************************************************
X * $Log:	curses.h,v $
SHAR_EOF
echo "End of  part 7"
echo "File hdrs/curses.h is continued in part 8"
echo "8" > shar3_seq_.tmp
exit 0
Newsgroups: biz.sco.sources