home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-02-04 | 35.6 KB | 1,043 lines |
- Newsgroups: comp.sources.misc
- From: berg@pool.informatik.rwth-aachen.de (Stephen R. van den Berg)
- Subject: v35i030: procmail - mail processing package v2.80, Part09/11
- Message-ID: <1993Feb5.020657.16855@sparky.imd.sterling.com>
- X-Md4-Signature: 3b4544919cbea3fff852c082644bf20e
- Date: Fri, 5 Feb 1993 02:06:57 GMT
- Approved: kent@sparky.imd.sterling.com
-
- Submitted-by: berg@pool.informatik.rwth-aachen.de (Stephen R. van den Berg)
- Posting-number: Volume 35, Issue 30
- Archive-name: procmail/part09
- Environment: sendmail, smail, MMDF, mailsurr, UNIX, POSIX
- Supersedes: procmail: Volume 31, Issue 40-44
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 9 (of 11)."
- # Contents: procmail280/man/procmailrc.man procmail280/src/multigram.c
- # Wrapped by berg@hathi on Thu Feb 4 15:28:00 1993
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'procmail280/man/procmailrc.man' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'procmail280/man/procmailrc.man'\"
- else
- echo shar: Extracting \"'procmail280/man/procmailrc.man'\" \(17104 characters\)
- sed "s/^X//" >'procmail280/man/procmailrc.man' <<'END_OF_FILE'
- X.Id $Id: procmailrc.man,v 1.14 1993/01/13 15:20:37 berg Exp $
- X.TH PROCMAILRC 5 \*(Dt BuGless
- X.na
- X.SH NAME
- Xprocmailrc \- procmail rcfile
- X.SH SYNOPSIS
- X.B $HOME/+PROCMAILRC+
- X.ad
- X.Sh DESCRIPTION
- XFor a quick start, see
- X.B NOTES
- Xat the end of the
- X.BR procmail (1)
- Xman page.
- X.PP
- XThe rcfile can contain a mixture of environment variable assignments (some
- Xof which have special meanings to procmail), and recipes. In their most
- Xsimple appearance, the recipes are simply one line regular expressions
- Xthat are searched for in the header of the arriving mail, the first recipe
- Xthat matches is used to determine where the mail has to go (usually a file).
- X.PP
- XIf a matching recipe does not specify any special flags (like `+FILTER+' or
- X`+CONTINUE+') and the recipe is successful (i.e. no write failures or other
- Xcalamities), then processing of the rcfile will cease at this point, and
- Xprocmail will consider the mail to have been delivered.
- X.PP
- XThis enables you to presort your mail extremely straightforward into several
- Xmailfolders. Bear in mind though that the mail can arrive concurrently in
- Xthese mailfolders (if several procmail programs happen to run at the same time,
- Xnot unlikely if a lot of mail arrives), to make sure this does not result in a
- Xmess, proper use of lockfiles is highly recommended.
- X.PP
- XThe environment variable
- X.B assignments
- Xand
- X.B recipes
- Xcan be freely intermixed in the rcfile. If any environment variable has
- Xa special meaning to procmail, it will be used appropiately the moment
- Xit is parsed. (i.e. you can change the current directory whenever you
- Xwant by specifying a new
- X.BR MAILDIR ,
- Xswitch lockfiles by specifying a new
- X.BR LOCKFILE ,
- Xchange the umask at any time, etc., the possibilities are endless :-).
- X.PP
- XThe assignments and substitutions of these environment variables are handled
- Xexactly like in
- X.BR sh (1)
- X(that includes all possible quotes and escapes),
- Xwith the added bonus that blanks around the '=' sign are ignored and that,
- Xif an environment variable appears without a trailing '=', it will be
- Xremoved from the environment. Any program in backquotes started by procmail
- Xwill have the entire mail at its stdin.
- X.PP
- X.Ss Comments
- XA word beginning with # and all the following characters up to a NEWLINE
- Xare ignored.
- X.Ss Recipes
- X.PP
- XA line starting with ':' marks the beginning of a recipe. It has the
- Xfollowing format:
- X.PP
- X.Rs
- X: [\fInumber\fP] [\fIflags\fP] [ : [\fIlocallockfile\fP] ]
- X<zero or more conditions (one per line)>
- X<exactly one action line>
- X.Re
- X.PP
- XThe
- X.I number
- Xis optional (defaults to 1) and specifies the number of conditions that
- Xfollow the first line of the recipe. Conditions are complete lines that are
- Xpassed on to the internal egrep
- X.BR literally ,
- Xexcept for leading and trailing whitespace.
- XThese regular expressions are
- X.B completely
- Xcompatible to the normal
- X.BR egrep (1)
- Xregular expressions.
- X.PP
- XConditions are anded; if
- X.I number
- Xis zero, then the condition is always true and no conditions are expected
- Xnext.
- X.PP
- X.I Flags
- Xcan be any of the following:
- X.Tp 0.5i
- X.B +HEAD_GREP+
- XEgrep the header (default).
- X.Tp
- X.B +BODY_GREP+
- XEgrep the body.
- X.Tp
- X.B +DISTINGUISH_CASE+
- XTell the internal egrep to distinguish between upper and lower case (defaults
- Xto ignoring case).
- X.Tp
- X.B +ALSO_NEXT_RECIPE+
- XThis recipe will depend on the last preceding recipe without the
- X`+ALSO_NEXT_RECIPE+' or `+ALSO_N_IF_SUCC+' flag. This allows you to chain
- Xactions that depend on a common condition. The number of conditions that
- Xare expected to follow default to none.
- X.Tp
- X.B +ALSO_N_IF_SUCC+
- XHas the same meaning as the `+ALSO_NEXT_RECIPE+' flag, but will depend on the
- X.I successful
- Xcompletion of the immediately preceding recipe as well.
- X.Tp
- X.B +PASS_HEAD+
- XFeed the header to the pipe (default).
- X.Tp
- X.B +PASS_BODY+
- XFeed the body to the pipe (default).
- X.Tp
- X.B +FILTER+
- XConsider the pipe as a filter.
- X.Tp
- X.B +CONTINUE+
- XContinue processing rcfile even if this recipe matches (not needed when 'f'
- Xspecified).
- X.Tp
- X.B +WAIT_EXIT+
- XWait for the filter or program to finish and check its exitcode (normally
- Xignored); if the filter is unsuccessful, then the text will not have been
- Xfiltered.
- X.Tp
- X.B +WAIT_EXIT_QUIET+
- XHas the same meaning as the `+WAIT_EXIT+' flag, but will suppress any
- X`Program failure' message.
- X.Tp
- X.B +IGNORE_WRITERR+
- XIgnore any write errors on this recipe (i.e. usually due to an early closed
- Xpipe).
- X.PP
- XThere are some special conditions you can use that are not straight regular
- Xexpressions. To select them, the first character of the condition must
- Xbe a:
- X.Tp 0.5i
- X.B !
- XInvert the condition.
- X.Tp
- X.B $
- XEvaluate the remainder according to
- X.BR sh (1)
- Xsubstitution rules inside double quotes.
- X.Tp
- X.B ?
- XUse the exitcode of the specified program.
- X.Tp
- X.B <
- XCheck if the total length of the mail is shorter than the specified (in
- Xdecimal) number of bytes.
- X.Tp
- X.B >
- XAnalogous to '<'.
- X.Tp
- X.B \e
- XTo quote any of the above at the start of the line.
- X.Ss "Local lockfile"
- X.PP
- XIf you put a second ':' on the first recipe line, then procmail will use a
- X.I locallockfile
- X(for this recipe only). You can optionally specify the locallockfile
- Xto use; if you don't however, procmail will use the destination filename
- X(or the filename following the first '>>') and will append $LOCKEXT to it.
- X.Ss "Recipe action line"
- X.PP
- XThe action line can start with the following characters:
- X.Tp
- X.B !
- XForwards to all the specified mail addresses.
- X.Tp
- X.B |
- XStarts the specified program, possibly in $SHELL if any
- Xof the characters $SHELLMETAS are spotted. You can optionally prepend this
- Xpipe symbol with
- X.IR variable= ,
- Xwhich will cause stdout of the program to be captured in the environment
- X.IR variable .
- X.PP
- XAnything else will be taken as a mailbox name (either a filename or a
- Xdirectory, absolute or relative to the current directory (see MAILDIR)).
- XIf it is a filename (or nonexistent), the mail will be appended to it.
- X.PP
- XIf it is a directory, the mail will be delivered to a newly created, guaranteed
- Xto be unique file named $MSGPREFIX* in the specified directory. If the
- Xdirectory name ends in "+MCDIRSEP++chCURDIR+", then this directory is presumed
- Xto be an MH folder; i.e. procmail will use the next number it finds available.
- X.Ss "Environment variable defaults"
- X.Tp 2.2i
- X.B "LOGNAME, HOME and SHELL"
- XYour (the recipient's) defaults
- X.Tp
- X.B SHELLMETAS
- X\&+DEFshellmetas+
- X.Tp
- X.B SHELLFLAGS
- X\&+DEFshellflags+
- X.Tp
- X.BR ORGMAIL
- X\&+SYSTEM_MBOX+
- X.Tp
- X.B MAILDIR
- X\&+DEFmaildir+
- X.br
- X(Unless the name of the first successfully opened rcfile starts with
- X`+chCURDIR++MCDIRSEP+', in which case it defaults to `+chCURDIR+')
- X.Tp
- X.B DEFAULT
- X\&+DEFdefault+
- X.Tp
- X.B MSGPREFIX
- X\&+DEFmsgprefix+
- X.Tp
- X.B SENDMAIL
- X\&+DEFsendmail+
- X.Tp
- X.B COMSAT
- X\&+DEFcomsat+
- X.br
- X(If an rcfile is specified on the command line)
- X.Tp
- X.B LOCKEXT
- X\&+DEFlockext+
- X.Tp
- X.B LOCKFILE
- X\&+DEFdefaultlock+
- X.br
- X(After procmail closed the last rcfile)+PRESTENV++LD_ENV_FIX+
- X.Ss Environment
- X.PP
- XBefore you get lost in the multitude of environment variables, keep in mind
- Xthat all of them have reasonable defaults.
- X.Tp 1.2i
- X.B MAILDIR
- XCurrent directory while procmail is executing (that means that all paths
- Xare relative to $MAILDIR).
- X.Tp
- X.B DEFAULT
- XDefault
- X.B mailbox
- Xfile (if not told otherwise, procmail will dump mail in this mailbox).
- XProcmail will automatically use LOCKFILE=$DEFAULT$LOCKEXT prior to writing
- Xto this mailbox.
- X.Tp
- X.B MSGPREFIX
- XFilename prefix that is used when delivering to a directory (not used when
- Xdelivering to an MH directory).
- X.Tp
- X.B LOGFILE
- XAll incoming messages will be logged here with their `+FROM+' and `Subject:'
- Xlines in the header, and an additional line specifying what folder it
- Xfinally went to and how long (in bytes) the message was. This file will
- Xalso contain any error or diagnostic messages from procmail
- X(normally none :-) or any other programs started by procmail. If this file
- Xis not specified, any diagnostics or error messages will
- X+pconsole++console++aconsole+
- X.Tp
- X.B VERBOSE
- XYou can turn on
- X.I extended diagnostics
- Xby setting this variable to `yes' or `on', to turn it off again set it to `no'
- Xor `off'.
- X.Tp
- X.B LOG
- XAnything assigned to this variable will be echoed in $LOGFILE.
- X.Tp
- X.B ORGMAIL
- XUsually the system mailbox (\fBOR\fPi\fBG\fPinal \fBMAIL\fPbox). If, for
- Xsome obscure reason (like `\fBfilesystem full\fP') the mail could not be
- Xdelivered, then this mailbox will be the last resort. If procmail
- Xfails to save the mail in here (deep, deep trouble :-), then the mail
- Xwill bounce back to the sender.
- X.Tp
- X.B LOCKFILE
- XGlobal semaphore file. If this file already exists, procmail
- Xwill wait until it has gone before proceeding, and will create it itself
- X(cleaning it up when ready, of course). If more than one
- X.I lockfile
- Xare specified, then the previous one will be removed before trying to create
- Xthe new one. The use of a global lockfile is discouraged, whenever possible
- Xuse locallockfiles (on a per recipe basis) instead.
- X.Tp
- X.B LOCKEXT
- XDefault extension that is appended to a destination file to determine
- Xwhat local
- X.I lockfile
- Xto use (only if turned on, on a per-recipe basis).
- X.Tp
- X.B LOCKSLEEP
- XNumber of seconds procmail will sleep before retrying on a
- X.I lockfile
- X(if it already existed); if not specified, it defaults to +DEFlocksleep+
- Xseconds.
- X.Tp
- X.B LOCKTIMEOUT
- XNumber of seconds that have to have passed since a
- X.I lockfile
- Xwas last modified/created before procmail decides that this must be an
- Xerroneously leftover lockfile that can be removed by force now. If zero,
- Xthen no timeout will be used and procmail will wait forever until the
- Xlockfile is removed; if not specified, it defaults to +DEFlocktimeout+ seconds.
- XThis variable is useful to prevent indefinite hangups of
- X.BR sendmail /procmail.
- XProcmail is immune to clock skew.
- X.Tp
- X.B TIMEOUT
- XNumber of seconds that have to have passed before procmail decides that
- Xsome child it started must be hanging. The offending program will receive
- Xa TERMINATE signal from procmail, and processing of the rcfile will continue.
- XIf zero, then no timeout will be used and procmail will wait forever until the
- Xchild has terminated; if not specified, it defaults to +DEFtimeout+ seconds.
- X.Tp
- X.B HOST
- XIf this is not the
- X.I hostname
- Xof the machine, processing of the current
- X.I rcfile
- Xwill immediately cease. If other rcfiles were specified on the
- Xcommand line, processing will continue with the next one. If all rcfiles
- Xare exhausted, the program will terminate, but will not generate an error
- X(i.e. to the mailer it will seem that the mail has been delivered). Only the
- Xfirst +HOSTNAMElen+ characters of the HOST are significant.
- X.Tp
- X.B UMASK
- XThe name says it all (if it doesn't, then forget about this one :-). It
- Xis taken as an
- X.B octal
- Xnumber. If not specified, it defaults to +INIT_UMASK+.
- X.Tp
- X.B SHELLMETAS
- XIf any of the characters in SHELLMETAS appears in the line specifying
- Xa filter or program, the line will be fed to $SHELL
- Xinstead of being executed directly.
- X.Tp
- X.B SHELLFLAGS
- XAny invocation of $SHELL will be like:
- X.br
- X"$SHELL" "$SHELLFLAGS" "$*";
- X.Tp
- X.B SENDMAIL
- XIf you're not using the
- X.I forwarding
- Xfacility don't worry about this one. It specifies the program being
- Xcalled to forward any mail.
- X.br
- XIt gets invoked as: "$SENDMAIL" "$@";
- X.Tp
- X.B NORESRETRY
- XNumber of retries that are to be made if any `\fBprocess table full\fP',
- X`\fBfile table full\fP', `\fBout of memory\fP' or
- X`\fBout of swap space\fP' error should occur. If this number is negative,
- Xthen procmail will retry indefinitely; if not specified, it defaults to
- X+DEFnoresretry+ times. The retries occur with a $SUSPEND second interval. The
- Xidea behind this is, that if e.g. the
- X.I swap
- X.I space
- Xhas been exhausted or the
- X.I process
- X.I table
- Xis full, usually several other programs will either detect this as well
- Xand abort or crash 8-), thereby freeing valuable
- X.I resources
- Xfor procmail.
- X.Tp
- X.B SUSPEND
- XNumber of seconds that procmail will pause if it has to wait for something
- Xthat is currently unavailable (memory, fork, etc.); if not specified, it will
- Xdefault to +DEFsuspend+ seconds. See also:
- X.BR LOCKSLEEP .
- X.Tp
- X.B LINEBUF
- XLength of the internal line buffers, cannot be set smaller than +MINlinebuf+.
- XAll lines read from the
- X.I rcfile
- Xshould not exceed $LINEBUF characters before and after expansion. If not
- Xspecified, it defaults to +DEFlinebuf+. This limit, of course, does
- X.I not
- Xapply to the mail itself, which can have arbitrary line lengths, or could
- Xbe a binary file for that matter.
- X.Tp
- X.B DELIVERED
- XIf set to `yes' procmail will pretend (to the mail agent) the mail
- Xhas been delivered. If mail cannot be delivered after meeting this
- Xassignment (to `yes'), the mail will be lost (i.e. it will not bounce).
- X.Tp
- X.B TRAP
- XWhen procmail terminates it will execute the contents of this variable.
- X.Tp
- X.B INCLUDERC
- XNames an rcfile (relative to the current directory) which will be included
- Xhere as if it were part of the current rcfile. Unlimited nesting is
- Xpermitted.
- X.Tp
- X.B COMSAT
- X.BR Comsat (8)/ biff (1)
- Xnotification is on by default, it can be turned off by setting this variable
- Xto `no'. Alternatively the biff-service can be customised by setting it to
- Xeither `service+SERV_ADDRsep+', `+SERV_ADDRsep+hostname', or
- X`service+SERV_ADDRsep+hostname'. When not specified it defaults
- Xto +COMSATservice++SERV_ADDRsep++COMSAThost+.
- X.Sh EXAMPLES
- XLook in the
- X.BR procmailex (5)
- Xman page.
- X.Sh CAVEATS
- XContinued lines in an action line that specifies a program always have to end
- Xin a backslash, even if the underlying shell would not need or want the
- Xbackslash to indicate continuation. This is due to the two pass parsing
- Xprocess needed (first procmail, then the shell (or not, depending on
- X.BR SHELLMETAS )).
- X.PP
- XDon't put comments on the regular expression condition lines in a
- Xrecipe, these lines are fed to the internal egrep
- X.I literally
- X(except for continuation backslashes at the end of a line).
- X.PP
- XWatch out for deadlocks when doing unhealthy things like forwarding mail
- Xto your own account. Deadlocks can be broken by proper use of
- X.BR LOCKTIMEOUT .
- X.PP
- XAny default values that procmail has for some environment variables will
- X.B always
- Xoverride the ones that were already defined. If you really want to
- Xoverride the defaults, you either have to put them in the
- X.B rcfile
- Xor on the command line as arguments.
- X.PP
- XIf you specify only a `+PASS_HEAD+' or a `+PASS_BODY+' flag on a recipe,
- Xand the recipe matches, then, unless a `+FILTER+' or `+CONTINUE+' flag is
- Xpresent as well, the body respectively the header of the mail will be silently
- Xlost.
- X.PP
- XThe `+CONTINUE+' flag defaults to on when capturing stdout of a recipe in an
- Xenvironment variable.
- X.Sh "SEE ALSO"
- X.na
- X.nh
- X.BR procmail (1),
- X.BR procmailex (5),
- X.BR sh (1),
- X.BR csh (1),
- X.BR mail (1),
- X.BR mailx (1),
- X.BR binmail (1),
- X.BR uucp (1),
- X.BR aliases (5),
- X.BR sendmail (8),
- X.BR egrep (1),
- X.BR grep (1),
- X.BR biff (1),
- X.BR comsat (8),
- X.BR lockfile (1),
- X.BR formail (1)
- X.hy
- X.ad
- X.Sh BUGS
- XThe only substitutions of environment variables that can be handled by
- Xprocmail itself are of the type $name, ${name}, $$, $? and $\-; whereas $\-
- Xwill be substituted by the name of the last folder delivered
- Xto.+UPPERCASE_USERNAMES+
- X.PP
- XA line buffer of length $LINEBUF is used when processing the
- X.IR rcfile ,
- Xany expansions
- X.B have
- Xto fit within this limit; if they don't, behaviour is undefined.
- X.PP
- XIf the global lockfile has a
- X.I relative
- Xpath, and the current directory
- Xis not the same as when the global lockfile was created, then the global
- Xlockfile will not be removed if procmail exits at that point (remedy:
- Xuse
- X.I absolute
- Xpaths to specify global lockfiles).
- X.PP
- XWhen capturing stdout from a recipe into an environment variable, exactly
- Xone trailing newline will be stripped.
- X.PP
- XBy using the `^' or `$' in other spots than at the start respectively
- Xend of a line you can use the internal egrep to do multiline matches.
- X.PP
- XWhen the regular expression starts with `^^' it will
- X.I only
- Xmatch at the very start of the text.
- X.Sh MISCELLANEOUS
- XIf the regular expression contains `\fB+TOkey+\fP' it will be substituted by
- X.na
- X.nh
- X`\fB+TOsubstitute+\fP',
- Xwhich should catch all destination specifications.
- X.hy
- X.ad
- X.PP
- XIf the regular expression contains `\fB+FROMDkey+\fP' it will be
- Xsubstituted by
- X.na
- X.nh
- X`\fB+FROMDsubstitute+\fP',
- Xwhich should catch mails coming from most daemons (how's that for a regular
- Xexpression :-).
- X.hy
- X.ad
- X.PP
- XWhen assigning boolean values to variables like VERBOSE, DELIVERED or COMSAT,
- Xprocmail accepts as true every string starting with: a non-zero value, `on',
- X`y' or `t'. False is every string starting with: a zero value, `off', `n' or
- X`f'.
- X.PP
- XIf the action line of a recipe specifies a program, a sole backslash-newline
- Xpair in it on an otherwise empty line will be converted into a newline.
- X.Sh NOTES
- XSince whitespace generally is ignored in the rcfile you can indent everything
- Xto taste.
- X.PP
- XThe leading `|' on the action line to specify a program or filter is stripped
- Xbefore checking for $SHELLMETAS.
- X.PP
- XFiles included with the INCLUDERC directive containing only environment
- Xvariable assignments can be shared with sh.
- X.PP
- XFor
- X.I really
- Xcomplicated processing you can even consider calling
- X.B procmail
- Xrecursively.
- END_OF_FILE
- if test 17104 -ne `wc -c <'procmail280/man/procmailrc.man'`; then
- echo shar: \"'procmail280/man/procmailrc.man'\" unpacked with wrong size!
- fi
- # end of 'procmail280/man/procmailrc.man'
- fi
- if test -f 'procmail280/src/multigram.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'procmail280/src/multigram.c'\"
- else
- echo shar: Extracting \"'procmail280/src/multigram.c'\" \(15547 characters\)
- sed "s/^X//" >'procmail280/src/multigram.c' <<'END_OF_FILE'
- X/************************************************************************
- X * multigram - The human mail address reader *
- X * *
- X * It uses multigrams to intelligently filter out mail addresses *
- X * from the garbage in any arbitrary mail. *
- X * Multigram is currently unable to pick out addresses that *
- X * contain embedded whitespace. *
- X * This program also contains some swiss-army-knife mailinglist *
- X * support features. *
- X * *
- X * Seems to be relatively bug free. *
- X * *
- X * Copyright (c) 1990-1992, S.R. van den Berg, The Netherlands *
- X * #include "README" *
- X ************************************************************************/
- X#ifdef RCS
- Xstatic /*const*/char rcsid[]=
- X "$Id: multigram.c,v 1.21 1993/02/02 15:27:19 berg Exp $";
- X#endif
- Xstatic /*const*/char rcsdate[]="$Date: 1993/02/02 15:27:19 $";
- X#include "includes.h"
- X#include "sublib.h"
- X#include "shell.h"
- X#include "ecommon.h"
- X
- X#define BUFSTEP 16
- X#define COPYBUF 16384
- X/*#define SPEEDBUF COPYBUF /* uncomment to get a speed increase? */
- X#define SCALE_WEIGHT 0x7fff
- X
- X#define DEFmaxgram 4
- X#define DEFminweight (SCALE_WEIGHT/4) /* sanity cutoff value */
- X#define DEFbest_matches 2
- X
- X#define PROCMAIL "../.bin/procmail" /* some configurable paths */
- X#define DEFAULTS_DIR ".etc"
- X#define GLOCKFILE "../.etc/rc.lock"
- X#define RCMAIN "./.etc/rc.main"
- X#define LLOCKFILE "rc.lock"
- X#define REQUEST "-request"
- X#define RCSUBMIT "./rc.submit"
- X#define RCREQUEST "./rc.request"
- X#define RCPOST "./../.etc/rc.post"
- X#define RCINIT "RC_INIT=rc.init"
- X#define XENVELOPETO "X_ENVELOPE_TO="
- X#define LIST "list="
- X
- X#define metoo_SENDMAIL "-om"
- X#define nometoo_SENDMAIL "-omF"
- X#define REMOV1_DELIM "(Only"
- X#define REMOV2_DELIM "addresses below this line can be automatically removed)"
- X#define NOT_METOO "(-n)"
- X
- Xstruct string{char*text,*itext;size_t buflen;};
- X
- Xstatic remov_delim;
- X
- XstrnIcmp(a,b,l)const char*a,*b;size_t l; /* stub */
- X{ return strncmp(a,b,l);
- X}
- X /* read a string from a file into a struct string buffer */
- Xstatic size_t readstr(file,p,linewise)FILE*const file;struct string*p;
- X const int linewise;
- X{ size_t len;int i,firstspc;
- X static const char rem1str[]=REMOV1_DELIM,rem2str[]=REMOV2_DELIM;
- X for(len=firstspc=0;;)
- X { switch(i=getc(file))
- X { case ' ':case '\t':case '\n':
- X if(!len) /* only skip leading space */
- X continue;
- X if(!linewise) /* do we need a complete line? */
- X break; /* no, a word will do */
- X if(!firstspc) /* already seen spaces? */
- X { if(i=='\n') /* no, so check for EOL */
- X { p->text[len]='\0'; /* terminate the first word, split */
- X if(++len==p->buflen) /* still buffer space left? */
- X p->text=realloc(p->text,p->buflen+=BUFSTEP);
- X break;
- X }
- X i='\0';firstspc=1;
- X } /* not the first word on the line, continue */
- X if(i=='\n')
- X break;
- X default:p->text[len]=i; /* regular character, store it */
- X if(++len==p->buflen) /* watch our buffer space */
- X p->text=realloc(p->text,p->buflen+=BUFSTEP);
- X continue; /* next character */
- X case EOF:;
- X }
- X p->text[len]='\0'; /* terminate the buffer in any case */
- X if(linewise&&!remov_delim&&!strcmp(p->text,rem1str)&&
- X !strcmp(p->text+sizeof rem1str,rem2str)) /* special delimiter? */
- X remov_delim=1;
- X return len;
- X }
- X}
- X
- Xstatic char*tstrdup(p)const char*const p;
- X{ return strcpy(malloc(strlen(p)+1),p);
- X}
- X
- Xstatic void lowcase(str)struct string*const str; /* make lowercase */
- X{ register char*p;
- X for(p=str->itext=tstrdup(str->text);*p;p++)
- X if((unsigned)*p-'A'<'Z'-'A')
- X *p+='a'-'A';
- X}
- X
- Xstatic void elog(a)const char*const a;
- X{ fputs(a,stderr);
- X}
- X /* the program names */
- Xstatic const char idhash[]="idhash",flist[]="flist",dirsep[]=DIRSEP;
- Xstatic const char*progname="multigram";
- X
- Xvoid nlog(a)const char*const a; /* log error with identification */
- X{ elog(progname);elog(": ");elog(a);
- X}
- X /* finds the next character */
- Xstatic char*lastdirsep(filename)const char*filename;
- X{ const char*p; /* following the last DIRSEP */
- X while(p=strpbrk(filename,dirsep))
- X filename=p+1;
- X return(char*)filename;
- X}
- X /* check rc.lock file age */
- Xstatic void rclock(file,stbuf)const char*const file;struct stat*const stbuf;
- X{ while(!stat(file,stbuf)&&time((time_t*)0)-stbuf->st_mtime<DEFlocktimeout)
- X sleep(DEFlocksleep); /* wait, if appropriate */
- X}
- X
- Xstatic char*argstr(first,last)const char*first,*last; /* construct */
- X{ char*chp;size_t i; /* an argument assignment */
- X strcpy(chp=malloc((i=strlen(first))+strlen(last)+1),first);
- X strcpy(chp+i,last);return chp;
- X}
- X
- Xstatic PROGID;
- X
- Xmain(minweight,argv)char*argv[];
- X{ struct string fuzzstr,hardstr;FILE*hardfile;const char*addit=0;
- X struct match{char*fuzz,*hard;int metric;long lentry,offs1,offs2;}
- X **best,*curmatch=0;
- X unsigned best_matches,maxgram,maxweight,charoffs=0,remov=0,renam=0,
- X chkmetoo=(char*)progid-(char*)progid;
- X int lastfrom;
- X static const char usage[]=
- X "Usage: multigram [-cdmr] [-b nnn] [-l nnn] [-w nnn] [-a address] filename\n";
- X if(minweight) /* sanity check, any arguments at all? */
- X { char*chp;
- X if(!strcmp(chp=lastdirsep(argv[0]),flist)) /* suid flist prog? */
- X { struct stat stbuf;
- X *chp='\0'; /* security check, 3 hardlinks!? */
- X if(!chdir(argv[0])&&!lstat(flist,&stbuf)&&S_ISREG(stbuf.st_mode)&&
- X stbuf.st_mode&S_ISUID&&stbuf.st_uid==geteuid()&&stbuf.st_nlink==3&&
- X !chdir(chPARDIR))
- X { static const char request[]=REQUEST,xenvlpto[]=XENVELOPETO,
- X rcrequest[]=RCREQUEST,rcpost[]=RCPOST,list[]=LIST,
- X *pmexec[]={PROCMAIL,RCSUBMIT,RCINIT,0,0,rcrequest,rcpost,0};
- X#define Endpmexec(i) (pmexec[maxindex(pmexec)-(i)])
- X char*arg;
- X if(minweight!=2) /* wrong number of arguments? */
- X { elog("Usage: flist listname[-request]\n");return EX_USAGE;
- X }
- X chp=strchr(arg=argv[1],'\0'); /* check for -request */
- X if(chp-arg>STRLEN(request)&&!strcmp(chp-=STRLEN(request),request))
- X *chp='\0',pmexec[1]=rcrequest,Endpmexec(1)=0,Endpmexec(2)=rcpost;
- X else
- X chp=0;
- X if(chdir(arg)) /* goto the list's subdirectory */
- X pmexec[1]=RCMAIN,Endpmexec(2)=0,chdir(DEFAULTS_DIR);
- X Endpmexec(4)=argstr(list,arg); /* pass on the list name */
- X if(chp) /* was it a -request list? */
- X *chp= *request; /* then restore the leading '-' */
- X Endpmexec(3)=argstr(xenvlpto,arg);setuid(stbuf.st_uid);
- X setgid(stbuf.st_gid);rclock(GLOCKFILE,&stbuf); /* stall */
- X rclock(LLOCKFILE,&stbuf);
- X execve(pmexec[0],(char*const*)pmexec,environ); /* start procmail */
- X nlog("Couldn't exec \"");elog(pmexec[0]);elog("\"\n");
- X return EX_UNAVAILABLE; /* panic */
- X }
- X nlog("Missing permissions\n");return EX_NOPERM;
- X }
- X setgid(getgid());setuid(getuid()); /* revoke suid permissions */
- X if(!strcmp(chp,idhash)) /* idhash program? */
- X { unsigned long hash=0;int i;
- X if(minweight!=1)
- X { elog("Usage: idhash\n");return EX_USAGE;
- X }
- X while(i=fgetc(stdin),!feof(stdin)) /* hash away! */
- X hash=hash*67067L+i;
- X printf("%lx",hash);return EX_OK;
- X }
- X minweight=SCALE_WEIGHT;best_matches=maxgram=0;
- X while((chp= *++argv)&&*chp=='-')
- X for(chp++;;)
- X { int c;
- X switch(c= *chp++)
- X { case 'c':charoffs=1;continue;
- X case 'd':remov=1;continue;
- X case 'r':renam=1;continue;
- X case 'm':chkmetoo=1;continue;
- X case 'a':
- X if(!*chp&&!(chp= *++argv))
- X goto usg;
- X addit=chp;break;
- X case 'b':case 'l':case 'w':
- X { int i;
- X ;{ const char*ochp;
- X if(!*chp&&!(chp= *++argv)||
- X (i=strtol(ochp=chp,(char**)&chp,10),chp==ochp))
- X goto usg;
- X }
- X switch(c)
- X { case 'b':best_matches=i;continue;
- X case 'l':minweight=i;continue;
- X case 'w':maxgram=i;continue;
- X }
- X }
- X case HELPOPT1:case HELPOPT2:elog(usage);
- X elog(
- X "\t-a address\tadd this address to the list\
- X\n\t-b nnn\t\tmaximum no. of best matches shown\
- X\n\t-c\t\tdisplay offsets in characters\
- X\n\t-d\t\tdelete address from list\
- X\n\t-m\t\tcheck for metoo\
- X\n\t-l nnn\t\tlower bound metric\
- X\n\t-r\t\trename address on list\
- X\n\t-w nnn\t\twindow width used when matching\n");return EX_USAGE;
- X case '-':
- X if(!*chp)
- X { chp= *++argv;goto lastopt;
- X }
- X default:goto usg;
- X case '\0':;
- X }
- X break;
- X }
- Xlastopt:
- X if(!chp||*++argv||renam+remov+!!addit>1)
- X goto usg;
- X if(!(hardfile=fopen(chp,remov||renam||addit?"r+":"r")))
- X { nlog("Couldn't open \"");elog(chp);elog("\"\n");return EX_IOERR;
- X }
- X#ifdef SPEEDBUF /* allocate a bigger stdio buffer */
- X setvbuf(hardfile,malloc(SPEEDBUF),_IOFBF,(size_t)SPEEDBUF);
- X#endif
- X }
- X else
- Xusg:
- X { elog(usage);return EX_USAGE;
- X }
- X if(addit) /* special subfunction, to add entries */
- X { int lnl;long lasttell; /* to the dist file */
- X for(lnl=1,lasttell=0;;)
- X { switch(getc(hardfile)) /* step through the file */
- X { case '\n':
- X if(!lnl) /* looking for trailing newlines */
- X lnl=1,lasttell=ftell(hardfile);
- X continue;
- X default:lnl=0;continue;
- X case EOF:; /* or the end of the file */
- X }
- X break;
- X } /* go back there, and add the new entry */
- X fseek(hardfile,lasttell,SEEK_SET);fprintf(hardfile,"%s\n",addit);
- X printf("Added: %s\n",addit);fclose(hardfile);return EX_OK;
- X }
- X if(!maxgram)
- X maxgram=DEFmaxgram;
- X maxgram--;
- X if(minweight==SCALE_WEIGHT)
- X minweight=DEFminweight;
- X if(!best_matches)
- X best_matches=DEFbest_matches;
- X fuzzstr.text=malloc(fuzzstr.buflen=BUFSTEP);
- X hardstr.text=malloc(hardstr.buflen=BUFSTEP);
- X ;{ int i;
- X best=malloc(best_matches--*sizeof*best);i=best_matches;
- X do
- X { best[i]=malloc(sizeof**best);best[i]->hard=malloc(1);
- X best[i]->fuzz=malloc(1);best[i]->metric= -SCALE_WEIGHT;
- X }
- X while(i--);
- X }
- X for(lastfrom= -1;readstr(stdin,&fuzzstr,0);)
- X { int meter,maxmetric;size_t fuzzlen;long linentry,offs1,offs2;
- X ;{ char*chp,*echp;int parens;
- X echp=strchr(chp=fuzzstr.text,'\0')-1;
- X do
- X { switch(*echp)
- X { case '.':case ',':case ';':case ':':case '?':case '!':*echp='\0';
- X continue;
- X }
- X break;
- X }
- X while(--echp>chp); /* roughly check if it could be a mail address */
- X if(lastfrom<=0&&!strpbrk(chp,"@/")&&(!strchr(chp,'!')||
- X strchr(chp,'|')||strchr(chp,',')||strstr(chp,"..")))
- X { if(lastfrom<0)
- X lastfrom=!strcmp(SHFROM,chp);
- X continue; /* apparently not an email address */
- X }
- X lastfrom=0;
- X for(parens=0;chp=strchr(chp,'(');chp++,parens++);
- X for(chp=fuzzstr.text;chp=strchr(chp,')');chp++,parens--);
- X if(*(chp=fuzzstr.text)=='(')
- X { if(!parens&&*echp==')')
- X { *echp='\0';goto shftleft;
- X }
- X if(parens>0)
- Xshftleft: tmemmove(chp,chp+1,strlen(chp));
- X }
- X else if(parens<0&&*echp==')')
- X *echp='\0';
- X if(*(chp=fuzzstr.text)=='<'&&*(echp=strchr(chp,'\0')-1)=='>'
- X &&echp==strpbrk(chp,"([\">,; \t\n")) /* strip '<' and '>' ? */
- X *echp='\0',tmemmove(chp,chp+1,echp-chp);
- X if(!(fuzzlen=strlen(chp)))
- X continue;;
- X if(!curmatch)
- X curmatch=malloc(sizeof*curmatch);
- X curmatch->fuzz=tstrdup(chp);curmatch->hard=malloc(1);
- X curmatch->metric= -SCALE_WEIGHT;
- X }
- X lowcase(&fuzzstr);fseek(hardfile,0L,SEEK_SET);
- X maxmetric=best[best_matches]->metric;
- X for(remov_delim=offs2=linentry=0;
- X offs1=offs2,readstr(hardfile,&hardstr,1);)
- X { size_t minlen,hardlen,maxlen;register size_t gramsize;
- X offs2=ftell(hardfile);linentry++;
- X if(*hardstr.text=='(')
- X continue; /* unsuitable for matches */
- X lowcase(&hardstr);
- X if((minlen=hardlen=strlen(hardstr.text))>(maxlen=fuzzlen))
- X minlen=fuzzlen,maxlen=hardlen;
- X if((gramsize=minlen-1)>maxgram)
- X gramsize=maxgram;
- X maxweight=SCALE_WEIGHT/(gramsize+1);
- X meter=(int)((unsigned long)SCALE_WEIGHT/2*minlen/maxlen)-
- X SCALE_WEIGHT/2;
- X do /* reset local multigram counter */
- X { register lmeter=0;size_t cmaxlen=maxlen;
- X ;{ register const char*fzz,*hrd;
- X fzz=fuzzstr.itext;
- X do
- X { for(hrd=fzz+1;hrd=strchr(hrd,*fzz);) /* is it present in */
- X if(!strncmp(++hrd,fzz+1,gramsize)) /* own string? */
- X { if(cmaxlen>gramsize+1)
- X cmaxlen--;
- X goto dble_gram; /* skip until it's last */
- X }
- X for(hrd=hardstr.itext;hrd=strchr(hrd,*fzz);) /* otherwise */
- X if(!strncmp(++hrd,fzz+1,gramsize)) /* search it in the */
- X { lmeter++;break; /* dist entry */
- X }
- Xdble_gram:; }
- X while(*(++fzz+gramsize)); /* next gram */
- X }
- X if(lmeter)
- X { unsigned weight;
- X if(cmaxlen>minlen)
- X cmaxlen=minlen;
- X meter+=lmeter*(weight=maxweight/(unsigned)(cmaxlen-gramsize));
- X meter-=weight*
- X (unsigned long)((lmeter+=gramsize-cmaxlen)<0?-lmeter:lmeter)/
- X cmaxlen;
- X }
- X }
- X while(gramsize--); /* search all gramsizes down to one */
- X free(hardstr.itext); /* check if we had any luck */
- X if(meter>maxmetric&&(remov_delim||!renam&&!remov))
- X { curmatch->metric=maxmetric=meter;curmatch->lentry=linentry;
- X free(curmatch->hard);hardlen++;
- X hardlen+=strlen(hardstr.text+hardlen)+1;
- X curmatch->hard=malloc(hardlen+=strlen(hardstr.text+hardlen)+1);
- X tmemmove(curmatch->hard,hardstr.text,hardlen);
- X curmatch->offs1=offs1;curmatch->offs2=offs2;
- X }
- X }
- X free(fuzzstr.itext);
- X if(curmatch->metric>=0) /* maybe this match can be put in the array */
- X { struct match*mp,**mmp; /* of best matches so far */
- X free((mp= *(mmp=best+best_matches))->fuzz);free(mp->hard);free(mp);
- X while(--mmp>=best&&(mp= *mmp)->metric<curmatch->metric)
- X mmp[1]=mp; /* keep it sorted */
- X mmp[1]=curmatch;curmatch=0;
- X }
- X else
- X free(curmatch->fuzz),free(curmatch->hard);
- X }
- X ;{ int i;struct match*mp;
- X for(i=0;i<=best_matches&&(mp=best[i++])->metric>=minweight;)
- X if(chkmetoo)
- X printf("%s\n",strcmp(mp->hard+strlen(mp->hard)+1,NOT_METOO)
- X ?metoo_SENDMAIL:nometoo_SENDMAIL);
- X else
- X printf("%3ld %-34s %5d %-34s\n",
- X charoffs?mp->offs1:mp->lentry,mp->hard,mp->metric,mp->fuzz);
- X if((mp= *best)->metric>=minweight)
- X { struct match*worse;
- X if(renam)
- X { long line;int i,w1;
- X maxweight>>=1;
- X for(i=1,line=mp->lentry,w1=mp->metric,worse=0;
- X i<=best_matches&&(mp=best[i++])->metric>=minweight;)
- X if(mp->lentry==line&&mp->metric+maxweight<w1)
- X { goto remv1;
- X }
- X for(i=1;i<=best_matches&&(mp=best[i++])->metric>=minweight;)
- X if(mp->metric+maxweight<w1)
- Xremv1: { worse=mp;mp= *best;goto remv;
- X }
- X nlog("Couldn't find a proper address pair\n");goto norenam;
- X }
- X if(remov)
- Xremv: { char*buf;long offs1,offs2;size_t readin;
- X buf=malloc(COPYBUF);offs1=mp->offs1;offs2=mp->offs2;
- X while(fseek(hardfile,offs2,SEEK_SET),
- X readin=fread(buf,1,COPYBUF,hardfile))
- X { offs2=ftell(hardfile);fseek(hardfile,offs1,SEEK_SET);
- X if(buf[readin-1]=='\n') /* try and remove some empty lines */
- X while(readin>1&&buf[readin-2]=='\n') /* at the end, since */
- X readin--; /* every time could be the last */
- X fwrite(buf,1,readin,hardfile);offs1=ftell(hardfile);
- X }
- X free(buf);fseek(hardfile,offs1,SEEK_SET);
- X printf("Removed: %s\n",mp->hard);
- X if(renam)
- X fputs(worse->fuzz,hardfile),printf("Added: %s\n",worse->fuzz);
- X do putc('\n',hardfile); /* erase the tail */
- X while(ftell(hardfile)<offs2);
- X fclose(hardfile);
- X }
- X return EX_OK;
- X }
- X }
- X if(remov||renam)
- X { nlog("Couldn't even find a single address\n");
- X }
- Xnorenam:
- X return 1;
- X}
- END_OF_FILE
- if test 15547 -ne `wc -c <'procmail280/src/multigram.c'`; then
- echo shar: \"'procmail280/src/multigram.c'\" unpacked with wrong size!
- fi
- # end of 'procmail280/src/multigram.c'
- fi
- echo shar: End of archive 9 \(of 11\).
- cp /dev/null ark9isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 11 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
- --
- Sincerely, berg@pool.informatik.rwth-aachen.de
- Stephen R. van den Berg (AKA BuGless). berg@physik.tu-muenchen.de
-
- "Be spontaneous!"
-
- exit 0 # Just in case...
-