home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume25
/
procmail
/
part02
< prev
next >
Wrap
Text File
|
1991-11-03
|
55KB
|
1,681 lines
Newsgroups: comp.sources.misc
From: berg@messua.informatik.rwth-aachen.de (Stephen R. van den Berg)
Subject: v25i002: procmail - mail processing program v2.31, Part02/04
Message-ID: <1991Nov3.230004.9816@sparky.imd.sterling.com>
X-Md4-Signature: 7bdbc968ca0a90061d1344ebc28bccb8
Date: Sun, 3 Nov 1991 23:00:04 GMT
Approved: kent@sparky.imd.sterling.com
Submitted-by: berg@messua.informatik.rwth-aachen.de (Stephen R. van den Berg)
Posting-number: Volume 25, Issue 2
Archive-name: procmail/part02
Environment: UNIX, sendmail, smail, MMDF
Supersedes: procmail2.10: Volume 20, Issue 89-91
#! /bin/sh
# This is a shell archive. Remove anything before this line, then feed it
# into a shell via "sh file" or similar. To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# Contents: procmail/README procmail/examples/mailinglist
# procmail/formail.c procmail/regexp.c procmail/retint.c
# Wrapped by kent@sparky on Sun Nov 3 16:50:05 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo ' "shar: End of archive 2 (of 4)."'
if test -f 'procmail/README' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'procmail/README'\"
else
echo shar: Extracting \"'procmail/README'\" \(3992 characters\)
sed "s/^X//" >'procmail/README' <<'END_OF_FILE'
XProcmail & formail mail processing package.
XCopyright (c) 1990-1991, S.R.van den Berg, The Netherlands.
X
XSome legal stuff:
X
XUse this software package at your own risk. The programmer can not
Xbe held liable for any incurred damages, directly or indirectly due to
Xthe use of this software.
X
XYou are encouraged to distribute this package freely. This package is
Xhowever not to be 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 intact. You are allowed to
Xtake parts from this distribution and distribute these separately as long
Xas you retain the copyright messages. If you redistribute any part of this
Xpackage in a modified form, be sure to mark the parts you changed.
XIf you have some important changes that might be usefull to the rest of the
Xworld, contact me instead.
X
X-------------------------- SYSTEM REQUIREMENTS -------------------------------
X
XAny *NIX-alike system.
X
XSendmail, smail, MMDF or compatible mailers.
X
XThe most important system calls that need to be supported (among others):
Xdup(),wait(),getpwent(),fork(),pipe()
X
XFor a more complete list of all library references see "includes.h"
X
X------------------------------ DESCRIPTION -----------------------------------
X
XThe procmail mail processing program. (v2.31 1991/10/22)
X
XCan be used to create mail-servers, mailing lists, sort your incoming mail
Xinto separate folders/files (real convenient when subscribing to one or more
Xmailing lists), preprocess your mail, or selectively forward certain incoming
Xmail automatically to someone.
X
XThe accompanying formail program enables you to generate autoreplies, split up
Xdigests/mailboxes into the original messages, do some very simple
Xheader-munging, or force mail into mail-format (with leading From line).
X
XFor installation instructions see the INSTALL file.
X
X----------------------
X
XI made the utmost effort to make procmail as robust as any program can be
X(every conceivable system error is caught *and* handled).
X
XProcmail was designed to deliver the mail under the worst conditions
X(file system full, out of swap space, process table full, file table full,
Xmissing support files, unavailable 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 or reenter the mailqueue
X(your choice).
X
XFor a more extensive list of features see the FEATURES file.
X
X----------------------
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 find any
Xbugs (highly unlikely :-), I would be pleased (well, sort of :-) to hear
Xabout 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(BTW, if you should find any spelling or grammar errors in these files,
Xit's not priority one, but if you were sending me mail anyway, don't hesitate
Xto point them out to me; I like correct English just as much as you do).
X
XPlease note that this program essentially is supposed to be static, that
Xmeans no extra features (honouring the VNIX spirit) are supposed to be
Xadded (though any usefull suggestions will be appreciated and evaluated if
Xtime permits).
X
XCheers,
X Stephen R. van den Berg at RWTH-Aachen, Germany.
X
XInternet E-mail: berg@messua.informatik.rwth-aachen.de
X berg@physik.tu-muenchen.de
X
XOr: P.O.Box 21074
X 6369 ZG Simpelveld
X The Netherlands
X
X----------------------
XA recent version can be picked up at various comp.sources.misc archives.
XThe latest version can be obtained directly from the ftp-archive at:
X
X amaru.informatik.rwth-aachen.de (137.226.112.31)
X
X as compressed tar file: pub/unix/procmail.tar.Z
X or in compressed shar format: pub/unix/procmail.0?.Z
X----------------------
END_OF_FILE
if test 3992 -ne `wc -c <'procmail/README'`; then
echo shar: \"'procmail/README'\" unpacked with wrong size!
fi
# end of 'procmail/README'
fi
if test -f 'procmail/examples/mailinglist' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'procmail/examples/mailinglist'\"
else
echo shar: Extracting \"'procmail/examples/mailinglist'\" \(11152 characters\)
sed "s/^X//" >'procmail/examples/mailinglist' <<'END_OF_FILE'
X$Id: mailinglist,v 2.3 1991/10/18 15:39:20 berg Rel $
X
X How to set up mailing lists
X ---------------------------
X
X Written by Stephen R. van den Berg.
X berg@messua.informatik.rwth-aachen.de
X berg@physik.tu-muenchen.de
X
XThis document mainly describes a sendmail environment, much of it applies
Xto non-sendmail mail agents as well.
X
X
XContents:
X--------- 1. Intro
X 2. Bouncing mail
X 3. The disadvantages
X 4. How to circumvent these disadvantages
X 5. Why use procmail to filter the mailinglist mail?
X 6. How do I use procmail to filter the mailinglist mail?
X 7. Now, what does the above all do?
X 8. The result of this exercise
X
X1. Intro
X -----
X
XThe simplest and most direct way to do it is by insert a line in
Xthe /usr/lib/aliases file looking like:
X
Xmylist: fred,john, wilma, barney@bedrock, pebbles
X
XNow all the mail arriving at your machine for "mylist" (either local or
Xmylist@your.domain) will be automatically forwarded to all the mentioned
Xaddresses (fred, john, etc.).
X
XThe address mylist@your.domain is intended for submissions to the list that
Xare supposed to be forwarded to all the subscribers. For the administrative
Xtasks like removals from the list, new subscribtions to the list, or address
Xchanges of subscribers one should create a second entry in the /usr/lib/aliases
Xfile:
X
Xmylist-request: your_login_name@your.domain
X
X
X2. Bouncing mail
X -------------
X
XIn order to deal with bouncing mail gracefully, an extra precaution should
Xbe taken. If for example mail to wilma bounces (user non-existent, mail
Xfilesystem full, etc.), it will bounce back to the original sender.
XNow, the only person that should be concerned with distribution failures
Xshould be the mylist-request holder. Therefore you should be using a
Xsendmail special alias like:
X
Xowner-mylist: mylist-request@your.domain
X
XThis way local mail will bounce back to mylist-request@your.domain.
X
X
X3. The disadvantages
X -----------------
X
XIf you are using the above methods, some obvious disadvantages come to mind
Xhowever:
X
Xa. The subscriber list can not exceed 1000 bytes (on most sendmails).
Xb. The subscriber list can not be changed on-the-fly (/usr/lib/aliases needs
X to be edited, and newaliases has to be run).
Xc. People can not be prevented from submitting messages like "Please remove
X me from this mailinglist" to mylist (and thereby annoying all subscribers).
Xd. People can not be guarded from themselves in case they insert
X "Return-Receipt-To:" fields in their headers (if they are particularly
X unlucky, they will receive an acknowledge mail from *every* subscriber's
X sendmail).
Xe. People including "Errors-To:" or "Sender:" fields can cause the bounce
X messages to bypass owner-mylist anyway.
Xf. There is no way of limiting the number of submitters, i.e. every person
X who knows the name of the mailing list and who can send mail to your.domain
X is able to submit messages to the list. This means, for example, that you
X can not limit a mailing list to local users (i.e. only local users can
X submit).
Xg. You are unable to insert a "Reply-To: mylist@your.domain" in case you
X would want to (this makes replying to the list easier).
X
X
X4. How to circumvent these disadvantages
X -------------------------------------
X
Xa. Can be circumvented by using nested aliases like:
X mylist: mylist1, mylist2
X mylist1: fred,john
X mylist2: wilma,barney@bedrock,pebbles
X This can however, become extremely messy to maintain.
X
Xb. This can partly be avoided if you use aliases like:
X mylist: :input:/path/to/the/memberfile
X The memberfile should contain:
X fred,john,wilma,barney@bedrock,pebbles
X You can not avoid using newaliases however, and *will* get extremely messy
X if you have to start using nested aliases.
X
Xc. Can only be taken care of by using a mailfilter like procmail.
X
Xd. Can only be taken care of by using a mailfilter like procmail.
X
Xe. Can only be taken care of by using a mailfilter like procmail.
X
Xf. Can only be taken care of by using a mailfilter like procmail.
X
Xh. Can only be taken care of by using a mailfilter like procmail.
X
X
X5. Why use procmail to filter the mailinglist mail?
X ------------------------------------------------
X
XInstead of using a mailfilter you could also take care of most of the problems
Xthree till seven by editing the sendmail.cf file. I strongly would recommend
Xagainst this approach however, since this will be too much of a customizing
Xoperation and surely will not be a trivial task (in all cases). As a general
Xrule: don't mess with a sendmail.cf file once it is working :-).
X
XNow, you could, instead of procmail, simply use immediate VNIX commands
Xlike grep, sed, awk to do the mail filtering. Again, there are some obvious
Xdisadvantages with this approach:
X
XA. In case any system resources go out (no more file descriptors, no more
X swap space, process table full, file system full (for temporary files))
X your awk or shell script will fail generously (i.e. several bad things
X could happen: mail munged, truncated, lost, hanging awk or sh programs,
X etc., you get the picture).
X
XB. All mail headers (including From: and Reply-To:) could very well be
X multi-line headers; it will be very difficult to make it understandable
X to awk that somehow the header line could continue on the next line
X (in case you want to remove a header, or do some complicated substitution).
X
XC. Another hairy problem will be determining the end of the header, of course
X that is solvable, but you have to make some extra precautions in your
X awk script to ensure that any substitutions/changes will not occur in
X the body of the message.
X
Xprocmail does not *directly* allow you to change any headers, but that
Xfeature is not really necessary since you can tell procmail to send ONLY the
Xheader through some filter of your choice.
X
XTo comment on the previously mentioned three disadvantages:
X
XA. procmail takes care of that. Should the filter have problems anyway,
X procmail will graciously notice that the filter was in some kind of
X trouble, and will try something else with the original unmunged mail
X (you can specify what it should do of course, obvious choices: try
X the same filter again, drop the mail in a file and send you a notice,
X forward the mail to you instead (unfiltered), etc.)
X
XB. procmail will concatenate any headers that were continued according to
X the RCF 822 recommendations, i.e. your filters will see one line per header.
X
XC. procmail can be told to send the header, the body or both through the
X filter, hence your filter need not watch out to avoid doing any
X substitutions in the body, and the filter can therefore be a lot simpler.
X
XProcmail has some additional advantages too:
X
X -- It will probably all go a bit faster, since only the header of the mail
X is being piped through the filter. Also, procmail reads in the mail in
X 16KB chunks, not line by line as sed does.
X
X -- You could use procmail to filter out any messages to the normal mailing
X list that should have gone to the mylist-request and remail them to
X mylist-request.
X
XWell, anyway, as you see, procmail does not give you everything you would want,
Xbut this was intentional in accordance to the true VNIX spirit (modularity).
XWhat procmail does provide is a *very* reliable hook (you might say it
Xprovides an anchor :-) for any mail processing you might do. For the more
Xcomplex things you still have to use shell scripts or call other programs
Xfrom within procmail, but then again, that saves you from learning any
Xparticular syntax procmail would have had to do the same.
X
XAs it happens, the accompanying formail program is able to cater to most
X(if not all) of your needs regarding mail munging.
X
X
X6. How do I use procmail to filter the mailinglist mail?
X -----------------------------------------------------
X
XFirst you have to create these two entries in your /usr/lib/aliases file of
Xmachine "your.domain" (the mylist: line should be typed in as one long line):
X
Xmylist: "|IFS=' ';exec /usr/local/bin/procmail /some/path/listrc subscribers=/some/path/memberlist list=mylist@your.domain listreq=mylist-request@your.domain"
Xmylist-request: your_login_name@your.domain
Xowner-mylist: mylist-request
X
XCreate a file named /some/path/memberlist which contains the names of the
Xsubscribers separated by whitespace (blanks, tabs or newlines) like:
X
X fred john wilma barney@bedrock pebbles
X
XThe /some/path/listrc file you should look like the sample listrc file
Xsupplied in this directory. This listrc file need only be present once,
Xit will cater for all the mailinglists you like to create.
X
X
X7. Now, what does the above all do?
X --------------------------------
X
XIf mail arrives at mylist, first of all procmail will be started using
X/some/path/listrc as the rcfile. Then it will grep the header to check if
Xit could be a bounced message after all (from postmaster or mailer-daemon),
Xor if it probably is a request message. If neither applies, procmail will
Xfilter just the header of the message through formail.
X
Xformail will remove any "Return-Receipt-To:" fields, and will provide plain
Xsubstitutes for "Errors-To:" and "Sender:". Then it will look for
Xany "Reply-To:" fields which are already in the header and rewrite them
Xas "Old-Reply-To:"; after having done this it will add your "Reply-To:"
Xfield. BTW, the "Return-Receipt-To:" and "Errors-To:" fields are not
Xrecommended by the RFC-822, they are however commonly supported by most
Xsendmails; if they are not supported however, they won't hurt, they will
Xsimply be ignored.
X
XThen, the mail is piped into $SENDMAIL which receives, as command line
Xarguments, the addresses of all subscribers. The option -f will only
Xtake effect if sendmail is running under daemon privileges; this only
Xoccurs if the sender of the mail is *not* a local user; if the sender
Xis a local user, then sendmail (and procmail) runs as the local user.
X
X*********************************** WARNING **********************************
X* *
X* For this reason it might be wise to allow writing of the memberlist file *
X* only (to a list maintainer), keep the listrc file under *root supervision* *
X* (i.e. owned by a very reliable person (e.g. root), world readable, but NOT *
X* world writeable). *
X* *
X******************************************************************************
X
X
X8. The result of this exercise
X ---------------------------
X
XAs you can see, we have addressed and solved every single one of the original
Xseven problems (well, ok, except problem f, that one is left as an excercise
Xto the reader; shouldn't be too difficult).
X
X
XP.S. Any suggestions/corrections/improvements on this document are welcome.
END_OF_FILE
if test 11152 -ne `wc -c <'procmail/examples/mailinglist'`; then
echo shar: \"'procmail/examples/mailinglist'\" unpacked with wrong size!
fi
# end of 'procmail/examples/mailinglist'
fi
if test -f 'procmail/formail.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'procmail/formail.c'\"
else
echo shar: Extracting \"'procmail/formail.c'\" \(12561 characters\)
sed "s/^X//" >'procmail/formail.c' <<'END_OF_FILE'
X/************************************************************************
X * formail.c a mail (re)formatter *
X * *
X * Seems to be relatively bug free. *
X * *
X * Copyright (c) 1990-1991, S.R.van den Berg, The Netherlands *
X * The sources can be freely copied for non-commercial use. *
X * #include "README" *
X * *
X ************************************************************************/
X#ifdef RCS
Xstatic char rcsid[]="$Id: formail.c,v 2.11 1991/10/18 15:33:23 berg Rel $";
X#endif
Xstatic char rcsdate[]="$Date: 1991/10/18 15:33:23 $";
X#include "config.h" /* slight overkill */
X#include "includes.h"
X
Xchar*pstrspn();
X
X#define BSIZE 4096
X
X#define NAMEPREFIX "formail: "
X#define HEAD_DELIMITER ':'
X
X#define Re (re+1)
X#define Nextchar(x) do{x=getchar();if(feof(stdin))goto foundeof;}while(0)
X#define putssn(a,l) tputssn(a,(size_t)(l))
X#define putcs(a) (errout=putc(a,mystdout))
X#define PRDO poutfd[0]
X#define PWRO poutfd[1]
X
Xstatic const char From[]=FROM,replyto[]="Reply-To:",Fromm[]="From:",
X returnpath[]="Return-Path",sender[]="Sender:",outofmem[]="Out of memory\n",
X subject[]="Subject:",re[]=" Re:",couldntw[]="Couldn't write to stdout",
X references[]="References:",messageid[]="Message-ID:",Date[]="Date:",
X article[]="Article ",Path[]="Path:",Received[]="Received:",To[]="To: ",
X OldP[]=OLD_PREFIX,inreplyto[]="In-Reply-To:",errorsto[]="Errors-To",
X retreceiptto[]="Return-Receipt-To";
Xconst char binsh[]=BinSh;
X/*
X * sender determination fields in order of importance reliability
X * reply-address determination fields (wrepl specifies the weight)
X */
Xstatic const struct {const char*head;int len,wrepl;}sest[]=
X{ {errorsto,STRLEN(errorsto),5},{retreceiptto,STRLEN(retreceiptto),6},
X {sender,STRLEN(sender),0},{replyto,STRLEN(replyto),4},
X {Fromm,STRLEN(Fromm),2},{returnpath,STRLEN(returnpath),1}
X};
X/*
X * digest splitting fields
X */
Xstatic const struct {const char*hedr;int lnr;}cdigest[]=
X{ {Fromm,STRLEN(Fromm)},{Date,STRLEN(Date)},{subject,STRLEN(subject)},
X {article,STRLEN(article)},{Path,STRLEN(Path)},{Received,STRLEN(Received)}
X};
X
Xstatic struct {const char*const headr;const int lenr;char*rexp;}rex[]=
X{ {subject,STRLEN(subject)},{references,STRLEN(references)},
X {messageid,STRLEN(messageid)}
X};
X#define subj rex[0]
X#define refr rex[1]
X#define msid rex[2]
X#define mxl(a,b) mx(STRLEN(a),STRLEN(b))
X#ifndef MAILBOX_SEPARATOR
X#define dig_HDR_LEN mx(mxl(From,Fromm),mxl(Date,subject))
X#define mboxseparator From
X#define flushseparator(i,p)
X#else
Xstatic const char mboxseparator[]=MAILBOX_SEPARATOR;
X#define flushseparator(i,p) \
X do{i=p;p=0;do{int x;Nextchar(x);}while(--i);}while(0)
X#define dig_HDR_LEN \
X mx(mx(mxl(From,Fromm),mxl(Date,subject)),STRLEN(mboxseparator))
X#endif
Xstatic struct hedit{char*hline;long hlen;struct hedit*next;}*hlist;
Xstatic errout,oldstdout,quiet;
Xstatic pid_t child= -1;
Xstatic FILE*mystdout;
Xstatic size_t nrskip,nrtotal= -1;
X
X#ifdef NOstrstr
Xchar*strstr(whole,part)const char*whole,*const part;
X{ register const char*w,*p;
X do
X { w=whole;p=part;
X do
X if(!*p)
X return(char*)whole;
X while(*w++==*p++);}
X while(*whole++);
X return(char*)0;
X}
X#endif
X
Xvoid*tmalloc(len)const size_t len;
X{ void*p;
X if(p=malloc(len))
X return p;
X nlog(outofmem);exit(EX_OSERR);
X}
X
Xvoid*trealloc(old,len)void*old;const size_t len;
X{ if(old=realloc(old,len))
X return old;
X nlog(outofmem);exit(EX_OSERR);
X}
X
X#include "shell.h"
X
Xstruct hedit*overrideh(target)const char*const target;
X{ const struct hedit*hlp;size_t len;
X for(hlp=hlist;hlp;hlp=hlp->next)
X { len=hlp->hlen;
X if(hlp->hlen<0)
X len= -hlp->hlen; /* absolute */
X if(!strnicmp(hlp->hline,target,len)) /* header found */
X return (struct hedit*)hlp;
X }
X return(struct hedit*)0; /* no header found */
X}
X
Xmain(lastm,argv)const char*const argv[];
X{ int i,ch,nowm,split=0,force=0,bogus=1,every=0,areply=0,trust=0,digest=0,
X nowait=0,keepb=0;
X size_t buflen,p=0,lnl=0,ll;time_t t;char*buf,*chp,*namep;struct hedit*hlp;
X while(chp=(char*)*++argv)
X { if((lastm= *chp++)==FM_SKIP)
X goto number;
X else if(lastm!=FM_TOTAL)
X goto usg;
X for(;;)
X { switch(lastm= *chp++)
X { case FM_TRUST:trust=1;continue;
X case FM_REPLY:areply=1;continue;
X case FM_FORCE:force=1;continue;
X case FM_EVERY:every=1;bogus=0;continue;
X case FM_DIGEST:digest=1;continue;
X case FM_NOWAIT:nowait=1;continue;
X case FM_KEEPB:keepb=1;continue;
X case FM_QUIET:quiet=1;continue;
X case FM_SPLIT:split=1;
X if(!*chp&&*++argv)
X goto parsedoptions;
X goto usg;
Xnumber: default:
X if(*chp-'0'>(unsigned)9)
X {
Xusg: log(FM_USAGE);return EX_USAGE;
X }
X ll=strtol(chp,(char**)0,10);
X if(lastm==FM_SKIP)
X nrskip=ll;
X else
X nrtotal=ll;
X break;
X case FM_BOGUS:bogus=0;continue;
X case FM_REN_INSERT:case FM_DEL_INSERT:hlp=hlist;
X (hlist=malloc(sizeof*hlist))->next=hlp;
X if(!*chp&&!(chp=(char*)*++argv)) /* concatenated or seperate? */
X goto usg;
X hlist->hline=chp; /* add header string */
X if(!(buf=strchr(chp,HEAD_DELIMITER)))
X { nlog("Invalid field-name:");logqnl(chp);goto usg;
X }
X buflen=buf-chp+1;
X hlist->hlen=lastm==FM_REN_INSERT?buflen:-(long)buflen;
X case '\0':;
X }
X break;
X }
X }
Xparsedoptions:
X#ifdef MAILBOX_SEPARATOR
X if(split)
X { bogus=0;every=1;
X }
X#endif
X mystdout=stdout;signal(SIGPIPE,SIG_IGN);
X if(split)
X { oldstdout=dup(STDOUT);fclose(stdout);startprog(argv);
X }
X else if(every)
X goto usg;
X *(namep=malloc(1))='\0';buf=malloc(buflen=BSIZE);t=time((time_t*)0);
X i=maxindex(rex);
X do *(rex[i].rexp=malloc(1))='\0';
X while(i--);
X while('\n'==(ch=getchar()));
X for(;;) /* start parsing the header */
X { if((buf[p++]=ch)=='\n')
X { if(lnl==p-1) /* end of header reached */
X break;
X switch(ch=getchar()) /* concatenate continued lines */
X { case ' ':case '\t':p--;continue;
X }
X chp=buf+lnl;
X#ifdef MAILBOX_SEPARATOR
X if(!strncmp(mboxseparator,chp,STRLEN(mboxseparator)))
X { if(!lnl)
X { if(split)
X { p=0;goto redigest;
X }
X force=1; /* separator up front, don't add a 'From ' line */
X }
X else if(bogus)
X *chp=' ';
X }
X#endif
X i=maxindex(rex);
X while(strnicmp(rex[i].headr,chp,ll=rex[i].lenr)&&i--);
X if(i>=0) /* found anything already? */
X { ll=p-lnl-ll;
X ((char*)tmemmove(rex[i].rexp=realloc(rex[i].rexp,ll),
X buf+lnl+rex[i].lenr,ll))[ll-1]='\0';
X }
X else if(!strncmp(From,chp,STRLEN(From)))
X { if(!lnl) /* was the real "From " line */
X { nowm=trust?1:3/*wreply*/;ll=lnl+STRLEN(From);goto foundfrom;
X }
X#ifndef MAILBOX_SEPARATOR
X if(bogus)
X { tmemmove(chp+1,chp,p++-lnl);*chp=ESCAP; /* disarm */
X }
X#endif
X }
X else
X { i=maxindex(sest);
X do
X if(!strnicmp(sest[i].head,chp,sest[i].len))
X { nowm=areply?keepb&&sest[i].head==replyto?
X maxindex(sest)+1:sest[i].wrepl:i;
X ll=lnl+sest[i].len;
Xfoundfrom: buf[p]='\0';
X if(chp=strchr(buf+ll,'<')) /* extract the address */
X ll=chp-buf+1;
X if((i=strcspn(chp=pstrspn(buf+ll," \t"),">(\n \t"))&&
X (!*namep||nowm>lastm))
X { ((char*)tmemmove(namep=realloc(namep,i+1),chp,i))[i]='\0';
X lastm=strstr(chp,".UUCP")?nowm-maxindex(sest)-1:nowm;
X }
X break;
X }
X while(i--);
X }
X if(hlp=overrideh(buf+lnl)) /* replace or delete header? */
X { if(hlp->hlen<0) /* just delete it */
X { p=lnl;continue;
X }
X if(p+2>=buflen) /* trouble if BSIZE<STRLEN(OldP) */
X buf=realloc(buf,buflen+=BSIZE);
X tmemmove(buf+lnl+STRLEN(OldP),buf+lnl,p-lnl);
X tmemmove(buf+lnl,OldP,STRLEN(OldP));p+=STRLEN(OldP);
X }
X lnl=p;continue;
X }
X if(p>=buflen-2)
X buf=realloc(buf,buflen+=BSIZE);
Xredigest:
X ch=getchar();
X if(feof(stdin))
X ch='\n'; /* make sure the header ends with 2 newlines */
X }
X if(areply||!force&&strncmp(buf,From,STRLEN(From)))
X { if(!areply||!overrideh(To))
X { putss(areply?(areply=2),To:From);
X if(*namep) /* found any sender address? */
X putss(namep);
X else
X putss(UNKNOWN);}
X if(areply)
X { if(areply==2)
X putnl();
X if(*subj.rexp&&!overrideh(subject)) /* any Subject: ? */
X { putss(subject);chp=subj.rexp;
X if(strnicmp(pstrspn(chp," "),Re,STRLEN(Re)))
X putss(re); /* no Re: , add one ourselves */
X putss(chp);putnl();
X }
X if(*refr.rexp||*msid.rexp) /* any Message-ID: or References: ? */
X { if(!overrideh(references))
X { putss(references);
X if(*refr.rexp)
X { putss(refr.rexp);
X if(*msid.rexp)
X putnl();
X }
X if(*msid.rexp)
X { putss(msid.rexp);putnl();
X }
X }
X if(*msid.rexp&&!overrideh(inreplyto))
X { putss(inreplyto);putss(msid.rexp);putnl();
X }
X }
X }
X else
X { putcs(' ');putss(ctime(&t));
X }
X }
X if(!areply)
X putssn(buf,p-1);
X for(hlp=hlist;hlp;hlp=hlp->next)
X { ll=hlp->hlen;
X if(hlp->hlen<0)
X ll= -hlp->hlen;
X if(hlp->hline[ll])
X { putss(hlp->hline);putnl(); /* inject our new headers */
X }
X }
X putnl();
X if(areply)
X { if(!keepb)
X { if(mystdout!=stdout)
X closemine();
X opensink(); /* discard the body */
X }
X }
X p=0;lnl=1; /* clear buffer, important! */
X if(!bogus&&!split)
X for(;;putcs(i))
X Nextchar(i);
X for(;;) /* continue the quest */
X {
X do /* read line until not From */
X { if(p==buflen-1)
X buf=realloc(buf,++buflen);
X Nextchar(i=buf[p]);
X if(++p==STRLEN(mboxseparator))
X if(!strncmp(mboxseparator,buf,STRLEN(mboxseparator)))
X { if(every)
X { flushseparator(i,p);goto splitit; /* optionally flush */
X }
X else if(split&&lnl)
X lnl=2; /* mark line as possible postmark */
X else if(bogus) /* disarm */
X {
X#ifndef MAILBOX_SEPARATOR
X putcs(ESCAP);break;
X#else
X Nextchar(i);*buf=' ';putssn(buf,p);*buf=i;p=1;continue;
X#endif
X }
X }
X if((i=='\n'||p!=1)&&areply)
X { if(bogus)
X putcs(ESCAP);
X break;
X }
X if(lnl==1&&digest)
X { ll=maxindex(cdigest);
X do /* check for new digest header */
X if(p==cdigest[ll].lnr&&!strncmp(buf,cdigest[ll].hedr,p))
X { *namep=lnl=0;goto splitit; /* pretend we started over */
X }
X while(ll--);
X }
X }
X while(i!='\n'&&(lnl==2||p<dig_HDR_LEN));
X if(lnl==2)
X { buf[p]='\0'; /* perform more thorough check for postmark */
X if((ll=strcspn(chp=pstrspn(buf+STRLEN(From)," ")," \t\n"))&&
X *(chp+=ll)==' '&&(ll= *(chp=pstrspn(chp," ")))!='\t'&&ll!='\n')
X {
Xsplitit: if((fclose(mystdout)==EOF||errout==EOF)&&!quiet)
X { nlog(couldntw);log(", continuing...\n");split= -1;
X }
X if(!nowait)
X waitforit();
X startprog(argv);
X if(!lnl) /* digest split? */
X goto redigest;
X i='\n';
X }
X }
X lnl=p==1;putssn(buf,p);p=0;
X if(i!='\n')
X do Nextchar(i);
X while(putcs(i),i!='\n');
X }
Xfoundeof:
X putssn(buf,p);closemine();child= -1;waitforit(); /* wait for everyone */
X return split<0?EX_IOERR:EX_OK;
X}
X
Xlog(a)const char*const a;
X{ fputs(a,stderr);
X}
X
Xlogqnl(a)const char*a;
X{ log(" \"");log(a);log("\"\n");
X}
X
Xputss(a)const char*a;
X{ while(*a)
X putcs(*a++);
X}
X
Xtputssn(a,l)const char*a;size_t l;
X{ while(l--)
X putcs(*a++);
X}
X
Xstartprog(argv)const char*const*const argv;
X{ int poutfd[2];
X if(!nrtotal)
X goto squelch;
X if(nrskip)
X { --nrskip;
Xsquelch:
X opensink();return;
X }
X if(nrtotal>0)
X --nrtotal;
X dup(oldstdout);pipe(poutfd);
X if(!(child=fork()))
X { close(oldstdout);close(PWRO);fclose(stdin);dup(PRDO);close(PRDO);
X shexec(argv);
X }
X close(STDOUT);close(PRDO);
X if(STDOUT!=dup(PWRO)||!(mystdout=fdopen(STDOUT,"a")))
X nofild();
X close(PWRO);
X if(-1==child)
X { nlog("Can't fork\n");exit(EX_OSERR);
X }
X}
X
Xnofild()
X{ nlog("File table full\n");exit(EX_OSERR);
X}
X
Xwaitforit()
X{ int i;pid_t j;
X while(child!=(j=wait(&i))||(i&127)==127)
X if(-1==j)
X return;
X}
X
Xnlog(a)const char*const a;
X{ log(NAMEPREFIX);log(a);
X}
X
Xclosemine()
X{ if((fclose(mystdout)==EOF||errout==EOF)&&!quiet)
X { nlog(couldntw);log("\n");;exit(EX_IOERR);
X }
X}
X
Xopensink()
X{ if(!(mystdout=fopen(DevNull,"a")))
X nofild();
X}
X
Xputnl()
X{ putcs('\n');
X}
X
Xstrnicmp(a,b,l)register const char*a,*b;register unsigned l;
X{ int i,j;
X if(l) /* case insensitive strncmp */
X do
X { while(*a&&*a==*b&&--l)
X ++a,++b;
X if(!l)
X break;
X if((i= *a++)>='A'&&i<='Z')
X i+='a'-'A';
X if((j= *b++)>='A'&&j<='Z')
X j+='a'-'A';
X if(j!=i)
X return i>j?1:-1;
X }
X while(i&&j&&--l);
X return 0;
X}
END_OF_FILE
if test 12561 -ne `wc -c <'procmail/formail.c'`; then
echo shar: \"'procmail/formail.c'\" unpacked with wrong size!
fi
# end of 'procmail/formail.c'
fi
if test -f 'procmail/regexp.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'procmail/regexp.c'\"
else
echo shar: Extracting \"'procmail/regexp.c'\" \(10952 characters\)
sed "s/^X//" >'procmail/regexp.c' <<'END_OF_FILE'
X/************************************************************************
X * Custom regular expression library, *fully* egrep compatible *
X * *
X * Copyright (c) 1990-1991, S.R.van den Berg, The Netherlands *
X * The sources can be freely copied for non-commercial use. *
X * #include "README" *
X * *
X ************************************************************************/
X#ifdef RCS
Xstatic char rcsid[]="$Id: regexp.c,v 2.4 1991/10/18 15:34:23 berg Rel $";
X#endif
X#include "config.h"
X#include "procmail.h"
X#include "shell.h"
X
X#define R_BEG_GROUP '('
X#define R_END_GROUP ')'
X#define R_OR '|'
X#define R_0_OR_MORE '*'
X#define R_0_OR_1 '?'
X#define R_1_OR_MORE '+'
X#define R_DOT '.'
X#define R_SOL '^'
X#define R_EOL '$'
X#define R_BEG_CLASS '['
X#define R_NOT_CLASS '^'
X#define R_RANGE '-'
X#define R_END_CLASS ']'
X#define R_ESCAPE '\\'
X
X#define OPB 256
Xenum opcode {OPC_EPS=OPB,OPC_CLASS,OPC_DOT,OPC_FIN};
X
X#define bit_type unsigned
X#define bit_bits (sizeof(bit_type)*8)
X#define bit_index(which) ((unsigned)(which)/bit_bits)
X#define bit_mask(which) ((unsigned)1<<(unsigned)(which)%bit_bits)
X#define bit_toggle(name,which) (name[bit_index(which)]^=bit_mask(which))
X#define bit_test(name,which) (!!(name[bit_index(which)]&bit_mask(which)))
X#define bit_set(name,which,value) \
X (value?(name[bit_index(which)]|=bit_mask(which)):\
X (name[bit_index(which)]&=~bit_mask(which)))
X#define bit_field(name,size) bit_type name[((size)+bit_bits-1)/bit_bits]
X
X#define SZ(x) (sizeof(struct x))
X#define Ceps (struct eps*)
X
X/* the spawn and stack members are reused in the normal opcodes as pc fields */
Xstatic struct eps{int opc;struct eps*next,*spawn,*stack;}*r,aleps;
Xstatic uchar*p;
Xstatic ignore_case;
X
Xstruct chclass {int opc_;struct eps*next_,*spawn_,*stack_;bit_field(c,256);};
X
Xstatic puteps(spot,to,aswell)struct eps*const spot; /* epsilon transition */
X const struct eps*const to,*const aswell;
X{ spot->opc=OPC_EPS;spot->next=to!=spot?Ceps to:Ceps aswell;
X spot->spawn=aswell!=spot?Ceps aswell:Ceps to;spot->stack=0;
X}
X
Xstatic putneps(spot,to)struct eps*const spot;const struct eps*const to;
X{ puteps(spot,to,spot+1);
X}
X
X#define rAc (((struct chclass*)r)->c)
X
Xstatic bseti(i,j)unsigned i;const int j;
X{ bit_set(rAc,i,j); /* mark 'i' as being in the class */
X if(ignore_case) /* mark the other case too */
X { if(i-'A'<26) /* uppercase */
X i+='a'-'A';
X else if(i-'a'<26) /* lowercase */
X i-='a'-'A';
X else return; /* no case */
X bit_set(rAc,i,j);
X }
X}
X
Xstatic por();
X
Xstatic psimp(e)struct eps const*const e;
X{ switch(*p)
X { case R_BEG_GROUP: /* not so simple after all */
X ++p;por(e);return;
X case R_BEG_CLASS: /* a simple class */
X { uchar i,j=R_NOT_CLASS==*++p; /* char to save space */
X if(e)
X { r->opc=OPC_CLASS;r->next=Ceps e;r->spawn=r->stack=0;
X i=maxindex(rAc);
X do rAc[i]=j?~0:0; /* preset the bit field */
X while(i--);
X }
X if(j) /* skip the 'not' modifier */
X { ++p;bit_toggle(rAc,'\n');
X }
X if(*p==R_END_CLASS) /* right at the start, can not mean the end */
X { ++p;
X if(e)
X { i=R_END_CLASS;bit_toggle(rAc,R_END_CLASS);
X }
X }
X else if(*p==R_RANGE) /* take it literally */
X { ++p;
X if(e)
X { i=R_RANGE;bit_toggle(rAc,R_RANGE);
X }
X }
X for(;;++p)
X { switch(*p)
X { case R_END_CLASS:++p;
X case '\0':r=Ceps((char*)r+SZ(chclass));return;
X /* add code here to take care of special escapes */
X case R_RANGE:
X switch(*++p)
X { default:
X if(e)
X while(++i<*p) /* mark all in the range */
X bseti(i,!j);
X break;
X case '\0':case R_END_CLASS:--p; /* literally */
X }
X }
X if(e)
X bseti(i= *p,!j); /* a normal character, mark it */
X }
X }
X case '\0':return;
X case R_DOT: /* matches everything but a newline */
X if(e)
X { r->opc=OPC_DOT;goto fine;
X }
X goto fine2;
X case R_SOL:case R_EOL: /* match a newline (in effect) */
X if(e)
X { r->opc='\n';goto fine;
X }
X goto fine2;
X case R_ESCAPE: /* quote something */
X if(!*++p) /* nothing to quote */
X --p;
X /* add code here to take care of special escapes */
X }
X if(e) /* a regular character */
X { r->opc=ignore_case&&(unsigned)*p-'A'<26?*p+'a'-'A':*p;
Xfine:
X r->next=Ceps e;r->spawn=r->stack=0;
X }
Xfine2:
X ++p;++r;
X}
X
Xstatic skipent()
X{ switch(*p++)
X { case '\0':p--;break;
X case R_ESCAPE: /* something is quoted */
X /* add code here to take care of special escapes */
X if(*p)
X ++p;
X break;
X case R_BEG_GROUP: /* something big lies ahead */
X for(;;)
X { switch(*p)
X { case R_END_GROUP:++p; /* back in the civilised world */
X case '\0':return;
X }
X skipent(); /* skip it one entity at a time */
X }
X case R_BEG_CLASS: /* skip class :-) */
X if(*p==R_NOT_CLASS)
X ++p;
X if(*p==R_END_CLASS)
X ++p;
X for(;;)
X switch(*p++)
X { case '\0':--p;
X case R_END_CLASS:return;
X /* add code here to take care of special escapes */
X }
X }
X}
X /* EOS is needed to save stack space */
X#define EOS(x) (p[1]==R_OR||p[1]==R_END_GROUP||!p[1]?Ceps e:(x))
X
Xstatic pnorm(e)struct eps const*const e;
X{ void*pold;struct eps*rold;
X for(;;)
X { pold=p;skipent();rold=r; /* skip it first, so we can look if it is */
X switch(*p) /* followed by any of the postfix operators */
X { case R_0_OR_MORE:p=pold;++r;
X if(e) /* first an epsilon, then the rest */
X { psimp(rold);putneps(rold,EOS(r));
X }
X else
X psimp((struct eps*)0);
X goto incagoon;
X case R_1_OR_MORE:p=pold;psimp((struct eps*)0); /* first the rest */
X if(e) /* and then an epsilon */
X { puteps(r,rold,EOS(r+1));p=pold;pold=r;r=rold;psimp(pold);
X }
X ++r;goto incagoon;
X case R_0_OR_1:p=pold;++r;psimp((struct eps*)0);
X if(e) /* first an epsilon, then the rest */
X { putneps(rold,r=EOS(r));p=pold;pold=r;r=rold+1;psimp(pold);
X }
Xincagoon: switch(*++p) /* at the end of this group already? */
X { case R_OR:case R_END_GROUP:case '\0':return;
X }
X continue; /* regular end of the group */
X case R_OR:case R_END_GROUP:case '\0':p=pold;psimp(e);return;
X }
X p=pold;psimp((struct eps*)0);
X if(e) /* no fancy postfix operators, plain vanilla */
X { p=pold;pold=r;r=rold;psimp(pold);
X }
X }
X}
X
Xstatic por(e)struct eps const*const e;
X{ uchar*pold;struct eps*rold;
X for(;;)
X for(pold=p;;)
X { switch(*p)
X { default:skipent();continue; /* still in this 'or' group */
X case '\0':case R_END_GROUP: /* found the end of the group */
X if(p==pold) /* empty 'or' group */
X { if(e)
X puteps(r,e,e); /* misused epsilon as branch */
X ++r;
X }
X else
X { p=pold;pnorm(e); /* normal last group */
X }
X if(*p)
X ++p;
X return;
X case R_OR:rold=r++;
X if(p==pold) /* empty 'or' group */
X { if(e)
X putneps(rold,e); /* special epsilon */
X }
X else
X { p=pold;pnorm(e); /* normal 'or' group, first an */
X if(e) /* epsilon, then the rest */
X putneps(rold,r);
X }
X ++p;
X }
X break;
X }
X}
X
Xstatic findandrep(old,new)register struct eps*const*const old;
X struct eps*const new;
X{ register struct eps*i;
X for(i=r;;++i) /* change all pointers from *old to new */
X { if(&i->next!=old&&i->next==*old)
X i->next=new;
X if(&i->spawn!=old&&i->spawn==*old)
X i->spawn=new;
X switch(i->opc)
X { case OPC_FIN:return; /* last one, ready */
X case OPC_CLASS:i=Ceps((char*)(i-1)+SZ(chclass));
X }
X }
X}
X /* break up any closed epsilon circles, otherwise they can't be executed */
Xstatic fillout(stack)struct eps**const stack;
X{ if((*stack)->opc!=OPC_EPS||(*stack)->stack)
X return 0;
X (*stack)->stack=(struct eps*)p; /* mark this one as used */
X#define RECURS(nxt,spwn) \
X do\
X if((*stack)->nxt->stack==(struct eps*)p)\
X { findandrep(*stack,(*stack)->nxt);*stack=(*stack)->spwn;return 1;\
X }\
X while(fillout(&(*stack)->nxt));
X RECURS(next,spawn);RECURS(spawn,next);return 0; /* recurse */
X}
X
Xvoid*regcomp(a,ign_case)char const*const a;
X{ struct eps*st;size_t i; /* first a trial run, determine memory needed */
X p=(uchar*)a;ignore_case=ign_case;r= &aleps+2;por((struct eps*)0);p=(uchar*)a;
X st=malloc(i=(char*)r-(char*)&aleps);putneps(st,r=st+1); /* really compile */
X por(Ceps((char*)st+i-SZ(eps)));r->opc=OPC_FIN;r->stack=0; /* add end */
X for(r=st;;++st) /* simplify the compiled code (i.e. */
X switch(st->opc) /* take out cyclic epsilon references) */
X { case OPC_FIN:return r; /* finished */
X case OPC_EPS:p=(uchar*)st;fillout(&st);break; /* check tree */
X case OPC_CLASS:st=Ceps((char*)(st-1)+SZ(chclass)); /* skip */
X }
X}
X
Xchar*regexec(code,text,len,ign_case)struct eps*code;const uchar*text;
X long len;const int ign_case;
X{ register struct eps*reg,*t,*stack,*other,*this;int i,th1,ot1;
X if(code[1].opc==OPC_EPS)
X ++code;
X (this=code)->stack=0;th1=offsetof(struct eps,spawn);
X ot1=offsetof(struct eps,stack);
X#define XOR1 (offsetof(struct eps,spawn)^offsetof(struct eps,stack))
X#define PC(this,t) (*(struct eps**)((char*)(this)+(t)))
X i='\n';goto setups; /* make sure any beginning-of-line-hooks catch */
X do
X { i= *text++; /* get the next real-text character */
Xlastrun: /* switch this & other pc-stack */
X th1^=XOR1;ot1^=XOR1;this=other;
Xsetups:
X reg=(other=stack=code)->next;goto nostack;
X do /* pop next entry off this pc-stack */
X { reg=(t=this)->next;this=PC(t,th1);PC(t,th1)=0;goto nostack;
X do /* pop next entry off the work-stack */
X { stack=(t=stack)->stack;t->stack=0;reg=t->spawn;
Xnostack: switch(reg->opc-OPB) /* push spawned branch on the work-stack */
X { default:
X if(ign_case&&(unsigned)i-'A'<26)
X i+='a'-'A'; /* transmogrify it to lowercase */
X if(i==reg->opc) /* regular character match */
X goto yep;
X break;
X case OPC_EPS-OPB:reg->stack=stack;reg=(stack=reg)->next;
X goto nostack;
X case OPC_FIN-OPB: /* hurray! complete regexp match */
X return (char*)text-1; /* return one past the match */
X case OPC_CLASS-OPB:
X if(bit_test(((struct chclass*)reg)->c,i))
X goto yep; /* character in class */
X break;
X case OPC_DOT-OPB: /* dot-wildcard */
X if(i!='\n')
Xyep: if(!PC(reg,ot1)) /* state not yet pushed */
X { PC(reg,ot1)=other;other=reg; /* push location onto */
X } /* other pc-stack */
X }
X }
X while(stack); /* the work-stack is not empty */
X }
X while(this!=code); /* this pc-stack is not empty */
X }
X while(--len>=0); /* still text to search */
X if(i>=0)
X { ++text;i= -1;goto lastrun; /* out of text, check if we just matched */
X }
X return 0; /* no match */
X}
END_OF_FILE
if test 10952 -ne `wc -c <'procmail/regexp.c'`; then
echo shar: \"'procmail/regexp.c'\" unpacked with wrong size!
fi
# end of 'procmail/regexp.c'
fi
if test -f 'procmail/retint.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'procmail/retint.c'\"
else
echo shar: Extracting \"'procmail/retint.c'\" \(11077 characters\)
sed "s/^X//" >'procmail/retint.c' <<'END_OF_FILE'
X/************************************************************************
X * Collection of routines that return an int (sort of anyway :-) *
X * *
X * Copyright (c) 1990-1991, S.R.van den Berg, The Netherlands *
X * The sources can be freely copied for non-commercial use. *
X * #include "README" *
X * *
X ************************************************************************/
X#ifdef RCS
Xstatic char rcsid[]="$Id: retint.c,v 2.12 1991/10/22 15:31:26 berg Rel $";
X#endif
X#include "config.h"
X#include "procmail.h"
X#include "shell.h"
X
Xsetdef(name,contents)const char*const name,*const contents;
X{ strcat(strcat(strcpy(sgetcp=buf2,name),"="),contents);
X readparse(buf,sgetc,0);sputenv(buf);
X}
X
Xchar*lastexec,*backblock;
Xlong backlen; /* length of backblock, filter recovery block */
Xpid_t pidfilt,pidchild;
Xint pbackfd[2]; /* the emergency backpipe :-) */
X
Xpipthrough(line,source,len)char*line,*source;const long len;
X{ int pinfd[2],poutfd[2];
X rpipe(pbackfd);rpipe(pinfd); /* main pipes setup */
X if(!(pidchild=sfork())) /* create a sending procmail */
X { backblock=source;backlen=len;signal(SIGTERM,stermchild);
X signal(SIGINT,stermchild);signal(SIGHUP,stermchild);
X signal(SIGQUIT,stermchild);rclose(rc);rclose(PRDI);rclose(PRDB);
X rpipe(poutfd);rclose(STDOUT);
X if(!(pidfilt=sfork())) /* create the filter */
X { rclose(PWRO);rclose(PWRB);rdup(PWRI);rclose(PWRI);getstdin(PRDO);
X callnewprog(line);
X }
X rclose(PWRI);rclose(PRDO);
X if(forkerr(pidfilt,line))
X { rclose(PWRO);stermchild();
X }
X if(dump(PWRO,source,len)) /* send in the text to be filtered */
X { writeerr(line);stermchild();
X }
X if(pwait&&waitfor(pidfilt)!=EX_OK) /* check the exitcode of the filter */
X { progerr(line);stermchild();
X }
X rclose(PWRB);exit(EX_OK); /* allow parent to proceed */
X }
X rclose(PWRI);rclose(PWRB);getstdin(PRDI);
X if(forkerr(pidchild,procmailn))
X return 1;
X return 0; /* we stay behind to read back the filtered text */
X}
X
Xwaitfor(pid)const pid_t pid; /* wait for a specific process */
X{ int i;pid_t j;
X while(pid!=(j=wait(&i))||(i&127)==127)
X if(-1==j)
X return EX_UNAVAILABLE;
X return i>>8&255;
X}
X
Xgetstdin(pip)const int pip;
X{ rclose(STDIN);rdup(pip);rclose(pip);
X}
X
Xcallnewprog(newname)const char*const newname;
X{ if(sh) /* should we start a shell? */
X { const char*newargv[4];
X yell(executing,newname);newargv[3]=0;newargv[2]=newname;
X newargv[1]=tgetenv(shellflags);*newargv=tgetenv(shell);shexec(newargv);
X }
X {register const char*p;int argc;const char**newargv;
X argc=1;p=newname; /* If no shell, chop up the arguments ourselves */
X if(verbose)
X { log(executing);log(oquote);goto no_1st_comma;
X }
X do /* show chopped up command line */
X { if(verbose)
X { log(",");
Xno_1st_comma:
X log(p);
X }
X while(*p++);
X }
X while(argc++,*p!=TMNATE);
X if(verbose)
X log(cquote);
X newargv=malloc(argc*sizeof*newargv);p=newname;argc=0; /* alloc argv array */
X do
X { newargv[argc++]=p;
X while(*p++);
X }
X while(*p!=TMNATE);
X newargv[argc]=0;shexec(newargv);
X }
X}
X
Xwriteerr(line)const char*const line;
X{ log("Error while writing to");logqnl(line);
X}
X
Xforkerr(pid,a)const pid_t pid;const char*const a;
X{ if(pid==-1)
X { log("Failed forking");logqnl(a);return 1;
X }
X return 0;
X}
X
Xprogerr(line)const char*const line;
X{ log("Program failure of");logqnl(line);
X}
X
Xopena(a)const char*const a;
X{ lastfolder=cstr(lastfolder,a);yell("Opening",a);
X#ifdef O_CREAT
X return ropen(a,O_WRONLY|O_APPEND|O_CREAT,0666);
X#else
X {int fd;
X return(fd=ropen(a,O_WRONLY))<0?creat(a,0666):fd;
X }
X#endif
X}
X
Xyell(a,b)const char*const a,*const b; /* log if -d option set */
X{ if(verbose)
X { log(a);logqnl(b);
X }
X}
X
Xunlock(lockp)const char**const lockp;
X{ lcking=1;
X if(*lockp)
X { yell("Unlocking",*lockp);
X if(unlink(*lockp))
X { log("Couldn't unlock");logqnl(*lockp);
X }
X free(*lockp);*lockp=0;
X }
X lcking=0;
X if(nextexit==1) /* make sure we are not inside terminate already */
X { log(newline);terminate();
X }
X}
X
Xnomemerr()
X{ log("Out of memory\nbuffer 0: \"");buf[linebuf-1]=buf2[linebuf-1]='\0';
X log(buf);log("\"\nbuffer 1:");logqnl(buf2);retval=EX_OSERR;terminate();
X}
X
Xlogqnl(a)const char*const a;
X{ log(oquote);log(a);log(cquote);
X}
X
Xnextrcfile() /* next rcfile specified on the command line */
X{ const char*p;
X while(p= *gargv)
X { gargv++;
X if(!strchr(p,'='))
X { rcfile=p;return 1;
X }
X }
X return 0;
X}
X
Xrclose(fd)const int fd; /* a sysV secure close (signal immune) */
X{ int i;
X while((i=close(fd))&&errno==EINTR);
X return i;
X}
X
Xrwrite(fd,a,len)const int fd,len;void*const a; /* a sysV secure write */
X{ int i;
X while(0>(i=write(fd,a,(size_t)len))&&errno==EINTR);
X return i;
X}
X
Xrread(fd,a,len)const int fd,len;void*const a; /* a sysV secure read */
X{ int i;
X while(0>(i=read(fd,a,(size_t)len))&&errno==EINTR);
X return i;
X}
X
Xropen(name,mode,mask)const char*const name;const int mode;const mode_t mask;
X{ int i,r; /* a sysV secure open */
X for(lcking=4,r=noresretry;0>(i=open(name,mode,mask));)
X if(errno!=EINTR&&!((errno==EMFILE||errno==ENFILE)&&(r<0||r--)))
X break; /* survives a temporary "file table full" condition */
X lcking=0;return i;
X}
X
Xrdup(p)const int p;
X{ int i,r;
X for(lcking=4,r=noresretry;0>(i=dup(p));) /* catch "file table full" */
X if(!((errno==EMFILE||errno==ENFILE)&&(r<0||r--)))
X break;
X lcking=0;return i;
X}
X
Xrpipe(fd)int fd[2];
X{ int i,r;
X for(lcking=4,r=noresretry;0>(i=pipe(fd));) /* catch "file table full" */
X if(!((errno==EMFILE||errno==ENFILE)&&(r<0||r--)))
X { *fd=fd[1]= -1;break;
X }
X lcking=0;return i;
X}
X
Xlockit(name,lockp)char*name;const char**const lockp;
X{ int i;struct stat stbuf;
X unlock(lockp); /* unlock any previous lockfile FIRST */
X if(!*name)
X return;
X for(lcking=1;;) /* to prevent deadlocks (I hate deadlocks) */
X { yell("Locking",name);
X if(!NFSxopen(name))
X { *lockp=tstrdup(name); /* lock acquired, hurray! */
Xterm: lcking=0;
X if(nextexit)
X { log(whilstwfor);log("lockfile");logqnl(name);terminate();
X }
X return; /* check if it's time for a lock override */
X }
X if(errno==EEXIST&&!stat(name,&stbuf)&&!stbuf.st_size)
X { time_t t;
X if(locktimeout&&(t=time((time_t*)0),!stat(name,&stbuf))) /* stat */
X if(locktimeout<t-stbuf.st_mtime) /* till unlink should be atomic */
X { if(unlink(name)) /* but can't guarantee that */
X { log("Forced unlock denied on");logqnl(name);
X }
X else
X { log("Forcing lock on");logqnl(name);suspend();
X }
X }
X }
X else /* maybe filename too long, shorten and retry */
X { if(errno!=ENOTDIR&&0<(i=strlen(name)-1)&&!strchr(dirsep,name[i-1]))
X { name[i]='\0';continue;
X }
X log("Lockfailure on");logqnl(name);return;
X }
X sleep((unsigned)locksleep);
X if(nextexit)
X goto term;
X }
X}
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,tgetenv(lockext)),&loclock);
X}
X
Xterminate()
X{ nextexit=2; /* prevent multiple invocations of terminate */
X if(getpid()==thepid)
X { if(retval!=EX_OK)
X { log(Mail);
X log(fakedelivery?"lost\n":
X retval==EX_TEMPFAIL?"requeued\n":"bounced\n");
X }
X unlock(&loclock);unlock(&globlock);unlockfd();
X }
X exit(retval);
X}
X
Xignoreterm()
X{ signal(SIGTERM,SIG_IGN);signal(SIGHUP,SIG_IGN);signal(SIGINT,SIG_IGN);
X signal(SIGQUIT,SIG_IGN);
X}
X
Xsuspend()
X{ long t;
X sleep((unsigned)suspendv);
X if(alrmtime)
X if((t=alrmtime-time((time_t*)0))<=1) /* if less than 1s timeout */
X ftimeout(); /* activate it by hand now */
X else /* set it manually again, to avoid problems with */
X alarm((unsigned)t); /* badly implemented sleep library functions */
X}
X
Xinittmout(progname)const char*const progname;
X{ lastexec=cstr(lastexec,progname);
X alrmtime=timeoutv?time((time_t*)0)+(unsigned)timeoutv:0;
X alarm((unsigned)timeoutv);
X}
X
Xskipspace()
X{ while(testb(' ')||testb('\t'));
X}
X
Xsgetc() /* a fake fgetc for a string */
X{ return *sgetcp?*(uchar*)sgetcp++:EOF;
X}
X
Xskipped(x)const char*const x;
X{ log("Skipped");logqnl(x);
X}
X
Xconcatenate(old)char*const old;
X{ register char*p=old;
X while(*p!=TMNATE) /* concatenate all other arguments */
X { while(*p++);
X p[-1]=' ';
X }
X *p=p[-1]='\0';return*old;
X}
X
Xdetab(p)char*p;
X{ while(p=strchr(p,'\t'))
X *p=' '; /* take out all tabs */
X}
X
Xstatic uchar rcbuf[STDBUF],*rcbufp,*rcbufend; /* buffers for custom stdio */
Xstatic ungetb; /* pushed back char */
X
Xbopen(name)const char*const name; /* my fopen */
X{ rcbufp=rcbufend=0;ungetb= -1;yell("Rcfile:",name);
X return rc=ropen(name,O_RDONLY);
X}
X
Xgetbl(p)char*p; /* my gets */
X{ int i;char*q;
X for(q=p;;)
X { switch(i=getb())
X { case '\n':case EOF:
X *p='\0';return p!=q; /* did we read anything at all? */
X }
X *p++=i;
X }
X}
X
Xgetb() /* my fgetc */
X{ if(ungetb>=0) /* anything pushed back? */
X { int i;
X i=ungetb;ungetb= -1;return i;
X }
X if(rcbufp==rcbufend)
X { rcbufend=rcbuf+rread(rc,rcbufp=rcbuf,STDBUF); /* refill */
X }
X return rcbufp<rcbufend?*rcbufp++:EOF;
X}
X
Xtestb(x)const int x; /* fgetc that only succeeds if it matches */
X{ int i;
X if((i=getb())==x)
X return 1;
X ungetb=i;return 0;
X}
X
Xalphanum(c)const int c;
X{ return c>='0'&&c<='9'||c>='a'&&c<='z'||c>='A'&&c<='Z'||c=='_';
X}
X /* open file or new file in directory */
Xdeliver(boxname)char*const boxname;
X{ struct stat stbuf;
X strcpy(buf,boxname); /* boxname can be found back in buf */
X return stat(buf,&stbuf)||!S_ISDIR(stbuf.st_mode)?
X (tofolder=1,opena(buf)):dirmail();
X}
X
X#ifndef lockfd
Xstatic oldlockfd; /* the fd we locked last */
X#ifdef F_SETLKW
Xstatic struct flock flck; /* why can't it be a local variable? */
X
Xlockfd(fd) /* the POSIX-fcntl() lock */
X{ flck.l_type=F_WRLCK;flck.l_whence=SEEK_SET;flck.l_len=0;
X flck.l_start=tell(fd);lcking=5;fd=fcntl(oldlockfd=fd,F_SETLKW,&flck);
X lcking=0;return fd;
X}
X
Xunlockfd()
X{ flck.l_type=F_UNLCK;return fcntl(oldlockfd,F_SETLK,&flck);
X}
X#else
X#ifdef F_LOCK
Xstatic long oldlockoffset;
X
Xlockfd(fd) /* the sysV-lockf() */
X{ oldlockoffset=tell(fd);lcking=5;fd=lockf(oldlockfd=fd,F_LOCK,0L);lcking=0;
X return fd;
X}
X
Xunlockfd()
X{ lseek(oldlockfd,oldlockoffset,SEEK_SET);return lockf(oldlockfd,F_ULOCK,0L);
X}
X#else
X#ifdef LOCK_EX
Xlockfd(fd) /* the BSD-flock() */
X{ lcking=5;fd=flock(oldlockfd=fd,LOCK_EX);lcking=0;return fd;
X}
X
Xunlockfd()
X{ return flock(oldlockfd,LOCK_UN);
X}
X#endif
X#endif
X#endif
X#endif
X
X#include "exopen.h"
X
XNFSxopen(name)char*name; /* an NFS secure exclusive file open */
X{char*p,*q;int j= -2,i;
X for(q=name;p=strpbrk(q,dirsep);q=p+1); /* find last DIRSEP */
X i=q-name;strncpy(p=malloc(i+UNIQnamelen),name,i);
X if(unique(p,p+i,0))
X j=myrename(p,name); /* try and rename it, fails if nonexclusive */
X free(p);return j;
X}
END_OF_FILE
if test 11077 -ne `wc -c <'procmail/retint.c'`; then
echo shar: \"'procmail/retint.c'\" unpacked with wrong size!
fi
# end of 'procmail/retint.c'
fi
echo shar: End of archive 2 \(of 4\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 4 archives.
rm -f ark[1-9]isdone
else
echo You still must unpack the following archives:
echo " " ${MISSING}
fi
exit 0
exit 0 # Just in case...
--
Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD UUCP: uunet!sparky!kent
Phone: (402) 291-8300 FAX: (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.