home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume35 / procmail / part09 < prev    next >
Encoding:
Text File  |  1993-02-04  |  35.6 KB  |  1,043 lines

  1. Newsgroups: comp.sources.misc
  2. From: berg@pool.informatik.rwth-aachen.de (Stephen R. van den Berg)
  3. Subject: v35i030:  procmail - mail processing package v2.80, Part09/11
  4. Message-ID: <1993Feb5.020657.16855@sparky.imd.sterling.com>
  5. X-Md4-Signature: 3b4544919cbea3fff852c082644bf20e
  6. Date: Fri, 5 Feb 1993 02:06:57 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: berg@pool.informatik.rwth-aachen.de (Stephen R. van den Berg)
  10. Posting-number: Volume 35, Issue 30
  11. Archive-name: procmail/part09
  12. Environment: sendmail, smail, MMDF, mailsurr, UNIX, POSIX
  13. Supersedes: procmail: Volume 31, Issue 40-44
  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 9 (of 11)."
  22. # Contents:  procmail280/man/procmailrc.man procmail280/src/multigram.c
  23. # Wrapped by berg@hathi on Thu Feb  4 15:28:00 1993
  24. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  25. if test -f 'procmail280/man/procmailrc.man' -a "${1}" != "-c" ; then 
  26.   echo shar: Will not clobber existing file \"'procmail280/man/procmailrc.man'\"
  27. else
  28. echo shar: Extracting \"'procmail280/man/procmailrc.man'\" \(17104 characters\)
  29. sed "s/^X//" >'procmail280/man/procmailrc.man' <<'END_OF_FILE'
  30. X.Id $Id: procmailrc.man,v 1.14 1993/01/13 15:20:37 berg Exp $
  31. X.TH PROCMAILRC 5 \*(Dt BuGless
  32. X.na
  33. X.SH NAME
  34. Xprocmailrc \- procmail rcfile
  35. X.SH SYNOPSIS
  36. X.B $HOME/+PROCMAILRC+
  37. X.ad
  38. X.Sh DESCRIPTION
  39. XFor a quick start, see
  40. X.B NOTES
  41. Xat the end of the
  42. X.BR procmail (1)
  43. Xman page.
  44. X.PP
  45. XThe rcfile can contain a mixture of environment variable assignments (some
  46. Xof which have special meanings to procmail), and recipes.  In their most
  47. Xsimple appearance, the recipes are simply one line regular expressions
  48. Xthat are searched for in the header of the arriving mail, the first recipe
  49. Xthat matches is used to determine where the mail has to go (usually a file).
  50. X.PP
  51. XIf a matching recipe does not specify any special flags (like `+FILTER+' or
  52. X`+CONTINUE+') and the recipe is successful (i.e. no write failures or other
  53. Xcalamities), then processing of the rcfile will cease at this point, and
  54. Xprocmail will consider the mail to have been delivered.
  55. X.PP
  56. XThis enables you to presort your mail extremely straightforward into several
  57. Xmailfolders.  Bear in mind though that the mail can arrive concurrently in
  58. Xthese mailfolders (if several procmail programs happen to run at the same time,
  59. Xnot unlikely if a lot of mail arrives), to make sure this does not result in a
  60. Xmess, proper use of lockfiles is highly recommended.
  61. X.PP
  62. XThe environment variable
  63. X.B assignments
  64. Xand
  65. X.B recipes
  66. Xcan be freely intermixed in the rcfile. If any environment variable has
  67. Xa special meaning to procmail, it will be used appropiately the moment
  68. Xit is parsed. (i.e. you can change the current directory whenever you
  69. Xwant by specifying a new
  70. X.BR MAILDIR ,
  71. Xswitch lockfiles by specifying a new
  72. X.BR LOCKFILE ,
  73. Xchange the umask at any time, etc., the possibilities are endless :-).
  74. X.PP
  75. XThe assignments and substitutions of these environment variables are handled
  76. Xexactly like in
  77. X.BR sh (1)
  78. X(that includes all possible quotes and escapes),
  79. Xwith the added bonus that blanks around the '=' sign are ignored and that,
  80. Xif an environment variable appears without a trailing '=', it will be
  81. Xremoved from the environment.  Any program in backquotes started by procmail
  82. Xwill have the entire mail at its stdin.
  83. X.PP
  84. X.Ss Comments
  85. XA word beginning with # and all the following characters up to a NEWLINE
  86. Xare ignored.
  87. X.Ss Recipes
  88. X.PP
  89. XA line starting with ':' marks the beginning of a recipe.  It has the
  90. Xfollowing format:
  91. X.PP
  92. X.Rs
  93. X: [\fInumber\fP] [\fIflags\fP] [ : [\fIlocallockfile\fP] ]
  94. X<zero or more conditions (one per line)>
  95. X<exactly one action line>
  96. X.Re
  97. X.PP
  98. XThe
  99. X.I number
  100. Xis optional (defaults to 1) and specifies the number of conditions that
  101. Xfollow the first line of the recipe.  Conditions are complete lines that are
  102. Xpassed on to the internal egrep
  103. X.BR literally ,
  104. Xexcept for leading and trailing whitespace.
  105. XThese regular expressions are
  106. X.B completely
  107. Xcompatible to the normal
  108. X.BR egrep (1)
  109. Xregular expressions.
  110. X.PP
  111. XConditions are anded; if
  112. X.I number
  113. Xis zero, then the condition is always true and no conditions are expected
  114. Xnext.
  115. X.PP
  116. X.I Flags
  117. Xcan be any of the following:
  118. X.Tp 0.5i
  119. X.B +HEAD_GREP+
  120. XEgrep the header (default).
  121. X.Tp
  122. X.B +BODY_GREP+
  123. XEgrep the body.
  124. X.Tp
  125. X.B +DISTINGUISH_CASE+
  126. XTell the internal egrep to distinguish between upper and lower case (defaults
  127. Xto ignoring case).
  128. X.Tp
  129. X.B +ALSO_NEXT_RECIPE+
  130. XThis recipe will depend on the last preceding recipe without the
  131. X`+ALSO_NEXT_RECIPE+' or `+ALSO_N_IF_SUCC+' flag.  This allows you to chain
  132. Xactions that depend on a common condition.  The number of conditions that
  133. Xare expected to follow default to none.
  134. X.Tp
  135. X.B +ALSO_N_IF_SUCC+
  136. XHas the same meaning as the `+ALSO_NEXT_RECIPE+' flag, but will depend on the
  137. X.I successful
  138. Xcompletion of the immediately preceding recipe as well.
  139. X.Tp
  140. X.B +PASS_HEAD+
  141. XFeed the header to the pipe (default).
  142. X.Tp
  143. X.B +PASS_BODY+
  144. XFeed the body to the pipe (default).
  145. X.Tp
  146. X.B +FILTER+
  147. XConsider the pipe as a filter.
  148. X.Tp
  149. X.B +CONTINUE+
  150. XContinue processing rcfile even if this recipe matches (not needed when 'f'
  151. Xspecified).
  152. X.Tp
  153. X.B +WAIT_EXIT+
  154. XWait for the filter or program to finish and check its exitcode (normally
  155. Xignored); if the filter is unsuccessful, then the text will not have been
  156. Xfiltered.
  157. X.Tp
  158. X.B +WAIT_EXIT_QUIET+
  159. XHas the same meaning as the `+WAIT_EXIT+' flag, but will suppress any
  160. X`Program failure' message.
  161. X.Tp
  162. X.B +IGNORE_WRITERR+
  163. XIgnore any write errors on this recipe (i.e. usually due to an early closed
  164. Xpipe).
  165. X.PP
  166. XThere are some special conditions you can use that are not straight regular
  167. Xexpressions.  To select them, the first character of the condition must
  168. Xbe a:
  169. X.Tp 0.5i
  170. X.B !
  171. XInvert the condition.
  172. X.Tp
  173. X.B $
  174. XEvaluate the remainder according to
  175. X.BR sh (1)
  176. Xsubstitution rules inside double quotes.
  177. X.Tp
  178. X.B ?
  179. XUse the exitcode of the specified program.
  180. X.Tp
  181. X.B <
  182. XCheck if the total length of the mail is shorter than the specified (in
  183. Xdecimal) number of bytes.
  184. X.Tp
  185. X.B >
  186. XAnalogous to '<'.
  187. X.Tp
  188. X.B \e
  189. XTo quote any of the above at the start of the line.
  190. X.Ss "Local lockfile"
  191. X.PP
  192. XIf you put a second ':' on the first recipe line, then procmail will use a
  193. X.I locallockfile
  194. X(for this recipe only).  You can optionally specify the locallockfile
  195. Xto use; if you don't however, procmail will use the destination filename
  196. X(or the filename following the first '>>') and will append $LOCKEXT to it.
  197. X.Ss "Recipe action line"
  198. X.PP
  199. XThe action line can start with the following characters:
  200. X.Tp
  201. X.B !
  202. XForwards to all the specified mail addresses.
  203. X.Tp
  204. X.B |
  205. XStarts the specified program, possibly in $SHELL if any
  206. Xof the characters $SHELLMETAS are spotted.  You can optionally prepend this
  207. Xpipe symbol with
  208. X.IR variable= ,
  209. Xwhich will cause stdout of the program to be captured in the environment
  210. X.IR variable .
  211. X.PP
  212. XAnything else will be taken as a mailbox name (either a filename or a
  213. Xdirectory, absolute or relative to the current directory (see MAILDIR)).
  214. XIf it is a filename (or nonexistent), the mail will be appended to it.
  215. X.PP
  216. XIf it is a directory, the mail will be delivered to a newly created, guaranteed
  217. Xto be unique file named $MSGPREFIX* in the specified directory.  If the
  218. Xdirectory name ends in "+MCDIRSEP++chCURDIR+", then this directory is presumed
  219. Xto be an MH folder; i.e. procmail will use the next number it finds available.
  220. X.Ss "Environment variable defaults"
  221. X.Tp 2.2i
  222. X.B "LOGNAME, HOME and SHELL"
  223. XYour (the recipient's) defaults
  224. X.Tp
  225. X.B SHELLMETAS
  226. X\&+DEFshellmetas+
  227. X.Tp
  228. X.B SHELLFLAGS
  229. X\&+DEFshellflags+
  230. X.Tp
  231. X.BR ORGMAIL
  232. X\&+SYSTEM_MBOX+
  233. X.Tp
  234. X.B MAILDIR
  235. X\&+DEFmaildir+
  236. X.br
  237. X(Unless the name of the first successfully opened rcfile starts with
  238. X`+chCURDIR++MCDIRSEP+', in which case it defaults to `+chCURDIR+')
  239. X.Tp
  240. X.B DEFAULT
  241. X\&+DEFdefault+
  242. X.Tp
  243. X.B MSGPREFIX
  244. X\&+DEFmsgprefix+
  245. X.Tp
  246. X.B SENDMAIL
  247. X\&+DEFsendmail+
  248. X.Tp
  249. X.B COMSAT
  250. X\&+DEFcomsat+
  251. X.br
  252. X(If an rcfile is specified on the command line)
  253. X.Tp
  254. X.B LOCKEXT
  255. X\&+DEFlockext+
  256. X.Tp
  257. X.B LOCKFILE
  258. X\&+DEFdefaultlock+
  259. X.br
  260. X(After procmail closed the last rcfile)+PRESTENV++LD_ENV_FIX+
  261. X.Ss Environment
  262. X.PP
  263. XBefore you get lost in the multitude of environment variables, keep in mind
  264. Xthat all of them have reasonable defaults.
  265. X.Tp 1.2i
  266. X.B MAILDIR
  267. XCurrent directory while procmail is executing (that means that all paths
  268. Xare relative to $MAILDIR).
  269. X.Tp
  270. X.B DEFAULT
  271. XDefault
  272. X.B mailbox
  273. Xfile (if not told otherwise, procmail will dump mail in this mailbox).
  274. XProcmail will automatically use LOCKFILE=$DEFAULT$LOCKEXT prior to writing
  275. Xto this mailbox.
  276. X.Tp
  277. X.B MSGPREFIX
  278. XFilename prefix that is used when delivering to a directory (not used when
  279. Xdelivering to an MH directory).
  280. X.Tp
  281. X.B LOGFILE
  282. XAll incoming messages will be logged here with their `+FROM+' and `Subject:'
  283. Xlines in the header, and an additional line specifying what folder it
  284. Xfinally went to and how long (in bytes) the message was.  This file will
  285. Xalso contain any error or diagnostic messages from procmail
  286. X(normally none :-) or any other programs started by procmail.  If this file
  287. Xis not specified, any diagnostics or error messages will
  288. X+pconsole++console++aconsole+
  289. X.Tp
  290. X.B VERBOSE
  291. XYou can turn on
  292. X.I extended diagnostics
  293. Xby setting this variable to `yes' or `on', to turn it off again set it to `no'
  294. Xor `off'.
  295. X.Tp
  296. X.B LOG
  297. XAnything assigned to this variable will be echoed in $LOGFILE.
  298. X.Tp
  299. X.B ORGMAIL
  300. XUsually the system mailbox (\fBOR\fPi\fBG\fPinal \fBMAIL\fPbox).  If, for
  301. Xsome obscure reason (like `\fBfilesystem full\fP') the mail could not be
  302. Xdelivered, then this mailbox will be the last resort.  If procmail
  303. Xfails to save the mail in here (deep, deep trouble :-), then the mail
  304. Xwill bounce back to the sender.
  305. X.Tp
  306. X.B LOCKFILE
  307. XGlobal semaphore file.  If this file already exists, procmail
  308. Xwill wait until it has gone before proceeding, and will create it itself
  309. X(cleaning it up when ready, of course).  If more than one
  310. X.I lockfile
  311. Xare specified, then the previous one will be removed before trying to create
  312. Xthe new one.  The use of a global lockfile is discouraged, whenever possible
  313. Xuse locallockfiles (on a per recipe basis) instead.
  314. X.Tp
  315. X.B LOCKEXT
  316. XDefault extension that is appended to a destination file to determine
  317. Xwhat local
  318. X.I lockfile
  319. Xto use (only if turned on, on a per-recipe basis).
  320. X.Tp
  321. X.B LOCKSLEEP
  322. XNumber of seconds procmail will sleep before retrying on a
  323. X.I lockfile
  324. X(if it already existed); if not specified, it defaults to +DEFlocksleep+
  325. Xseconds.
  326. X.Tp
  327. X.B LOCKTIMEOUT
  328. XNumber of seconds that have to have passed since a
  329. X.I lockfile
  330. Xwas last modified/created before procmail decides that this must be an
  331. Xerroneously leftover lockfile that can be removed by force now.  If zero,
  332. Xthen no timeout will be used and procmail will wait forever until the
  333. Xlockfile is removed; if not specified, it defaults to +DEFlocktimeout+ seconds.
  334. XThis variable is useful to prevent indefinite hangups of
  335. X.BR sendmail /procmail.
  336. XProcmail is immune to clock skew.
  337. X.Tp
  338. X.B TIMEOUT
  339. XNumber of seconds that have to have passed before procmail decides that
  340. Xsome child it started must be hanging.  The offending program will receive
  341. Xa TERMINATE signal from procmail, and processing of the rcfile will continue.
  342. XIf zero, then no timeout will be used and procmail will wait forever until the
  343. Xchild has terminated; if not specified, it defaults to +DEFtimeout+ seconds.
  344. X.Tp
  345. X.B HOST
  346. XIf this is not the
  347. X.I hostname
  348. Xof the machine, processing of the current
  349. X.I rcfile
  350. Xwill immediately cease. If other rcfiles were specified on the
  351. Xcommand line, processing will continue with the next one.  If all rcfiles
  352. Xare exhausted, the program will terminate, but will not generate an error
  353. X(i.e. to the mailer it will seem that the mail has been delivered).  Only the
  354. Xfirst +HOSTNAMElen+ characters of the HOST are significant.
  355. X.Tp
  356. X.B UMASK
  357. XThe name says it all (if it doesn't, then forget about this one :-).  It
  358. Xis taken as an
  359. X.B octal
  360. Xnumber.  If not specified, it defaults to +INIT_UMASK+.
  361. X.Tp
  362. X.B SHELLMETAS
  363. XIf any of the characters in SHELLMETAS appears in the line specifying
  364. Xa filter or program, the line will be fed to $SHELL
  365. Xinstead of being executed directly.
  366. X.Tp
  367. X.B SHELLFLAGS
  368. XAny invocation of $SHELL will be like:
  369. X.br
  370. X"$SHELL" "$SHELLFLAGS" "$*";
  371. X.Tp
  372. X.B SENDMAIL
  373. XIf you're not using the
  374. X.I forwarding
  375. Xfacility don't worry about this one.  It specifies the program being
  376. Xcalled to forward any mail.
  377. X.br
  378. XIt gets invoked as: "$SENDMAIL" "$@";
  379. X.Tp
  380. X.B NORESRETRY
  381. XNumber of retries that are to be made if any `\fBprocess table full\fP',
  382. X`\fBfile table full\fP', `\fBout of memory\fP' or
  383. X`\fBout of swap space\fP' error should occur.  If this number is negative,
  384. Xthen procmail will retry indefinitely; if not specified, it defaults to
  385. X+DEFnoresretry+ times.  The retries occur with a $SUSPEND second interval.  The
  386. Xidea behind this is, that if e.g. the
  387. X.I swap
  388. X.I space
  389. Xhas been exhausted or the
  390. X.I process
  391. X.I table
  392. Xis full, usually several other programs will either detect this as well
  393. Xand abort or crash 8-), thereby freeing valuable
  394. X.I resources
  395. Xfor procmail.
  396. X.Tp
  397. X.B SUSPEND
  398. XNumber of seconds that procmail will pause if it has to wait for something
  399. Xthat is currently unavailable (memory, fork, etc.); if not specified, it will
  400. Xdefault to +DEFsuspend+ seconds.  See also:
  401. X.BR LOCKSLEEP .
  402. X.Tp
  403. X.B LINEBUF
  404. XLength of the internal line buffers, cannot be set smaller than +MINlinebuf+.
  405. XAll lines read from the
  406. X.I rcfile
  407. Xshould not exceed $LINEBUF characters before and after expansion.  If not
  408. Xspecified, it defaults to +DEFlinebuf+.  This limit, of course, does
  409. X.I not
  410. Xapply to the mail itself, which can have arbitrary line lengths, or could
  411. Xbe a binary file for that matter.
  412. X.Tp
  413. X.B DELIVERED
  414. XIf set to `yes' procmail will pretend (to the mail agent) the mail
  415. Xhas been delivered.  If mail cannot be delivered after meeting this
  416. Xassignment (to `yes'), the mail will be lost (i.e. it will not bounce).
  417. X.Tp
  418. X.B TRAP
  419. XWhen procmail terminates it will execute the contents of this variable.
  420. X.Tp
  421. X.B INCLUDERC
  422. XNames an rcfile (relative to the current directory) which will be included
  423. Xhere as if it were part of the current rcfile.  Unlimited nesting is
  424. Xpermitted.
  425. X.Tp
  426. X.B COMSAT
  427. X.BR Comsat (8)/ biff (1)
  428. Xnotification is on by default, it can be turned off by setting this variable
  429. Xto `no'.  Alternatively the biff-service can be customised by setting it to
  430. Xeither `service+SERV_ADDRsep+', `+SERV_ADDRsep+hostname', or
  431. X`service+SERV_ADDRsep+hostname'.  When not specified it defaults
  432. Xto +COMSATservice++SERV_ADDRsep++COMSAThost+.
  433. X.Sh EXAMPLES
  434. XLook in the
  435. X.BR procmailex (5)
  436. Xman page.
  437. X.Sh CAVEATS
  438. XContinued lines in an action line that specifies a program always have to end
  439. Xin a backslash, even if the underlying shell would not need or want the
  440. Xbackslash to indicate continuation.  This is due to the two pass parsing
  441. Xprocess needed (first procmail, then the shell (or not, depending on
  442. X.BR SHELLMETAS )).
  443. X.PP
  444. XDon't put comments on the regular expression condition lines in a
  445. Xrecipe, these lines are fed to the internal egrep
  446. X.I literally
  447. X(except for continuation backslashes at the end of a line).
  448. X.PP
  449. XWatch out for deadlocks when doing unhealthy things like forwarding mail
  450. Xto your own account.  Deadlocks can be broken by proper use of
  451. X.BR LOCKTIMEOUT .
  452. X.PP
  453. XAny default values that procmail has for some environment variables will
  454. X.B always
  455. Xoverride the ones that were already defined.  If you really want to
  456. Xoverride the defaults, you either have to put them in the
  457. X.B rcfile
  458. Xor on the command line as arguments.
  459. X.PP
  460. XIf you specify only a `+PASS_HEAD+' or a `+PASS_BODY+' flag on a recipe,
  461. Xand the recipe matches, then, unless a `+FILTER+' or `+CONTINUE+' flag is
  462. Xpresent as well, the body respectively the header of the mail will be silently
  463. Xlost.
  464. X.PP
  465. XThe `+CONTINUE+' flag defaults to on when capturing stdout of a recipe in an
  466. Xenvironment variable.
  467. X.Sh "SEE ALSO"
  468. X.na
  469. X.nh
  470. X.BR procmail (1),
  471. X.BR procmailex (5),
  472. X.BR sh (1),
  473. X.BR csh (1),
  474. X.BR mail (1),
  475. X.BR mailx (1),
  476. X.BR binmail (1),
  477. X.BR uucp (1),
  478. X.BR aliases (5),
  479. X.BR sendmail (8),
  480. X.BR egrep (1),
  481. X.BR grep (1),
  482. X.BR biff (1),
  483. X.BR comsat (8),
  484. X.BR lockfile (1),
  485. X.BR formail (1)
  486. X.hy
  487. X.ad
  488. X.Sh BUGS
  489. XThe only substitutions of environment variables that can be handled by
  490. Xprocmail itself are of the type $name, ${name}, $$, $? and $\-; whereas $\-
  491. Xwill be substituted by the name of the last folder delivered
  492. Xto.+UPPERCASE_USERNAMES+
  493. X.PP
  494. XA line buffer of length $LINEBUF is used when processing the
  495. X.IR rcfile ,
  496. Xany expansions
  497. X.B have
  498. Xto fit within this limit; if they don't, behaviour is undefined.
  499. X.PP
  500. XIf the global lockfile has a
  501. X.I relative
  502. Xpath, and the current directory
  503. Xis not the same as when the global lockfile was created, then the global
  504. Xlockfile will not be removed if procmail exits at that point (remedy:
  505. Xuse
  506. X.I absolute
  507. Xpaths to specify global lockfiles).
  508. X.PP
  509. XWhen capturing stdout from a recipe into an environment variable, exactly
  510. Xone trailing newline will be stripped.
  511. X.PP
  512. XBy using the `^' or `$' in other spots than at the start respectively
  513. Xend of a line you can use the internal egrep to do multiline matches.
  514. X.PP
  515. XWhen the regular expression starts with `^^' it will
  516. X.I only
  517. Xmatch at the very start of the text.
  518. X.Sh MISCELLANEOUS
  519. XIf the regular expression contains `\fB+TOkey+\fP' it will be substituted by
  520. X.na
  521. X.nh
  522. X`\fB+TOsubstitute+\fP',
  523. Xwhich should catch all destination specifications.
  524. X.hy
  525. X.ad
  526. X.PP
  527. XIf the regular expression contains `\fB+FROMDkey+\fP' it will be
  528. Xsubstituted by
  529. X.na
  530. X.nh
  531. X`\fB+FROMDsubstitute+\fP',
  532. Xwhich should catch mails coming from most daemons (how's that for a regular
  533. Xexpression :-).
  534. X.hy
  535. X.ad
  536. X.PP
  537. XWhen assigning boolean values to variables like VERBOSE, DELIVERED or COMSAT,
  538. Xprocmail accepts as true every string starting with: a non-zero value, `on',
  539. X`y' or `t'.  False is every string starting with: a zero value, `off', `n' or
  540. X`f'.
  541. X.PP
  542. XIf the action line of a recipe specifies a program, a sole backslash-newline
  543. Xpair in it on an otherwise empty line will be converted into a newline.
  544. X.Sh NOTES
  545. XSince whitespace generally is ignored in the rcfile you can indent everything
  546. Xto taste.
  547. X.PP
  548. XThe leading `|' on the action line to specify a program or filter is stripped
  549. Xbefore checking for $SHELLMETAS.
  550. X.PP
  551. XFiles included with the INCLUDERC directive containing only environment
  552. Xvariable assignments can be shared with sh.
  553. X.PP
  554. XFor
  555. X.I really
  556. Xcomplicated processing you can even consider calling
  557. X.B procmail
  558. Xrecursively.
  559. END_OF_FILE
  560. if test 17104 -ne `wc -c <'procmail280/man/procmailrc.man'`; then
  561.     echo shar: \"'procmail280/man/procmailrc.man'\" unpacked with wrong size!
  562. fi
  563. # end of 'procmail280/man/procmailrc.man'
  564. fi
  565. if test -f 'procmail280/src/multigram.c' -a "${1}" != "-c" ; then 
  566.   echo shar: Will not clobber existing file \"'procmail280/src/multigram.c'\"
  567. else
  568. echo shar: Extracting \"'procmail280/src/multigram.c'\" \(15547 characters\)
  569. sed "s/^X//" >'procmail280/src/multigram.c' <<'END_OF_FILE'
  570. X/************************************************************************
  571. X *    multigram - The human mail address reader            *
  572. X *                                    *
  573. X *    It uses multigrams to intelligently filter out mail addresses    *
  574. X *    from the garbage in any arbitrary mail.                *
  575. X *    Multigram is currently unable to pick out addresses that    *
  576. X *    contain embedded whitespace.                    *
  577. X *    This program also contains some swiss-army-knife mailinglist    *
  578. X *    support features.                        *
  579. X *                                    *
  580. X *    Seems to be relatively bug free.                *
  581. X *                                    *
  582. X *    Copyright (c) 1990-1992, S.R. van den Berg, The Netherlands    *
  583. X *    #include "README"                        *
  584. X ************************************************************************/
  585. X#ifdef RCS
  586. Xstatic /*const*/char rcsid[]=
  587. X "$Id: multigram.c,v 1.21 1993/02/02 15:27:19 berg Exp $";
  588. X#endif
  589. Xstatic /*const*/char rcsdate[]="$Date: 1993/02/02 15:27:19 $";
  590. X#include "includes.h"
  591. X#include "sublib.h"
  592. X#include "shell.h"
  593. X#include "ecommon.h"
  594. X
  595. X#define BUFSTEP        16
  596. X#define COPYBUF        16384
  597. X/*#define SPEEDBUF    COPYBUF           /* uncomment to get a speed increase? */
  598. X#define SCALE_WEIGHT    0x7fff
  599. X
  600. X#define DEFmaxgram    4
  601. X#define DEFminweight    (SCALE_WEIGHT/4)          /* sanity cutoff value */
  602. X#define DEFbest_matches 2
  603. X
  604. X#define PROCMAIL    "../.bin/procmail"      /* some configurable paths */
  605. X#define DEFAULTS_DIR    ".etc"
  606. X#define GLOCKFILE    "../.etc/rc.lock"
  607. X#define RCMAIN        "./.etc/rc.main"
  608. X#define LLOCKFILE    "rc.lock"
  609. X#define REQUEST        "-request"
  610. X#define RCSUBMIT    "./rc.submit"
  611. X#define RCREQUEST    "./rc.request"
  612. X#define RCPOST        "./../.etc/rc.post"
  613. X#define RCINIT        "RC_INIT=rc.init"
  614. X#define XENVELOPETO    "X_ENVELOPE_TO="
  615. X#define LIST        "list="
  616. X
  617. X#define metoo_SENDMAIL        "-om"
  618. X#define nometoo_SENDMAIL    "-omF"
  619. X#define REMOV1_DELIM "(Only"
  620. X#define REMOV2_DELIM "addresses below this line can be automatically removed)"
  621. X#define NOT_METOO    "(-n)"
  622. X
  623. Xstruct string{char*text,*itext;size_t buflen;};
  624. X
  625. Xstatic remov_delim;
  626. X
  627. XstrnIcmp(a,b,l)const char*a,*b;size_t l;                 /* stub */
  628. X{ return strncmp(a,b,l);
  629. X}
  630. X            /* read a string from a file into a struct string buffer */
  631. Xstatic size_t readstr(file,p,linewise)FILE*const file;struct string*p;
  632. X const int linewise;
  633. X{ size_t len;int i,firstspc;
  634. X  static const char rem1str[]=REMOV1_DELIM,rem2str[]=REMOV2_DELIM;
  635. X  for(len=firstspc=0;;)
  636. X   { switch(i=getc(file))
  637. X      { case ' ':case '\t':case '\n':
  638. X       if(!len)                  /* only skip leading space */
  639. X          continue;
  640. X       if(!linewise)              /* do we need a complete line? */
  641. X          break;                       /* no, a word will do */
  642. X       if(!firstspc)                 /* already seen spaces? */
  643. X        { if(i=='\n')                 /* no, so check for EOL */
  644. X           { p->text[len]='\0';      /* terminate the first word, split */
  645. X         if(++len==p->buflen)         /* still buffer space left? */
  646. X            p->text=realloc(p->text,p->buflen+=BUFSTEP);
  647. X         break;
  648. X           }
  649. X          i='\0';firstspc=1;
  650. X        }             /* not the first word on the line, continue */
  651. X       if(i=='\n')
  652. X          break;
  653. X    default:p->text[len]=i;              /* regular character, store it */
  654. X       if(++len==p->buflen)               /* watch our buffer space */
  655. X          p->text=realloc(p->text,p->buflen+=BUFSTEP);
  656. X       continue;                       /* next character */
  657. X    case EOF:;
  658. X      }
  659. X     p->text[len]='\0';             /* terminate the buffer in any case */
  660. X     if(linewise&&!remov_delim&&!strcmp(p->text,rem1str)&&
  661. X      !strcmp(p->text+sizeof rem1str,rem2str))           /* special delimiter? */
  662. X    remov_delim=1;
  663. X     return len;
  664. X   }
  665. X}
  666. X
  667. Xstatic char*tstrdup(p)const char*const p;
  668. X{ return strcpy(malloc(strlen(p)+1),p);
  669. X}
  670. X
  671. Xstatic void lowcase(str)struct string*const str;       /* make lowercase */
  672. X{ register char*p;
  673. X  for(p=str->itext=tstrdup(str->text);*p;p++)
  674. X     if((unsigned)*p-'A'<'Z'-'A')
  675. X    *p+='a'-'A';
  676. X}
  677. X
  678. Xstatic void elog(a)const char*const a;
  679. X{ fputs(a,stderr);
  680. X}
  681. X                            /* the program names */
  682. Xstatic const char idhash[]="idhash",flist[]="flist",dirsep[]=DIRSEP;
  683. Xstatic const char*progname="multigram";
  684. X
  685. Xvoid nlog(a)const char*const a;            /* log error with identification */
  686. X{ elog(progname);elog(": ");elog(a);
  687. X}
  688. X                         /* finds the next character */
  689. Xstatic char*lastdirsep(filename)const char*filename;
  690. X{ const char*p;                    /* following the last DIRSEP */
  691. X  while(p=strpbrk(filename,dirsep))
  692. X     filename=p+1;
  693. X  return(char*)filename;
  694. X}
  695. X                           /* check rc.lock file age */
  696. Xstatic void rclock(file,stbuf)const char*const file;struct stat*const stbuf;
  697. X{ while(!stat(file,stbuf)&&time((time_t*)0)-stbuf->st_mtime<DEFlocktimeout)
  698. X     sleep(DEFlocksleep);                 /* wait, if appropriate */
  699. X}
  700. X
  701. Xstatic char*argstr(first,last)const char*first,*last;        /* construct */
  702. X{ char*chp;size_t i;                   /* an argument assignment */
  703. X  strcpy(chp=malloc((i=strlen(first))+strlen(last)+1),first);
  704. X  strcpy(chp+i,last);return chp;
  705. X}
  706. X
  707. Xstatic PROGID;
  708. X
  709. Xmain(minweight,argv)char*argv[];
  710. X{ struct string fuzzstr,hardstr;FILE*hardfile;const char*addit=0;
  711. X  struct match{char*fuzz,*hard;int metric;long lentry,offs1,offs2;}
  712. X   **best,*curmatch=0;
  713. X  unsigned best_matches,maxgram,maxweight,charoffs=0,remov=0,renam=0,
  714. X   chkmetoo=(char*)progid-(char*)progid;
  715. X  int lastfrom;
  716. X  static const char usage[]=
  717. X "Usage: multigram [-cdmr] [-b nnn] [-l nnn] [-w nnn] [-a address] filename\n";
  718. X  if(minweight)                  /* sanity check, any arguments at all? */
  719. X   { char*chp;
  720. X     if(!strcmp(chp=lastdirsep(argv[0]),flist))         /* suid flist prog? */
  721. X      { struct stat stbuf;
  722. X    *chp='\0';                /* security check, 3 hardlinks!? */
  723. X    if(!chdir(argv[0])&&!lstat(flist,&stbuf)&&S_ISREG(stbuf.st_mode)&&
  724. X     stbuf.st_mode&S_ISUID&&stbuf.st_uid==geteuid()&&stbuf.st_nlink==3&&
  725. X     !chdir(chPARDIR))
  726. X     { static const char request[]=REQUEST,xenvlpto[]=XENVELOPETO,
  727. X        rcrequest[]=RCREQUEST,rcpost[]=RCPOST,list[]=LIST,
  728. X        *pmexec[]={PROCMAIL,RCSUBMIT,RCINIT,0,0,rcrequest,rcpost,0};
  729. X#define Endpmexec(i)    (pmexec[maxindex(pmexec)-(i)])
  730. X       char*arg;
  731. X       if(minweight!=2)               /* wrong number of arguments? */
  732. X        { elog("Usage: flist listname[-request]\n");return EX_USAGE;
  733. X        }
  734. X       chp=strchr(arg=argv[1],'\0');           /* check for -request */
  735. X       if(chp-arg>STRLEN(request)&&!strcmp(chp-=STRLEN(request),request))
  736. X          *chp='\0',pmexec[1]=rcrequest,Endpmexec(1)=0,Endpmexec(2)=rcpost;
  737. X       else
  738. X          chp=0;
  739. X       if(chdir(arg))             /* goto the list's subdirectory */
  740. X          pmexec[1]=RCMAIN,Endpmexec(2)=0,chdir(DEFAULTS_DIR);
  741. X       Endpmexec(4)=argstr(list,arg);        /* pass on the list name */
  742. X       if(chp)                  /* was it a -request list? */
  743. X          *chp= *request;             /* then restore the leading '-' */
  744. X       Endpmexec(3)=argstr(xenvlpto,arg);setuid(stbuf.st_uid);
  745. X       setgid(stbuf.st_gid);rclock(GLOCKFILE,&stbuf);        /* stall */
  746. X       rclock(LLOCKFILE,&stbuf);
  747. X       execve(pmexec[0],(char*const*)pmexec,environ);  /* start procmail */
  748. X       nlog("Couldn't exec \"");elog(pmexec[0]);elog("\"\n");
  749. X       return EX_UNAVAILABLE;                    /* panic */
  750. X     }
  751. X    nlog("Missing permissions\n");return EX_NOPERM;
  752. X      }
  753. X     setgid(getgid());setuid(getuid());          /* revoke suid permissions */
  754. X     if(!strcmp(chp,idhash))                  /* idhash program? */
  755. X      { unsigned long hash=0;int i;
  756. X    if(minweight!=1)
  757. X     { elog("Usage: idhash\n");return EX_USAGE;
  758. X     }
  759. X    while(i=fgetc(stdin),!feof(stdin))               /* hash away! */
  760. X       hash=hash*67067L+i;
  761. X    printf("%lx",hash);return EX_OK;
  762. X      }
  763. X     minweight=SCALE_WEIGHT;best_matches=maxgram=0;
  764. X     while((chp= *++argv)&&*chp=='-')
  765. X    for(chp++;;)
  766. X     { int c;
  767. X       switch(c= *chp++)
  768. X        { case 'c':charoffs=1;continue;
  769. X          case 'd':remov=1;continue;
  770. X          case 'r':renam=1;continue;
  771. X          case 'm':chkmetoo=1;continue;
  772. X          case 'a':
  773. X         if(!*chp&&!(chp= *++argv))
  774. X            goto usg;
  775. X         addit=chp;break;
  776. X          case 'b':case 'l':case 'w':
  777. X           { int i;
  778. X         ;{ const char*ochp;
  779. X            if(!*chp&&!(chp= *++argv)||
  780. X             (i=strtol(ochp=chp,(char**)&chp,10),chp==ochp))
  781. X               goto usg;
  782. X          }
  783. X         switch(c)
  784. X          { case 'b':best_matches=i;continue;
  785. X            case 'l':minweight=i;continue;
  786. X            case 'w':maxgram=i;continue;
  787. X          }
  788. X           }
  789. X          case HELPOPT1:case HELPOPT2:elog(usage);
  790. X         elog(
  791. X "\t-a address\tadd this address to the list\
  792. X\n\t-b nnn\t\tmaximum no. of best matches shown\
  793. X\n\t-c\t\tdisplay offsets in characters\
  794. X\n\t-d\t\tdelete address from list\
  795. X\n\t-m\t\tcheck for metoo\
  796. X\n\t-l nnn\t\tlower bound metric\
  797. X\n\t-r\t\trename address on list\
  798. X\n\t-w nnn\t\twindow width used when matching\n");return EX_USAGE;
  799. X          case '-':
  800. X         if(!*chp)
  801. X          { chp= *++argv;goto lastopt;
  802. X          }
  803. X          default:goto usg;
  804. X          case '\0':;
  805. X        }
  806. X       break;
  807. X     }
  808. Xlastopt:
  809. X     if(!chp||*++argv||renam+remov+!!addit>1)
  810. X    goto usg;
  811. X     if(!(hardfile=fopen(chp,remov||renam||addit?"r+":"r")))
  812. X      { nlog("Couldn't open \"");elog(chp);elog("\"\n");return EX_IOERR;
  813. X      }
  814. X#ifdef SPEEDBUF                   /* allocate a bigger stdio buffer */
  815. X     setvbuf(hardfile,malloc(SPEEDBUF),_IOFBF,(size_t)SPEEDBUF);
  816. X#endif
  817. X   }
  818. X  else
  819. Xusg:
  820. X   { elog(usage);return EX_USAGE;
  821. X   }
  822. X  if(addit)                  /* special subfunction, to add entries */
  823. X   { int lnl;long lasttell;                 /* to the dist file */
  824. X     for(lnl=1,lasttell=0;;)
  825. X      { switch(getc(hardfile))                /* step through the file */
  826. X     { case '\n':
  827. X          if(!lnl)                /* looking for trailing newlines */
  828. X         lnl=1,lasttell=ftell(hardfile);
  829. X          continue;
  830. X       default:lnl=0;continue;
  831. X       case EOF:;                   /* or the end of the file */
  832. X     }
  833. X    break;
  834. X      }                     /* go back there, and add the new entry */
  835. X     fseek(hardfile,lasttell,SEEK_SET);fprintf(hardfile,"%s\n",addit);
  836. X     printf("Added: %s\n",addit);fclose(hardfile);return EX_OK;
  837. X   }
  838. X  if(!maxgram)
  839. X     maxgram=DEFmaxgram;
  840. X  maxgram--;
  841. X  if(minweight==SCALE_WEIGHT)
  842. X     minweight=DEFminweight;
  843. X  if(!best_matches)
  844. X     best_matches=DEFbest_matches;
  845. X  fuzzstr.text=malloc(fuzzstr.buflen=BUFSTEP);
  846. X  hardstr.text=malloc(hardstr.buflen=BUFSTEP);
  847. X  ;{ int i;
  848. X     best=malloc(best_matches--*sizeof*best);i=best_matches;
  849. X     do
  850. X      { best[i]=malloc(sizeof**best);best[i]->hard=malloc(1);
  851. X    best[i]->fuzz=malloc(1);best[i]->metric= -SCALE_WEIGHT;
  852. X      }
  853. X     while(i--);
  854. X   }
  855. X  for(lastfrom= -1;readstr(stdin,&fuzzstr,0);)
  856. X   { int meter,maxmetric;size_t fuzzlen;long linentry,offs1,offs2;
  857. X     ;{ char*chp,*echp;int parens;
  858. X    echp=strchr(chp=fuzzstr.text,'\0')-1;
  859. X    do
  860. X     { switch(*echp)
  861. X        { case '.':case ',':case ';':case ':':case '?':case '!':*echp='\0';
  862. X         continue;
  863. X        }
  864. X       break;
  865. X     }
  866. X    while(--echp>chp);    /* roughly check if it could be a mail address */
  867. X    if(lastfrom<=0&&!strpbrk(chp,"@/")&&(!strchr(chp,'!')||
  868. X     strchr(chp,'|')||strchr(chp,',')||strstr(chp,"..")))
  869. X     { if(lastfrom<0)
  870. X          lastfrom=!strcmp(SHFROM,chp);
  871. X       continue;              /* apparently not an email address */
  872. X     }
  873. X    lastfrom=0;
  874. X    for(parens=0;chp=strchr(chp,'(');chp++,parens++);
  875. X    for(chp=fuzzstr.text;chp=strchr(chp,')');chp++,parens--);
  876. X    if(*(chp=fuzzstr.text)=='(')
  877. X     { if(!parens&&*echp==')')
  878. X        { *echp='\0';goto shftleft;
  879. X        }
  880. X       if(parens>0)
  881. Xshftleft:     tmemmove(chp,chp+1,strlen(chp));
  882. X     }
  883. X    else if(parens<0&&*echp==')')
  884. X       *echp='\0';
  885. X    if(*(chp=fuzzstr.text)=='<'&&*(echp=strchr(chp,'\0')-1)=='>'
  886. X     &&echp==strpbrk(chp,"([\">,; \t\n"))          /* strip '<' and '>' ? */
  887. X       *echp='\0',tmemmove(chp,chp+1,echp-chp);
  888. X    if(!(fuzzlen=strlen(chp)))
  889. X       continue;;
  890. X    if(!curmatch)
  891. X       curmatch=malloc(sizeof*curmatch);
  892. X    curmatch->fuzz=tstrdup(chp);curmatch->hard=malloc(1);
  893. X    curmatch->metric= -SCALE_WEIGHT;
  894. X      }
  895. X     lowcase(&fuzzstr);fseek(hardfile,0L,SEEK_SET);
  896. X     maxmetric=best[best_matches]->metric;
  897. X     for(remov_delim=offs2=linentry=0;
  898. X      offs1=offs2,readstr(hardfile,&hardstr,1);)
  899. X      { size_t minlen,hardlen,maxlen;register size_t gramsize;
  900. X    offs2=ftell(hardfile);linentry++;
  901. X    if(*hardstr.text=='(')
  902. X       continue;                   /* unsuitable for matches */
  903. X    lowcase(&hardstr);
  904. X    if((minlen=hardlen=strlen(hardstr.text))>(maxlen=fuzzlen))
  905. X       minlen=fuzzlen,maxlen=hardlen;
  906. X    if((gramsize=minlen-1)>maxgram)
  907. X       gramsize=maxgram;
  908. X    maxweight=SCALE_WEIGHT/(gramsize+1);
  909. X    meter=(int)((unsigned long)SCALE_WEIGHT/2*minlen/maxlen)-
  910. X     SCALE_WEIGHT/2;
  911. X    do                    /* reset local multigram counter */
  912. X     { register lmeter=0;size_t cmaxlen=maxlen;
  913. X       ;{ register const char*fzz,*hrd;
  914. X          fzz=fuzzstr.itext;
  915. X          do
  916. X           { for(hrd=fzz+1;hrd=strchr(hrd,*fzz);)     /* is it present in */
  917. X            if(!strncmp(++hrd,fzz+1,gramsize))          /* own string? */
  918. X             { if(cmaxlen>gramsize+1)
  919. X              cmaxlen--;
  920. X               goto dble_gram;             /* skip until it's last */
  921. X             }
  922. X         for(hrd=hardstr.itext;hrd=strchr(hrd,*fzz);)    /* otherwise */
  923. X            if(!strncmp(++hrd,fzz+1,gramsize))     /* search it in the */
  924. X             { lmeter++;break;                   /* dist entry */
  925. X             }
  926. Xdble_gram:;    }
  927. X          while(*(++fzz+gramsize));                /* next gram */
  928. X        }
  929. X       if(lmeter)
  930. X        { unsigned weight;
  931. X          if(cmaxlen>minlen)
  932. X         cmaxlen=minlen;
  933. X          meter+=lmeter*(weight=maxweight/(unsigned)(cmaxlen-gramsize));
  934. X          meter-=weight*
  935. X           (unsigned long)((lmeter+=gramsize-cmaxlen)<0?-lmeter:lmeter)/
  936. X           cmaxlen;
  937. X        }
  938. X     }
  939. X    while(gramsize--);         /* search all gramsizes down to one */
  940. X    free(hardstr.itext);             /* check if we had any luck */
  941. X    if(meter>maxmetric&&(remov_delim||!renam&&!remov))
  942. X     { curmatch->metric=maxmetric=meter;curmatch->lentry=linentry;
  943. X       free(curmatch->hard);hardlen++;
  944. X       hardlen+=strlen(hardstr.text+hardlen)+1;
  945. X       curmatch->hard=malloc(hardlen+=strlen(hardstr.text+hardlen)+1);
  946. X       tmemmove(curmatch->hard,hardstr.text,hardlen);
  947. X       curmatch->offs1=offs1;curmatch->offs2=offs2;
  948. X     }
  949. X      }
  950. X     free(fuzzstr.itext);
  951. X     if(curmatch->metric>=0)     /* maybe this match can be put in the array */
  952. X      { struct match*mp,**mmp;               /* of best matches so far */
  953. X    free((mp= *(mmp=best+best_matches))->fuzz);free(mp->hard);free(mp);
  954. X    while(--mmp>=best&&(mp= *mmp)->metric<curmatch->metric)
  955. X       mmp[1]=mp;                       /* keep it sorted */
  956. X    mmp[1]=curmatch;curmatch=0;
  957. X      }
  958. X     else
  959. X    free(curmatch->fuzz),free(curmatch->hard);
  960. X   }
  961. X  ;{ int i;struct match*mp;
  962. X     for(i=0;i<=best_matches&&(mp=best[i++])->metric>=minweight;)
  963. X    if(chkmetoo)
  964. X       printf("%s\n",strcmp(mp->hard+strlen(mp->hard)+1,NOT_METOO)
  965. X        ?metoo_SENDMAIL:nometoo_SENDMAIL);
  966. X    else
  967. X       printf("%3ld %-34s %5d %-34s\n",
  968. X        charoffs?mp->offs1:mp->lentry,mp->hard,mp->metric,mp->fuzz);
  969. X     if((mp= *best)->metric>=minweight)
  970. X      { struct match*worse;
  971. X    if(renam)
  972. X     { long line;int i,w1;
  973. X       maxweight>>=1;
  974. X       for(i=1,line=mp->lentry,w1=mp->metric,worse=0;
  975. X        i<=best_matches&&(mp=best[i++])->metric>=minweight;)
  976. X          if(mp->lentry==line&&mp->metric+maxweight<w1)
  977. X           { goto remv1;
  978. X           }
  979. X       for(i=1;i<=best_matches&&(mp=best[i++])->metric>=minweight;)
  980. X          if(mp->metric+maxweight<w1)
  981. Xremv1:           { worse=mp;mp= *best;goto remv;
  982. X           }
  983. X       nlog("Couldn't find a proper address pair\n");goto norenam;
  984. X     }
  985. X    if(remov)
  986. Xremv:     { char*buf;long offs1,offs2;size_t readin;
  987. X       buf=malloc(COPYBUF);offs1=mp->offs1;offs2=mp->offs2;
  988. X       while(fseek(hardfile,offs2,SEEK_SET),
  989. X        readin=fread(buf,1,COPYBUF,hardfile))
  990. X        { offs2=ftell(hardfile);fseek(hardfile,offs1,SEEK_SET);
  991. X          if(buf[readin-1]=='\n')      /* try and remove some empty lines */
  992. X         while(readin>1&&buf[readin-2]=='\n')    /* at the end, since */
  993. X            readin--;             /* every time could be the last */
  994. X          fwrite(buf,1,readin,hardfile);offs1=ftell(hardfile);
  995. X        }
  996. X       free(buf);fseek(hardfile,offs1,SEEK_SET);
  997. X       printf("Removed: %s\n",mp->hard);
  998. X       if(renam)
  999. X          fputs(worse->fuzz,hardfile),printf("Added: %s\n",worse->fuzz);
  1000. X       do putc('\n',hardfile);               /* erase the tail */
  1001. X       while(ftell(hardfile)<offs2);
  1002. X       fclose(hardfile);
  1003. X     }
  1004. X    return EX_OK;
  1005. X      }
  1006. X   }
  1007. X  if(remov||renam)
  1008. X   { nlog("Couldn't even find a single address\n");
  1009. X   }
  1010. Xnorenam:
  1011. X  return 1;
  1012. X}
  1013. END_OF_FILE
  1014. if test 15547 -ne `wc -c <'procmail280/src/multigram.c'`; then
  1015.     echo shar: \"'procmail280/src/multigram.c'\" unpacked with wrong size!
  1016. fi
  1017. # end of 'procmail280/src/multigram.c'
  1018. fi
  1019. echo shar: End of archive 9 \(of 11\).
  1020. cp /dev/null ark9isdone
  1021. MISSING=""
  1022. for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
  1023.     if test ! -f ark${I}isdone ; then
  1024.     MISSING="${MISSING} ${I}"
  1025.     fi
  1026. done
  1027. if test "${MISSING}" = "" ; then
  1028.     echo You have unpacked all 11 archives.
  1029.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1030. else
  1031.     echo You still need to unpack the following archives:
  1032.     echo "        " ${MISSING}
  1033. fi
  1034. ##  End of shell archive.
  1035. exit 0
  1036. -- 
  1037. Sincerely,                                  berg@pool.informatik.rwth-aachen.de
  1038.            Stephen R. van den Berg (AKA BuGless).    berg@physik.tu-muenchen.de
  1039.  
  1040. "Be spontaneous!"
  1041.  
  1042. exit 0 # Just in case...
  1043.