[alt.sources] Procmail, mail processing package

berg%cip-s01.informatik.rwth-aachen.de@unido.bitnet (AKA Solitair) (01/03/91)

The Procmail mail processing package. (v1.01 1990-12-12)

Can be used to create mail-servers, mailing lists, sort your incoming mail
(real convenient when subscribing to one or more mailing lists),
preprocess your mail, or selectively forward certain incoming mail
automatically to someone.

Procmail was designed to deliver the mail under the worst conditions
(file system full, process table full, missing support files,
unavailable executables; it all doesn't matter).
Should (in the unlikely event) procmail be unable to deliver your mail
somewhere, the mail will bounce back to the sender.

Cheers,
       Stephen R. van den Berg  at the RWTH-Aachen, Germany.

---- Cut Here and feed the following to sh ----
#!/bin/sh
# This is Procmail, a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 01/02/1991 by berg%cip-s01.informatik.rwth-aachen.de@unido.bitnet
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#    193 -rw-r--r-- HISTORY
#    574 -rw-r--r-- Makefile
#    500 -rw-r--r-- Manifest
#   2908 -rw-r--r-- README
#     29 -rw-r--r-- forward
#   1988 -rw-r--r-- lockfile.1
#   1957 -rw-r--r-- lockfile.c
#  12508 -rw-r--r-- procmail.1
#  19371 -rw-r--r-- procmail.c
#    311 -rw-r--r-- procmailrc
#    364 -rw-r--r-- rmail
#
# ============= HISTORY ==============
if test -f 'HISTORY' -a X"$1" != X"-c"; then
	echo 'x - skipping HISTORY (File already exists)'
else
echo 'x - extracting HISTORY (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'HISTORY' &&
X1990-12-07: First release (after 2 weeks of coding and testing)
X1990-12-12: Added fsync to procmail.c
X	    Removed longjmp in lockfile.c out of the signal handler
X		(not portable, so I'm told)
SHAR_EOF
chmod 0644 HISTORY ||
echo 'restore of HISTORY failed'
Wc_c="`wc -c < 'HISTORY'`"
test 193 -eq "$Wc_c" ||
	echo 'HISTORY: original size 193, current size' "$Wc_c"
fi
# ============= Makefile ==============
if test -f 'Makefile' -a X"$1" != X"-c"; then
	echo 'x - skipping Makefile (File already exists)'
else
echo 'x - extracting Makefile (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
XBINDIR = /global/bin
XMANDIR = /global/man/man1
X
XCFLAGS = -O
XLDFLAGS = -s
X
XCC = cc
XINSTALL= install -m 755
XRM= rm -f
X
Xall:	procmail lockfile
X
Xprocmail: procmail.o
X	${CC} ${CFLAGS} -o procmail procmail.o ${LDFLAGS}
X
Xprocmail.o: procmail.c
X	${CC} ${CFLAGS} -c procmail.c
X
Xlockfile: lockfile.o
X	${CC} ${CFLAGS} -o lockfile lockfile.o ${LDFLAGS}
X
Xlockfile.o: lockfile.c
X	${CC} ${CFLAGS} -c lockfile.c
X
Xinstall: all
X	${INSTALL} procmail lockfile ${BINDIR}
X	chmod 644 procmail.1 lockfile.1
X	cp procmail.1 lockfile.1 ${MANDIR}
X
Xclean:
X	${RM} procmail.o lockfile.o procmail lockfile
SHAR_EOF
chmod 0644 Makefile ||
echo 'restore of Makefile failed'
Wc_c="`wc -c < 'Makefile'`"
test 574 -eq "$Wc_c" ||
	echo 'Makefile: original size 574, current size' "$Wc_c"
fi
# ============= Manifest ==============
if test -f 'Manifest' -a X"$1" != X"-c"; then
	echo 'x - skipping Manifest (File already exists)'
else
echo 'x - extracting Manifest (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Manifest' &&
XMakefile	We all know what that is.
XREADME		Important, read it.
XHISTORY		Recent and ancient changes (or bugs) documented.
Xlockfile.1	Man page for lockfile.
Xlockfile.c	You guessed it.
XManifest	Right again.
Xprocmail.1	Yes, the man page for procmail.
Xprocmail.c	Yes, exactly.
Xprocmailrc	A sample .procmailrc file
Xrmail		A sample shell script that demonstrates how to use
X		lockfiles while reading the mail (to ensure mail integrity
X		as soon as you exit the mail program)
Xforward		A sample .forward file
SHAR_EOF
chmod 0644 Manifest ||
echo 'restore of Manifest failed'
Wc_c="`wc -c < 'Manifest'`"
test 500 -eq "$Wc_c" ||
	echo 'Manifest: original size 500, current size' "$Wc_c"
fi
# ============= README ==============
if test -f 'README' -a X"$1" != X"-c"; then
	echo 'x - skipping README (File already exists)'
else
echo 'x - extracting README (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'README' &&
XSome legal stuff:
X
XUse this software package at your own risk.  The programmer can not
Xbe held liable for any incurred damages due to the use of this software.
X
XYou are encouraged to distribute this package freely.  This package is
Xhowever not to be not sold (minor transfer costs excepted) or included in
Xany commercially sold software package (if you want to do this anyway,
Xcontact me (address below), and we'll work something out).
X
XIf you distribute it, please leave the package in tact.  If you have some
Ximportant changes that might be usefull to the rest of the world, contact
Xme instead.
X
X----------------------
X
XThe Procmail mail processing package. (v1.01 1990-12-12)
X
XCan be used to create mail-servers, mailing lists, sort your incoming mail
X(real convenient when subscribing to one or more mailing lists),
Xpreprocess your mail, or selectively forward certain incoming mail
Xautomatically to someone.
X
XTo install it, edit the Makefile accordingly (if it doesn't compile at first,
Xcheck if some extra header files should be included (or left out) in order
Xto make any used types/defines/declarations for the library compatible).
X
XBTW, the warnings you get about 'loop not entered at top': they're normal :-).
X
XIf you really want to, you can change some of the defines at the start
Xof procmail.c to suit your needs.  They should be sufficient for most
Xpeople though.
X
XEvery user that wants to use procmail should have a .forward and a
X.procmailrc file in his HOME directory.  For starters, you can look
Xat the supplied example "forward" and "procmailrc" files.
X(BTW, be sure to make .forward *world* readable).
X
XFor more info about the program, see the man page.
X
X----------------------
X
XAlthough I can't guarantee that the procmail (or lockfile) program
Xwill perform as required, I must say that I made the utmost effort
Xto make procmail as robust as any program can be.
X
XProcmail was designed to deliver the mail under the worst conditions
X(file system full, process table full, missing support files,
Xunavailable executables; it all doesn't matter).
XShould (in the unlikely event) procmail be unable to deliver your mail
Xsomewhere, the mail will bounce back to the sender.
X
XHowever, as with any program, bugs can not be completely ruled out.
XI tested the program extensively, and believe it should be relatively
Xbug free (no known bug at the time).  Should, however, anyone
Xfind any bugs (highly unlikely :-), I would be pleased (well, sort of :-)
Xto hear about it.  Please send me the patches or bug report.
XI'll look at them and will try to fix it in a future release.
X
XCheers,
X       Stephen R. van den Berg  at the RWTH-Aachen, Germany.
X
XInternet address: berg%cip-s01.informatik.rwth-aachen.de@unido.bitnet
XBitnet address:   berg%cip-s01.informatik.rwth-aachen.de@unido
X
XPlease don't swamp me with mail, we have to pay for *incoming* mail here.
XSo, think twice before sending me something.
SHAR_EOF
chmod 0644 README ||
echo 'restore of README failed'
Wc_c="`wc -c < 'README'`"
test 2908 -eq "$Wc_c" ||
	echo 'README: original size 2908, current size' "$Wc_c"
fi
# ============= forward ==============
if test -f 'forward' -a X"$1" != X"-c"; then
	echo 'x - skipping forward (File already exists)'
else
echo 'x - extracting forward (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'forward' &&
X"|exec /global/bin/procmail"
SHAR_EOF
chmod 0644 forward ||
echo 'restore of forward failed'
Wc_c="`wc -c < 'forward'`"
test 29 -eq "$Wc_c" ||
	echo 'forward: original size 29, current size' "$Wc_c"
fi
# ============= lockfile.1 ==============
if test -f 'lockfile.1' -a X"$1" != X"-c"; then
	echo 'x - skipping lockfile.1 (File already exists)'
else
echo 'x - extracting lockfile.1 (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'lockfile.1' &&
X.\" @(#)lockfile.1 1.29 90/12/07 SMI;
X.de Sh
X.br
X.ne 6
X.SH \\$1
X..
X.de Ss
X.br
X.ne 6
X.SS \\$1
X..
X.TH LOCKFILE 1 "07 December 1990"
X.SH NAME
Xlockfile \- conditional semaphore-file creator
X.SH SYNOPSIS
X.B lockfile
X[
X.BI \- sleeptime
X] [
X.BI \-r retries
X] [
X.B \-!
X]
X.I filename
X\&.\|.\|.
X.Sh "DESCRIPTION"
X.B lockfile
Xcan be used to create one or more
X.I semaphore
X.IR files .
XIf
X.B lockfile
Xcan't create all the specified files (in the specified order), it waits
X.I sleeptime
X(defaults to 8) seconds and retries the last file that didn't succeed.
XYou can specify the number of
X.I retries
Xto do until failure is returned.
XIf the number of
X.I retries
Xis 0 (default)
X.B lockfile
Xwill retry forever.
X.LP
XIf the number of
X.I retries
Xexpires before all files have been created,
X.B lockfile
Xreturns failure and removes all the files it created up till that point.
X.LP
XThe return value of
X.B lockfile
Xcan be easily inverted by specifying
X.B \-!
Xas an argument (handy in shell scripts).
X.LP
XAll flags can be specified anywhere on the command line, they will be
Xprocessed when encountered.  The command line is simply parsed from
Xleft to right.
X.LP
XAll files created by
X.B lockfile
Xwill have access permission 0, and therefore will have to be removed
Xwith
X.B rm
X.BR \-f .
X.Sh "SEE ALSO"
X.LP
X.BR rm (1),
X.BR mail (1),
X.BR binmail (1),
X.BR sendmail (8),
X.BR procmail (1)
X.Sh "BUGS & SPECIAL FEATURES"
X.LP
X.B lockfile
Xis only a simple program and can't process concatenated options.
X.LP
XMultiple
X.B \-!
Xflags will not toggle the return status.
X.Sh "NOTES"
X.LP
XSince flags can occur anywhere on the command line, any filename starting
Xwith an '-' has to be preceded by another '-'.
X.LP
XThe number of
X.I retries
Xis global.  That is, it is not reset when a new file is being created.
XIt can, however, be reset by specifying
X.RI \-r newretries
Xafter every file on the command line.
X.Sh "AUTHOR"
X.LP
XStephen R. van den Berg at RWTH-Aachen, Germany
X.RS
Xberg%cip-s01.informatik.rwth-aachen.de@unido.bitnet
X.RE
SHAR_EOF
chmod 0644 lockfile.1 ||
echo 'restore of lockfile.1 failed'
Wc_c="`wc -c < 'lockfile.1'`"
test 1988 -eq "$Wc_c" ||
	echo 'lockfile.1: original size 1988, current size' "$Wc_c"
fi
# ============= lockfile.c ==============
if test -f 'lockfile.c' -a X"$1" != X"-c"; then
	echo 'x - skipping lockfile.c (File already exists)'
else
echo 'x - extracting lockfile.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'lockfile.c' &&
X/************************************************************************
X *      lockfile.c      a conditional semaphore-file creator		*
X *                                                                      *
X *      Version 1.01 1990-12-07						*
X *      Is relatively bug free.						*
X *                                                                      *
X *      Created by S.R.van den Berg, The Netherlands			*
X *      The sources can be freely copied for any use.			*
X *                                                                      *
X *      If you have had to make major changes in order to get it        *
X *      running on a different machine, please send me the patches.     *
X *      That way I might include them in a future version.              *
X ************************************************************************/
X#include <stdio.h>
X#include <fcntl.h>
X#include <signal.h>
X
Xint exitflag;
X
Xvoid failure(){
X exitflag=1;}
X
Xmain(argc,argv)char*argv[];{int sleepsec,retries,i,invert;char**p,*cp;
X sleepsec=8;retries=invert=0;
X if(argc<2){
X   putse("Usage: lockfile [-nnn] [-rnnn] [-!] file ...\n");return 2;}
Xagain:
X p=argv+1;signal(SIGHUP,failure);signal(SIGINT,failure);
X signal(SIGQUIT,failure);signal(SIGTERM,failure);
X do
X    if(*(cp=*p)=='-')
X       switch(cp[1]){
X       case'!':invert=1;break;
X       case'r':retries=strtol(cp+2,NULL,10);break;
X       case'-':cp++;goto filename;
X       default:
X          if(sleepsec>=0)
X             sleepsec=strtol(cp+1,NULL,10);}
X    else
Xfilename:
X      if(sleepsec<0)
X         unlink(cp);
X      else{
X         while(0>(i=open(cp,O_WRONLY|O_CREAT|O_EXCL|O_SYNC,0))){
X            if(exitflag||retries==1){
X               sleepsec=-1;*p=0;goto again;}
X            sleep(sleepsec);
X            if(retries)
X               retries--;}
X         close(i);}
X while(*++p);
Xreturn invert^(sleepsec<0);}
X
X#define	STDERR	2
X
Xputse(a)char*a;{char*b;
X b=a-1;
X while(*++b);
X write(STDERR,a,b-a);}
SHAR_EOF
chmod 0644 lockfile.c ||
echo 'restore of lockfile.c failed'
Wc_c="`wc -c < 'lockfile.c'`"
test 1957 -eq "$Wc_c" ||
	echo 'lockfile.c: original size 1957, current size' "$Wc_c"
fi
# ============= procmail.1 ==============
if test -f 'procmail.1' -a X"$1" != X"-c"; then
	echo 'x - skipping procmail.1 (File already exists)'
else
echo 'x - extracting procmail.1 (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'procmail.1' &&
X.\" @(#)procmail.1 1.29 90/12/07 SMI;
X.de Sh
X.br
X.ne 6
X.SH \\$1
X..
X.de Ss
X.br
X.ne 6
X.SS \\$1
X..
X.TH PROCMAIL 1 "07 December 1990"
X.SH NAME
Xprocmail \- autonomous mail processor
X.SH SYNOPSIS
X.B procmail
X[
X.IB environmentvariable = value
X|
X.I rcfilename
X] 
X\&.\|.\|.
X.Sh "DESCRIPTION"
X.LP
XFor a quick start, see
X.B NOTES
Xat the end.
X.LP
X.B procmail
Xfirst sets some environment variables to default values, then, if no
Xarguments are specified, it reads the mail message from stdin until an
XEOF, separates
Xthe body from the header and starts to look for a file named
X.B .procmailrc
Xin your home directory.
X.LP
X.Ss "Defaults"
X.TP 22
X.B USER, HOME and SHELL
XYour (the recipient's) defaults
X.TP
X.B SHELLMETA
X"'`^&#{}()[]*?|<>~;!\\
X.TP
X.B MAILDIR
X$HOME
X.TP
X.BR DEFAULT
X$MAILDIR/.mailbox
X.TP
X.B ORGMAIL
X/var/spool/mail/$USER
X.TP
X.B GREP
X/usr/bin/egrep
X.TP
X.B SENDMAIL
X/usr/lib/sendmail
X.TP
X.B LOCKEXT
X\&.lock
X.Ss "Environment"
X.TP 12
X.B MAILDIR
XDefault directory while
X.B procmail
Xis executing (that means that all paths are relative to $MAILDIR).
X.TP
X.B DEFAULT
XDefault mailbox file (if not told otherwise,
X.B procmail
Xwill dump mail in this file)
X.TP
X.B LOGFILE
XAll incoming messages will be logged here with their 'From' and 'Subject'
Xlines in the header.  This file will also contain any error or diagnostic
Xmessages from
X.BR procmail .
X(Normally none :-)  If this file is not specified it defaults to
X.BR /dev/null.
X.TP
X.B ORGMAIL
XUsually the system mailbox (ORiGinal MAILbox).  If, for some obscure
Xreason (like "filesystem full") the mail could not be delivered, then this
Xfile will be the last resort.  If
X.B procmail
Xfails to save the mail in here (deep, deep trouble :-), then the mail
Xwill bounce back to the sender.
X.TP
X.B LOCKFILE
XGlobal semaphore file.  If this file already exists,
X.B procmail
Xwill wait until it has gone before proceeding, and will create it itself
X(cleaning it up when ready, of course).  If more than one lockfile are
Xspecified, then the previous one will be removed before trying to create
Xthe new one.
X.TP
X.B LOCKEXT
XDefault extension that is appended to a destination file to determine
Xwhat local lockfile to use (only if turned on, on a per-recipe basis).
X.TP
X.B LOCKSLEEP
XNumber of seconds
X.B procmail
Xwill sleep before retrying on a lockfile (if it already existed);
Xdefaults to 8 seconds.
X.TP
X.B HOST
XIf this is not the hostname of the machine, processing of the current
Xrcfile will immediately cease.  If other rcfiles were specified on the
Xcommand line, processing will continue with the next one.  If all rcfiles
Xare exhausted, the program will terminate, but will not generate an error
X(i.e. to the mailer it will seem that the mail has been delivered).
X.TP
X.B UMASK
XThe name says it all (if it doesn't, then forget about this one :-).  It
Xis taken as an octal number.  If not specified, it defaults to 077.
X.TP
X.B GREP
XThe program that gets called for parsing regular expressions.  It is called
Xas: $GREP -s[i] -e "$*";
X.TP
X.B SHELLMETA
XIf any of the characters in
X.B SHELLMETA
Xappears in the line specifying a filter or program, the line will be feeded
Xto
X.B $SHELL
Xinstead of being executed directly.
X.TP
X.B SHELLFLAGS
XIf not specified, any invocation of
X.B $SHELL
Xwill be like: $SHELL -c "$*";  if specified the invocation will
Xbe: $SHELL $SHELLFLAGS "$*";
X.TP
X.B SENDMAIL
XIf you're not using the
X.I forwarding facility
Xdon't worry about this one.  It specifies the program being called to
Xforward any mail.  It gets invoked as: $SENDMAIL $*;
X.Sh "ARGUMENTS"
X.LP
XAny arguments containing an '=' are considered to be environment variable
Xassignments, they will
X.B all
Xbe evaluated after the default values have been
Xassigned and before the
X.B first
Xrcfile is parsed.
X.LP
XAny other arguments are presumed to be rcfiles; 
X.B procmail
Xwill start with the first one it finds on the command line.  The following
Xones will only be parsed if the preceding ones have a not matching
X.B HOST
Xdirective entry.
X.LP
XIf no rcfiles are specified, it looks for $HOME/.sortmailrc.  If not even
Xthat can be found processing will continue according to the default
Xsettings of the environment variables and the ones specified on the command
Xline.
X.Sh "RCFILE FORMAT"
X.LP
XEnvironment variable assignments and recipes can be freely intermixed
Xin the rcfile.  If any environment variable has a special meaning to
X.BR procmail ,
Xit will used appropiately the moment it is parsed. (i.e. you can
Xchange the default directory whenever you want by specifying a new
X.BR MAILDIR ,
Xswitch lockfiles by specifying a new
X.B LOCKFILE
X(normal persons won't need this particular application though), change
Xthe umask at any time, etc., the possibilities are endless :-).
X.LP
XValues being assigned to environment variables (in the rcfile) can not
Xcontain any space, tab, newline or '#' characters.  Anything on a line
Xfrom the first '#' till EOL is considered comment.
X.Ss "Recipes"
X.LP
XA line starting with ':' marks the beginning of a recipe.  It has the
Xfollowing format:
X.LP
X: [
X.I number
X] [
X.I options
X] [ : [
X.I locallockfile
X] ]
X.LP
XThe
X.I number
Xis optional
X(defaults to 1) and specifies the number of conditionals following this
Xline.  Conditionals are complete lines that are passed onto
X.B $GREP
X.BR literally ,
Xconditionals are anded; if
X.I number
Xis zero, then the condition is always true and no conditionals are expected
Xnext.
X.LP
X.I Options
Xcan be any of the following (don't insert spaces in between, from the first
Xcharacter that is not a valid option till EOL will be skipped without notice):
X.TP 5
X.B H
XFeed the header to $GREP (default)
X.TP
X.B B
XFeed the body to $GREP
X.TP
X.B I
XTell $GREP to ignore case
X.TP
X.B h
XFeed the header to the pipe (default)
X.TP
X.B b
XFeed the body to the pipe (default)
X.TP
X.B f
XConsider the pipe as a filter (ignored if a file)
X.TP
X.B c
XContinue processing rcfile even if this recipe matches (not needed if 'f'
Xspecified)
X.TP
X.B w
XWait for the process to finish (and check if it was successfull, normally
Xignored).  If you specify 'f' and it matches, and the original mailer
Xwill be told that the mail has already been delivered.  If you want
X.B procmail
Xto stall telling the mailer that the mail has been delivered, until
Xit really has been, you'll have to specify 'w' together with every 'f'.
XIf you don't, the mailer will be told that the mail has been delivered
Xsuccesfully as soon as the first 'f' without a 'w' has been processed.
XThis option is also advisable if you specified any
X.I locallockfile
Xon this recipe.
X.TP
X.B s
XMake
X.B procmail
Xsecure.  If any fork fails in the current recipe, retry until it succeeds
X(usefull when running on machines that occasionally have a full process tables,
Xto ensure normal delivery in any case).  If a fork fails and 's' is
Xnot specified, it simply is logged in $LOGFILE and parsing of the rcfile
Xcontinues (eventually delivering to $DEFAULT if no more recipes match).
X.Ss "Local lockfile"
X.LP
XIf you put a second ':' on the first recipe line, then
X.B procmail
Xwill use a
X.I locallockfile
X(for this recipe only).  You optionally can specify
Xthe
X.I locallockfile
Xto use; if you don't however,
X.B procmail
Xwill use the filename specified as the destination (or the filename
Xfollowing the first '>>') and will append $LOCKEXT to it.
X.Ss "Recipe destination"
X.LP
XThe next line can start with the following characters:
X.TP
X.B !
XForwards to all the specified mail addresses (comments are 
X.B not
Xignored on
Xthis line).
X.TP
X.B |
XStarts the specified program, possibly in $SHELL if any
Xof the $METASHELL characters are found (that means comments are normally
Xprocessed by the shell on
X.B this
Xline).
X.LP
XAnything else will be taken as a filename (relative to $MAILDIR).  After the
Xfilename everything will be ignored till EOL.
X.Sh "EXAMPLES"
X.LP
XSome example recipes are listed below:
X.LP
XSort out all mail to mailling list scuba-dive.
X.RS
X.LP
X:
X.PD 0
X.LP
X^TOscuba
X.LP
Xscubafile
X.PD
X.LP
X.RE
XForward all mail from peter about compilers to william (and keep a copy
Xof it here in petcompil).
X.RS
X.LP
X:2 bc
X.PD 0
X.LP
X^From.*peter
X.LP
X^Subject:.*compilers
X.LP
X! william@somewhere.edu
X.LP
X:2
X.LP
X^From.*peter
X.LP
X^Subject:.*compilers
X.LP
Xpetcompil
X.PD
X.RE
X.LP
XAdd the headers of all messages to your private header collection (for
Xstatistics or mail debugging); and use the lockfile "headc.lock".  In order
Xto make sure the lockfile is not removed until the pipe has finished,
Xyou have to specify option 'w'; otherwise the lockfile would be removed as
Xsoon as the pipe had accepted the mail.
X.RS
X.LP
X:0hwc:
X.PD 0
X.LP
X| uncompress headc.Z; cat >>headc; compress headc
X.RE
X.PD
X.LP
XPrepend a linecount at the beginning of all multi part messages
X(and don't allow
X.B procmail
Xto terminate early if the filter succeeds).  Use "templock" as lockfile.
X.RS
X.LP
X:wbf:templock
X.PD 0
X.LP
X^Subject:.*\\(.*(/|of).*\\)
X.LP
X|echo "wc output:";tee tempf|wc;cat tempf;rm tempf
X.PD
X.RE
X.LP
XDump all mail from at jobs into one file, filter out the interesting
Xparts of the header first.
X.LP
X.RS
X:2fh
X.PD 0
X.LP
X^From root
X.LP
X^Subject: Output from "at" job
X.LP
X|echo "From at job";echo;egrep "^Date:"
X.LP
X:b
X.LP
X^From at job
X.LP
Xatjunk
X.RE
X.PD
X.Sh "FILES"
X.PD 0
X.TP 22
X.B /etc/passwd
Xto get the recipients USER, HOME and SHELL variable defaults
X.TP
X.B /var/spool/mail/$USER
Xdefault last resort to put mail
X.TP 
X.B $HOME/.procmailrc
Xdefault rc file
X.TP
X.B $HOME/.mailbox
Xdefault mailbox
X.TP
X.B /var/spool/mail/$USER.lock
Xlockfile for standard system mail directory (not used by
X.B procmail
Xunless you explicitly tell it to)
X.TP
X.B /lib/sendmail
Xdefault mail forwarder
X.TP
X.B /usr/bin/egrep
Xdefault regular expression parser
X.PD
X.Sh "SEE ALSO"
X.LP
X.BR sh (1),
X.BR csh (1),
X.BR mail (1),
X.BR binmail (1),
X.BR uucp (1C),
X.BR aliases (5),
X.BR sendmail (8),
X.BR egrep (1V),
X.BR lockfile (1)
X.Sh "DIAGNOSTICS"
X.TP 23
XError while writing to "x"
Xnonexistent subdirectory, no write permission, or disk full
X.TP
XFailed forking "x"
Xnot possible if 's' flag specified on the recipe
X.TP
XProgram failure of "x"
Xsome pipe or program that was started by
X.B procmail
Xreturned a non-null value
X.TP
XFailed to execute "x"
Xprogram not in path, or not executable
X.TP
XCouldn't unlink "x"
Xlockfile was already gone, or write permission to the directory were the
Xlockfile is has been denied
X.TP
XOut of memory
Xprobably a runaway filter that dumps junk into
X.BR procmail,
Xor the system could be out of swapspace
X.TP
XLockfailure
Xcan only occur if you specify some real weird (and illegal) lockfilenames
X.PD
X.Sh "BUGS & SPECIAL FEATURES"
X.LP
XIf you don't explicitly tell
X.B procmail
Xto wait (option 'w') for the pipe or program to finish, it won't wait
Xand will terminate early (not knowing if the pipe or program returns
Xsuccess).
X.LP
XThe only substitutions of environment variables that can be handled by
X.B procmail
Xitself are of the type $name.
X.LP
XA line buffer of 2048 bytes is used when processing the
X.BR rcfile ,
Xany environment variable expansions
X.B have
Xto fit within this limit.
X.LP
XRace conditions sometimes result in a failure to remove a lock file
X(e.g. forwarding mail to yourself (on the same account) could (not
Xnecessarily) be a problem).
X.LP
XIn the unlikely event that you absolutely need to kill
X.B procmail
Xbefore it has finished, then first try and use
Xthe regular kill command (SIGTERM), otherwise some
X.I lockfiles
Xmight not get removed.
X.LP
XYou should create a shell script that uses
X.BR lockfile (1)
Xbefore invoking
Xthe mail program on any mailbox file other than the system mailbox.
X.Sh "NOTES"
X.LP
XAny program executed from within
X.B procmail
Xwill be searched for in the PATH variable (you have to specify it though).
XIt is advisable however, to specify an absolute path for $GREP, because
Xit get's executed fairly often.
X.LP
XIf the regular expression starts with "^TO" it will be substituted by
X"^(To|Cc|Apparently-To):.*", which should catch all destination
Xspecifications.
X.LP
XAny lines in the body of the message that look like postmarks are prepended
Xwith '>' (disarms bogus mailheaders).
X.LP
XYour .forward (beware, it
X.B has
Xto be world readable) file should contain (include the quotes):
X.LP
X"|exec /global/bin/procmail"
X.Ss "A sample small .procmailrc:"
X.PD 0
X.LP
XPATH=/bin:/usr/bin:/global/bin
X.LP
XMAILDIR=$HOME/mail	#you'd better make sure it exists
X.LP
XDEFAULT=$MAILDIR/mbox
X.LP
XLOGFILE=$MAILDIR/from
X.LP
XLOCKFILE=$HOME/.lockmail
X.LP
X:
X.LP
X^From.*berg
X.LP
Xfrom_me
X.LP
X:
X.LP
X^Subject:.*Flame
X.LP
X/dev/null
X.PD
X.Sh "AUTHOR"
X.LP
XStephen R. van den Berg at RWTH-Aachen, Germany
X.RS
Xberg%cip-s01.informatik.rwth-aachen.de@unido.bitnet
X.RE
SHAR_EOF
chmod 0644 procmail.1 ||
echo 'restore of procmail.1 failed'
Wc_c="`wc -c < 'procmail.1'`"
test 12508 -eq "$Wc_c" ||
	echo 'procmail.1: original size 12508, current size' "$Wc_c"
fi
# ============= procmail.c ==============
if test -f 'procmail.c' -a X"$1" != X"-c"; then
	echo 'x - skipping procmail.c (File already exists)'
else
echo 'x - extracting procmail.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'procmail.c' &&
X/************************************************************************
X *	procmail.c	an autonomous mail processor			*
X *									*
X *	Version 1.01 (No bugs yet :-) 1990-12-12			*
X *	Seems to be relatively bug free.				*
X *									*
X *	Copyright (c) 1990, S.R.van den Berg, The Netherlands		*
X *	The sources can be freely copied for non-commercial use.	*
X *									*
X *	Don't complain about the formatting, I know it's		*
X *	unconventional, but it's my standard format (I have my		*
X *	reasons).  If you don't like it, feed it through your		*
X *	favourite C beautifier.  The program has been tested on		*
X *	SUN's, but should work on almost every *NIX system that		*
X *	has a fork() and execvp() command.  The only things that	*
X *	might need some changes are the include files.			*
X *	There might be one problem if your system doesn't know		*
X *	fsync(fd).  Define NOfsync in that case.			*
X *									*
X *	If you have had to make major changes in order to get it	*
X *	running on a different machine, please send me the patches.	*
X *	That way I might include them in a future version.		*
X *									*
X *	Please note that this program essentially is supposed to be	*
X *	static, that means no extra features (IMHO no more are		*
X *	needed) are supposed to be added.				*
X ************************************************************************/
X#ifdef	NOsync			/* If you don't want syncs at all define */
X#define	fsync(fd)		/* NOsync.  Only recommended if procmail */
X#define	sync()			/* isn't used in a networked environment */
X#else
X# ifdef	NOfsync			/* If you don't have fsync, define NOfsync */
X# define fsync(fd) sync()	/* sync will be used instead.  Is a bit    */
X# endif				/* slower, but works nevertheless	   */
X#endif
X
X#include <stdio.h>
X#ifndef	NO_ANSI_PROT		/* define this if your headers are not ansi */
X# include <stdlib.h>
X#else
X  char*getenv();
X  typedef int pid_t;
X#endif
X#include <fcntl.h>
X#include <pwd.h>
X#include <sys/wait.h>
X#include <signal.h>
X#include <malloc.h>
X#include <string.h>
X#include <errno.h>
X#ifndef	SEEK_SET
X#define	SEEK_SET	0
X#endif
Xextern char**environ;
X#define	STDIN	0
X#define	STDOUT	1
X#define	STDERR	2
Xtypedef unsigned t_buf;	/* for malloc, realloc and memmove
X                          This type determines the maximum message length */
X
X#define	BFSIZ		2048		/* max expanded line length */
X#define	BLKSIZ		(1<<14)		/* blocksize while reading/writing */
X#define	PROCMAILRC	".procmailrc"
X#define	SUSPENSION	64
X#define	DEFlocksleep	8
X#define	TOkey		"^TO"
X#define	TOkeylen	3
X#define	TOsubstitute	"^(To|Cc|Apparently-To):.*"
X#define	DEFshellmeta	"\"'`^&#{}()[]*?|<>~;!\\"   /* never put '$' in here */
X#define	DEFmaildir	"$HOME"
X#define	DEFdefault	"$MAILDIR/.mailbox"
X#define	DEForgmail	"/var/spool/mail/$USER"
X#define	DEFgrep		"/usr/bin/egrep"
X#define	DEFsendmail	"/usr/lib/sendmail"
X#define	DEFlockext	".lock"
X
Xchar buf[BFSIZ],buf2[BFSIZ],maildir[]="MAILDIR",defaultf[]="DEFAULT",
X logfile[]="LOGFILE",lockfile[]="LOCKFILE",grep[]="GREP",host[]="HOST",
X locksleep[]="LOCKSLEEP",orgmail[]="ORGMAIL",eumask[]="UMASK",
X shellmeta[]="SHELLMETA",shellflags[]="SHELLFLAGS",shell[]="SHELL",
X sendmail[]="SENDMAIL",lockext[]="LOCKEXT",devnull[]="/dev/null",
X newline[]="\n",binsh[]="/bin/sh",home[]="HOME",**gargv,
X *rcfile=PROCMAILRC,*globlock,*loclock,*tolock;
X#define	PREAD	(poutfd[0])
X#define	PWRITE	(poutfd[1])
X#define	tscrc(a,b)	(0<fscanf(rc,a,b))
X#define	scrc(a,b)	fscanf(rc,a,b)
Xint retval=1,flaggerd=1,locked,verrgrandchild,sh,pwait,secur,locking,nextexit,
X locknext;
Xpid_t mother;
XFILE*rc;
X
X#ifndef	__STDC__
Xvoid*memmove(to,from,count)register void*to,*from;register t_buf count;{
X void*old;
X old=to;count++;--(char*)to;--(char*)from;
X if(to<=from){
X   goto jiasc;
X   do{
X      *++(char*)to=*++(char*)from;
Xjiasc:;}
X   while(--count);}
X else{
X   (char*)to+=count;(char*)from+=count;
X   goto jidesc;
X   do{
X      *--(char*)to=*--(char*)from;
Xjidesc:;}
X   while(--count);}
X return old;}
X#endif
X
Xpid_t sfork(){pid_t i;		/* if secur is set, it doesn't return untill */
X while((i=fork())&&secur&&i==-1)  /* the fork was successfull */
X   sleep(SUSPENSION);
X return i;}
X
Xvoid*tmalloc(len)t_buf len;{void*p;
X if(p=malloc(len))
X   return p;
X nomemerr();}
X
Xvoid*trealloc(old,len)void*old;t_buf len;{
X if(old=realloc(old,len))
X   return old;
X nomemerr();}
X
Xvoid terminate(){
X if(locking){
X   nextexit=1;return;}
X sync();	/* sorry, but this is mail we're dealing with, safety first */
X unlock(&loclock);	/* local lock files are removed in any case */
X if(mother&&mother!=getpid())
X   if(retval)	/* tell mam we're in trouble, let her clean up the mess */
X      kill(mother,SIGQUIT);   /* don't try this at home, kids :-) */
X   else{
X      kill(mother,SIGHUP);goto allok;} /* You can go home mam, everything ok */
X else
Xallok:		/* global lockfile should be removed by the last surviving */
X   unlock(&globlock);	/* procmail */
X exit(retval);}
X
Xvoid flagger(){		/* hey, we received a SIGHUP */
X flaggerd=1;}
X
Xvoid errgrandchild(){	/* my grandchildren scream in despair */
X verrgrandchild=1;}
X
Xlong dump(s,source,len)int s;char*source;long len;{int i;long ol;
X if(s>=0){
X    ol=len;
X    while(i=rwrite(s,source,BLKSIZ<len?BLKSIZ:(int)len)){
X       if(i<0){
X          i=0;goto writefin;}
X       len-=i;source+=i;}
X    if(!len&&(ol<2||!(source[-1]=='\n'&&source[-2]=='\n')))
X       rwrite(s,newline,1); /* message always ends with a newline */
Xwritefin:
X    fsync(s);rclose(s);return len-i;}
X return len?len:-1;}	/* return an error even if nothing was to be sent */
X
Xlong pipin(line,source,len)char*line,*source;long len;{pid_t pid;
X int poutfd[2];
X pipe(poutfd);
X if(!(pid=sfork(line))){
X   rclose(PWRITE);redirect(PREAD);callnewprog(line);}
X rclose(PREAD);forkerr(pid,line);
X if(len=dump(PWRITE,source,len))
X   writeerr(line);
X if(pwait&&waitfor(pid)){
X   progerr(line);len=1;}
X return len;}
X
Xchar*readdyn(bf,filled)char*bf;long*filled;{int i;
X goto jumpin;
X do{
X   *filled+=i;
Xjumpin:
X   bf=trealloc(bf,(t_buf)*filled+BLKSIZ);}  /* dynamic adjust */
X while(0<(i=read(STDIN,bf+*filled,BLKSIZ)));
X if(!*filled)
X   return trealloc(bf,1);
X return trealloc(bf,(t_buf)*filled+1);} /* minimize the buffer space + 1 */
X
Xchar*cat(a,b)char*a,*b;{
X return strcat(strcpy(buf,a),b);}
X
Xchar*findel(start,end)register char*start,*end;{/* find the first empty line */
X while(start<end)
X   if(*start++=='\n'&&start<end&&*start=='\n')
X      return start+1;
X return end;}
X
Xmain(argc,argv)char*argv[];{struct passwd *pass;static char flags[10];
X char*themail,*thebody,*chp,*startchar,*chp2;int i;
X long tobesent,filled,rcoffset;
X mother=getpid();setbuf(stdin,(void*)0);
X umask(077);setpwent();setdef(home,(pass=getpwuid(getuid()))->pw_dir);
X chdir(pass->pw_dir);setdef("USER",pass->pw_name);
X setdef(shell,pass->pw_shell);endpwent();
X setdef(shellmeta,DEFshellmeta);
X setdef(maildir,DEFmaildir);setdef(defaultf,DEFdefault);
X setdef(orgmail,DEForgmail);setdef(grep,DEFgrep);
X setdef(sendmail,DEFsendmail);setdef(lockext,DEFlockext);
X chdir(getenv(maildir));fdreopena(devnull,STDERR);fdreopena(devnull,STDOUT);
X gargv=argv+1;nextrcfile();
X thebody=themail=tmalloc((t_buf)(argc=1));filled=rcoffset=0;
Xchangedmail:
X themail=readdyn(themail,&filled);	/* read in the mail */
Xonlyhead:
X startchar=filled+(thebody=themail);
X while(thebody<startchar&&*thebody++=='\n');	/* skip leading garbage */
X thebody=findel(thebody,startchar);	/* find the end of the header */
X chp=thebody;
X while(startchar>(chp=findel(chp,startchar))){	/* search for bogus headers */
X   if(startchar-chp<8)
X      break;
X   if(0>=sscanf(chp,"From%1[ ]",buf))
X      continue;
X   chp2=chp;chp+=5;
X#define	SKIPWHILE(x)	while(x){ if(++chp>=startchar) break;}
X   SKIPWHILE(*chp==' ')
X   SKIPWHILE((i=*chp)&&i!=' '&&i!='\t'&&i!='\n')
X   SKIPWHILE(*chp==' ');
X   if((i=*chp)&&i!='\n'&&i!='\t'){ /* we can move immediately because we */
X      memmove(chp2+1,chp2,(t_buf)(startchar-chp2)); /* have one spare byte */
X      *chp2='>';themail=trealloc(chp2=themail,++filled);
X#define	ADJUST(x)	((x)=themail+((x)-chp2))
X      ADJUST(thebody);ADJUST(startchar);ADJUST(chp);}}
Xnochangedmail:
X waitflagger();	/* if we're a child, wait for the parental guidance */
Xchangerc:
X rc=fopen(strcat(cat(getenv(home),"/"),rcfile),"r");
X fseek(rc,rcoffset,SEEK_SET);signal(SIGINT,terminate);
X signal(SIGQUIT,terminate);signal(SIGTERM,terminate);signal(SIGHUP,SIG_IGN);
Xgoon:
X while(unlock(&loclock),!feof(rc)||argv[argc]){
X   while(chp=argv[argc]){	/* interpret command line specs first */
X      argc++;strcpy(buf2,chp);
X      if(chp=strchr(buf2,'=')){
X         chp++;goto argenv;}}
X   if(tscrc(" %1[:]",flags)){	/* check for a recipe */
X      skipblanks();i=sh=1;
X      if(tscrc("%[0-9]",buf2)){
X         sscanf(buf2,"%d",&sh);skipblanks();}
X      *flags='\0';scrc("%9[HBIhbfcws]",flags);skipblanks();
X      if(tolock)	/* clear temporary buffer for lockfile name */
X         free(tolock);
X      tolock=0;
X      if((locknext=(tscrc("%1[:]",buf)))&&
X         (skipblanks(),tscrc("%[^ \t\n#]",buf)))
X         tolock=strdup(buf);
X      startchar=themail;tobesent=thebody-themail;
X      if(strchr(flags,'B'))	/* what needs to be piped into grep? */
X         if(strchr(flags,'H'))
X            tobesent=filled;
X         else{
X            startchar=thebody;tobesent=filled-tobesent;}
X      while(sh--){	/* any conditions (left) */
X         skiptoeol();scrc("%[^\n]",buf2);
X         if(!strncmp(buf2,TOkey,TOkeylen))
X            cat(TOsubstitute,buf2+TOkeylen);
X         else
X            strcpy(buf,buf2);
X         if(i)		/* check out all conditions */
X            i=!grepin(buf,startchar,tobesent,!strchr(flags,'I'));}
X      startchar=themail;tobesent=filled;  /* body, header or both? */
X      if(strchr(flags,'h')&&!strchr(flags,'b'))
X         tobesent=thebody-themail;
X      else if(strchr(flags,'b'))
X         tobesent-=(startchar=thebody)-themail;
X      chp=buf+strlen(cat(getenv(sendmail)," "));sh=0;
X      pwait=!!strchr(flags,'w');secur=!!strchr(flags,'s');
X      if(tscrc(" ! %[^\n]",chp)){	/* forward the mail */
X         if(i)
X            goto forward;}
X      else if(tscrc("| %[^\n]",buf2)){	/* pipe the mail */
X         if(i){
X            if(sh=!!strpbrk(buf2,getenv(shellmeta)))
X               strcpy(buf,buf2);
X            else
X               parse();
X            chp=buf;*buf2='\0';
X            while(i=*chp)  /* find the implicit lockfile name ('>>name') */
X              if(chp++,i=='>'&&*chp=='>'){
X                 while((i=*++chp)==' '||i=='\t');
X                 sscanf(chp,"%[^ \t\n#'\");|<>]",buf2);break;}
X            lcllock();
X            if(strchr(flags,'f')){
X               if(startchar==themail&&tobesent!=filled){ /* if only 'h' */
X                  char*dest;long dfilled=0;
X                  if(pipthrough(buf,startchar,tobesent))
X                     goto goon;
X                  dest=readdyn(tmalloc(1),&dfilled);filled-=tobesent;
X                  if(tobesent<dfilled)  /* adjust buffer size (only bigger) */
X                     themail=trealloc(themail,(t_buf)(dfilled+filled));
X                  memmove(themail+dfilled,themail+tobesent,(t_buf)filled);
X                  memmove(themail,dest,(t_buf)dfilled);free(dest);
X                  themail=trealloc(themail,(t_buf)(filled+=dfilled));
X                  goto onlyhead;}	/* and determine the header again */
X               rcoffset=ftell(rc);  /* needed because we have to fclose it */
X               if(pipthrough(buf,startchar,tobesent))
X                  goto goon;
X               filled=startchar-themail;goto changedmail;}
Xforward:    if(!pipin(buf,startchar,tobesent)&&!strchr(flags,'c'))
X               goto mailed;}}
X      else{				/* append the mail to a file */
X         scrc("%s",buf2);skipcomment();
X         if(i){
X            parse();strcpy(buf2,buf);lcllock();
X            if(!dump(opena(buf),startchar,tobesent)&&!strchr(flags,'c'))
X               goto mailed;}}}
X   else if(tscrc("%[A-Z_a-z0-9] = ",buf)){ /* then it must be an assignment */
X      *(chp=buf+strlen(buf))='=';*++chp='\0';scrc("%[^ \t\n#]",chp);
X      skipcomment();strcpy(buf2,buf);
Xargenv:
X      parse();putenv(strdup(buf));chp[-1]='\0';
X      if(!strcmp(buf,maildir))
X         chdir(chp);
X      else if(!strcmp(buf,logfile))
X         fdreopena(chp,STDERR);
X      else if(!strcmp(buf,lockfile))
X         lockit(chp,&globlock);
X      else if(!strcmp(buf,eumask)){
X         sscanf(chp,"%o",&i);umask(i);}
X      else if(!strcmp(buf,host)){
X         *buf2='\0';gethostname(buf2,BFSIZ);
X         if(strcmp(chp,buf2)){
X            if(nextrcfile()){
X               fclose(rc);rcoffset=0;goto changerc;}
X            retval=0;terminate();}}}
X   else if(!skipcomment()){	/* either comment or garbage */
X      scrc("%[^\n] ",buf);log("Skipped: \"");log(buf);logqnl();}}
X if(dump(opena(chp=getenv(defaultf)),themail,filled)){	/* default maildest */
X   writeerr(chp);	/* if it fails, don't panic, try the last resort */
X   if(dump(opena(chp=getenv(orgmail)),themail,filled))
X      writeerr(chp);goto mailerr;}	/* now you can panic */
Xmailed:
X retval=0;	/* we're home free, mail delivered */
Xmailerr:
X unlock(&loclock);	/* any local lock file still around? */
X do{
X   chp=buf-1;
X   while(themail<thebody&&chp<buf+BFSIZ-1&&(*++chp=*themail++)!='\n');
X   if(chp<buf)
X      chp++;
X   *chp='\0';
X   if(0<sscanf(buf,"From%*1[^:]%[^\n]",buf2)){
X      log("From ");goto foundsorf;}
X   else if(0<sscanf(buf,"Subject:%[^\n]",buf2)){
X      log(" Subject:");
Xfoundsorf:
X      log(buf2);log(newline);}}  /* log it's arrival */
X while(themail<thebody);
X terminate();}
X
Xsetdef(name,contents)char*name,*contents;{
X strcat(strcat(strcpy(buf2,name),"="),contents);parse();putenv(strdup(buf));}
X
Xskipblanks(){
X fscanf(rc,"%*[ \t]");}
X
Xskiptoeol(){
X fscanf(rc,"%*[^\n]");return fgetc(rc);}
X
Xskipcomment(){char i[2];	/* no comment :-) */
X if(tscrc("%1[\n]",i))
X   return 1;
X skipblanks();return scrc("%1[#]",i)?skiptoeol():feof(rc);}
X
Xpipthrough(line,source,len)char*line,*source;long len;{pid_t pidf,pid;
X int pinfd[2],poutfd[2];
X#define PWR2 (pinfd[1])
X#define PRD2 (pinfd[0])
X pipe(poutfd);pipe(pinfd);
X if(!(pidf=sfork())){	/* the filter is started here */
X   rclose(PWRITE);redirect(PREAD);rclose(STDOUT);dup(PWR2);rclose(PWR2);
X   rclose(PRD2);callnewprog(line);}
X rclose(PREAD);rclose(PWR2);
X if(forkerr(pidf,line)){
X   rclose(PRD2);return 1;}
X flaggerd=0;signal(SIGHUP,flagger);
X if(!(pid=sfork())){   /* this process will read back the filtered mail */
X   rclose(PWRITE);signal(SIGHUP,flagger);kill(getppid(),SIGHUP);redirect(PRD2);
X   if(!pwait&&mother){  /* if the wait chain ends here, notify mother */
X      kill(mother,SIGHUP);mother=0;}  /* everything ok mam, go ahead and die */
X   return 0;}	/* we will go ahead however (with or without mother) */
X rclose(PRD2);
X if(forkerr(pid,"sortmail")){
X   kill(pidf,SIGTERM);return 1;}
X if(dump(PWRITE,source,len)){
X   kill(pidf,SIGTERM);kill(pid,SIGTERM);writeerr(line);return 1;}
X waitflagger();verrgrandchild=flaggerd=0;signal(SIGQUIT,errgrandchild);
X if(pwait){
X   if(waitfor(pidf)){  /* if the pipe fails, we will continue ourselves */
X      progerr(line);kill(pid,SIGTERM);return 1;}
X   for(kill(pid,SIGHUP),loclock=0;;sleep(SUSPENSION)){
X      if(flaggerd)
X         break;
X      if(verrgrandchild){
X         signal(SIGQUIT,terminate);return 1;}}}
X else
X   kill(pid,SIGHUP);  /* go ahead child, you have control */
X exit(0);}
X
Xwaitflagger(){
X while(!flaggerd)
X   sleep(SUSPENSION);}
X
Xgrepin(expr,source,len,casesens)char*expr,*source;long len;{pid_t pid;
X int i,poutfd[2];static char*newargv[5]={0,"-si","-e"};
X newargv[1][2]=casesens?'\0':'i';*newargv=getenv(grep);newargv[3]=expr;
X pipe(poutfd);
X if(!(pid=sfork())){
X   rclose(PWRITE);redirect(PREAD);shexec(newargv);}
X rclose(PREAD);dump(PWRITE,source,len);
X if(!forkerr(pid,*newargv))
X    return waitfor(pid);
X return 127;}
X
Xwaitfor(pid)pid_t pid;{int i;
X while(pid!=wait(&i)||(i&127)==127);
X return i>>8&255;}
X
Xredirect(pip){
X getstdinpipe(pip);fclose(rc);}
X
Xgetstdinpipe(pip){
X rclose(STDIN);dup(pip);rclose(pip);}
X
Xcallnewprog(newname)char*newname;{int argc;char*endp,**newargv;
X register char*p;
X p=newname;
X if(sh){char*newargv[4];	/* should we start a shell? */
X   newargv[3]=0;newargv[2]=p;newargv[1]=(p=getenv(shellflags))?p:"-c";
X   *newargv=getenv(shell);shexec(newargv);}
X argc=2;
X while(*p){  /* If no shell, we'll have to chop up the arguments ourselves */
X   if(*p==' '||*p=='\t'){
X      argc++;*p='\0';
X      while(*++p==' '||*p=='\t')
X         *p='\0';
X      continue;}
X   p++;}
X endp=p;*(newargv=tmalloc(argc*sizeof*newargv))=p=newname;argc=1;
X for(;;){
X   while(*p)
X      p++;
X   while(!*p){
X      if(p==endp){
X         newargv[argc]=0;shexec(newargv);}
X      p++;}
X   newargv[argc++]=p;}}
X
Xparse(){int i;char*org,*dest,*bd;   /* Implicitly copies from buf2 to buf */
X dest=buf;org=buf2;
X while(i=*org++)
X   if(i!='$')
X      *dest++=i;
X   else{	/* substitute the thing... */
X      bd=buf2;
X      while((i=*org)>='A'&&i<='Z'||i>='a'&&i<='z'||i>='0'&&i<='9'||i=='_'){
X         *bd++=i;org++;}
X      *bd='\0';
X      if(bd=getenv(buf2)){
X         strcpy(dest,bd);
X         while(*dest)
X            dest++;}}
X *dest='\0';}
X
Xwriteerr(line)char*line;{
X log("Error while writing to \"");log(line);logqnl();}
X
Xforkerr(pid,a)pid_t pid;char*a;{
X if(pid==-1){
X   log("Failed forking \"");log(a);logqnl();return 1;}
X return 0;}
X
Xprogerr(line)char*line;{
X log("Program failure of \"");log(line);logqnl();}
X
Xlog(a)char*a;{char*b;
X b=a-1;
X while(*++b);
X rwrite(STDERR,a,b-a);}
X
Xopena(a)char*a;{
Xreturn open(a,O_WRONLY|O_APPEND|O_CREAT,0666);}
X
Xfdreopena(a,fd)char*a;{  /* Currently only works for 0,1,2 */
X rclose(fd);return opena(a);}
X
Xshexec(argv)char**argv;{int i;char**newargv,**p;
X execvp(*argv,argv);	/* if this one fails, we retry it as a shell script */
X for(p=argv,i=1;i++,*p++;);
X newargv=tmalloc(i*sizeof*p);
X for(*(p=newargv)=binsh;*++p=*++argv;);
X execve(*newargv,newargv,environ);	/* no shell script? -> trouble */
X log("Failed to execute \"");log(*argv);logqnl();exit(1);}
X
Xunlock(lockp)char**lockp;{
X locking=1;
X if(*lockp){
X   if(unlink(*lockp)){
X      log("Couldn't unlink \"");log(*lockp);logqnl();}
X   free(*lockp);*lockp=0;}
X locking=0;}
X
Xnomemerr(){
X log("Out of memory\nbuffer 0: \"");log(buf);log("\"\nbuffer 1: \"");
X log(buf2);logqnl();retval=1;terminate();}
X
Xlogqnl(){
X log("\"\n");}
X
Xnextrcfile(){char*p;   /* find the next rcfile specified on the command line */
X while(p=*gargv){
X   gargv++;
X   if(!strchr(p,'=')){
X      rcfile=p;return 1;}}
X return 0;}
X
Xrclose(fd){int i;	/* a sysV secure close (signal immune) */
X while((i=close(fd))&&errno==EINTR);
X return i;}
X
Xrwrite(fd,a,len)void*a;{int i;	/* a sysV secure write (signal immune) */
X while(0>(i=write(fd,a,len))&&errno==EINTR);
X return i;}
X
Xlockit(name,lockp)char*name,**lockp;{int i;char*p;
X unlock(lockp);
X for(locking=1;;){
X   if(0<=(i=open(name,O_WRONLY|O_CREAT|O_EXCL|O_SYNC,0))){
X      rclose(i);*lockp=strdup(name);
Xterm: locking=0;
X      if(nextexit)
X         terminate();
X      return;}
X   if(errno==ENAMETOOLONG){	/* if too long, make it shorter and retry */
X      if(0<(i=strlen(name)-1)){
X         name[i]='\0';continue;}
X      log("Lockfailure\n");return;}
X   i=DEFlocksleep;
X   if(p=getenv(locksleep))
X      sscanf(p,"%i",&i);
X   sleep(i);
X   if(nextexit)
X      goto term;}}
X
Xlcllock(){	/* lock a local file (if need be) */
X if(locknext)
X   if(tolock)
X      lockit(tolock,&loclock);
X   else
X      lockit(strcat(buf2,getenv(lockext)),&loclock);}
X
X/* That's all folks */
SHAR_EOF
chmod 0644 procmail.c ||
echo 'restore of procmail.c failed'
Wc_c="`wc -c < 'procmail.c'`"
test 19371 -eq "$Wc_c" ||
	echo 'procmail.c: original size 19371, current size' "$Wc_c"
fi
# ============= procmailrc ==============
if test -f 'procmailrc' -a X"$1" != X"-c"; then
	echo 'x - skipping procmailrc (File already exists)'
else
echo 'x - extracting procmailrc (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'procmailrc' &&
XPATH=$PATH:$HOME/bin:/usr/bin:/global/bin:/usr/ucb:/bin
XMAILDIR=$HOME/mail	# You'd better make sure it exists
XDEFAULT=$MAILDIR/mbox
XLOGFILE=$MAILDIR/from
XLOCKFILE=$HOME/.lockmail
X
X::
X^From.*thf
Xtodd
X
X:2fh
X^From root
X^Subject: Output from "at" job
X|echo "From at job";echo;egrep "^Date:"
X:b
X^From at job
Xatjunk
X
SHAR_EOF
chmod 0644 procmailrc ||
echo 'restore of procmailrc failed'
Wc_c="`wc -c < 'procmailrc'`"
test 311 -eq "$Wc_c" ||
	echo 'procmailrc: original size 311, current size' "$Wc_c"
fi
# ============= rmail ==============
if test -f 'rmail' -a X"$1" != X"-c"; then
	echo 'x - skipping rmail (File already exists)'
else
echo 'x - extracting rmail (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'rmail' &&
X#!/bin/sh
X#
X# specify the mailbox file you want to read on the command line
X#
Xcd $HOME/mail
XLOCKFILE=$HOME/.lockmail
Xif lockfile -! -r1 $LOCKFILE
Xthen
X echo Mail is currently arriving, please wait...
X while
X   lockfile -! -4 -r2 $LOCKFILE
X do
X echo Mail is still arriving...
X done
Xfi
X#
X# Call you favourite mailer here.
X#
X/usr/ucb/mail -f $*
X#
X#
X#
Xrm -f $LOCKFILE
SHAR_EOF
chmod 0644 rmail ||
echo 'restore of rmail failed'
Wc_c="`wc -c < 'rmail'`"
test 364 -eq "$Wc_c" ||
	echo 'rmail: original size 364, current size' "$Wc_c"
fi
exit 0
-- 
Sincerely,                 berg%cip-s01.informatik.rwth-aachen.de@unido.bitnet
           Stephen R. van den Berg.
"I code it in 5 min, optimize it in 90 min, because it's so well optimized:
it runs in only 5 min.  Actually, most of the time I optimize programs."