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

  1. Newsgroups: comp.sources.misc
  2. From: berg@messua.informatik.rwth-aachen.de (Stephen R. van den Berg)
  3. Subject:  v28i003:  procmail - mail processing program v2.61, Part03/05
  4. Message-ID: <1992Feb2.030752.24162@sparky.imd.sterling.com>
  5. X-Md4-Signature: 54e82e612217da10cf0e59137f438823
  6. Date: Sun, 2 Feb 1992 03:07:52 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 3
  11. Archive-name: procmail/part03
  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 3 (of 5)."
  22. # Contents:  procmail/examples/advanced procmail/goodies.c
  23. #   procmail/nonint.c procmail/regexp.c
  24. # Wrapped by berg@tabaqui on Fri Jan 31 14:16:34 1992
  25. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  26. if test -f 'procmail/examples/advanced' -a "${1}" != "-c" ; then 
  27.   echo shar: Will not clobber existing file \"'procmail/examples/advanced'\"
  28. else
  29. echo shar: Extracting \"'procmail/examples/advanced'\" \(7701 characters\)
  30. sed "s/^X//" >'procmail/examples/advanced' <<'END_OF_FILE'
  31. XDiscusses:
  32. X        1. Crossmounted mailboxes
  33. X        2. Procmail as an integrated local mail delivery agent
  34. X        2a.Special directions for sites with smail
  35. X        3. Security considerations (when installing procmail suid root)
  36. X        4. How to generate autoreplies
  37. X        5. Some exorbitant examples of rcfile formats
  38. X        6. Some advanced examples of the use of the 'A' flag
  39. X
  40. X                ---
  41. X
  42. X1. Crossmounted mailboxes
  43. X   ----------------------
  44. X
  45. XFor users that have crossmounted mailboxes (i.e. you can access the very
  46. Xsame mail from a whole bunch of different workstations), but on machines
  47. Xwith differing architectures (i.e. you need different executables), and they
  48. Xhave to explicitly use (i.e. the system administrator did not arrange,
  49. Xfor example, /usr/local/bin/procmail to have exactly the right contents
  50. Xdepending on from which machine it is called) two executables of procmail,
  51. XI have the following suggestion to use as a .forward file (examples are for
  52. Xsparc and sun3 architectures):
  53. X
  54. X"|IFS=' ';if /usr/bin/sparc;then exec /home/berg/bin.sun4/procmail;else exec /home/berg/bin.sun3/procmail;fi #YOUR_LOGIN_NAME"
  55. X
  56. Xor alternatively:
  57. X
  58. X"|IFS=' ';exec /home/berg/bin.`/usr/bin/arch`/procmail #YOUR_LOGIN_NAME"
  59. X
  60. XPlease note, in the .forward file there can NOT be any newlines between
  61. Xthe doublequotes, i.e. the former example *has* to be typed in as one long
  62. Xline.
  63. X
  64. X                ---
  65. X
  66. X2. Procmail as an integrated local mail delivery agent
  67. X   ---------------------------------------------------
  68. X
  69. XCompletely integrating procmail in the mail delivery means that mail is
  70. Xdelivered as normal, unless a .procmailrc file is present in the home
  71. Xdirectory of the recipient.  This will be completely independent of the
  72. Xfact if a .forward file is present.  This will not break anything, it
  73. Xjust makes the use of procmail easier because people are not required to
  74. Xstart up procmail from within their .forward files.  Creation of a .procmailrc
  75. Xfile will suffice.
  76. X
  77. XIn order to do this, the following line should take the place of the standard
  78. XMlocal rule in your sendmail.cf (this way sendmail will start up procmail with
  79. Xroot priv, procmail will immediately setuid itself to the recipient's uid):
  80. X
  81. XMlocal, P=/usr/local/bin/procmail, F=lsSDFMuhP, S=10, R=20, A=procmail -d $u
  82. X
  83. XIf your sendmail does not allow starting programs with root privs (the
  84. X'S' flag), you can instead make procmail suid root.  This will not create
  85. Xa security hole, procmail will normally setuid immediately to the real
  86. Xuid (effectively losing root privs), or will immediately setuid to the
  87. Xrecipient's uid (and be completely loyal to the recipient's absent or present
  88. X.procmailrc file).  Actually installing procmail suid root is a slightly more
  89. Xflexible approach (not at all more dangerous).
  90. X
  91. XIf using the suid root version of procmail, you only need to insert the
  92. Xfollowing: line in your sendmail.cf:
  93. X
  94. XMlocal, P=/usr/local/bin/procmail, F=lsDFMuhP, S=10, R=20, A=procmail -d $u
  95. X
  96. XSo, to summarise, if you install procmail not-suid-root you should use the
  97. Xfirst rule (with the 'S' flag), and if you install it suid-root you should
  98. Xuse the second rule (without the 'S' flag).  If you install procmail
  99. Xnot-suid-root you cannot use the second rule, since procmail will not be
  100. Xable to change uid to the recipient, and therefore it cannot read/write
  101. Xthe recipient's files (including any .procmailrc).  The alternative would
  102. Xbe that procmail already has the recipient's uid upon startup, this is not
  103. Xpossible in sendmail without changing some configuration options.
  104. X
  105. XIn addition to needing root priviliges upon startup, on some systems procmail
  106. Xneeds to be sgid to daemon or mail.  One way to check is by looking at the
  107. Xcurrent mail delivery agent (usually /bin/mail) and to mimic its permissions,
  108. Xowner and group.
  109. X
  110. X                ---
  111. X
  112. X2a.Special directions for sites with smail
  113. X   ---------------------------------------
  114. X
  115. XReplace any existing "local"-entry in the /usr/lib/smail/transports file
  116. X(create one, if need be) with the following two lines:
  117. X
  118. Xlocal: return_path, local, from, driver=pipe;
  119. X    cmd="/usr/local/bin/procmail -d $($user$)"
  120. X
  121. XFor any ideas on suid/sgid modes which *might* be needed, see the previous
  122. Xparagraph (2).
  123. X
  124. X                ---
  125. X
  126. X3. Security considerations (when installing procmail suid root)
  127. X   -------------------------------------------------------------
  128. X
  129. XIf in EXPLICIT DELIVERY mode (typically when called from within sendmail)
  130. Xprocmail will ALWAYS change UID and gid to the RECIPIENT's defaults as soon as
  131. Xit starts reading the recipient's $HOME/.procmailrc file.
  132. X
  133. XIf NOT in explicit delivery mode (typically when called from within the
  134. Xrecipient's $HOME/.forward file) procmail will ALWAYS change UID and gid to
  135. Xthe real uid and gid of the INVOKER (effectively losing any suid or sgid
  136. Xpriviliges).
  137. X
  138. XThese two precautions should effectively eliminate any security holes because
  139. Xprocmail will always have the uid of the person whose commands it is executing.
  140. X
  141. XTo summarise, procmail will only behave better if made suid/sgid something,
  142. Xit should never make things worse.
  143. X
  144. X                ---
  145. X
  146. X4. How to generate autoreplies
  147. X   ---------------------------
  148. X
  149. XUsing a recipe like the following, you can generate autoreplies to mail
  150. Xreceived by you:
  151. X
  152. X: 2 h c
  153. X!^From +(postmaster|Mailer)
  154. X!^From +your_own_login_name
  155. X| formail -r | (cat; echo "Mail received.") | $SENDMAIL -t
  156. X
  157. XAs you can see, I made sure that neither bouncing mail (from postmaster or the
  158. Xmailer-daemon), nor mail coming from yourself will be autoreplied.  If this
  159. Xprecaution would not be taken, disaster could result ("ringing" mail).
  160. XThe abovementioned recipe should be inserted before all other recipes in
  161. Xyour rcfile, however, it is advisable to put it *after* any recipes that
  162. Xprocess mailinglist subscriptions;  it generally is not a good idea to
  163. Xgenerate autoreplies to mailinglists.
  164. X
  165. X                ---
  166. X
  167. X5. Some exorbitant examples of rcfile formats
  168. X   ------------------------------------------
  169. X
  170. X# Now follows an example of what you can do in a procmailrc file
  171. XHELLO=oneword
  172. XHELLO="two words"
  173. XHELLO='two words'    HELLO  =    one\
  174. Xword
  175. XHELLO=two\ words
  176. XHELLO=two\ `echo words`
  177. XHELLO=            # empty
  178. XHELLO            # This will wipe "HELLO" from the environment
  179. XHELLO     =    "three words"\ yes
  180. XHELLO    =    "$HELLO `cat somefile`    "    # Trailing blanks
  181. XHELLO = "wheeee`date`${HELLO} this works too"     HELLO = 'But so does this!'
  182. X
  183. X# As you can see, every trick in the book of /bin/sh programming can be used
  184. X# (and more).
  185. X
  186. XLOCALLOCKFILE = llf
  187. X
  188. X  ::$LOCALLOCKFILE
  189. Xgrep for this
  190. X |$HELLO        # calls up a program named "But" with 3 arguments
  191. X
  192. X:: "test ing"        # lockfilename with a space in it
  193. Xgrep for this
  194. X  |$HELLO
  195. X
  196. X:
  197. Xor for this
  198. X|"$HELLO"        # tries to call up a program named "But so does this!"
  199. X
  200. X:
  201. Xand this
  202. X|$HELLO \
  203. Xthere        # action lines can be continued
  204. X
  205. X                ---
  206. X
  207. X6. Some advanced examples of the use of the 'A' flag
  208. X   -------------------------------------------------
  209. X
  210. X:c        # Specify the 'c' otherwise we never arrive at the next recipe
  211. X^From Myfriend
  212. Xevery_message_from_my_friend        # Mailbox for everything he/she writes
  213. X
  214. X:Ac            # Note the 'c' again
  215. X! my_other_friend      # Forward everything Myfriend writes to my_other_friend
  216. X
  217. X:1Ac
  218. X^Subject:.*jokes
  219. X! my_third_friend    # Forward everything Myfriend writes about jokes
  220. X            # to my_third_friend
  221. X
  222. X:2A
  223. X^Subject:.*parties
  224. X!beach
  225. X! my_third_friend    # Forward everything Myfriend writes about parties,
  226. X            # except beach parties, to my_third_friend
  227. X
  228. X:A            # Provide a mail sink, in order to fake procmail into
  229. X/dev/null        # believing that the mail was absorbed/delivered,
  230. X            # even if the mail was about beach parties :-).
  231. X        # This is not the best solution though, better would be to
  232. X        # rearrange these last five recipes so that the current
  233. X        # number one or two is last, the current number five can be
  234. X        # omitted then.
  235. X
  236. X                ---
  237. END_OF_FILE
  238. if test 7701 -ne `wc -c <'procmail/examples/advanced'`; then
  239.     echo shar: \"'procmail/examples/advanced'\" unpacked with wrong size!
  240. fi
  241. # end of 'procmail/examples/advanced'
  242. fi
  243. if test -f 'procmail/goodies.c' -a "${1}" != "-c" ; then 
  244.   echo shar: Will not clobber existing file \"'procmail/goodies.c'\"
  245. else
  246. echo shar: Extracting \"'procmail/goodies.c'\" \(9098 characters\)
  247. sed "s/^X//" >'procmail/goodies.c' <<'END_OF_FILE'
  248. X/************************************************************************
  249. X *    Collection of library-worthy routines                *
  250. X *                                    *
  251. X *    Copyright (c) 1990-1992, S.R. van den Berg, The Netherlands    *
  252. X *    The sources can be freely copied for non-commercial use.    *
  253. X *    #include "README"                        *
  254. X *                                    *
  255. X ************************************************************************/
  256. X#ifdef RCS
  257. Xstatic char rcsid[]="$Id: goodies.c,v 2.13 1992/01/31 11:32:45 berg Rel $";
  258. X#endif
  259. X#include "config.h"
  260. X#include "procmail.h"
  261. X#include "shell.h"
  262. X
  263. X#define NOTHING_YET    (-1)    /* readparse understands a very complete    */
  264. X#define SKIPPING_SPACE    0    /* subset of the standard /bin/sh syntax    */
  265. X#define NORMAL_TEXT    1    /* that includes single-, double- and back- */
  266. X#define DOUBLE_QUOTED    2    /* quotes, backslashes and $subtitutions    */
  267. X#define SINGLE_QUOTED    3
  268. X
  269. X/* sarg==0 : normal parsing, split up arguments like in /bin/sh
  270. X * sarg==1 : environment assignment parsing, parse up till first whitespace
  271. X * sarg==2 : normal parsing, split up arguments by single spaces
  272. X */
  273. Xreadparse(p,fgetc,sarg)register char*p;int(*const fgetc)();const int sarg;
  274. X{ static i;int got;char*startb;
  275. X  for(got=NOTHING_YET;;)            /* buf2 is used as scratch space */
  276. X   {
  277. Xloop:
  278. X     i=fgetc();
  279. X     if(buf+linebuf-3<p)        /* doesn't catch everything, just a hint */
  280. X      { log("Exceeded LINEBUF\n");p=buf+linebuf-3;goto ready;
  281. X      }
  282. Xnewchar:
  283. X     switch(i)
  284. X      { case EOF:
  285. X       if(got>NORMAL_TEXT)
  286. Xearly_eof:    log(unexpeof);
  287. Xready:       if(got!=SKIPPING_SPACE||sarg)  /* not terminated yet or sarg==2 ? */
  288. X          *p++='\0';
  289. X       *p=TMNATE;return;
  290. X    case '\\':
  291. X       if(got==SINGLE_QUOTED)
  292. X          break;
  293. X       switch(i=fgetc())
  294. X        { case EOF:goto early_eof;              /* can't quote EOF */
  295. X          case '\n':continue;            /* concatenate lines */
  296. X          case '#':
  297. X         if(got>SKIPPING_SPACE) /* escaped comment at start of word? */
  298. X            goto noesc;            /* apparently not, literally */
  299. X          case ' ':case '\t':case '\'':
  300. X         if(got==DOUBLE_QUOTED)
  301. X            goto noesc;
  302. X          case '"':case '\\':case '$':case '`':goto nodelim;
  303. X        }
  304. X       if(got>NORMAL_TEXT)
  305. Xnoesc:          *p++='\\';        /* nothing to escape, just echo both */
  306. X       break;
  307. X    case '`':
  308. X       if(got==SINGLE_QUOTED)
  309. X          goto nodelim;
  310. X       for(startb=p;;)                   /* mark your position */
  311. X        { switch(i=fgetc())             /* copy till next backquote */
  312. X           { case '\\':
  313. X            switch(i=fgetc())
  314. X             { case EOF:log(unexpeof);goto forcebquote;
  315. X               case '\n':continue;
  316. X               case '"':
  317. X              if(got!=DOUBLE_QUOTED)
  318. X                 break;
  319. X               case '\\':case '$':case '`':goto escaped;
  320. X             }
  321. X            *p++='\\';break;
  322. X         case '"':
  323. X            if(got!=DOUBLE_QUOTED)    /* missing closing backquote? */
  324. X               break;
  325. Xforcebquote:     case EOF:case '`':
  326. X          { int osh=sh;
  327. X            *p='\0';
  328. X            if(!(sh=!!strpbrk(startb,tgetenv(shellmetas))))
  329. X             { const char*save=sgetcp;
  330. X               sgetcp=p=tstrdup(startb);readparse(startb,sgetc,0);
  331. X               free(p);sgetcp=save;               /* chopped up */
  332. X             }            /* drop source buffer, read from program */
  333. X            startb=fromprog(p=startb,startb);sh=osh;   /* restore sh */
  334. X            if(!sarg&&got!=DOUBLE_QUOTED)
  335. X             { i=0;startb=p;goto simplsplit;          /* split it up */
  336. X             }
  337. X            if(i=='"'||got<=SKIPPING_SPACE)   /* missing closing ` ? */
  338. X               got=NORMAL_TEXT;                 /* or sarg!=0 ? */
  339. X            p=startb;goto loop;
  340. X          }
  341. X         case '\n':i=';';           /* newlines separate commands */
  342. X           }
  343. Xescaped:      *p++=i;
  344. X        }
  345. X    case '"':
  346. X       switch(got)
  347. X        { case DOUBLE_QUOTED:got=NORMAL_TEXT;continue;    /* closing " */
  348. X          case SINGLE_QUOTED:goto nodelim;
  349. X        }
  350. X       got=DOUBLE_QUOTED;continue;                /* opening " */
  351. X    case '\'':
  352. X       switch(got)
  353. X        { case DOUBLE_QUOTED:goto nodelim;
  354. X          case SINGLE_QUOTED:got=NORMAL_TEXT;continue;}    /* closing ' */
  355. X       got=SINGLE_QUOTED;continue;                /* opening ' */
  356. X    case '#':
  357. X       if(got>SKIPPING_SPACE)        /* comment at start of word? */
  358. X          break;
  359. X       while((i=fgetc())!=EOF&&i!='\n');            /* skip till EOL */
  360. X       goto ready;
  361. X    case '$':
  362. X       if(got==SINGLE_QUOTED)
  363. X          break;
  364. X       if(EOF==(i=fgetc()))
  365. X        { *p++='$';goto ready;
  366. X        }
  367. X       startb=buf2;
  368. X       if(i=='{')                          /* ${name} */
  369. X        { while(EOF!=(i=fgetc())&&alphanum(i))
  370. X         *startb++=i;
  371. X          *startb='\0';
  372. X          if(i!='}')
  373. X           { log("Bad substitution of");logqnl(buf2);continue;
  374. X           }
  375. X          i='\0';
  376. X        }
  377. X       else if(alphanum(i))                        /* $name */
  378. X        { do *startb++=i;
  379. X          while(EOF!=(i=fgetc())&&alphanum(i));
  380. X          if(i==EOF)
  381. X         i='\0';
  382. X          *startb='\0';
  383. X        }
  384. X       else if(i=='$')                      /* $$ =pid */
  385. X        { ultstr(0,(unsigned long)thepid,p);goto ieofstr;
  386. X        }
  387. X       else if(i=='-')                   /* $- =lastfolder */
  388. X        { strcpy(p,lastfolder);
  389. Xieofstr:      i='\0';goto eofstr;
  390. X        }
  391. X       else
  392. X        { *p++='$';goto newchar;               /* not a substitution */
  393. X        }
  394. X       startb=(char*)tgetenv(buf2);
  395. X       if(!sarg&&got!=DOUBLE_QUOTED)
  396. Xsimplsplit:   for(;;startb++)          /* simply split it up in arguments */
  397. X           { switch(*startb)
  398. X          { case ' ':case '\t':case '\n':
  399. X               if(got<=SKIPPING_SPACE)
  400. X              continue;
  401. X               *p++='\0';got=SKIPPING_SPACE;continue;
  402. X            case '\0':goto eeofstr;
  403. X          }
  404. X         *p++= *startb;got=NORMAL_TEXT;
  405. X           }
  406. X       else
  407. X        { strcpy(p,startb);                   /* simply copy it */
  408. Xeofstr:          if(got<=SKIPPING_SPACE)        /* can only occur if sarg!=0 */
  409. X         got=NORMAL_TEXT;
  410. X          p=strchr(p,'\0');
  411. X        }
  412. Xeeofstr:   if(i)                 /* already read next character? */
  413. X          goto newchar;
  414. X       continue;
  415. X    case ' ':case '\t':
  416. X       switch(got)
  417. X        { case NORMAL_TEXT:
  418. X         if(sarg==1)
  419. X            goto ready;        /* already fetched a single argument */
  420. X         got=SKIPPING_SPACE;*p++=sarg?' ':'\0';     /* space or \0 sep. */
  421. X          case NOTHING_YET:case SKIPPING_SPACE:continue;    /* skip space */
  422. X        }
  423. X    case '\n':
  424. X       if(got<=NORMAL_TEXT)
  425. X          goto ready;                /* EOL means we're ready */
  426. X      }
  427. Xnodelim:
  428. X     *p++=i;                       /* ah, a normal character */
  429. X     if(got<=SKIPPING_SPACE)         /* should we bother to change mode? */
  430. X    got=NORMAL_TEXT;
  431. X   }
  432. X}
  433. X
  434. Xultstr(minwidth,val,dest)unsigned long val;char*dest;
  435. X{ int i;unsigned long j;
  436. X  j=val;i=0;                       /* a beauty, isn't it :-) */
  437. X  do i++;                       /* determine needed width */
  438. X  while(j/=10);
  439. X  while(--minwidth>=i)                 /* fill up any excess width */
  440. X     *dest++=' ';
  441. X  *(dest+=i)='\0';
  442. X  do *--dest='0'+val%10;              /* display value backwards */
  443. X  while(val/=10);
  444. X}
  445. X
  446. Xsputenv(a)char*a;          /* smart putenv, the way it was supposed to be */
  447. X{ static struct lienv{struct lienv*next;char name[255];}*myenv;
  448. X  static alloced;int i,remove;char*split,**preenv;struct lienv*curr,**last;
  449. X  yell("Assigning",a);remove=0;a=tstrdup(a);        /* make working copy */
  450. X  if(!(split=strchr(a,'=')))               /* assignment or removal? */
  451. X   { remove=1;i=strlen(a);*(split=i+(a=realloc(a,i+2)))='=';
  452. X     split[1]='\0';
  453. X   }
  454. X  i= ++split-a;
  455. X  for(curr= *(last= &myenv);curr;curr= *(last= &curr->next))
  456. X     if(!strncmp(a,curr->name,i))         /* is it one I created earlier? */
  457. X      { split=curr->name;*last=curr->next;free(curr);
  458. X    for(preenv=environ;*preenv!=split;preenv++);
  459. X    goto wipenv;
  460. X      }
  461. X  for(preenv=environ;*preenv;preenv++)
  462. X     if(!strncmp(a,*preenv,i))           /* is it in the standard environment? */
  463. X      {
  464. Xwipenv: while(*preenv=preenv[1])   /* wipe this entry out of the environment */
  465. X       preenv++;
  466. X    break;
  467. X      }
  468. X  i=(preenv-environ+2)*sizeof*environ;
  469. X  if(alloced)           /* have we ever alloced the environ array before? */
  470. X     environ=realloc(environ,i);
  471. X  else
  472. X   { alloced=1;environ=tmemmove(malloc(i),environ,i-sizeof*environ);
  473. X   }
  474. X  if(!remove)          /* if not remove, then add it to both environments */
  475. X   { for(preenv=environ;*preenv;preenv++);
  476. X     curr=malloc(ioffsetof(struct lienv,name)+strlen(a)+1);
  477. X     strcpy(*preenv=curr->name,a);free(a);preenv[1]=0;curr->next=myenv;
  478. X     myenv=curr;
  479. X   }
  480. X}
  481. X                /* strtol replacement which lacks range checking */
  482. X#ifdef NOstrtol
  483. Xlong strtol(start,ptr,base)const char*start,**const ptr;
  484. X{ long result;const char*str=start;unsigned i;int sign,found;
  485. X  if(base>=36||base<(sign=found=result=0))
  486. X     goto fault;
  487. X  for(;;str++)                      /* skip leading whitespace */
  488. X   { switch(*str)
  489. X      { case '\t':case '\n':case '\v':case '\f':case '\r':case ' ':continue;
  490. X      }
  491. X     break;
  492. X   }
  493. X  switch(*str)                               /* any signs? */
  494. X   { case '-':sign=1;
  495. X     case '+':str++;
  496. X   }
  497. X  if(*str=='0')                         /* leading zero(s)? */
  498. X   { start++;
  499. X     if((i= *++str)=='x'||i=='X')            /* leading 0x or 0X? */
  500. X    if(!base||base==16)
  501. X     { base=16;str++;                /* hexadecimal all right */
  502. X     }
  503. X    else
  504. X       goto fault;
  505. X     else if(!base)
  506. X    base=8;                         /* then it is octal */
  507. X   }
  508. X  else if(!base)
  509. X     base=10;                          /* or else decimal */
  510. X  goto jumpin;
  511. X  do
  512. X   { found=1;result=result*base+i;++str;         /* start converting */
  513. Xjumpin:
  514. X     if((i= *str-'0')<10);
  515. X     else if(i-'A'+'0'<26)
  516. X    i-='A'-10-'0';
  517. X     else if(i-'a'+'0'<26)
  518. X    i-='a'-10-'0';
  519. X     else
  520. X    break;                        /* not of this world */
  521. X   }
  522. X  while(i<base);                      /* still of this world */
  523. Xfault:
  524. X  if(ptr)
  525. X    *ptr=found?str:start;                   /* how far did we get */
  526. X  return sign?-result:result;
  527. X}
  528. X#endif
  529. END_OF_FILE
  530. if test 9098 -ne `wc -c <'procmail/goodies.c'`; then
  531.     echo shar: \"'procmail/goodies.c'\" unpacked with wrong size!
  532. fi
  533. # end of 'procmail/goodies.c'
  534. fi
  535. if test -f 'procmail/nonint.c' -a "${1}" != "-c" ; then 
  536.   echo shar: Will not clobber existing file \"'procmail/nonint.c'\"
  537. else
  538. echo shar: Extracting \"'procmail/nonint.c'\" \(7358 characters\)
  539. sed "s/^X//" >'procmail/nonint.c' <<'END_OF_FILE'
  540. X/************************************************************************
  541. X *    Collection of routines that don't return int            *
  542. X *                                    *
  543. X *    Copyright (c) 1990-1992, S.R. van den Berg, The Netherlands    *
  544. X *    The sources can be freely copied for non-commercial use.    *
  545. X *    #include "README"                        *
  546. X *                                    *
  547. X ************************************************************************/
  548. X#ifdef RCS
  549. Xstatic char rcsid[]="$Id: nonint.c,v 2.16 1992/01/31 12:35:17 berg Rel $";
  550. X#endif
  551. X#include "config.h"
  552. X#include "procmail.h"
  553. X
  554. X#define nomemretry    noresretry
  555. X#define noforkretry    noresretry
  556. X
  557. Xvoid*tmalloc(len)const size_t len;    /* this malloc can survive a temporary */
  558. X{ void*p;int i;                    /* "out of swap space" condition */
  559. X  if(p=malloc(len))
  560. X     return p;
  561. X  if(p=malloc(1))
  562. X     free(p);               /* works on some systems with latent free */
  563. X  for(lcking=2,i=nomemretry;i<0||i--;)
  564. X   { suspend();             /* problems?  don't panic, wait a few secs till */
  565. X     if(p=malloc(len))         /* some other process has paniced (and died 8-) */
  566. X      { lcking=0;return p;
  567. X      }
  568. X   }
  569. X  nomemerr();
  570. X}
  571. X
  572. Xvoid*trealloc(old,len)void*const old;const size_t len;
  573. X{ void*p;int i;
  574. X  if(p=realloc(old,len))
  575. X     return p;                    /* for comment see tmalloc above */
  576. X  if(p=malloc(1))
  577. X    free(p);
  578. X  for(lcking=2,i=nomemretry;i<0||i--;)
  579. X   { suspend();
  580. X     if(p=realloc(old,len))
  581. X      { lcking=0;return p;
  582. X      }
  583. X   }
  584. X  nomemerr();
  585. X}
  586. X               /* line buffered to keep concurrent entries untangled */
  587. Xlog(new)const char*const new;
  588. X{ int lnew,i;static lold;static char*old;char*p;
  589. X  if(lnew=strlen(new))                        /* anything? */
  590. X   {
  591. X#ifndef O_CREAT
  592. X     lseek(STDERR,0L,SEEK_END);          /* locking should be done actually */
  593. X#endif
  594. X     if(nextexit)
  595. X    goto direct;                   /* careful, in terminate code */
  596. X     i=lold+lnew;
  597. X     if(p=lold?realloc(old,i):malloc(i))         /* unshelled malloc */
  598. X      { memmove((old=p)+lold,new,(size_t)lnew);               /* append */
  599. X    if(p[(lold=i)-1]=='\n')                         /* EOL? */
  600. X     { rwrite(STDERR,p,i);lold=0;free(p);        /* flush the line(s) */
  601. X     }
  602. X      }
  603. X     else                       /* no memory, force flush */
  604. X      { if(lold)
  605. X     { rwrite(STDERR,old,i);lold=0;free(old);
  606. X     }
  607. Xdirect: rwrite(STDERR,new,lnew);
  608. X      }
  609. X   }
  610. X}
  611. X
  612. X#include "shell.h"
  613. X
  614. Xpid_t sfork()                /* this fork can survive a temporary */
  615. X{ pid_t i;int r;               /* "process table full" condition */
  616. X  r=noforkretry;
  617. X  while((i=fork())==-1)
  618. X   { lcking=3;
  619. X     if(!(r<0||r--))
  620. X    break;
  621. X     suspend();
  622. X   }
  623. X  lcking=0;return i;
  624. X}
  625. X
  626. Xvoid srequeue()
  627. X{ retval=EX_TEMPFAIL;sterminate();
  628. X}
  629. X
  630. Xvoid slose()
  631. X{ fakedelivery=2;sterminate();
  632. X}
  633. X
  634. Xvoid sbounce()
  635. X{ retval=EX_CANTCREAT;sterminate();
  636. X}
  637. X
  638. Xextern char*lastexec,*backblock;        /* see retint.c for comment */
  639. Xextern long backlen;
  640. Xextern pid_t pidfilt,pidchild;
  641. Xextern pbackfd[2];
  642. X
  643. Xvoid stermchild()
  644. X{ if(pidfilt>0)            /* don't kill what is not ours, we might be root */
  645. X     kill(pidfilt,SIGTERM);
  646. X  log("Rescue of unfiltered data ");
  647. X  if(dump(PWRB,backblock,backlen))    /* pump back the data via the backpipe */
  648. X     log("failed\n");
  649. X  else
  650. X     log("succeeded\n");
  651. X  exit(EX_UNAVAILABLE);
  652. X}
  653. X
  654. Xvoid ftimeout()
  655. X{ alarm(0);alrmtime=0;
  656. X  if(pidchild>0&&!kill(pidchild,SIGTERM))       /* careful, killing again */
  657. X      { log("Timeout, terminating");logqnl(lastexec);
  658. X      }
  659. X  signal(SIGALRM,ftimeout);
  660. X}
  661. X
  662. Xlong dump(s,source,len)const int s;const char*source;long len;
  663. X{ int i;
  664. X  if(s>=0)
  665. X   { fdlock(s);lastdump=len;mboxseparator(s);  /* prepend optional separator */
  666. X#ifndef O_CREAT
  667. X     lseek(s,0L,SEEK_END);
  668. X#endif
  669. X     if(len&&tofile)               /* if it is a file, trick NFS into an */
  670. X      { --len;rwrite(s,source++,1);sleep(1);            /* a_time<m_time */
  671. X      }
  672. X     while(i=rwrite(s,source,BLKSIZ<len?BLKSIZ:(int)len))
  673. X      { if(i<0)
  674. X     { i=0;goto writefin;
  675. X     }
  676. X    len-=i;source+=i;
  677. X      }
  678. X     if(!len&&(lastdump<2||!(source[-1]=='\n'&&source[-2]=='\n')))
  679. X    lastdump++,rwrite(s,newline,1);           /* message always ends with a */
  680. X     mboxseparator(s);         /* newline and an optional custom separator */
  681. Xwritefin:
  682. X     fdunlock();rclose(s);return ignwerr?(ignwerr=0):len-i;
  683. X   }
  684. X  return len?len:-1;       /* return an error even if nothing was to be sent */
  685. X}
  686. X
  687. Xlong pipin(line,source,len)char*const line;char*source;long len;
  688. X{ int poutfd[2];
  689. X  rpipe(poutfd);
  690. X  if(!(pidchild=sfork()))                    /* spawn program */
  691. X   { rclose(PWRO);rclose(rc);getstdin(PRDO);callnewprog(line);
  692. X   }
  693. X  rclose(PRDO);
  694. X  if(forkerr(pidchild,line))
  695. X     return 1;
  696. X  if(len=dump(PWRO,source,len))                /* dump mail in the pipe */
  697. X     writeerr(line);               /* pipe was shut in our face, get mad */
  698. X  if(pwait&&waitfor(pidchild)!=EX_OK)        /* optionally check the exitcode */
  699. X   { progerr(line);len=1;
  700. X   }
  701. X  pidchild=0;
  702. X  if(!sh)
  703. X     concatenate(line);
  704. X  lastfolder=cstr(lastfolder,line);return len;
  705. X}
  706. X
  707. Xchar*readdyn(bf,filled)char*bf;long*const filled;
  708. X{ int i;long oldsize;
  709. X  oldsize= *filled;goto jumpin;
  710. X  do
  711. X   { *filled+=i;                /* change listed buffer size */
  712. Xjumpin:
  713. X#ifdef SMALLHEAP
  714. X     if((size_t)*filled>=(size_t)(*filled+BLKSIZ))
  715. X    lcking=2,nomemerr();
  716. X#endif
  717. X     bf=realloc(bf,*filled+BLKSIZ);    /* dynamically adjust the buffer size */
  718. Xjumpback:;
  719. X   }
  720. X  while(0<(i=rread(STDIN,bf+*filled,BLKSIZ)));            /* read mail */
  721. X  if(pidchild>0)
  722. X   { pidchild=0;getstdin(PRDB);               /* filter ready, get backpipe */
  723. X     if(1==rread(STDIN,buf,1))                  /* backup pipe closed? */
  724. X      { bf=realloc(bf,(*filled=oldsize+1)+BLKSIZ);bf[oldsize]= *buf;
  725. X     goto jumpback;                   /* filter goofed, rescue data */
  726. X      }
  727. X   }
  728. X  pidchild=0;                    /* child must be gone by now */
  729. X  if(!*filled)
  730. X     return realloc(bf,1);             /* +1 for housekeeping purposes */
  731. X  return realloc(bf,*filled+1);            /* minimise the buffer space */
  732. X}
  733. X
  734. Xchar*fromprog(name,dest)char*const name;char*dest;
  735. X{ int pinfd[2];long nls;
  736. X  rpipe(pinfd);inittmout(name);
  737. X  if(!(pidchild=sfork()))                    /* spawn program */
  738. X   { rclose(STDIN);opena(devnull);rclose(PRDI);rclose(rc);rclose(STDOUT);
  739. X     rdup(PWRI);rclose(PWRI);callnewprog(name);
  740. X   }
  741. X  rclose(PWRI);nls=0;
  742. X  if(!forkerr(pidchild,name))
  743. X   { while(0<rread(PRDI,dest,1))                /* read its lips */
  744. X    if(*dest=='\n')                    /* careful with newlines */
  745. X       nls++;            /* trailing newlines should be discarded */
  746. X    else
  747. X     { if(nls)
  748. X          for(dest[nls]= *dest;*dest++='\n',--nls;);     /* fill them in */
  749. X       dest++;
  750. X     }
  751. X     waitfor(pidchild);
  752. X   }
  753. X  pidchild=0;rclose(PRDI);*dest='\0';return dest;
  754. X}
  755. X
  756. Xchar*cat(a,b)const char*const a,*const b;
  757. X{ return strcat(strcpy(buf,a),b);
  758. X}
  759. X
  760. Xchar*tstrdup(a)const char*const a;
  761. X{ int i;
  762. X  i=strlen(a)+1;return tmemmove(malloc(i),a,i);
  763. X}
  764. X
  765. Xconst char*tgetenv(a)const char*const a;
  766. X{ const char*b;
  767. X  return(b=getenv(a))?b:"";
  768. X}
  769. X
  770. Xchar*cstr(a,b)const char*const a,*const b;    /* dynamic buffer management */
  771. X{ if(a)
  772. X     free(a);
  773. X  return tstrdup(b);
  774. X}
  775. X
  776. Xlong renvint(i,env)const long i;const char*const env;
  777. X{ const char*p;long t;
  778. X  t=strtol(env,&p,10);return p==env?i:t;       /* parse it like a decimal nr */
  779. X}
  780. X
  781. Xchar*egrepin(expr,source,len,casesens)const char*expr,*source;
  782. X const long len;
  783. X{ source=bregexec(expr=bregcomp(expr,!casesens),
  784. X   source,len>0?(size_t)len:(size_t)0,!casesens);
  785. X  free(expr);return(char*)source;
  786. X}
  787. X
  788. Xchar*lastdirsep(filename)const char*filename;     /* finds the next character */
  789. X{ const char*p;                    /* following the last DIRSEP */
  790. X  while(p=strpbrk(filename,dirsep))
  791. X     filename=p+1;
  792. X  return(char*)filename;
  793. X}
  794. END_OF_FILE
  795. if test 7358 -ne `wc -c <'procmail/nonint.c'`; then
  796.     echo shar: \"'procmail/nonint.c'\" unpacked with wrong size!
  797. fi
  798. # end of 'procmail/nonint.c'
  799. fi
  800. if test -f 'procmail/regexp.c' -a "${1}" != "-c" ; then 
  801.   echo shar: Will not clobber existing file \"'procmail/regexp.c'\"
  802. else
  803. echo shar: Extracting \"'procmail/regexp.c'\" \(10740 characters\)
  804. sed "s/^X//" >'procmail/regexp.c' <<'END_OF_FILE'
  805. X/************************************************************************
  806. X *    Custom regular expression library, *fully* egrep compatible    *
  807. X *                                    *
  808. X *    Seems to be relatively bug free.                *
  809. X *                                    *
  810. X *    Copyright (c) 1991-1992, S.R. van den Berg, The Netherlands    *
  811. X *    The sources can be freely copied for non-commercial use.    *
  812. X *    #include "README"                        *
  813. X *                                    *
  814. X ************************************************************************/
  815. X#ifdef RCS
  816. Xstatic char rcsid[]="$Id: regexp.c,v 2.12 1992/01/21 17:27:04 berg Rel $";
  817. X#endif
  818. X#include "config.h"
  819. X#include "procmail.h"
  820. X#include "shell.h"
  821. X
  822. X#define R_BEG_GROUP    '('
  823. X#define R_OR        '|'
  824. X#define R_END_GROUP    ')'
  825. X#define R_0_OR_MORE    '*'
  826. X#define R_0_OR_1    '?'
  827. X#define R_1_OR_MORE    '+'
  828. X#define R_DOT        '.'
  829. X#define R_SOL        '^'
  830. X#define R_EOL        '$'
  831. X#define R_BEG_CLASS    '['
  832. X#define R_NOT_CLASS    '^'
  833. X#define R_RANGE        '-'
  834. X#define R_END_CLASS    ']'
  835. X#define R_ESCAPE    '\\'
  836. X
  837. X#define BITS_P_CHAR        8
  838. X#define OPB            (1<<BITS_P_CHAR)
  839. X#define OPC_EPS            OPB
  840. X#define OPC_CLASS        (OPB+1)
  841. X#define OPC_DOT            (OPB+2)
  842. X#define OPC_FIN            (OPB+3)
  843. X
  844. X#define bit_type        unsigned
  845. X#define bit_bits        (sizeof(bit_type)*8)
  846. X#define bit_index(which)    ((unsigned)(which)/bit_bits)
  847. X#define bit_mask(which)        ((unsigned)1<<(unsigned)(which)%bit_bits)
  848. X#define bit_toggle(name,which)    (name[bit_index(which)]^=bit_mask(which))
  849. X#define bit_test(name,which)    (!!(name[bit_index(which)]&bit_mask(which)))
  850. X#define bit_set(name,which,value)    \
  851. X (value?(name[bit_index(which)]|=bit_mask(which)):\
  852. X (name[bit_index(which)]&=~bit_mask(which)))
  853. X#define bit_field(name,size)    bit_type name[((size)+bit_bits-1)/bit_bits]
  854. X
  855. X#define SZ(x)        (sizeof(struct x))
  856. X#define Ceps        (struct eps*)
  857. X#define epso(to,add)    (Ceps((char*)(to)+(add)))
  858. X#define ii        (aleps.topc)
  859. X#define jj        (aleps.ua.jjua)
  860. X#define jjp        (aleps.ua.tnext)
  861. X
  862. X/* the spawn and stack members are reused in the normal opcodes as pc fields */
  863. Xstatic struct eps{unsigned opc;struct eps*stack,*spawn,*next;}*r;
  864. Xstatic struct{unsigned topc;union{struct eps*tnext;int jjua;}ua;}aleps;
  865. Xstatic uchar*p;
  866. Xstatic ignore_case;
  867. X
  868. Xstruct chclass {unsigned opc_;struct eps*stack_,*spawn_,*next_;
  869. X bit_field(c,OPB);};
  870. X
  871. Xstatic puteps(spot,to,aswell)struct eps*const spot;    /* epsilon transition */
  872. X const struct eps*const to,*const aswell;
  873. X{ spot->opc=OPC_EPS;spot->next=to!=spot?Ceps to:Ceps aswell;
  874. X  spot->spawn=aswell!=spot?Ceps aswell:Ceps to;spot->stack=0;
  875. X}
  876. X
  877. Xstatic putneps(spot,to)struct eps*const spot;const struct eps*const to;
  878. X{ puteps(spot,to,spot+1);
  879. X}
  880. X
  881. X#define rAc    (((struct chclass*)r)->c)
  882. X
  883. Xstatic bseti(i,j)unsigned i;const int j;
  884. X{ bit_set(rAc,i,j);               /* mark 'i' as being in the class */
  885. X  if(ignore_case)                  /* mark the other case too */
  886. X   { if(i-'A'<26)                        /* uppercase */
  887. X    i+='a'-'A';
  888. X     else if(i-'a'<26)                        /* lowercase */
  889. X    i-='a'-'A';
  890. X     else return;                          /* no case */
  891. X     bit_set(rAc,i,j);
  892. X   }
  893. X}
  894. X
  895. Xstatic por();
  896. X
  897. Xstatic psimp(e)struct eps const*const e;
  898. X{ switch(*p)
  899. X   { case R_BEG_GROUP:++p;por(e);return;      /* not so simple after all */
  900. X     case R_BEG_CLASS:                       /* a simple class */
  901. X    jj=R_NOT_CLASS==*++p;
  902. X    if(e)
  903. X     { r->opc=OPC_CLASS;r->next=Ceps e;r->spawn=r->stack=0;
  904. X       ii=maxindex(rAc);
  905. X       do rAc[ii]=jj?~0:0;                 /* preset the bit field */
  906. X       while(ii--);
  907. X     }
  908. X    if(jj)                      /* skip the 'not' modifier */
  909. X     { ++p;
  910. X       if(e)
  911. X          bit_toggle(rAc,'\n');
  912. X     }
  913. X    if(*p==R_END_CLASS)      /* right at the start, cannot mean the end */
  914. X     { ++p;
  915. X       if(e)
  916. X        { ii=R_END_CLASS;bit_toggle(rAc,R_END_CLASS);
  917. X        }
  918. X     }
  919. X    else if(*p==R_RANGE)                /* take it literally */
  920. X     { ++p;
  921. X       if(e)
  922. X        { ii=R_RANGE;bit_toggle(rAc,R_RANGE);
  923. X        }
  924. X     }
  925. X    for(;;++p)
  926. X     { switch(*p)
  927. X        { case R_END_CLASS:++p;
  928. X          case '\0':r=epso(r,SZ(chclass));return;
  929. X          case R_RANGE:
  930. X         switch(*++p)
  931. X          { default:
  932. X               if(e)
  933. X              while(++ii<*p)        /* mark all in the range */
  934. X                 bseti(ii,!jj);
  935. X               break;
  936. X            case '\0':case R_END_CLASS:--p;        /* literally */
  937. X          }
  938. X        }
  939. X       if(e)
  940. X          bseti(ii= *p,!jj);          /* a normal character, mark it */
  941. X     }
  942. X     case '\0':return;
  943. X     case R_DOT:             /* matches everything but a newline */
  944. X    if(e)
  945. X     { r->opc=OPC_DOT;goto fine;
  946. X     }
  947. X    goto fine2;
  948. X     case R_EOL:case R_SOL:              /* match a newline (in effect) */
  949. X    if(e)
  950. X     { r->opc='\n';goto fine;
  951. X     }
  952. X    goto fine2;
  953. X     case R_ESCAPE:                      /* quote something */
  954. X    if(!*++p)                     /* nothing to quote */
  955. X       --p;
  956. X   }
  957. X  if(e)                              /* a regular character */
  958. X   { r->opc=ignore_case&&(unsigned)*p-'A'<26?*p+'a'-'A':*p;
  959. Xfine:
  960. X     r->next=Ceps e;r->spawn=r->stack=0;
  961. X   }
  962. Xfine2:
  963. X  ++p;++r;
  964. X}
  965. X
  966. X#define EOS(x)    (jjp?jjp:(x))
  967. X
  968. Xstatic pnorm(e)struct eps const*const e;
  969. X{ void*pold;struct eps*rold;
  970. X  for(;;)
  971. X   { pold=p;rold=r;psimp(Ceps 0);ii= *p;            /* skip it first */
  972. X     jjp=p[1]==R_OR||p[1]==R_END_GROUP||!p[1]?Ceps e:Ceps 0;
  973. X     if(e)
  974. X      { p=pold;pold=r;
  975. X      }
  976. X     switch(ii)               /* check for any of the postfix operators */
  977. X      { case R_0_OR_MORE:++r;
  978. X       if(e)              /* first an epsilon, then the rest */
  979. X        { putneps(rold,EOS(r));r=rold+1;psimp(rold);
  980. X        }
  981. X       goto incagoon;
  982. X    case R_1_OR_MORE:                   /* first the rest */
  983. X       if(e)                      /* and then an epsilon */
  984. X        { puteps(r,rold,EOS(r+1));r=rold;psimp(Ceps pold);
  985. X        }
  986. X       ++r;goto incagoon;
  987. X    case R_0_OR_1:++r;
  988. X       if(e)              /* first an epsilon, then the rest */
  989. X        { putneps(rold,r=EOS(r));pold=r;r=rold+1;psimp(Ceps pold);
  990. X        }
  991. Xincagoon:  switch(*++p)            /* at the end of this group already? */
  992. X        { case R_OR:case R_END_GROUP:case '\0':return;
  993. X        }
  994. X       continue;                 /* regular end of the group */
  995. X    case R_OR:case R_END_GROUP:case '\0':
  996. X       if(e)
  997. X        { r=rold;psimp(e);
  998. X        }
  999. X       return;
  1000. X      }
  1001. X     if(e)            /* no fancy postfix operators, plain vanilla */
  1002. X      { r=rold;psimp(Ceps pold);
  1003. X      }
  1004. X   }
  1005. X}
  1006. X
  1007. Xstatic por(e)struct eps const*const e;
  1008. X{ uchar*pold;struct eps*rold;
  1009. X  for(;;)
  1010. X     for(pold=p;;)
  1011. X      { rold=r;
  1012. X    switch(*p)
  1013. X     { default:pnorm(Ceps 0);r=rold;continue;     /* still in this group */
  1014. X       case '\0':case R_END_GROUP:           /* found the end of the group */
  1015. X          if(p==pold)                 /* empty 'or' group */
  1016. X           { if(e)
  1017. X            puteps(r,e,e);           /* misused epsilon as branch, */
  1018. X         ++r;        /* let the optimiser (fillout()) take it out */
  1019. X           }
  1020. X          else
  1021. X           { p=pold;pnorm(e);            /* normal last group */
  1022. X           }
  1023. X          if(*p)
  1024. X         ++p;
  1025. X          return;
  1026. X       case R_OR:++r;
  1027. X          if(p==pold)                 /* empty 'or' group */
  1028. X           { if(e)
  1029. X            putneps(rold,e);              /* special epsilon */
  1030. X           }
  1031. X          else
  1032. X           { p=pold;pnorm(e);          /* normal 'or' group, first an */
  1033. X         if(e)                   /* epsilon, then the rest */
  1034. X            putneps(rold,r);
  1035. X           }
  1036. X          ++p;
  1037. X     }
  1038. X    break;
  1039. X      }
  1040. X}
  1041. X
  1042. Xstatic findandrep(old,new)register struct eps**const old;
  1043. X struct eps*const new;
  1044. X{ register struct eps*i,*t= *old;               /* save the value */
  1045. X  for(i=r;;)                 /* change all pointers from *old to new */
  1046. X   { if(i->next==t)
  1047. X    i->next=new;
  1048. X     if(i->spawn==t)
  1049. X    i->spawn=new;
  1050. X     switch(i->opc)
  1051. X      { case OPC_FIN:*old=t;return;       /* last one, restore value, ready */
  1052. X    case OPC_CLASS:i=epso(i,SZ(chclass));break;
  1053. X    default:++i;
  1054. X      }
  1055. X   }
  1056. X}
  1057. X
  1058. X#define drs(m)    (*(struct eps**)((char*)*stack+(ioffsetof(struct eps,m)^ofs)))
  1059. X
  1060. Xstatic cstack(stack,ofs)struct eps**const stack;
  1061. X{ if(drs(next)->stack==Ceps p)
  1062. X   { findandrep(*stack,drs(next));*stack=drs(spawn);return 1;
  1063. X   }
  1064. X  return 0;
  1065. X}
  1066. X    /* break up any closed epsilon circles, otherwise they can't be executed */
  1067. Xstatic fillout(stack)struct eps**const stack;
  1068. X{ if((*stack)->opc!=OPC_EPS||(*stack)->stack)
  1069. X     return 0;
  1070. X  (*stack)->stack=Ceps p;                /* mark this one as used */
  1071. X#define RECURS(nxt)    \
  1072. X  do\
  1073. X     if(cstack(stack,ioffsetof(struct eps,nxt)^ioffsetof(struct eps,next)))\
  1074. X    return 1;\
  1075. X  while(fillout(&(*stack)->nxt))
  1076. X  RECURS(next);RECURS(spawn);return 0;                  /* recurse */
  1077. X}
  1078. X
  1079. Xvoid*bregcomp(a,ign_case)char const*a;
  1080. X{ struct eps*st;size_t i;      /* first a trial run, determine memory needed */
  1081. X  p=(uchar*)a;ignore_case=ign_case;r=Ceps&aleps+1;por(Ceps 0);
  1082. X  st=malloc((i=(char*)r-(char*)&aleps)+ioffsetof(struct eps,stack)+sizeof r);
  1083. X  putneps(st,r=st+1);p=(uchar*)a;por(Ceps((char*)st+i));   /* really compile */
  1084. X  r->opc=OPC_FIN;r->stack=0;                  /* tack on the end */
  1085. X  for(r=st;;)                 /* simplify the compiled code (i.e. */
  1086. X     switch(st->opc)              /* take out cyclic epsilon references) */
  1087. X      { case OPC_FIN:return r;                     /* finished */
  1088. X    case OPC_CLASS:st=epso(st,SZ(chclass));break;             /* skip */
  1089. X    case OPC_EPS:p=(uchar*)st;fillout(&st);               /* check tree */
  1090. X    default:++st;                         /* skip too */
  1091. X      }
  1092. X}
  1093. X
  1094. X#define XOR1        \
  1095. X (ioffsetof(struct eps,spawn)^ioffsetof(struct eps,stack))
  1096. X#define PC(this,t)    (*(struct eps**)((char*)(this)+(t)))
  1097. X
  1098. Xchar*bregexec(code,text,len,ign_case)struct eps*code;const uchar*text;
  1099. X size_t len;
  1100. X{ register struct eps*reg,*t,*stack,*other,*this;unsigned i,th1,ot1;
  1101. X  if(code[1].opc==OPC_EPS)
  1102. X     ++code;           /* two epsilons at the start would be superfluous */
  1103. X  (this=code)->stack=0;th1=ioffsetof(struct eps,spawn);
  1104. X  ot1=ioffsetof(struct eps,stack);--text;++len;
  1105. X  i='\n';goto setups;          /* make sure any beginning-of-line-hooks catch */
  1106. X  do
  1107. X   { i= *++text;             /* get the next real-text character */
  1108. Xlastrun:                     /* switch this & other pc-stack */
  1109. X     th1^=XOR1;ot1^=XOR1;this=other;
  1110. Xsetups:
  1111. X     reg=(other=stack=code)->next;goto nostack;
  1112. X     do                     /* pop next entry off this pc-stack */
  1113. X      { reg=(t=this)->next;this=PC(t,th1);PC(t,th1)=0;goto nostack;
  1114. X    do                /* pop next entry off the work-stack */
  1115. X     { stack=(t=stack)->stack;t->stack=0;reg=t->spawn;
  1116. Xnostack:   switch(reg->opc-OPB)        /* push spawned branch on the work-stack */
  1117. X        { default:
  1118. X         if(ign_case&&i-'A'<26)
  1119. X            i+='a'-'A';             /* transmogrify it to lowercase */
  1120. X         if(i==reg->opc)          /* regular character match */
  1121. X            goto yep;
  1122. X         break;
  1123. X          case OPC_EPS-OPB:reg->stack=stack;reg=(stack=reg)->next;
  1124. X         goto nostack;
  1125. X          case OPC_FIN-OPB:           /* hurray!  complete regexp match */
  1126. X         return(char*)text;        /* return one past the match */
  1127. X          case OPC_CLASS-OPB:
  1128. X         if(bit_test(((struct chclass*)reg)->c,i))
  1129. X            goto yep;                   /* character in class */
  1130. X         break;
  1131. X          case OPC_DOT-OPB:                     /* dot-wildcard */
  1132. X         if(i!='\n')
  1133. Xyep:            if(!PC(reg,ot1))             /* state not yet pushed */
  1134. X             { PC(reg,ot1)=other;other=reg;    /* push location onto */
  1135. X             }                       /* other pc-stack */
  1136. X        }
  1137. X     }
  1138. X    while(stack);                  /* the work-stack is not empty */
  1139. X      }
  1140. X     while(this!=code);                   /* this pc-stack is not empty */
  1141. X   }
  1142. X  while(--len);                         /* still text to search */
  1143. X  if(ign_case!=2)                          /* out of text */
  1144. X   { ign_case=2;len=1;++text;goto lastrun;     /* check if we just matched */
  1145. X   }
  1146. X  return 0;                             /* no match */
  1147. X}
  1148. END_OF_FILE
  1149. if test 10740 -ne `wc -c <'procmail/regexp.c'`; then
  1150.     echo shar: \"'procmail/regexp.c'\" unpacked with wrong size!
  1151. fi
  1152. # end of 'procmail/regexp.c'
  1153. fi
  1154. echo shar: End of archive 3 \(of 5\).
  1155. cp /dev/null ark3isdone
  1156. MISSING=""
  1157. for I in 1 2 3 4 5 ; do
  1158.     if test ! -f ark${I}isdone ; then
  1159.     MISSING="${MISSING} ${I}"
  1160.     fi
  1161. done
  1162. if test "${MISSING}" = "" ; then
  1163.     echo You have unpacked all 5 archives.
  1164.     rm -f ark[1-9]isdone
  1165. else
  1166.     echo You still need to unpack the following archives:
  1167.     echo "        " ${MISSING}
  1168. fi
  1169. ##  End of shell archive.
  1170. exit 0
  1171. -- 
  1172. Sincerely,                                berg@messua.informatik.rwth-aachen.de
  1173.            Stephen R. van den Berg (AKA BuGless).    berg@physik.tu-muenchen.de
  1174.  
  1175. He did a quarter of the work in *half* the time!
  1176.  
  1177. exit 0 # Just in case...
  1178.