home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume28 / procmail / part04 < prev    next >
Text File  |  1992-02-02  |  41KB  |  1,264 lines

  1. Newsgroups: comp.sources.misc
  2. From: berg@messua.informatik.rwth-aachen.de (Stephen R. van den Berg)
  3. Subject:  v28i004:  procmail - mail processing program v2.61, Part04/05
  4. Message-ID: <1992Feb2.030831.24233@sparky.imd.sterling.com>
  5. X-Md4-Signature: 5f7478dfdf143296dc69f2c7eb420e9e
  6. Date: Sun, 2 Feb 1992 03:08:31 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: berg@messua.informatik.rwth-aachen.de (Stephen R. van den Berg)
  10. Posting-number: Volume 28, Issue 4
  11. Archive-name: procmail/part04
  12. Environment: UNIX, sendmail, smail, MMDF
  13. Supersedes: procmail: Volume 25, Issue 01-04
  14.  
  15. #! /bin/sh
  16. # This is a shell archive.  Remove anything before this line, then unpack
  17. # it by saving it into a file and typing "sh file".  To overwrite existing
  18. # files, type "sh file -c".  You can also feed this as standard input via
  19. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  20. # will see the following message at the end:
  21. #        "End of archive 4 (of 5)."
  22. # Contents:  procmail/examples/mailinglist procmail/formail.c
  23. #   procmail/retint.c
  24. # Wrapped by berg@tabaqui on Fri Jan 31 14:16:36 1992
  25. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  26. if test -f 'procmail/examples/mailinglist' -a "${1}" != "-c" ; then 
  27.   echo shar: Will not clobber existing file \"'procmail/examples/mailinglist'\"
  28. else
  29. echo shar: Extracting \"'procmail/examples/mailinglist'\" \(11167 characters\)
  30. sed "s/^X//" >'procmail/examples/mailinglist' <<'END_OF_FILE'
  31. X$Id: mailinglist,v 2.4 1992/01/31 12:03:04 berg Rel $
  32. X
  33. X            How to set up mailing lists
  34. X            ---------------------------
  35. X
  36. X        Written by Stephen R. van den Berg.
  37. X                    berg@messua.informatik.rwth-aachen.de
  38. X                    berg@physik.tu-muenchen.de
  39. X
  40. XThis document mainly describes a sendmail environment, much of it applies
  41. Xto non-sendmail mail agents as well.
  42. X
  43. X
  44. XContents:
  45. X---------    1. Intro
  46. X        2. Bouncing mail
  47. X        3. The disadvantages
  48. X        4. How to circumvent these disadvantages
  49. X        5. Why use procmail to filter the mailinglist mail?
  50. X        6. How do I use procmail to filter the mailinglist mail?
  51. X        7. Now, what does the above all do?
  52. X        8. The result of this exercise
  53. X
  54. X1. Intro
  55. X   -----
  56. X
  57. XThe simplest and most direct way to do it is by insert a line in
  58. Xthe /usr/lib/aliases file looking like:
  59. X
  60. Xmylist: fred,john, wilma, barney@bedrock, pebbles
  61. X
  62. XNow all the mail arriving at your machine for "mylist" (either local or
  63. Xmylist@your.domain) will be automatically forwarded to all the mentioned
  64. Xaddresses (fred, john, etc.).
  65. X
  66. XThe address mylist@your.domain is intended for submissions to the list that
  67. Xare supposed to be forwarded to all the subscribers.  For the administrative
  68. Xtasks like removals from the list, new subscribtions to the list, or address
  69. Xchanges of subscribers one should create a second entry in the /usr/lib/aliases
  70. Xfile:
  71. X
  72. Xmylist-request: your_login_name@your.domain
  73. X
  74. X
  75. X2. Bouncing mail
  76. X   -------------
  77. X
  78. XIn order to deal with bouncing mail gracefully, an extra precaution should
  79. Xbe taken.  If for example mail to wilma bounces (user non-existent, mail
  80. Xfilesystem full, etc.), it will bounce back to the original sender.
  81. XNow, the only person that should be concerned with distribution failures
  82. Xshould be the mylist-request holder.  Therefore you should be using a
  83. Xsendmail special alias like:
  84. X
  85. Xowner-mylist: mylist-request@your.domain
  86. X
  87. XThis way local mail will bounce back to mylist-request@your.domain.
  88. X
  89. X
  90. X3. The disadvantages
  91. X   -----------------
  92. X
  93. XIf you are using the above methods, some obvious disadvantages come to mind
  94. Xhowever:
  95. X
  96. Xa. The subscriber list cannot exceed 1000 bytes (on most sendmails).
  97. Xb. The subscriber list cannot be changed on-the-fly (/usr/lib/aliases needs
  98. X   to be edited, and newaliases has to be run).
  99. Xc. People cannot be prevented from submitting messages like "Please remove
  100. X   me from this mailinglist" to mylist (and thereby annoying all subscribers).
  101. Xd. People cannot be guarded from themselves in case they insert
  102. X   "Return-Receipt-To:" fields in their headers (if they are particularly
  103. X   unlucky, they will receive an acknowledge mail from *every* subscriber's
  104. X   sendmail).
  105. Xe. People including "Errors-To:" or "Sender:" fields can cause the bounce
  106. X   messages to bypass owner-mylist anyway.
  107. Xf. There is no way of limiting the number of submitters, i.e. every person
  108. X   who knows the name of the mailing list and who can send mail to your.domain
  109. X   is able to submit messages to the list.  This means, for example, that you
  110. X   cannot limit a mailing list to local users (i.e. only local users can
  111. X   submit).
  112. Xg. You are unable to insert a "Reply-To: mylist@your.domain" in case you
  113. X   would want to (this makes replying to the list easier).
  114. X
  115. X
  116. X4. How to circumvent these disadvantages
  117. X  -------------------------------------
  118. X
  119. Xa. Can be circumvented by using nested aliases like:
  120. X    mylist: mylist1, mylist2
  121. X    mylist1: fred,john
  122. X    mylist2: wilma,barney@bedrock,pebbles
  123. X   This can however, become extremely messy to maintain.
  124. X
  125. Xb. This can partly be avoided if you use aliases like:
  126. X    mylist: :input:/path/to/the/memberfile
  127. X   The memberfile should contain:
  128. X    fred,john,wilma,barney@bedrock,pebbles
  129. X   You cannot avoid using newaliases however, and *will* get extremely messy
  130. X   if you have to start using nested aliases.
  131. X
  132. Xc. Can only be taken care of by using a mailfilter like procmail.
  133. X
  134. Xd. Can only be taken care of by using a mailfilter like procmail.
  135. X
  136. Xe. Can only be taken care of by using a mailfilter like procmail.
  137. X
  138. Xf. Can only be taken care of by using a mailfilter like procmail.
  139. X
  140. Xh. Can only be taken care of by using a mailfilter like procmail.
  141. X
  142. X
  143. X5. Why use procmail to filter the mailinglist mail?
  144. X   ------------------------------------------------
  145. X
  146. XInstead of using a mailfilter you could also take care of most of the problems
  147. Xthree till seven by editing the sendmail.cf file.  I strongly would recommend
  148. Xagainst this approach however, since this will be too much of a customizing
  149. Xoperation and surely will not be a trivial task (in all cases).     As a general
  150. Xrule: don't mess with a sendmail.cf file once it is working :-).
  151. X
  152. XNow, you could, instead of procmail, simply use immediate VNIX commands
  153. Xlike grep, sed, awk to do the mail filtering.  Again, there are some obvious
  154. Xdisadvantages with this approach:
  155. X
  156. XA. In case any system resources go out (no more file descriptors, no more
  157. X   swap space, process table full, file system full (for temporary files))
  158. X   your awk or shell script will fail generously (i.e. several bad things
  159. X   could happen: mail munged, truncated, lost, hanging awk or sh programs,
  160. X   etc., you get the picture).
  161. X
  162. XB. All mail headers (including From: and Reply-To:) could very well be
  163. X   multi-line headers; it will be very difficult to make it understandable
  164. X   to awk that somehow the header line could continue on the next line
  165. X   (in case you want to remove a header, or do some complicated substitution).
  166. X
  167. XC. Another hairy problem will be determining the end of the header, of course
  168. X   that is solvable, but you have to make some extra precautions in your
  169. X   awk script to ensure that any substitutions/changes will not occur in
  170. X   the body of the message.
  171. X
  172. XProcmail does not *directly* allow you to change any headers, but that
  173. Xfeature is not really necessary since you can tell procmail to send ONLY the
  174. Xheader through some filter of your choice.
  175. X
  176. XTo comment on the previously mentioned three disadvantages:
  177. X
  178. XA. procmail takes care of that.     Should the filter have problems anyway,
  179. X   procmail will graciously notice that the filter was in some kind of
  180. X   trouble, and will try something else with the original unmunged mail
  181. X   (you can specify what it should do of course, obvious choices: try
  182. X   the same filter again, drop the mail in a file and send you a notice,
  183. X   forward the mail to you instead (unfiltered), etc.)
  184. X
  185. XB. procmail will concatenate any headers that were continued according to
  186. X   the RCF 822 recommendations, i.e. your filters will see one line per header.
  187. X
  188. XC. procmail can be told to send the header, the body or both through the
  189. X   filter, hence your filter need not watch out to avoid doing any
  190. X   substitutions in the body, and the filter can therefore be a lot simpler.
  191. X
  192. XProcmail has some additional advantages too:
  193. X
  194. X -- It will probably all go a bit faster, since only the header of the mail
  195. X    is being piped through the filter.    Also, procmail reads in the mail in
  196. X    16KB chunks, not line by line as sed does.
  197. X
  198. X -- You could use procmail to filter out any messages to the normal mailing
  199. X    list that should have gone to the mylist-request and remail them to
  200. X    mylist-request.
  201. X
  202. XWell, anyway, as you see, procmail does not give you everything you would want,
  203. Xbut this was intentional in accordance to the true VNIX spirit (modularity).
  204. XWhat procmail does provide is a *very* reliable hook (you might say it
  205. Xprovides an anchor :-) for any mail processing you might do.  For the more
  206. Xcomplex things you still have to use shell scripts or call other programs
  207. Xfrom within procmail, but then again, that saves you from learning any
  208. Xparticular syntax procmail would have had to do the same.
  209. X
  210. XAs it happens, the accompanying formail program is able to cater to most
  211. X(if not all) of your needs regarding mail munging.
  212. X
  213. XIf, on the other hand, you want to do more complex things like moderated
  214. Xmailing lists with several moderators, etc., if would suggest you take
  215. Xa look at the more complex/specialised mail-server packages like:
  216. Xlistserv available on cs.bu.edu, author: tasos@cs.bu.edu
  217. XOf course, most of what these packages can do, can be done with procmail as
  218. Xwell; it is just that you might be forced to write some additional shell
  219. Xscripts/programs to accomplish the same.
  220. X
  221. X
  222. X6. How do I use procmail to filter the mailinglist mail?
  223. X   -----------------------------------------------------
  224. X
  225. XFirst you have to create these two entries in your /usr/lib/aliases file of
  226. Xmachine "your.domain" (the mylist: line should be typed in as one long line):
  227. X
  228. Xmylist: "|IFS=' ';exec /usr/local/bin/procmail /some/path/listrc subscribers=/some/path/memberlist list=mylist@your.domain listreq=mylist-request@your.domain"
  229. Xmylist-request: your_login_name@your.domain
  230. Xowner-mylist: mylist-request
  231. X
  232. XCreate a file named /some/path/memberlist which contains the names of the
  233. Xsubscribers separated by whitespace (blanks, tabs or newlines) like:
  234. X
  235. X fred john  wilma  barney@bedrock  pebbles
  236. X
  237. XThe /some/path/listrc file you should look like the sample listrc file
  238. Xsupplied in this directory.  This listrc file need only be present once,
  239. Xit will cater for all the mailinglists you like to create.
  240. X
  241. X
  242. X7. Now, what does the above all do?
  243. X   --------------------------------
  244. X
  245. XIf mail arrives at mylist, first of all procmail will be started using
  246. X/some/path/listrc as the rcfile.  Then it will grep the header to check if
  247. Xit could be a bounced message after all (from postmaster or mailer-daemon),
  248. Xor if it probably is a request message.     If neither applies, procmail will
  249. Xfilter just the header of the message through formail.
  250. X
  251. Xformail will remove any "Return-Receipt-To:" fields, and will provide plain
  252. Xsubstitutes for "Errors-To:" and "Sender:".  Then it will look for
  253. Xany "Reply-To:" fields which are already in the header and rewrite them
  254. Xas "Old-Reply-To:";  after having done this it will add your "Reply-To:"
  255. Xfield.    BTW, the "Return-Receipt-To:" and "Errors-To:" fields are not
  256. Xrecommended by the RFC-822, they are however commonly supported by most
  257. Xsendmails;  if they are not supported however, they won't hurt, they will
  258. Xsimply be ignored.
  259. X
  260. XThen, the mail is piped into $SENDMAIL which receives, as command line
  261. Xarguments, the addresses of all subscribers.  The option -f will only
  262. Xtake effect if sendmail is running under daemon privileges; this only
  263. Xoccurs if the sender of the mail is *not* a local user; if the sender
  264. Xis a local user, then sendmail (and procmail) runs as the local user.
  265. X
  266. X*********************************** WARNING **********************************
  267. X*                                         *
  268. X* For this reason it might be wise to allow writing of the memberlist file   *
  269. X* only (to a list maintainer), keep the listrc file under *root supervision* *
  270. X* (i.e. owned by a very reliable person (e.g. root), world readable, but NOT *
  271. X* world writeable).                                 *
  272. X*                                         *
  273. X******************************************************************************
  274. X
  275. X
  276. X8. The result of this exercise
  277. X   ---------------------------
  278. X
  279. XAs you can see, we have addressed and solved every single one of the original
  280. Xseven problems (well, ok, except problem f, that one is left as an excercise
  281. Xto the reader; shouldn't be too difficult).
  282. X
  283. X
  284. XP.S. Any suggestions/corrections/improvements on this document are welcome.
  285. END_OF_FILE
  286. if test 11167 -ne `wc -c <'procmail/examples/mailinglist'`; then
  287.     echo shar: \"'procmail/examples/mailinglist'\" unpacked with wrong size!
  288. fi
  289. # end of 'procmail/examples/mailinglist'
  290. fi
  291. if test -f 'procmail/formail.c' -a "${1}" != "-c" ; then 
  292.   echo shar: Will not clobber existing file \"'procmail/formail.c'\"
  293. else
  294. echo shar: Extracting \"'procmail/formail.c'\" \(12963 characters\)
  295. sed "s/^X//" >'procmail/formail.c' <<'END_OF_FILE'
  296. X/************************************************************************
  297. X *    formail.c    a mail (re)formatter                *
  298. X *                                    *
  299. X *    Seems to be relatively bug free.                *
  300. X *                                    *
  301. X *    Copyright (c) 1990-1992, S.R. van den Berg, The Netherlands    *
  302. X *    The sources can be freely copied for non-commercial use.    *
  303. X *    #include "README"                        *
  304. X *                                    *
  305. X ************************************************************************/
  306. X#ifdef RCS
  307. Xstatic char rcsid[]="$Id: formail.c,v 2.16 1992/01/09 17:23:14 berg Rel $";
  308. X#endif
  309. Xstatic char rcsdate[]="$Date: 1992/01/09 17:23:14 $";
  310. X#include "config.h"                      /* slight overkill */
  311. X#include "includes.h"
  312. X
  313. Xchar*pstrspn();
  314. X
  315. X#define BSIZE        4096
  316. X
  317. X#define NAMEPREFIX    "formail: "
  318. X#define HEAD_DELIMITER    ':'
  319. X
  320. X#define Re        (re+1)
  321. X#define Nextchar(x)    do{if((x=getchar())==EOF)goto foundeof;}while(0)
  322. X#define putssn(a,l)    tputssn(a,(size_t)(l))
  323. X#define putcs(a)    (errout=putc(a,mystdout))
  324. X#define PRDO        poutfd[0]
  325. X#define PWRO        poutfd[1]
  326. X
  327. Xstatic const char From[]=FROM,replyto[]="Reply-To:",Fromm[]="From:",
  328. X returnpath[]="Return-Path",sender[]="Sender:",outofmem[]="Out of memory\n",
  329. X subject[]="Subject:",re[]=" Re:",couldntw[]="Couldn't write to stdout",
  330. X references[]="References:",messageid[]="Message-ID:",Date[]="Date:",
  331. X article[]="Article ",Path[]="Path:",Received[]="Received:",To[]="To: ",
  332. X OldP[]=OLD_PREFIX,inreplyto[]="In-Reply-To:",errorsto[]="Errors-To",
  333. X retreceiptto[]="Return-Receipt-To";
  334. Xconst char binsh[]=BinSh;
  335. X/*
  336. X *    sender determination fields in order of importance reliability
  337. X *    reply-address determination fields (wrepl specifies the weight)
  338. X */
  339. Xstatic const struct {const char*head;int len,wrepl;}sest[]=
  340. X{ {errorsto,STRLEN(errorsto),5},{retreceiptto,STRLEN(retreceiptto),6},
  341. X  {sender,STRLEN(sender),0},{replyto,STRLEN(replyto),4},
  342. X  {Fromm,STRLEN(Fromm),2},{returnpath,STRLEN(returnpath),1}
  343. X};
  344. X/*
  345. X *    digest splitting fields
  346. X */
  347. Xstatic const struct {const char*hedr;int lnr;}cdigest[]=
  348. X{ {Fromm,STRLEN(Fromm)},{Date,STRLEN(Date)},{subject,STRLEN(subject)},
  349. X  {article,STRLEN(article)},{Path,STRLEN(Path)},{Received,STRLEN(Received)}
  350. X};
  351. X
  352. Xstatic struct {const char*const headr;const int lenr;char*rexp;}rex[]=
  353. X{ {subject,STRLEN(subject)},{references,STRLEN(references)},
  354. X  {messageid,STRLEN(messageid)}
  355. X};
  356. X#define subj    rex[0]
  357. X#define refr    rex[1]
  358. X#define msid    rex[2]
  359. X#define mxl(a,b)    mx(STRLEN(a),STRLEN(b))
  360. X#ifndef MAILBOX_SEPARATOR
  361. X#define dig_HDR_LEN    mx(mxl(From,Fromm),mxl(Date,subject))
  362. X#define mboxseparator    From
  363. X#define flushseparator(i,p)
  364. X#else
  365. Xstatic const char mboxseparator[]=MAILBOX_SEPARATOR;
  366. X#define flushseparator(i,p)    \
  367. X do{i=p;p=0;do{int x;Nextchar(x);}while(--i);}while(0)
  368. X#define dig_HDR_LEN    \
  369. X mx(mx(mxl(From,Fromm),mxl(Date,subject)),STRLEN(mboxseparator))
  370. X#endif
  371. Xstatic struct hedit{char*hline;size_t hlen;
  372. X enum{h_ren,h_del,h_add,h_was,h_extract}htype;struct hedit*next;}*hlist;
  373. Xstatic errout,oldstdout,quiet;
  374. Xstatic pid_t child= -1;
  375. Xstatic FILE*mystdout;
  376. Xstatic size_t nrskip,nrtotal= -1;
  377. X
  378. X#ifdef    NOstrstr
  379. Xchar*strstr(whole,part)const char*whole,*const part;
  380. X{ register const char*w,*p;
  381. X  do
  382. X   { w=whole;p=part;
  383. X     do
  384. X    if(!*p)
  385. X       return(char*)whole;
  386. X     while(*w++==*p++);
  387. X   }
  388. X  while(*whole++);
  389. X  return(char*)0;
  390. X}
  391. X#endif
  392. X
  393. Xvoid*tmalloc(len)const size_t len;
  394. X{ void*p;
  395. X  if(p=malloc(len))
  396. X     return p;
  397. X  nlog(outofmem);exit(EX_OSERR);
  398. X}
  399. X
  400. Xvoid*trealloc(old,len)void*old;const size_t len;
  401. X{ if(old=realloc(old,len))
  402. X     return old;
  403. X  nlog(outofmem);exit(EX_OSERR);
  404. X}
  405. X
  406. X#include "shell.h"
  407. X
  408. Xstruct hedit*overrideh(target,keep)const char*const target;const int keep;
  409. X{ struct hedit*hlp;
  410. X  for(hlp=hlist;hlp;hlp=hlp->next)
  411. X     if(hlp->htype!=h_was&&!strnicmp(hlp->hline,target,hlp->hlen))
  412. X      { if(keep>0||hlp->htype!=h_add)              /* found field ok? */
  413. X       return (struct hedit*)hlp;
  414. X    hlp->htype=h_was;            /* temporarily disable field */
  415. X    if(keep)                    /* smaller than zero */
  416. X       break;
  417. X      }
  418. X  return(struct hedit*)0;                   /* no field found */
  419. X}
  420. X
  421. Xmain(lastm,argv)const char*const argv[];
  422. X{ int i,ch,nowm,split=0,force=0,bogus=1,every=0,areply=0,trust=0,digest=0,
  423. X   nowait=0,keepb=0,extract=0;
  424. X  size_t buflen,p=0,lnl=0,ll;time_t t;char*buf,*chp,*namep;struct hedit*hlp;
  425. X  while(chp=(char*)*++argv)
  426. X   { if((lastm= *chp++)==FM_SKIP)
  427. X    goto number;
  428. X     else if(lastm!=FM_TOTAL)
  429. X    goto usg;
  430. X     for(;;)
  431. X      { switch(lastm= *chp++)
  432. X     { case FM_TRUST:trust=1;continue;
  433. X       case FM_REPLY:areply=1;continue;
  434. X       case FM_FORCE:force=1;continue;
  435. X       case FM_EVERY:every=1;bogus=0;continue;
  436. X       case FM_DIGEST:digest=1;continue;
  437. X       case FM_NOWAIT:nowait=1;continue;
  438. X       case FM_KEEPB:keepb=1;continue;
  439. X       case FM_QUIET:quiet=1;continue;
  440. X       case FM_SPLIT:split=1;
  441. X          if(!*chp&&*++argv)
  442. X         goto parsedoptions;
  443. X          goto usg;
  444. Xnumber:       default:
  445. X          if(*chp-'0'>(unsigned)9)
  446. X           {
  447. Xusg:         log(FM_USAGE);return EX_USAGE;
  448. X           }
  449. X          ll=strtol(chp,(char**)0,10);
  450. X          if(lastm==FM_SKIP)
  451. X         nrskip=ll;
  452. X          else
  453. X         nrtotal=ll;
  454. X          break;
  455. X       case FM_BOGUS:bogus=0;continue;
  456. X       case FM_EXTRACT:extract=1;
  457. X       case FM_ADD_IFNOT:case FM_REN_INSERT:case FM_DEL_INSERT:hlp=hlist;
  458. X          (hlist=malloc(sizeof*hlist))->next=hlp;
  459. X          if(!*chp&&!(chp=(char*)*++argv))    /* concatenated or seperate? */
  460. X         goto usg;
  461. X          hlist->hline=chp;                 /* add field string */
  462. X          if(!(buf=strchr(chp,HEAD_DELIMITER)))
  463. X           { nlog("Invalid field-name:");logqnl(chp);goto usg;
  464. X           }
  465. X          hlist->hlen=buf-chp+1;
  466. X          hlist->htype=lastm==FM_REN_INSERT?h_ren:
  467. X           lastm==FM_DEL_INSERT?h_del:lastm==FM_ADD_IFNOT?h_add:h_extract;
  468. X       case '\0':;
  469. X     }
  470. X    break;
  471. X      }
  472. X   }
  473. Xparsedoptions:
  474. X#ifdef MAILBOX_SEPARATOR
  475. X  if(split)
  476. X   { every=1;
  477. X     if(!areply)
  478. X    bogus=0;
  479. X   }
  480. X#endif
  481. X  mystdout=stdout;signal(SIGPIPE,SIG_IGN);
  482. X  if(split)
  483. X   { oldstdout=dup(STDOUT);fclose(stdout);startprog(argv);
  484. X   }
  485. X  else if(every)
  486. X     goto usg;
  487. X  namep=malloc(1);buf=malloc(buflen=BSIZE);t=time((time_t*)0);
  488. X  i=maxindex(rex);
  489. X  do rex[i].rexp=malloc(1);
  490. X  while(i--);
  491. X  while('\n'==(ch=getchar()));
  492. Xstartover:
  493. X  *namep='\0';i=maxindex(rex);
  494. X  do *rex[i].rexp='\0';
  495. X  while(i--);
  496. X  for(;;)                     /* start parsing the header */
  497. X   { if((buf[p++]=ch)=='\n')
  498. X      { if(lnl==p-1)                    /* end of header reached */
  499. X       break;
  500. X    switch(ch=getchar())              /* concatenate continued lines */
  501. X     { case ' ':case '\t':p--;continue;
  502. X       case EOF:ch='\n';
  503. X     }
  504. X    chp=buf+lnl;
  505. X#ifdef MAILBOX_SEPARATOR
  506. X    if(!strncmp(mboxseparator,chp,STRLEN(mboxseparator)))
  507. X     { if(!lnl)
  508. X        { if(split)
  509. X           { p=0;goto redigest;
  510. X           }
  511. X          force=1;         /* separator up front, don't add a 'From ' line */
  512. X        }
  513. X       else if(bogus)
  514. X          *chp=' ';
  515. X     }
  516. X#endif
  517. X    i=maxindex(rex);
  518. X    while(strnicmp(rex[i].headr,chp,ll=rex[i].lenr)&&i--);
  519. X    if(i>=0)                  /* found anything already? */
  520. X     { ll=p-lnl-ll;
  521. X       ((char*)tmemmove(rex[i].rexp=realloc(rex[i].rexp,ll),
  522. X        buf+lnl+rex[i].lenr,ll))[ll-1]='\0';
  523. X     }
  524. X    else if(!strncmp(From,chp,STRLEN(From)))
  525. X     { if(!lnl)                /* was the real "From " line */
  526. X        { nowm=trust?1:3/*wreply*/;ll=lnl+STRLEN(From);goto foundfrom;
  527. X        }
  528. X#ifndef MAILBOX_SEPARATOR
  529. X       if(bogus)
  530. X        { tmemmove(chp+1,chp,p++-lnl);*chp=ESCAP;           /* disarm */
  531. X        }
  532. X#endif
  533. X     }
  534. X    else
  535. X     { i=maxindex(sest);
  536. X       do
  537. X          if(!strnicmp(sest[i].head,chp,sest[i].len))
  538. X           { nowm=areply?keepb&&sest[i].head==replyto?
  539. X          maxindex(sest)+1:sest[i].wrepl:i;
  540. X         ll=lnl+sest[i].len;
  541. Xfoundfrom:     buf[p]='\0';
  542. X         if(chp=strchr(buf+ll,'<'))          /* extract the address */
  543. X            ll=chp-buf+1;
  544. X         if((i=strcspn(chp=pstrspn(buf+ll," \t"),">(\n \t"))&&
  545. X          (!*namep||nowm>lastm))
  546. X          { ((char*)tmemmove(namep=realloc(namep,i+1),chp,i))[i]='\0';
  547. X            lastm=strstr(chp,".UUCP")?nowm-maxindex(sest)-1:nowm;
  548. X          }
  549. X         break;
  550. X        }
  551. X       while(i--);
  552. X     }
  553. X    if(hlp=overrideh(buf+lnl,areply))     /* replace or delete field? */
  554. X       switch(hlp->htype)
  555. X        { case h_extract:putssn(buf+lnl+hlp->hlen,p-lnl-hlp->hlen);
  556. X          case h_del:p=lnl;continue;           /* just delete it */
  557. X          case h_ren:
  558. X         if(p+2>=buflen)        /* trouble if BSIZE<STRLEN(OldP) */
  559. X            buf=realloc(buf,buflen+=BSIZE);
  560. X         tmemmove(buf+lnl+STRLEN(OldP),buf+lnl,p-lnl);
  561. X         tmemmove(buf+lnl,OldP,STRLEN(OldP));p+=STRLEN(OldP);
  562. X        }
  563. X    lnl=p;continue;
  564. X      }
  565. X     if(p>=buflen-2)
  566. X    buf=realloc(buf,buflen+=BSIZE);
  567. Xredigest:
  568. X     if((ch=getchar())==EOF)
  569. X    ch='\n';        /* make sure the header ends with 2 newlines */
  570. X   }
  571. X  if(!extract)
  572. X   { if(areply||!force&&strncmp(buf,From,STRLEN(From)))
  573. X      { if(!areply||!overrideh(To,!*namep))
  574. X     { putss(areply?(areply=2),To:From);
  575. X       if(*namep)                /* found any sender address? */
  576. X          putss(namep);
  577. X       else
  578. X          putss(UNKNOWN);
  579. X     }
  580. X    if(areply)
  581. X     { if(areply==2)
  582. X          putnl();
  583. X       if(*subj.rexp&&!overrideh(subject,-1))       /* any Subject: ? */
  584. X        { putss(subject);chp=subj.rexp;
  585. X          if(strnicmp(pstrspn(chp," "),Re,STRLEN(Re)))
  586. X         putss(re);               /* no Re: , add one ourselves */
  587. X          putss(chp);putnl();
  588. X        }
  589. X       if(*refr.rexp||*msid.rexp)     /* any Message-ID: or References: ? */
  590. X        { if(!overrideh(references,-1))
  591. X           { putss(references);
  592. X         if(*refr.rexp)
  593. X          { putss(refr.rexp);
  594. X            if(*msid.rexp)
  595. X               putnl();
  596. X          }
  597. X         if(*msid.rexp)
  598. X          { putss(msid.rexp);putnl();
  599. X          }
  600. X           }
  601. X          if(*msid.rexp&&!overrideh(inreplyto,-1))
  602. X           { putss(inreplyto);putss(msid.rexp);putnl();
  603. X           }
  604. X        }
  605. X     }
  606. X    else
  607. X     { putcs(' ');putss(ctime(&t));
  608. X     }
  609. X      }
  610. X     if(!areply)
  611. X    putssn(buf,p-1);
  612. X     for(hlp=hlist;hlp;hlp=hlp->next)
  613. X    if(hlp->htype==h_was)
  614. X       hlp->htype=h_add;              /* enable disabled field again */
  615. X    else if(hlp->hline[hlp->hlen])
  616. X     { putss(hlp->hline);putnl();            /* inject our new fields */
  617. X     }
  618. X     putnl();
  619. X   }
  620. X  if(areply&&!keepb||extract)
  621. X   { if(split)
  622. X    closemine();
  623. X     opensink();                     /* discard the body */
  624. X   }
  625. X  p=0;lnl=1;                     /* clear buffer, important! */
  626. X  if(!bogus&&!split)
  627. X     for(;;putcs(i))
  628. X    Nextchar(i);
  629. X  for(;;)                           /* continue the quest */
  630. X   { do                         /* read line until not From */
  631. X      { if(p==buflen-1)
  632. X       buf=realloc(buf,++buflen);
  633. X    Nextchar(i=buf[p]);
  634. X    if(++p==STRLEN(mboxseparator))
  635. X       if(!strncmp(mboxseparator,buf,STRLEN(mboxseparator)))
  636. X        { if(every)
  637. X           { flushseparator(i,p);goto splitit;     /* optionally flush */
  638. X           }
  639. X          else if(split&&lnl)
  640. X         lnl=2;               /* mark line as possible postmark */
  641. X          else if(bogus)                       /* disarm */
  642. X           {
  643. X#ifndef MAILBOX_SEPARATOR
  644. X         putcs(ESCAP);break;
  645. X#else
  646. X         Nextchar(i);*buf=' ';putssn(buf,p);*buf=i;p=1;continue;
  647. X#endif
  648. X           }
  649. X        }
  650. X    if(lnl==1&&digest)
  651. X     { ll=maxindex(cdigest);
  652. X       do                      /* check for new digest header */
  653. X          if(p==cdigest[ll].lnr&&!strncmp(buf,cdigest[ll].hedr,p))
  654. X         goto splitit;
  655. X       while(ll--);
  656. X     }
  657. X      }
  658. X     while(i!='\n'&&(lnl==2||p<dig_HDR_LEN));
  659. X     if(lnl==2)
  660. X      { buf[p]='\0';         /* perform more thorough check for postmark */
  661. X    if((ll=strcspn(chp=pstrspn(buf+STRLEN(From)," ")," \t\n"))&&
  662. X     *(chp+=ll)==' '&&(ll= *(chp=pstrspn(chp," ")))!='\t'&&ll!='\n')
  663. X     {
  664. Xsplitit:   if((fclose(mystdout)==EOF||errout==EOF)&&!quiet)
  665. X        { nlog(couldntw);log(", continuing...\n");split= -1;
  666. X        }
  667. X       if(!nowait)
  668. X          waitforit();
  669. X       startprog(argv);ch=i;--p;lnl=0;goto startover;
  670. X     }
  671. X      }
  672. X     if(areply&&bogus&&*buf!='\n')
  673. X    putcs(ESCAP);                      /* escape the body */
  674. X     lnl=p==1;putssn(buf,p);p=0;
  675. X     if(i!='\n')
  676. X    do Nextchar(i);
  677. X    while(putcs(i),i!='\n');
  678. X   }
  679. Xfoundeof:
  680. X  putssn(buf,p);closemine();child= -1;waitforit();    /* wait for everyone */
  681. X  return split<0?EX_IOERR:EX_OK;
  682. X}
  683. X
  684. Xlog(a)const char*const a;
  685. X{ fputs(a,stderr);
  686. X}
  687. X
  688. Xlogqnl(a)const char*a;
  689. X{ log(" \"");log(a);log("\"\n");
  690. X}
  691. X
  692. Xputss(a)const char*a;
  693. X{ while(*a)
  694. X     putcs(*a++);
  695. X}
  696. X
  697. Xtputssn(a,l)const char*a;size_t l;
  698. X{ while(l--)
  699. X     putcs(*a++);
  700. X}
  701. X
  702. Xstartprog(argv)const char*const*const argv;
  703. X{ int poutfd[2];
  704. X  if(!nrtotal)
  705. X     goto squelch;
  706. X  if(nrskip)
  707. X   { --nrskip;
  708. Xsquelch:
  709. X     opensink();return;
  710. X   }
  711. X  if(nrtotal>0)
  712. X     --nrtotal;
  713. X  dup(oldstdout);pipe(poutfd);
  714. X  if(!(child=fork()))
  715. X   { close(oldstdout);close(PWRO);fclose(stdin);dup(PRDO);close(PRDO);
  716. X     shexec(argv);
  717. X   }
  718. X  close(STDOUT);close(PRDO);
  719. X  if(STDOUT!=dup(PWRO)||!(mystdout=fdopen(STDOUT,"a")))
  720. X     nofild();
  721. X  close(PWRO);
  722. X  if(-1==child)
  723. X   { nlog("Can't fork\n");exit(EX_OSERR);
  724. X   }
  725. X}
  726. X
  727. Xnofild()
  728. X{ nlog("File table full\n");exit(EX_OSERR);
  729. X}
  730. X
  731. Xwaitforit()
  732. X{ int i;pid_t j;
  733. X  while(child!=(j=wait(&i))||(i&127)==127)
  734. X    if(-1==j)
  735. X       return;
  736. X}
  737. X
  738. Xnlog(a)const char*const a;
  739. X{ log(NAMEPREFIX);log(a);
  740. X}
  741. X
  742. Xclosemine()
  743. X{ if((fclose(mystdout)==EOF||errout==EOF)&&!quiet)
  744. X   { nlog(couldntw);log("\n");;exit(EX_IOERR);
  745. X   }
  746. X}
  747. X
  748. Xopensink()
  749. X{ if(!(mystdout=fopen(DevNull,"a")))
  750. X     nofild();
  751. X}
  752. X
  753. Xputnl()
  754. X{ putcs('\n');
  755. X}
  756. X
  757. Xstrnicmp(a,b,l)register const char*a,*b;register unsigned l;
  758. X{ int i,j;
  759. X  if(l)                         /* case insensitive strncmp */
  760. X     do
  761. X      { while(*a&&*a==*b&&--l)
  762. X       ++a,++b;
  763. X    if(!l)
  764. X       break;
  765. X    if((i= *a++)>='A'&&i<='Z')
  766. X       i+='a'-'A';
  767. X    if((j= *b++)>='A'&&j<='Z')
  768. X       j+='a'-'A';
  769. X    if(j!=i)
  770. X       return i>j?1:-1;
  771. X      }
  772. X     while(i&&j&&--l);
  773. X  return 0;
  774. X}
  775. END_OF_FILE
  776. if test 12963 -ne `wc -c <'procmail/formail.c'`; then
  777.     echo shar: \"'procmail/formail.c'\" unpacked with wrong size!
  778. fi
  779. # end of 'procmail/formail.c'
  780. fi
  781. if test -f 'procmail/retint.c' -a "${1}" != "-c" ; then 
  782.   echo shar: Will not clobber existing file \"'procmail/retint.c'\"
  783. else
  784. echo shar: Extracting \"'procmail/retint.c'\" \(12095 characters\)
  785. sed "s/^X//" >'procmail/retint.c' <<'END_OF_FILE'
  786. X/************************************************************************
  787. X *    Collection of routines that return an int (sort of anyway :-)    *
  788. X *                                    *
  789. X *    Copyright (c) 1990-1992, S.R. van den Berg, The Netherlands    *
  790. X *    The sources can be freely copied for non-commercial use.    *
  791. X *    #include "README"                        *
  792. X *                                    *
  793. X ************************************************************************/
  794. X#ifdef RCS
  795. Xstatic char rcsid[]="$Id: retint.c,v 2.23 1992/01/31 12:28:53 berg Rel $";
  796. X#endif
  797. X#include "config.h"
  798. X#include "procmail.h"
  799. X#include "shell.h"
  800. X
  801. Xsetdef(name,contents)const char*const name,*const contents;
  802. X{ strcat(strcat(strcpy(sgetcp=buf2,name),"="),contents);
  803. X  readparse(buf,sgetc,0);sputenv(buf);
  804. X}
  805. X
  806. Xchar*lastexec,*backblock;
  807. Xlong backlen;               /* length of backblock, filter recovery block */
  808. Xpid_t pidfilt,pidchild;
  809. Xint pbackfd[2];                       /* the emergency backpipe :-) */
  810. X
  811. Xpipthrough(line,source,len)char*line,*source;const long len;
  812. X{ int pinfd[2],poutfd[2];
  813. X  rpipe(pbackfd);rpipe(pinfd);                 /* main pipes setup */
  814. X  if(!(pidchild=sfork()))            /* create a sending procmail */
  815. X   { backblock=source;backlen=len;signal(SIGTERM,stermchild);
  816. X     signal(SIGINT,stermchild);signal(SIGHUP,stermchild);
  817. X     signal(SIGQUIT,stermchild);rclose(rc);rclose(PRDI);rclose(PRDB);
  818. X     rpipe(poutfd);rclose(STDOUT);
  819. X     if(!(pidfilt=sfork()))                /* create the filter */
  820. X      { rclose(PWRO);rclose(PWRB);rdup(PWRI);rclose(PWRI);getstdin(PRDO);
  821. X    callnewprog(line);
  822. X      }
  823. X     rclose(PWRI);rclose(PRDO);
  824. X     if(forkerr(pidfilt,line))
  825. X      { rclose(PWRO);stermchild();
  826. X      }
  827. X     if(dump(PWRO,source,len))          /* send in the text to be filtered */
  828. X      { writeerr(line);stermchild();
  829. X      }
  830. X     if(pwait&&waitfor(pidfilt)!=EX_OK)     /* check the exitcode of the filter */
  831. X      { progerr(line);stermchild();
  832. X      }
  833. X     rclose(PWRB);exit(EX_OK);              /* allow parent to proceed */
  834. X   }
  835. X  rclose(PWRI);rclose(PWRB);getstdin(PRDI);
  836. X  if(forkerr(pidchild,procmailn))
  837. X     return 1;
  838. X  return 0;            /* we stay behind to read back the filtered text */
  839. X}
  840. X
  841. Xwaitfor(pid)const pid_t pid;              /* wait for a specific process */
  842. X{ int i;pid_t j;
  843. X  while(pid!=(j=wait(&i))||(i&127)==127)
  844. X     if(-1==j)
  845. X    return EX_UNAVAILABLE;
  846. X  return i>>8&255;
  847. X}
  848. X
  849. Xgetstdin(pip)const int pip;
  850. X{ rclose(STDIN);rdup(pip);rclose(pip);
  851. X}
  852. X
  853. Xcallnewprog(newname)const char*const newname;
  854. X{ if(sh)                     /* should we start a shell? */
  855. X   { const char*newargv[4];
  856. X     yell(executing,newname);newargv[3]=0;newargv[2]=newname;
  857. X     newargv[1]=tgetenv(shellflags);*newargv=tgetenv(shell);shexec(newargv);
  858. X   }
  859. X {register const char*p;int argc;const char**newargv;
  860. X  argc=1;p=newname;         /* If no shell, chop up the arguments ourselves */
  861. X  if(verbose)
  862. X   { log(executing);log(oquote);goto no_1st_comma;
  863. X   }
  864. X  do                         /* show chopped up command line */
  865. X   { if(verbose)
  866. X      { log(",");
  867. Xno_1st_comma:
  868. X    log(p);
  869. X      }
  870. X     while(*p++);
  871. X   }
  872. X  while(argc++,*p!=TMNATE);
  873. X  if(verbose)
  874. X     log(cquote);
  875. X  newargv=malloc(argc*sizeof*newargv);p=newname;argc=0;     /* alloc argv array */
  876. X  do
  877. X   { newargv[argc++]=p;
  878. X     while(*p++);
  879. X   }
  880. X  while(*p!=TMNATE);
  881. X  newargv[argc]=0;shexec(newargv);
  882. X }
  883. X}
  884. X
  885. Xwriteerr(line)const char*const line;
  886. X{ log("Error while writing to");logqnl(line);
  887. X}
  888. X
  889. Xforkerr(pid,a)const pid_t pid;const char*const a;
  890. X{ if(pid==-1)
  891. X   { log("Failed forking");logqnl(a);return 1;
  892. X   }
  893. X  return 0;
  894. X}
  895. X
  896. Xprogerr(line)const char*const line;
  897. X{ log("Program failure of");logqnl(line);
  898. X}
  899. X
  900. Xopena(a)const char*const a;
  901. X{ lastfolder=cstr(lastfolder,a);yell("Opening",a);
  902. X#ifdef O_CREAT
  903. X  return ropen(a,O_WRONLY|O_APPEND|O_CREAT,NORMperm);
  904. X#else
  905. X {int fd;
  906. X  return(fd=ropen(a,O_WRONLY,0))<0?creat(a,NORMperm):fd;
  907. X }
  908. X#endif
  909. X}
  910. X
  911. Xyell(a,b)const char*const a,*const b;             /* log if -d option set */
  912. X{ if(verbose)
  913. X   { log(a);logqnl(b);
  914. X   }
  915. X}
  916. X
  917. Xunlock(lockp)const char**const lockp;
  918. X{ lcking=1;
  919. X  if(*lockp)
  920. X   { yell("Unlocking",*lockp);
  921. X     if(unlink(*lockp))
  922. X      { log("Couldn't unlock");logqnl(*lockp);
  923. X      }
  924. X     free(*lockp);*lockp=0;
  925. X   }
  926. X  lcking=0;
  927. X  if(nextexit==1)        /* make sure we are not inside terminate already */
  928. X   { log(newline);terminate();
  929. X   }
  930. X}
  931. X
  932. Xnomemerr()
  933. X{ log("Out of memory\nbuffer 0: \"");buf[linebuf-1]=buf2[linebuf-1]='\0';
  934. X  log(buf);log("\"\nbuffer 1:");logqnl(buf2);
  935. X  if(retval!=EX_TEMPFAIL)
  936. X     retval=EX_OSERR;
  937. X  terminate();
  938. X}
  939. X
  940. Xlogqnl(a)const char*const a;
  941. X{ log(oquote);log(a);log(cquote);
  942. X}
  943. X
  944. Xnextrcfile()            /* next rcfile specified on the command line */
  945. X{ const char*p;
  946. X  while(p= *gargv)
  947. X   { gargv++;
  948. X     if(!strchr(p,'='))
  949. X      { rcfile=p;return 1;
  950. X      }
  951. X   }
  952. X  return 0;
  953. X}
  954. X
  955. Xrclose(fd)const int fd;              /* a sysV secure close (signal immune) */
  956. X{ int i;
  957. X  while((i=close(fd))&&errno==EINTR);
  958. X  return i;
  959. X}
  960. X
  961. Xrwrite(fd,a,len)const int fd,len;void*const a;          /* a sysV secure write */
  962. X{ int i;
  963. X  while(0>(i=write(fd,a,(size_t)len))&&errno==EINTR);
  964. X  return i;
  965. X}
  966. X
  967. Xrread(fd,a,len)const int fd,len;void*const a;           /* a sysV secure read */
  968. X{ int i;
  969. X  while(0>(i=read(fd,a,(size_t)len))&&errno==EINTR);
  970. X  return i;
  971. X}
  972. X
  973. Xropen(name,mode,mask)const char*const name;const int mode;const mode_t mask;
  974. X{ int i,r;                           /* a sysV secure open */
  975. X  if(!lcking)                          /* already locking */
  976. X     lcking=4;
  977. X  for(r=noresretry;0>(i=open(name,mode,mask));)
  978. X     if(errno!=EINTR&&!((errno==EMFILE||errno==ENFILE)&&(r<0||r--)))
  979. X    break;         /* survives a temporary "file table full" condition */
  980. X  if(lcking==4)
  981. X     lcking=0;
  982. X  return i;
  983. X}
  984. X
  985. Xrdup(p)const int p;
  986. X{ int i,r;
  987. X  for(lcking=4,r=noresretry;0>(i=dup(p));)      /* catch "file table full" */
  988. X     if(!((errno==EMFILE||errno==ENFILE)&&(r<0||r--)))
  989. X    break;
  990. X  lcking=0;return i;
  991. X}
  992. X
  993. Xrpipe(fd)int fd[2];
  994. X{ int i,r;
  995. X  for(lcking=4,r=noresretry;0>(i=pipe(fd));)      /* catch "file table full" */
  996. X     if(!((errno==EMFILE||errno==ENFILE)&&(r<0||r--)))
  997. X      { *fd=fd[1]= -1;break;
  998. X      }
  999. X  lcking=0;return i;
  1000. X}
  1001. X
  1002. Xlockit(name,lockp)char*name;const char**const lockp;
  1003. X{ int i,permanent=2;struct stat stbuf;
  1004. X  unlock(lockp);               /* unlock any previous lockfile FIRST */
  1005. X  if(!*name)              /* to prevent deadlocks (I hate deadlocks) */
  1006. X     return;
  1007. X  name=tstrdup(name); /* allocate now, so we won't hang on memory *and* lock */
  1008. X  for(;;)
  1009. X   { yell("Locking",name);
  1010. X     if(!NFSxopen(name,LOCKperm))
  1011. X      { *lockp=name;break;               /* lock acquired, hurray! */
  1012. X      }
  1013. X     switch(errno)
  1014. X      { case EEXIST:
  1015. X     { time_t t;           /* check if it's time for a lock override */
  1016. X       if(!stat(name,&stbuf)&&stbuf.st_size<=MAX_LOCK_SIZE&&locktimeout
  1017. X        &&(t=time((time_t*)0),!stat(name,&stbuf))&&     /* stat till unlink */
  1018. X        locktimeout<t-stbuf.st_mtime)        /* should be atomic, */
  1019. X          if(unlink(name))               /* but I can't guarantee that */
  1020. X           { log("Forced unlock denied on");logqnl(name);
  1021. X           }
  1022. X          else
  1023. X           { log("Forcing lock on");logqnl(name);suspend();
  1024. X           }
  1025. X       break;
  1026. X     }
  1027. X    default:           /* maybe filename too long, shorten and retry */
  1028. X       if(0<(i=strlen(name)-1)&&!strchr(dirsep,name[i-1]))
  1029. X        { log("Truncating");logqnl(name);log(" and retrying lock\n");
  1030. X          name[i]='\0';continue;
  1031. X        }
  1032. Xfaillock:  log("Lock failure on");logqnl(name);goto term;
  1033. X    case ENOENT:case ENOTDIR:case EIO:case EACCES:
  1034. X       if(!--permanent)
  1035. X          goto faillock;
  1036. X    case ENOSPC:;
  1037. X#ifdef EDQUOT
  1038. X    case EDQUOT:;
  1039. X#endif
  1040. X      }
  1041. X     sleep((unsigned)locksleep);
  1042. X     if(nextexit)
  1043. X      {
  1044. Xterm:    free(name);break;             /* drop the preallocated buffer */
  1045. X      }
  1046. X   }
  1047. X  lcking=0;
  1048. X  if(nextexit)
  1049. X   { log(whilstwfor);log("lockfile");logqnl(name);terminate();
  1050. X   }
  1051. X}
  1052. X
  1053. Xlcllock()                   /* lock a local file (if need be) */
  1054. X{ if(locknext)
  1055. X     if(tolock)
  1056. X    lockit(tolock,&loclock);
  1057. X     else
  1058. X    lockit(strcat(buf2,tgetenv(lockext)),&loclock);
  1059. X}
  1060. X
  1061. Xsterminate()
  1062. X{ static const char*const msg[]={newline,0,"memory\n","fork\n",
  1063. X   "a file descriptor\n","a kernel lock\n"};
  1064. X  ignoreterm();
  1065. X  if(pidchild>0)        /* don't kill what is not ours, we might be root */
  1066. X     kill(pidchild,SIGTERM);
  1067. X  if(!nextexit)
  1068. X   { nextexit=1;log("Terminating prematurely");
  1069. X     if(1!=lcking)
  1070. X      { if(1<lcking)
  1071. X       log(whilstwfor);
  1072. X    log(msg[lcking]);terminate();
  1073. X      }
  1074. X   }
  1075. X}
  1076. X
  1077. Xterminate()
  1078. X{ nextexit=2;            /* prevent multiple invocations of terminate */
  1079. X  if(getpid()==thepid)
  1080. X   { if(retval!=EX_OK)
  1081. X      { log(Mail);
  1082. X    log(fakedelivery?"lost\n":
  1083. X     retval==EX_TEMPFAIL?"requeued\n":"bounced\n");
  1084. X      }
  1085. X     unlock(&loclock);unlock(&globlock);fdunlock();
  1086. X   }
  1087. X  exit(fakedelivery==2?EX_OK:retval);
  1088. X}
  1089. X
  1090. Xignoreterm()
  1091. X{ signal(SIGTERM,SIG_IGN);signal(SIGHUP,SIG_IGN);signal(SIGINT,SIG_IGN);
  1092. X  signal(SIGQUIT,SIG_IGN);
  1093. X}
  1094. X
  1095. Xsuspend()
  1096. X{ long t;
  1097. X  sleep((unsigned)suspendv);
  1098. X  if(alrmtime)
  1099. X     if((t=alrmtime-time((time_t*)0))<=1)      /* if less than 1s timeout */
  1100. X    ftimeout();                  /* activate it by hand now */
  1101. X     else            /* set it manually again, to avoid problems with */
  1102. X    alarm((unsigned)t);    /* badly implemented sleep library functions */
  1103. X}
  1104. X
  1105. Xinittmout(progname)const char*const progname;
  1106. X{ lastexec=cstr(lastexec,progname);
  1107. X  alrmtime=timeoutv?time((time_t*)0)+(unsigned)timeoutv:0;
  1108. X  alarm((unsigned)timeoutv);
  1109. X}
  1110. X
  1111. Xskipspace()
  1112. X{ while(testb(' ')||testb('\t'));
  1113. X}
  1114. X
  1115. Xsgetc()                        /* a fake fgetc for a string */
  1116. X{ return *sgetcp?*(uchar*)sgetcp++:EOF;
  1117. X}
  1118. X
  1119. Xskipped(x)const char*const x;
  1120. X{ log("Skipped");logqnl(x);
  1121. X}
  1122. X
  1123. Xconcatenate(old)char*const old;
  1124. X{ register char*p=old;
  1125. X  while(*p!=TMNATE)              /* concatenate all other arguments */
  1126. X   { while(*p++);
  1127. X     p[-1]=' ';
  1128. X   }
  1129. X  *p=p[-1]='\0';return*old;
  1130. X}
  1131. X
  1132. Xdetab(p)char*p;
  1133. X{ while(p=strchr(p,'\t'))
  1134. X     *p=' ';                        /* take out all tabs */
  1135. X}
  1136. X
  1137. Xstatic uchar rcbuf[STDBUF],*rcbufp,*rcbufend;     /* buffers for custom stdio */
  1138. Xstatic ungetb;                         /* pushed back char */
  1139. X
  1140. Xbopen(name)const char*const name;                 /* my fopen */
  1141. X{ rcbufp=rcbufend=0;ungetb= -1;yell("Rcfile:",name);
  1142. X  return rc=ropen(name,O_RDONLY,0);
  1143. X}
  1144. X
  1145. Xgetbl(p)char*p;                              /* my gets */
  1146. X{ int i;char*q;
  1147. X  for(q=p;;)
  1148. X   { switch(i=getb())
  1149. X      { case '\n':case EOF:
  1150. X       *p='\0';return p!=q;             /* did we read anything at all? */
  1151. X      }
  1152. X     *p++=i;
  1153. X   }
  1154. X}
  1155. X
  1156. Xgetb()                                 /* my fgetc */
  1157. X{ if(ungetb>=0)                        /* anything pushed back? */
  1158. X   { int i;
  1159. X     i=ungetb;ungetb= -1;return i;
  1160. X   }
  1161. X  if(rcbufp==rcbufend)
  1162. X   { rcbufend=rcbuf+rread(rc,rcbufp=rcbuf,STDBUF);           /* refill */
  1163. X   }
  1164. X  return rcbufp<rcbufend?*rcbufp++:EOF;
  1165. X}
  1166. X
  1167. Xtestb(x)const int x;           /* fgetc that only succeeds if it matches */
  1168. X{ int i;
  1169. X  if((i=getb())==x)
  1170. X     return 1;
  1171. X  ungetb=i;return 0;
  1172. X}
  1173. X
  1174. Xalphanum(c)const int c;
  1175. X{ return c>='0'&&c<='9'||c>='a'&&c<='z'||c>='A'&&c<='Z'||c=='_';
  1176. X}
  1177. X                       /* open file or new file in directory */
  1178. Xdeliver(boxname)char*const boxname;
  1179. X{ struct stat stbuf;
  1180. X  strcpy(buf,boxname);             /* boxname can be found back in buf */
  1181. X  return stat(buf,&stbuf)||!S_ISDIR(stbuf.st_mode)?
  1182. X   (tofolder=1,opena(buf)):dirmail();
  1183. X}
  1184. X
  1185. X#ifndef fdlock
  1186. Xstatic oldfdlock;                    /* the fd we locked last */
  1187. X#ifdef F_SETLKW
  1188. Xstatic struct flock flck;        /* why can't it be a local variable? */
  1189. X
  1190. Xfdlock(fd)                       /* the POSIX-fcntl() lock */
  1191. X{ flck.l_type=F_WRLCK;flck.l_whence=SEEK_SET;flck.l_len=0;
  1192. X  flck.l_start=tell(fd);lcking=5;fd=fcntl(oldfdlock=fd,F_SETLKW,&flck);
  1193. X  lcking=0;return fd;
  1194. X}
  1195. X
  1196. Xfdunlock()
  1197. X{ flck.l_type=F_UNLCK;return fcntl(oldfdlock,F_SETLK,&flck);
  1198. X}
  1199. X#else /* F_SETLKW */
  1200. X#ifdef F_LOCK
  1201. Xstatic long oldlockoffset;
  1202. X
  1203. Xfdlock(fd)                         /* the sysV-lockf() */
  1204. X{ oldlockoffset=tell(fd);lcking=5;fd=lockf(oldfdlock=fd,F_LOCK,0L);lcking=0;
  1205. X  return fd;
  1206. X}
  1207. X
  1208. Xfdunlock()
  1209. X{ lseek(oldfdlock,oldlockoffset,SEEK_SET);return lockf(oldfdlock,F_ULOCK,0L);
  1210. X}
  1211. X#else /* F_LOCK */
  1212. X#ifdef LOCK_EX
  1213. Xfdlock(fd)                          /* the BSD-flock() */
  1214. X{ lcking=5;fd=flock(oldfdlock=fd,LOCK_EX);lcking=0;return fd;
  1215. X}
  1216. X
  1217. Xfdunlock()
  1218. X{ return flock(oldfdlock,LOCK_UN);
  1219. X}
  1220. X#endif /* LOCK_EX */
  1221. X#endif /* F_LOCK */
  1222. X#endif /* F_SETLKW */
  1223. X#endif /* fdlock */
  1224. X
  1225. X#include "exopen.h"
  1226. X                    /* an NFS secure exclusive file open */
  1227. XNFSxopen(name,mode)char*name;const mode_t mode;
  1228. X{ char*p;int j= -2,i;
  1229. X  i=lastdirsep(name)-name;strncpy(p=malloc(i+UNIQnamelen),name,i);lcking=1;
  1230. X  if(unique(p,p+i,mode))
  1231. X     j=myrename(p,name);     /* try and rename it, fails if nonexclusive */
  1232. X  free(p);return j;
  1233. X}
  1234. END_OF_FILE
  1235. if test 12095 -ne `wc -c <'procmail/retint.c'`; then
  1236.     echo shar: \"'procmail/retint.c'\" unpacked with wrong size!
  1237. fi
  1238. # end of 'procmail/retint.c'
  1239. fi
  1240. echo shar: End of archive 4 \(of 5\).
  1241. cp /dev/null ark4isdone
  1242. MISSING=""
  1243. for I in 1 2 3 4 5 ; do
  1244.     if test ! -f ark${I}isdone ; then
  1245.     MISSING="${MISSING} ${I}"
  1246.     fi
  1247. done
  1248. if test "${MISSING}" = "" ; then
  1249.     echo You have unpacked all 5 archives.
  1250.     rm -f ark[1-9]isdone
  1251. else
  1252.     echo You still need to unpack the following archives:
  1253.     echo "        " ${MISSING}
  1254. fi
  1255. ##  End of shell archive.
  1256. exit 0
  1257. -- 
  1258. Sincerely,                                berg@messua.informatik.rwth-aachen.de
  1259.            Stephen R. van den Berg (AKA BuGless).    berg@physik.tu-muenchen.de
  1260.  
  1261. He did a quarter of the work in *half* the time!
  1262.  
  1263. exit 0 # Just in case...
  1264.