home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-02-02 | 39.6 KB | 1,264 lines |
- Newsgroups: comp.sources.misc
- From: berg@messua.informatik.rwth-aachen.de (Stephen R. van den Berg)
- Subject: v28i004: procmail - mail processing program v2.61, Part04/05
- Message-ID: <1992Feb2.030831.24233@sparky.imd.sterling.com>
- X-Md4-Signature: 5f7478dfdf143296dc69f2c7eb420e9e
- Date: Sun, 2 Feb 1992 03:08:31 GMT
- Approved: kent@sparky.imd.sterling.com
-
- Submitted-by: berg@messua.informatik.rwth-aachen.de (Stephen R. van den Berg)
- Posting-number: Volume 28, Issue 4
- Archive-name: procmail/part04
- Environment: UNIX, sendmail, smail, MMDF
- Supersedes: procmail: Volume 25, Issue 01-04
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 4 (of 5)."
- # Contents: procmail/examples/mailinglist procmail/formail.c
- # procmail/retint.c
- # Wrapped by berg@tabaqui on Fri Jan 31 14:16:36 1992
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- 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'\" \(11167 characters\)
- sed "s/^X//" >'procmail/examples/mailinglist' <<'END_OF_FILE'
- X$Id: mailinglist,v 2.4 1992/01/31 12:03:04 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 cannot exceed 1000 bytes (on most sendmails).
- Xb. The subscriber list cannot be changed on-the-fly (/usr/lib/aliases needs
- X to be edited, and newaliases has to be run).
- Xc. People cannot be prevented from submitting messages like "Please remove
- X me from this mailinglist" to mylist (and thereby annoying all subscribers).
- Xd. People cannot 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 cannot 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 cannot 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
- XIf, on the other hand, you want to do more complex things like moderated
- Xmailing lists with several moderators, etc., if would suggest you take
- Xa look at the more complex/specialised mail-server packages like:
- Xlistserv available on cs.bu.edu, author: tasos@cs.bu.edu
- XOf course, most of what these packages can do, can be done with procmail as
- Xwell; it is just that you might be forced to write some additional shell
- Xscripts/programs to accomplish the same.
- 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 11167 -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'\" \(12963 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-1992, 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.16 1992/01/09 17:23:14 berg Rel $";
- X#endif
- Xstatic char rcsdate[]="$Date: 1992/01/09 17:23:14 $";
- 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{if((x=getchar())==EOF)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;size_t hlen;
- X enum{h_ren,h_del,h_add,h_was,h_extract}htype;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 }
- 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,keep)const char*const target;const int keep;
- X{ struct hedit*hlp;
- X for(hlp=hlist;hlp;hlp=hlp->next)
- X if(hlp->htype!=h_was&&!strnicmp(hlp->hline,target,hlp->hlen))
- X { if(keep>0||hlp->htype!=h_add) /* found field ok? */
- X return (struct hedit*)hlp;
- X hlp->htype=h_was; /* temporarily disable field */
- X if(keep) /* smaller than zero */
- X break;
- X }
- X return(struct hedit*)0; /* no field 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,extract=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_EXTRACT:extract=1;
- X case FM_ADD_IFNOT: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 field string */
- X if(!(buf=strchr(chp,HEAD_DELIMITER)))
- X { nlog("Invalid field-name:");logqnl(chp);goto usg;
- X }
- X hlist->hlen=buf-chp+1;
- X hlist->htype=lastm==FM_REN_INSERT?h_ren:
- X lastm==FM_DEL_INSERT?h_del:lastm==FM_ADD_IFNOT?h_add:h_extract;
- X case '\0':;
- X }
- X break;
- X }
- X }
- Xparsedoptions:
- X#ifdef MAILBOX_SEPARATOR
- X if(split)
- X { every=1;
- X if(!areply)
- X bogus=0;
- 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);buf=malloc(buflen=BSIZE);t=time((time_t*)0);
- X i=maxindex(rex);
- X do rex[i].rexp=malloc(1);
- X while(i--);
- X while('\n'==(ch=getchar()));
- Xstartover:
- X *namep='\0';i=maxindex(rex);
- X do *rex[i].rexp='\0';
- X while(i--);
- 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 case EOF:ch='\n';
- 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,areply)) /* replace or delete field? */
- X switch(hlp->htype)
- X { case h_extract:putssn(buf+lnl+hlp->hlen,p-lnl-hlp->hlen);
- X case h_del:p=lnl;continue; /* just delete it */
- X case h_ren:
- 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 if((ch=getchar())==EOF)
- X ch='\n'; /* make sure the header ends with 2 newlines */
- X }
- X if(!extract)
- X { if(areply||!force&&strncmp(buf,From,STRLEN(From)))
- X { if(!areply||!overrideh(To,!*namep))
- X { putss(areply?(areply=2),To:From);
- X if(*namep) /* found any sender address? */
- X putss(namep);
- X else
- X putss(UNKNOWN);
- X }
- X if(areply)
- X { if(areply==2)
- X putnl();
- X if(*subj.rexp&&!overrideh(subject,-1)) /* 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,-1))
- 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,-1))
- 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 if(hlp->htype==h_was)
- X hlp->htype=h_add; /* enable disabled field again */
- X else if(hlp->hline[hlp->hlen])
- X { putss(hlp->hline);putnl(); /* inject our new fields */
- X }
- X putnl();
- X }
- X if(areply&&!keepb||extract)
- X { if(split)
- X closemine();
- X opensink(); /* discard the body */
- 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 { 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(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 goto splitit;
- 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);ch=i;--p;lnl=0;goto startover;
- X }
- X }
- X if(areply&&bogus&&*buf!='\n')
- X putcs(ESCAP); /* escape the body */
- 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 12963 -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/retint.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'procmail/retint.c'\"
- else
- echo shar: Extracting \"'procmail/retint.c'\" \(12095 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-1992, 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.23 1992/01/31 12:28:53 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,NORMperm);
- X#else
- X {int fd;
- X return(fd=ropen(a,O_WRONLY,0))<0?creat(a,NORMperm):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);
- X if(retval!=EX_TEMPFAIL)
- X retval=EX_OSERR;
- X 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 if(!lcking) /* already locking */
- X lcking=4;
- X for(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 if(lcking==4)
- X lcking=0;
- X 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,permanent=2;struct stat stbuf;
- X unlock(lockp); /* unlock any previous lockfile FIRST */
- X if(!*name) /* to prevent deadlocks (I hate deadlocks) */
- X return;
- X name=tstrdup(name); /* allocate now, so we won't hang on memory *and* lock */
- X for(;;)
- X { yell("Locking",name);
- X if(!NFSxopen(name,LOCKperm))
- X { *lockp=name;break; /* lock acquired, hurray! */
- X }
- X switch(errno)
- X { case EEXIST:
- X { time_t t; /* check if it's time for a lock override */
- X if(!stat(name,&stbuf)&&stbuf.st_size<=MAX_LOCK_SIZE&&locktimeout
- X &&(t=time((time_t*)0),!stat(name,&stbuf))&& /* stat till unlink */
- X locktimeout<t-stbuf.st_mtime) /* should be atomic, */
- X if(unlink(name)) /* but I 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 break;
- X }
- X default: /* maybe filename too long, shorten and retry */
- X if(0<(i=strlen(name)-1)&&!strchr(dirsep,name[i-1]))
- X { log("Truncating");logqnl(name);log(" and retrying lock\n");
- X name[i]='\0';continue;
- X }
- Xfaillock: log("Lock failure on");logqnl(name);goto term;
- X case ENOENT:case ENOTDIR:case EIO:case EACCES:
- X if(!--permanent)
- X goto faillock;
- X case ENOSPC:;
- X#ifdef EDQUOT
- X case EDQUOT:;
- X#endif
- X }
- X sleep((unsigned)locksleep);
- X if(nextexit)
- X {
- Xterm: free(name);break; /* drop the preallocated buffer */
- X }
- X }
- X lcking=0;
- X if(nextexit)
- X { log(whilstwfor);log("lockfile");logqnl(name);terminate();
- 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
- Xsterminate()
- X{ static const char*const msg[]={newline,0,"memory\n","fork\n",
- X "a file descriptor\n","a kernel lock\n"};
- X ignoreterm();
- X if(pidchild>0) /* don't kill what is not ours, we might be root */
- X kill(pidchild,SIGTERM);
- X if(!nextexit)
- X { nextexit=1;log("Terminating prematurely");
- X if(1!=lcking)
- X { if(1<lcking)
- X log(whilstwfor);
- X log(msg[lcking]);terminate();
- X }
- X }
- 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);fdunlock();
- X }
- X exit(fakedelivery==2?EX_OK: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,0);
- 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 fdlock
- Xstatic oldfdlock; /* the fd we locked last */
- X#ifdef F_SETLKW
- Xstatic struct flock flck; /* why can't it be a local variable? */
- X
- Xfdlock(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(oldfdlock=fd,F_SETLKW,&flck);
- X lcking=0;return fd;
- X}
- X
- Xfdunlock()
- X{ flck.l_type=F_UNLCK;return fcntl(oldfdlock,F_SETLK,&flck);
- X}
- X#else /* F_SETLKW */
- X#ifdef F_LOCK
- Xstatic long oldlockoffset;
- X
- Xfdlock(fd) /* the sysV-lockf() */
- X{ oldlockoffset=tell(fd);lcking=5;fd=lockf(oldfdlock=fd,F_LOCK,0L);lcking=0;
- X return fd;
- X}
- X
- Xfdunlock()
- X{ lseek(oldfdlock,oldlockoffset,SEEK_SET);return lockf(oldfdlock,F_ULOCK,0L);
- X}
- X#else /* F_LOCK */
- X#ifdef LOCK_EX
- Xfdlock(fd) /* the BSD-flock() */
- X{ lcking=5;fd=flock(oldfdlock=fd,LOCK_EX);lcking=0;return fd;
- X}
- X
- Xfdunlock()
- X{ return flock(oldfdlock,LOCK_UN);
- X}
- X#endif /* LOCK_EX */
- X#endif /* F_LOCK */
- X#endif /* F_SETLKW */
- X#endif /* fdlock */
- X
- X#include "exopen.h"
- X /* an NFS secure exclusive file open */
- XNFSxopen(name,mode)char*name;const mode_t mode;
- X{ char*p;int j= -2,i;
- X i=lastdirsep(name)-name;strncpy(p=malloc(i+UNIQnamelen),name,i);lcking=1;
- X if(unique(p,p+i,mode))
- X j=myrename(p,name); /* try and rename it, fails if nonexclusive */
- X free(p);return j;
- X}
- END_OF_FILE
- if test 12095 -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 4 \(of 5\).
- cp /dev/null ark4isdone
- MISSING=""
- for I in 1 2 3 4 5 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 5 archives.
- rm -f ark[1-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
- --
- Sincerely, berg@messua.informatik.rwth-aachen.de
- Stephen R. van den Berg (AKA BuGless). berg@physik.tu-muenchen.de
-
- He did a quarter of the work in *half* the time!
-
- exit 0 # Just in case...
-