home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume38 / procmail / part09 < prev    next >
Text File  |  1993-07-05  |  49KB  |  1,396 lines

  1. Newsgroups: comp.sources.misc
  2. From: berg@pool.informatik.rwth-aachen.de (Stephen R. van den Berg)
  3. Subject: v38i028:  procmail - mail processing package v2.90, Part09/11
  4. Message-ID: <1993Jul1.151348.21879@sparky.imd.sterling.com>
  5. X-Md4-Signature: 71ed12661abfa71ed2a54c31dab55d5a
  6. Sender: kent@sparky.imd.sterling.com (Kent Landfield)
  7. Organization: Sterling Software
  8. Date: Thu, 1 Jul 1993 15:13:48 GMT
  9. Approved: kent@sparky.imd.sterling.com
  10.  
  11. Submitted-by: berg@pool.informatik.rwth-aachen.de (Stephen R. van den Berg)
  12. Posting-number: Volume 38, Issue 28
  13. Archive-name: procmail/part09
  14. Environment: sendmail, smail, MMDF, mailsurr, UNIX, POSIX
  15. Supersedes: procmail: Volume 35, Issue 21-32,124,125
  16.  
  17. #! /bin/sh
  18. # This is a shell archive.  Remove anything before this line, then unpack
  19. # it by saving it into a file and typing "sh file".  To overwrite existing
  20. # files, type "sh file -c".  You can also feed this as standard input via
  21. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  22. # will see the following message at the end:
  23. #        "End of archive 9 (of 11)."
  24. # Contents:  procmail/initmake procmail/src/formail.c
  25. #   procmail/src/multigram.c
  26. # Wrapped by berg@tubastos on Thu Jul  1 14:06:18 1993
  27. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  28. if test -f 'procmail/initmake' -a "${1}" != "-c" ; then 
  29.   echo shar: Will not clobber existing file \"'procmail/initmake'\"
  30. else
  31. echo shar: Extracting \"'procmail/initmake'\" \(7781 characters\)
  32. sed "s/^X//" >'procmail/initmake' <<'END_OF_FILE'
  33. X#! /bin/sh
  34. X:
  35. X#$Id: initmake,v 1.37 1993/06/23 14:35:06 berg Exp $
  36. X
  37. XPATH=.:$PATH
  38. XBSHELL=$1
  39. Xshift; MSHELL=$1
  40. Xshift; RM="$1"
  41. Xshift; MV="$1"
  42. Xshift; LN="$1"
  43. Xshift; SEARCHLIBS="$1"
  44. Xshift; LIBPATHS="$1"
  45. Xshift; DEVNULL=$1
  46. Xshift; MAKE="$1"
  47. Xshift; O=$1
  48. Xshift; CC="$1"
  49. Xshift; CFLAGS1="$1"
  50. Xshift; LDFLAGS1="$1"
  51. Xshift; BINSS="$1"
  52. Xshift; MANS1S="$1"
  53. Xshift; MANS5S="$1"
  54. Xshift; SUBDIRS="$1"
  55. Xshift; BINDIR="$1"
  56. X
  57. Xtest 1 != $# &&
  58. X echo "Don't start this script directly, use \`make init'" && exit 1
  59. X
  60. Xtest -z "$MSHELL" || SHELL=$MSHELL
  61. Xcase "$SHELL" in
  62. X  *sh*)
  63. X     case "$SHELL" in
  64. X    *csh*) echo "Warning: really perverted make detected"; SHELL="";;
  65. X     esac;;
  66. X  *) echo "Warning: perverted make detected"; SHELL="";;
  67. Xesac
  68. Xtest -z "$SHELL" && SHELL=$BSHELL
  69. X
  70. Xexport SHELL PATH
  71. X
  72. XFGREP="fgrep"        # POSIX, or not POSIX, that is the question...
  73. Xif test \^hello = "`echo '^hello' | grep -F '^hello' 2>&1`"
  74. Xthen FGREP="grep -F"                # and POSIX it is!
  75. Xfi
  76. X
  77. Xecho hi | $FGREP hi >$DEVNULL
  78. Xt=$?
  79. Xecho ho | $FGREP hi >$DEVNULL
  80. Xf=$?
  81. Xif test 0 != $t -o 0 = $f
  82. Xthen
  83. X  echo "Your \"$FGREP\" program seems to be incapable of returning a proper"
  84. X  echo "exitvalue depending on the success of the search.  This script can"
  85. X  echo "not work without it."
  86. X  exit 2
  87. Xfi
  88. X
  89. Xif test ! -z "$LD_LIBRARY_PATH"
  90. Xthen
  91. X  echo '***************************** WARNING *********************************'
  92. X  echo '* You seem to have set the LD_LIBRARY_PATH variable, this might cause *'
  93. X  echo '* some trouble during the execution of this autoconf script.  If the  *'
  94. X  echo '* the make does not finish by itself, do a: "make clean",          *'
  95. X  echo '* clear LD_LIBRARY_PATH from the environment, and start over.          *'
  96. X  echo '***************************** WARNING *********************************'
  97. Xfi
  98. X
  99. Xcd src                # diving into the source directory ######
  100. Xcat >_autotst.c <<HERE
  101. Xmain()
  102. X{ return 0;
  103. X}
  104. XHERE
  105. X$RM _autotst.rrr _autotst.$O _autotst
  106. X
  107. Xcc=""
  108. X
  109. Xfor a in "$CC" cc gcc
  110. Xdo
  111. X  echo $a $CFLAGS1 _autotst.c -o _autotst $LDFLAGS1 >>_autotst.rrr
  112. X  test -z "$cc" -a ! -z "$a" &&
  113. X   ($a $CFLAGS1 _autotst.c -o _autotst $LDFLAGS1) >>_autotst.rrr \
  114. X   2>&1 && cc="$a"
  115. X  echo "::::" >>_autotst.rrr
  116. Xdone
  117. Xif test -z "$cc"
  118. Xthen
  119. X  echo 2>&1 "Whoeaaa!  There's something fishy going on here."
  120. X  echo 2>&1 "You have a look and see if you detect anything uncanny:"
  121. X  echo 2>&1 "-------------------------------------------------------"
  122. X  cat 2>&1 _autotst.rrr
  123. X  echo 2>&1 "-------------------------------------------------------"
  124. X  echo 2>&1 "I suggest you take a look at the definition of CFLAGS* and CC"
  125. X  echo 2>&1 "in the Makefile before you try make again."
  126. X  exit 1
  127. Xfi
  128. X$RM _autotst.rrr _autotst.$O _autotst
  129. Xecho "$cc seems to work fine, using that as the C-compiler"
  130. X
  131. Xcat >_autotst.c <<HERE
  132. X#include <sys/types.h>
  133. X#include <stdio.h>
  134. X#include <sys/stat.h>
  135. Xmain()
  136. X{ struct stat buf;return!&buf;
  137. X}
  138. XHERE
  139. X
  140. XCFLAGS=""
  141. X
  142. Xcase "$CFLAGS1" in
  143. X  *-D_POSIX_SOURCE*);;
  144. X  *)
  145. X     if $cc -c $CFLAGS1 _autotst.c >$DEVNULL 2>&1
  146. X     then
  147. X     :
  148. X     else
  149. X    $RM _autotst.$O
  150. X    $cc -c $CFLAGS1 -D_POSIX_SOURCE _autotst.c >$DEVNULL 2>&1 &&
  151. X     CFLAGS=" -D_POSIX_SOURCE"
  152. X     fi;;
  153. Xesac
  154. X
  155. XLDFLAGSC=""
  156. X
  157. Xtest -f _autotst.$O || $cc -c $CFLAGS1 $CFLAGS _autotst.c >$DEVNULL 2>&1
  158. X$cc $CFLAGS1 $CFLAGS _autotst.$O -o _autotst $LDFLAGS1 -lc >_autotst.rrr 2>&1 \
  159. X && if grep "[\"']c['\"]" _autotst.rrr >$DEVNULL
  160. X  then
  161. X  :
  162. X  else
  163. X     LDFLAGSC=" -lc"
  164. X  fi
  165. X
  166. XLDFLAGS="$SEARCHLIBS"
  167. Xfirstrun=yes
  168. X
  169. Xwhile $RM _autotst
  170. X $cc $CFLAGS1 $CFLAGS _autotst.$O -o _autotst $LDFLAGS1 $LDFLAGS \
  171. X  $LDFLAGSC >_autotst.rrr 2>&1
  172. X test $firstrun = yes -o ! -f _autotst
  173. Xdo
  174. X  firstrun=no
  175. X  set dummy $LDFLAGS
  176. X  shift
  177. X  echo 2>&1 "    ...scanning for $# libraries..."
  178. X  NEWLDFLAGS=""
  179. X  for a in $LDFLAGS dummy
  180. X  do
  181. X     if test dummy != $a
  182. X     then
  183. X    lib=`expr $a : '-l\(.*\)'`
  184. X    if $FGREP lib$lib _autotst.rrr >$DEVNULL ||
  185. X     $FGREP -e $a _autotst.rrr >$DEVNULL ||
  186. X     grep "[\"']$lib['\"]" _autotst.rrr >$DEVNULL
  187. X    then
  188. X    :
  189. X    else
  190. X       OLDIFS="$IFS"; IFS=":$IFS"
  191. X       found=no
  192. X       for libpath in $LIBPATHS $LD_LIBRARY_PATH
  193. X       do
  194. X          set $libpath/*lib$lib[A-Z.]*
  195. X          test -f $1 && found=yes
  196. X       done
  197. X       IFS="$OLDIFS"
  198. X       test yes = $found && NEWLDFLAGS="$NEWLDFLAGS $a"
  199. X    fi
  200. X     fi
  201. X  done
  202. X  if test a"$LDFLAGS" = a"$NEWLDFLAGS"
  203. X  then
  204. X     echo 2>&1 "Whoeaaa!  There's something fishy going on here."
  205. X     echo 2>&1 "You have a look and see if you detect anything uncanny:"
  206. X     echo 2>&1 "-------------------------------------------------------"
  207. X     cat 2>&1 _autotst.rrr
  208. X     echo 2>&1 "-------------------------------------------------------"
  209. X     echo 2>&1 \
  210. X       "I suggest you take a look at the definition of LDFLAGS* and SEARCHLIBS"
  211. X     echo 2>&1 "in the Makefile before you try make again."
  212. X     echo 2>&1 "Also: write me a mail showing the errorlog you just generated."
  213. X     echo 2>&1 "The errorlog can still be found in src/_autotst.rrr"
  214. X     echo 2>&1 "It would be helpful if you could mention what machine and OS"
  215. X     echo 2>&1 "you are trying to compile this on (uname -a).  Thanks."
  216. X     exit 1
  217. X  fi
  218. X  LDFLAGS="$NEWLDFLAGS"
  219. Xdone
  220. X
  221. X$RM _autotst.$O _autotst.c _autotst
  222. Xcd ..            # returning to the main procmail directory ######
  223. X
  224. XLDFLAGS="$LDFLAGS$LDFLAGSC"
  225. X
  226. Xtest -z "$CFLAGS" || echo "Added CFLAGS=$CFLAGS"
  227. Xtest -z "$LDFLAGS" || echo "Added LDFLAGS=$LDFLAGS"
  228. X
  229. Xfor a in $SUBDIRS
  230. Xdo
  231. X  if test ! -f $a/Makefile.init
  232. X  then
  233. X     $LN $a/Makefile $a/Makefile.init
  234. X     $RM $a/Makefile
  235. X     $LN $a/Makefile.init $a/Makefile
  236. X  fi
  237. Xdone
  238. X
  239. Xtest -f Makefile.0 || sed -e '/^# Makefile - mark/,$ !d' <Makefile >Makefile.0
  240. Xsed -e '/^# Makefile - mark/,$ d' <Makefile >_Makefile
  241. Xecho "# Makefile.1 - mark, don't (re)move this, a sed script needs it
  242. X" >>_Makefile
  243. X
  244. Xtest a$SHELL != a$MSHELL && echo  "SHELL    = $SHELL"    >>_Makefile
  245. Xecho "FGREP    = $FGREP"                >>_Makefile
  246. Xtest -z "$MAKE" && echo "MAKE    = make"            >>_Makefile
  247. Xtest a"$cc" != a"$CC" && echo "CC    = $cc"        >>_Makefile
  248. X
  249. Xecho "CFLAGS    = \$(CFLAGS1)$CFLAGS"            >>_Makefile
  250. Xecho "LDFLAGS    = \$(LDFLAGS1)$LDFLAGS"            >>_Makefile
  251. Xecho                            >>_Makefile
  252. X
  253. XMANSS=""
  254. XMANS1=""
  255. XMANS5=""
  256. XMANS=""
  257. XNMANS=""
  258. XBINS=""
  259. XNBINS=""
  260. Xfor a in $MANS1S
  261. Xdo
  262. X  MANSS="$MANSS $a.1"
  263. X  MANS1="$MANS1 $a.\$(MAN1SUFFIX)"
  264. Xdone
  265. Xfor a in $MANS5S
  266. Xdo
  267. X  MANSS="$MANSS $a.5"
  268. X  MANS5="$MANS5 $a.\$(MAN5SUFFIX)"
  269. Xdone
  270. Xfor a in $MANSS
  271. Xdo
  272. X  MANS="$MANS new/$a"
  273. X  NMANS="$NMANS ../new/$a"
  274. Xdone
  275. Xfor a in $BINSS
  276. Xdo
  277. X  BINS="$BINS new/$a"
  278. X  NBINS="$NBINS ../new/$a"
  279. Xdone
  280. X
  281. Xecho "BINS=$BINS"                    >>_Makefile
  282. Xecho "MANS=$MANS"                    >>_Makefile
  283. Xecho "MANS1=$MANS1"                    >>_Makefile
  284. Xecho "MANS5=$MANS5"                    >>_Makefile
  285. Xecho "MANSS=$MANSS"                    >>_Makefile
  286. Xecho "NBINS=$NBINS"                    >>_Makefile
  287. Xecho "NMANS=$NMANS"                    >>_Makefile
  288. Xecho                            >>_Makefile
  289. X
  290. Xfor a in $SUBDIRS
  291. Xdo
  292. X  sed -e '1,/^# Makefile.0 - mark/ d' <_Makefile >$a/_Makefile
  293. X  cat $a/Makefile.0 >>$a/_Makefile
  294. Xdone
  295. X
  296. Xecho "BINDIR=$BINDIR"                    >>src/_Makefile
  297. X
  298. Xfor a in $BINSS
  299. Xdo
  300. X  echo                            >>src/_Makefile
  301. X  echo "../new/$a: $a ../config.check"            >>src/_Makefile
  302. X  echo "    @\$(RM) \$@"                >>src/_Makefile
  303. X  echo "    \$(LN) $a \$@"                >>src/_Makefile
  304. Xdone
  305. X
  306. Xfor a in $MANSS
  307. Xdo
  308. X  echo                            >>man/_Makefile
  309. X  echo "../new/$a: $a ../config.check"            >>man/_Makefile
  310. X  echo "    @\$(RM) \$@"                >>man/_Makefile
  311. X  echo "    \$(LN) $a \$@"                >>man/_Makefile
  312. Xdone
  313. X
  314. Xfor a in $MANS1S
  315. Xdo
  316. X  echo                            >>man/_Makefile
  317. X  echo "$a.1: $a.man man.sed mansed"            >>man/_Makefile
  318. X  echo \
  319. X   "    \$(SHELL) ./mansed \$(SHELL) $a.man \$@ \"\$(RM)\" \$(DEVNULL)" \
  320. X   >>man/_Makefile
  321. Xdone
  322. X
  323. Xfor a in $MANS5S
  324. Xdo
  325. X  echo                            >>man/_Makefile
  326. X  echo "$a.5: $a.man man.sed mansed"            >>man/_Makefile
  327. X  echo \
  328. X   "    \$(SHELL) ./mansed \$(SHELL) $a.man \$@ \"\$(RM)\" \$(DEVNULL)" \
  329. X   >>man/_Makefile
  330. Xdone
  331. X
  332. Xcat Makefile.1 >>_Makefile
  333. X$MV _Makefile Makefile
  334. X
  335. Xsleep 1            # Some machines are just too speedy, make gets confused
  336. X
  337. Xfor a in $SUBDIRS
  338. Xdo
  339. X  echo "#" >>$a/_Makefile
  340. X  $MV $a/_Makefile $a/Makefile
  341. Xdone
  342. END_OF_FILE
  343. if test 7781 -ne `wc -c <'procmail/initmake'`; then
  344.     echo shar: \"'procmail/initmake'\" unpacked with wrong size!
  345. fi
  346. chmod +x 'procmail/initmake'
  347. # end of 'procmail/initmake'
  348. fi
  349. if test -f 'procmail/src/formail.c' -a "${1}" != "-c" ; then 
  350.   echo shar: Will not clobber existing file \"'procmail/src/formail.c'\"
  351. else
  352. echo shar: Extracting \"'procmail/src/formail.c'\" \(18606 characters\)
  353. sed "s/^X//" >'procmail/src/formail.c' <<'END_OF_FILE'
  354. X/************************************************************************
  355. X *    formail - The mail (re)formatter                *
  356. X *                                    *
  357. X *    Seems to be relatively bug free.                *
  358. X *                                    *
  359. X *    Copyright (c) 1990-1992, S.R. van den Berg, The Netherlands    *
  360. X *    #include "README"                        *
  361. X ************************************************************************/
  362. X#ifdef RCS
  363. Xstatic /*const*/char rcsid[]=
  364. X "$Id: formail.c,v 1.24 1993/06/23 12:56:01 berg Exp $";
  365. X#endif
  366. Xstatic /*const*/char rcsdate[]="$Date: 1993/06/23 12:56:01 $";
  367. X#include "includes.h"
  368. X#include <ctype.h>        /* iscntrl() */
  369. X#include "formail.h"
  370. X#include "sublib.h"
  371. X#include "shell.h"
  372. X#include "common.h"
  373. X#include "fields.h"
  374. X#include "ecommon.h"
  375. X#include "formisc.h"
  376. X
  377. Xstatic const char unknown[]=UNKNOWN,re[]=" Re:",fmusage[]=FM_USAGE,
  378. X From_[]=        FROM,                /* VNIX 'From ' line */
  379. X Article_[]=        "Article ",           /* USENET 'Article ' line */
  380. X x_[]=            "X-",                /* general extension */
  381. X old_[]=        OLD_PREFIX;                 /* my extension */
  382. X#define ssl(str)        str,STRLEN(str)
  383. X#define bsl(str)        {ssl(str)}
  384. X#define sslbar(str,bar1,bar2)    {ssl(str),STRLEN(bar1)-1,STRLEN(bar2)-1}
  385. X#include "header.h"
  386. X/*
  387. X *    sender determination fields in order of importance/reliability
  388. X *    reply-address determination fields (wrepl specifies the weight for
  389. X *    for regular replies, wtrepl specifies the weight for trusted users)
  390. X *
  391. X *    I bet this is the first time you see a bar graph in C-source-code :-)
  392. X */
  393. Xstatic const struct {const char*head;int len,wrepl,wtrepl;}sest[]=
  394. X{ sslbar(replyto    ,"******"    ,"********"    ),
  395. X  sslbar(Fromm        ,"*"        ,"****"        ),
  396. X  sslbar(retreceiptto    ,"********"    ,"*******"    ),
  397. X  sslbar(sender        ,"*****"    ,"******"    ),
  398. X  sslbar(res_replyto    ,"***********"    ,"***********"    ),
  399. X  sslbar(res_from    ,"***foo***"    ,"***bar***"    ),
  400. X  sslbar(res_sender    ,"**********"    ,"**********"    ),
  401. X  sslbar(errorsto    ,"*******"    ,"*****"    ),
  402. X  sslbar(path        ,"**"        ,"*"        ),
  403. X  sslbar(returnpath    ,"***"        ,"***"        ),
  404. X  sslbar(From_        ,"****"        ,"**"        )
  405. X};
  406. X
  407. Xstatic struct saved rex[]=
  408. X{ bsl(subject),bsl(references),bsl(messageid),bsl(date)
  409. X};
  410. X#define subj    (rex+0)
  411. X#define refr    (rex+1)
  412. X#define msid    (rex+2)
  413. X#define hdate    (rex+3)
  414. X
  415. X#ifdef sMAILBOX_SEPARATOR
  416. X#define emboxsep    smboxsep
  417. X#define MAILBOX_SEPARATOR
  418. Xstatic const char smboxsep[]=sMAILBOX_SEPARATOR;
  419. X#endif /* sMAILBOX_SEPARATOR */
  420. X#ifdef eMAILBOX_SEPARATOR
  421. X#ifdef emboxsep
  422. X#undef emboxsep
  423. X#else
  424. X#define MAILBOX_SEPARATOR
  425. X#endif
  426. Xstatic const char emboxsep[]=eMAILBOX_SEPARATOR;
  427. X#endif /* eMAILBOX_SEPARATOR */
  428. X
  429. Xconst char binsh[]=BinSh,sfolder[]=FOLDER,
  430. X couldntw[]="Couldn't write to stdout";
  431. Xint errout,oldstdout,quiet,buflast;
  432. Xpid_t child= -1;
  433. XFILE*mystdout;
  434. Xsize_t nrskip,nrtotal= -1,buflen,buffilled;
  435. Xlong totallen;
  436. Xchar*buf,*logsummary;
  437. Xstruct field*rdheader,*xheader,*Xheader;
  438. Xstatic struct field*iheader,*Iheader,*aheader,*Aheader,*Rheader,*nheader;
  439. X
  440. Xstatic void logfolder P((void))     /* estimate the no. of characters needed to */
  441. X{ size_t i;char num[8*sizeof totallen*4/10+1];           /* represent totallen */
  442. X  static const char tabchar[]=TABCHAR;
  443. X  if(logsummary)
  444. X   { putssn(sfolder,STRLEN(sfolder));i=strlen(logsummary)+STRLEN(sfolder);
  445. X     i-=i%TABWIDTH;
  446. X     do putssn(tabchar,STRLEN(tabchar));
  447. X     while((i+=TABWIDTH)<LENoffset);
  448. X     ultstr(7,totallen,num);putssn(num,strlen(num));putcs('\n');
  449. X   }
  450. X}
  451. X    /* checks if the last field in rdheader looks like a known digest header */
  452. Xstatic digheadr P((void))
  453. X{ char*chp;int i;size_t j;struct field*fp;
  454. X  for(fp=rdheader;fp->fld_next;fp=fp->fld_next);     /* skip to the last */
  455. X  i=maxindex(cdigest);chp=fp->fld_text;j=fp->id_len;
  456. X  while((cdigest[i].lnr!=j||strnIcmp(cdigest[i].hedr,chp,j))&&i--);
  457. X  return i>=0||j>STRLEN(old_)&&!strnIcmp(old_,chp,STRLEN(old_))||
  458. X   j>STRLEN(x_)&&!strnIcmp(x_,chp,STRLEN(x_));
  459. X}
  460. X
  461. Xstatic artheadr P((void))         /* could it be the start of an article? */
  462. X{ if(!rdheader&&!strncmp(buf,Article_,STRLEN(Article_)))
  463. X   { addbuf();rdheader->id_len=STRLEN(Article_);return 1;
  464. X   }
  465. X  return 0;
  466. X}
  467. X
  468. Xstatic PROGID;
  469. X
  470. Xmain(lastm,argv)const char*const argv[];
  471. X{ int i,split=0,force=0,bogus=1,every=0,areply=0,trust=0,digest=0,nowait=0,
  472. X   keepb=0,minfields=(char*)progid-(char*)progid,conctenate=0;
  473. X  size_t j,lnl,escaplen;char*chp,*namep,*escap=ESCAP;
  474. X  struct field*fldp,*fp2,**afldp,*fdate;
  475. X  if(lastm)                   /* sanity check, any argument at all? */
  476. X#define Qnext_arg()    if(!*chp&&!(chp=(char*)*++argv))goto usg
  477. X     while(chp=(char*)*++argv)
  478. X      { if((lastm= *chp++)==FM_SKIP)
  479. X       goto number;
  480. X    else if(lastm!=FM_TOTAL)
  481. X       goto usg;
  482. X    for(;;)
  483. X     { switch(lastm= *chp++)
  484. X        { case FM_TRUST:trust=1;continue;
  485. X          case FM_REPLY:areply=1;continue;
  486. X          case FM_FORCE:force=1;continue;
  487. X          case FM_EVERY:every=1;continue;
  488. X          case FM_DIGEST:digest=1;continue;
  489. X          case FM_NOWAIT:nowait=1;continue;
  490. X          case FM_KEEPB:keepb=1;continue;
  491. X          case FM_CONCATENATE:conctenate=1;continue;
  492. X          case FM_QUIET:quiet=1;continue;
  493. X          case FM_LOGSUMMARY:Qnext_arg();
  494. X         if(strlen(logsummary=chp)>MAXfoldlen)
  495. X            chp[MAXfoldlen]='\0';
  496. X         detab(chp);break;
  497. X          case FM_SPLIT:split=1;
  498. X         if(!*chp&&*++argv)
  499. X            goto parsedoptions;
  500. X         goto usg;
  501. X          case HELPOPT1:case HELPOPT2:elog(fmusage);elog(FM_HELP);
  502. X         goto xusg;
  503. X          case FM_MINFIELDS:Qnext_arg();chp++;
  504. X          default:chp--;
  505. Xnumber:         if(*chp-'0'>(unsigned)9)        /* the number is not >=0 */
  506. X            goto usg;
  507. X         i=strtol(chp,&chp,10);
  508. X         switch(lastm)            /* where does the number go? */
  509. X          { case FM_SKIP:nrskip=i;break;
  510. X            case FM_MINFIELDS:minfields=i;break;
  511. X            default:nrtotal=i;
  512. X          }
  513. X         continue;
  514. X          case FM_BOGUS:bogus=0;continue;
  515. X          case FM_QPREFIX:Qnext_arg();escap=chp;break;
  516. X          case FM_ADD_IFNOT:case FM_ADD_ALWAYS:case FM_REN_INSERT:
  517. X          case FM_DEL_INSERT:case FM_EXTRACT:case FM_EXTRC_KEEP:
  518. X          case FM_ReNAME:Qnext_arg();
  519. X         if(!breakfield(chp,lnl=strlen(chp)))
  520. X            goto invfield;
  521. X         chp[lnl]='\n';                   /* terminate the line */
  522. X         afldp=addfield(lastm==FM_REN_INSERT?&iheader:
  523. X          lastm==FM_DEL_INSERT?&Iheader:lastm==FM_ADD_IFNOT?&aheader:
  524. X          lastm==FM_ADD_ALWAYS?&Aheader:lastm==FM_EXTRACT?&xheader:
  525. X          lastm==FM_EXTRC_KEEP?&Xheader:&Rheader,chp,++lnl);
  526. X         if(lastm==FM_ReNAME)          /* then we need a second field */
  527. X          { int copied=0;
  528. X            for(namep=(chp=(fldp= *afldp)->fld_text)+lnl,
  529. X             chp+=lnl=fldp->id_len;chp<namep;++chp)
  530. X             { switch(*chp)              /* skip whitespace */
  531. X            { case ' ':case '\t':case '\n':continue;
  532. X            }
  533. X               break;
  534. X             }                   /* second field attached? */
  535. X            if(i=breakfield(chp,(size_t)(namep-chp)))  /* squeeze on */
  536. X               tmemmove(fldp->fld_text+lnl,chp,i),copied=1;
  537. X            else if(!(chp=(char*)*++argv)||     /* look at next arg */
  538. X             !(i=breakfield(chp,strlen(chp))))        /* no field? */
  539. Xinvfield:         { nlog("Invalid field-name:");logqnl(chp?chp:"");
  540. X               goto usg;
  541. X             }
  542. X            *afldp=fldp=
  543. X             realloc(fldp,FLD_HEADSIZ+(fldp->tot_len=lnl+i));
  544. X            if(!copied)               /* if not squeezed on yet */
  545. X               tmemmove(fldp->fld_text+lnl,chp,i);    /* squeeze now */
  546. X          }
  547. X          case '\0':;
  548. X        }
  549. X       break;
  550. X     }
  551. X      }
  552. Xparsedoptions:
  553. X  escaplen=strlen(escap);mystdout=stdout;signal(SIGPIPE,SIG_IGN);
  554. X  if(split)
  555. X   { oldstdout=dup(STDOUT);fclose(stdout);startprog((const char*Const*)argv);
  556. X     if(!minfields)                   /* no user specified minimum? */
  557. X    minfields=DEFminfields;                 /* take our default */
  558. X   }
  559. X  else if(every||digest||minfields)          /* these combinations are only */
  560. X     goto usg;                  /* valid in combination with split */
  561. X  if((xheader||Xheader)&&logsummary||keepb&&!(areply||xheader))
  562. Xusg:                             /* options sanity check */
  563. X   { elog(fmusage);                       /* impossible mix */
  564. Xxusg:
  565. X     return EX_USAGE;
  566. X   }
  567. X  buf=malloc(buflen=BSIZE);totallen=0;i=maxindex(rex); /* prime some buffers */
  568. X  do rex[i].rexp=malloc(1);
  569. X  while(i--);
  570. X  fdate=0;addfield(&fdate,date,STRLEN(date)); /* fdate is only for searching */
  571. X  while((buflast=getchar())=='\n');             /* skip leading garbage */
  572. X  if(!readhead())                        /* start looking */
  573. X   {
  574. X#ifdef sMAILBOX_SEPARATOR                  /* check for a leading */
  575. X     if(!strncmp(smboxsep,buf,STRLEN(smboxsep)))    /* mailbox separator */
  576. X      { buffilled=0;goto startover;                  /* skip it */
  577. X      }
  578. X#endif
  579. X     if(digest&&artheadr())
  580. X    goto startover;
  581. X   }
  582. X  else
  583. Xstartover:
  584. X     while(readhead());                 /* read in the whole header */
  585. X  ;{ size_t lenparkedbuf;void*parkedbuf;
  586. X     if(rdheader&&!strncmp(rdheader->fld_text,Article_,STRLEN(Article_)))
  587. X    rdheader->fld_text[STRLEN(Article_)-1]=HEAD_DELIMITER;       /* proper */
  588. X     namep=0;totallen=0;i=maxindex(rex);                /* field */
  589. X     do rex[i].rexl=0;
  590. X     while(i--);                 /* all state has been reset */
  591. X     for(fldp=rdheader;fldp;fldp=fldp->fld_next)    /* go through the linked */
  592. X      { int nowm;                    /* list of header-fields */
  593. X    if(conctenate)
  594. X       concatenate(fldp);             /* look for `sender' fields */
  595. X    chp=fldp->fld_text;j=fldp->id_len;i=maxindex(sest);
  596. X    while((sest[i].len!=j||strnIcmp(sest[i].head,chp,j))&&i--);
  597. X    if(i>=0&&(i!=maxindex(sest)||fldp==rdheader))      /* found anything? */
  598. X     { char*saddr;char*tmp;                 /* determine the weight */
  599. X       nowm=trust?sest[i].wtrepl:areply?i:sest[i].wrepl;chp+=j;
  600. X       tmp=malloc(j=fldp->tot_len-j);tmemmove(tmp,chp,j);
  601. X       tmp[j-1]='\0';chp=pstrspn(tmp," \t\n");
  602. X       for(saddr=0;;chp=skipwords(chp))        /* skip RFC 822 wise */
  603. X        { switch(*chp)
  604. X           { default:
  605. X            if(!saddr)           /* if we haven't got anything yet */
  606. X               saddr=chp;        /* this might be the address */
  607. X            continue;
  608. X         case '<':skipwords(saddr=chp);      /* hurray, machine useable */
  609. X         case '\0':;
  610. X           }
  611. X          break;
  612. X        }
  613. X       if(saddr)                /* any useful mailaddress found? */
  614. X        { if(*saddr)              /* did it have any length? */
  615. X           { if(strstr(saddr,".UUCP"))
  616. X            nowm-=(maxindex(sest)+2)*3;     /* depreciate .UUCP address */
  617. X         else if(!strpbrk(saddr,"@!/"))
  618. X            nowm-=(maxindex(sest)+2)*2;        /* depreciate "user" */
  619. X         else if(strchr(saddr,'@')&&!strchr(saddr,'.'))
  620. X            nowm-=maxindex(sest)+2;         /* depreciate user@host */
  621. X         if(!namep||nowm>lastm)        /* better than previous ones */
  622. X          { saddr=strcpy(malloc(strlen(saddr)+1),saddr);lastm=nowm;
  623. X            goto newnamep;
  624. X          }
  625. X           }
  626. X          else if(sest[i].head==returnpath)        /* nill Return-Path: */
  627. X           { saddr=0;lastm=maxindex(sest)+2;         /* override */
  628. Xnewnamep:     if(namep)
  629. X            free(namep);
  630. X         namep=saddr;
  631. X           }
  632. X        }
  633. X       free(tmp);
  634. X     }                   /* save headers for later perusal */
  635. X    i=maxindex(rex);chp=fldp->fld_text;j=fldp->id_len;    /* e.g. areply */
  636. X    while((rex[i].lenr!=j||strnIcmp(rex[i].headr,chp,j))&&i--);
  637. X    chp+=j;
  638. X    if(i>=0&&(j=fldp->tot_len-j)>1)              /* found anything? */
  639. X       tmemmove(rex[i].rexp=realloc(rex[i].rexp,rex[i].rexl=j),chp,j);
  640. X      }
  641. X     tmemmove(parkedbuf=malloc(buffilled),buf,lenparkedbuf=buffilled);
  642. X     buffilled=0;    /* moved the contents of buf out of the way temporarily */
  643. X     if(areply)              /* autoreply requested, we clean up the header */
  644. X      { for(fldp= *(afldp= &rdheader);fldp;)
  645. X       if(!(fp2=findf(fldp,iheader))||fp2->id_len<fp2->tot_len-1)
  646. X          *afldp=fldp->fld_next,free(fldp),fldp= *afldp;   /* remove all */
  647. X       else                    /* except the ones mentioned */
  648. X          fldp= *(afldp= &fldp->fld_next);               /* as -i ...: */
  649. X    loadbuf(to,STRLEN(to));loadchar(' ');       /* generate the To: field */
  650. X    if(namep)           /* did we find a valid return address at all? */
  651. X       loadbuf(namep,strlen(namep));          /* then insert it here */
  652. X    else
  653. X       loadbuf(unknown,STRLEN(unknown));        /* or insert our default */
  654. X    loadchar('\n');addbuf();               /* add it to rdheader */
  655. X    if(subj->rexl)                      /* any Subject: found? */
  656. X     { loadbuf(subject,STRLEN(subject));      /* sure, check for leading */
  657. X       if(strnIcmp(pstrspn(chp=subj->rexp," \t"),Re,STRLEN(Re)))  /* Re: */
  658. X          loadbuf(re,STRLEN(re));           /* no Re: , add one ourselves */
  659. X       loadsaved(subj);addbuf();
  660. X     }
  661. X    if(refr->rexl||msid->rexl)       /* any References: or Message-ID: */
  662. X     { loadbuf(references,STRLEN(references)); /* yes insert References: */
  663. X       if(refr->rexl)
  664. X        { if(msid->rexl)        /* if we're going to append a Message-ID */
  665. X         --refr->rexl;            /* suppress the trailing newline */
  666. X          loadsaved(refr);
  667. X        }
  668. X       if(msid->rexl)
  669. X          loadsaved(msid);               /* here's our missing newline */
  670. X       addbuf();
  671. X     }
  672. X    if(msid->rexl)             /* do we add an In-Reply-To: field? */
  673. X       loadbuf(inreplyto,STRLEN(inreplyto)),loadsaved(msid),addbuf();
  674. X      }                       /* are we allowed to add From_ lines? */
  675. X     else if(!force&&(!rdheader||!eqFrom_(rdheader->fld_text)))     /* missing? */
  676. X      { struct field*old;time_t t;         /* insert a From_ line up front */
  677. X    t=time((time_t*)0);old=rdheader;rdheader=0;
  678. X    loadbuf(From_,STRLEN(From_));
  679. X    if(namep)              /* we found a valid return address */
  680. X       loadbuf(namep,strlen(namep));
  681. X    else
  682. X       loadbuf(unknown,STRLEN(unknown));                /* Date: */
  683. X    if(!hdate->rexl||!findf(fdate,aheader))
  684. X       loadchar(' '),chp=ctime(&t),loadbuf(chp,strlen(chp)); /* no Date: */
  685. X    else                     /* we generate it ourselves */
  686. X       loadsaved(hdate);          /* yes, found Date:, then copy from it */
  687. X    addbuf();rdheader->fld_next=old;
  688. X      }
  689. X     for(fldp=aheader;fldp;fldp=fldp->fld_next)
  690. X    if(!findf(fldp,rdheader))           /* only add what didn't exist */
  691. X       addfield(&nheader,fldp->fld_text,fldp->tot_len);
  692. X     if((fldp= *(afldp= &rdheader))&&logsummary&&eqFrom_(fldp->fld_text))
  693. X    concatenate(fldp),putssn(fldp->fld_text,fldp->tot_len);
  694. X     while(fldp)
  695. X      { lnl=fldp->id_len;chp=fldp->fld_text;
  696. X    if(logsummary)
  697. X     { if(lnl==STRLEN(subject)&&!strnIcmp(chp,subject,lnl))
  698. X        { concatenate(fldp);chp[i=fldp->tot_len-1]='\0';detab(chp);
  699. X          putcs(' ');putssn(chp,i>=MAXSUBJECTSHOW?MAXSUBJECTSHOW:i);
  700. X          putcs('\n');
  701. X        }
  702. X     }
  703. X    if(findf(fldp,Iheader))                    /* delete fields */
  704. X     { *afldp=fldp->fld_next,free(fldp);fldp= *afldp;continue;
  705. X     }
  706. X    else if(fp2=findf(fldp,Rheader))      /* explicitly rename field */
  707. X       renfield(afldp,lnl,fp2->fld_text+lnl,fp2->tot_len-lnl);
  708. X    else if((fp2=findf(fldp,iheader))&&!(areply&&lnl==fp2->tot_len-1))
  709. X       renfield(afldp,(size_t)0,old_,STRLEN(old_)); /* implicitly rename */
  710. X    fldp= *(afldp= &(*afldp)->fld_next);
  711. X      }                    /* restore the saved contents of buf */
  712. X     tmemmove(buf,parkedbuf,buffilled=lenparkedbuf);free(parkedbuf);
  713. X   }
  714. X  flushfield(&rdheader);flushfield(&nheader);dispfield(Aheader);
  715. X  dispfield(iheader);dispfield(Iheader);
  716. X  if(namep)
  717. X     free(namep);
  718. X  if(!(xheader||Xheader))         /* we're not just extracting fields */
  719. X     lputcs('\n');        /* make sure it is followed by an empty line */
  720. X  if(!keepb&&(areply||xheader||Xheader))            /* decision time */
  721. X   { logfolder();                   /* we throw away the rest */
  722. X     if(split)
  723. X    closemine();
  724. X     opensink();                     /* discard the body */
  725. X   }
  726. X  lnl=1;                      /* last line was a newline */
  727. X  if(buffilled==1)           /* the header really ended with a newline */
  728. X     buffilled=0;          /* throw it away, since we already inserted it */
  729. X  while(buffilled||!lnl||buflast!=EOF)     /* continue the quest, line by line */
  730. X   { if(!buffilled)                      /* is it really empty? */
  731. X    readhead();                      /* read the next field */
  732. X     if(rdheader)            /* anything looking like a header found? */
  733. X      { if(eqFrom_(chp=rdheader->fld_text))          /* check if it's From_ */
  734. Xfromanyway:
  735. X     { register size_t k;
  736. X       if(split&&(lnl||every)&&    /* more thorough check for a postmark */
  737. X        (k=strcspn(chp=pstrspn(chp+STRLEN(From_)," \t")," \t\n"))&&
  738. X        *pstrspn(chp+k," \t")!='\n')
  739. X          goto accuhdr;             /* ok, postmark found, split it */
  740. X       if(bogus)                           /* disarm */
  741. X          lputssn(escap,escaplen);
  742. X     }
  743. X    else if(split&&digest&&(lnl||every)&&digheadr())      /* digest? */
  744. Xaccuhdr: { for(i=minfields;--i&&readhead()&&digheadr(););   /* found enough? */
  745. X       if(!i)                       /* then split it! */
  746. Xsplitit:    { if(!lnl)        /* did the previous mail end with an empty line? */
  747. X         lputcs('\n');                  /* but now it does :-) */
  748. X          logfolder();
  749. X          if((fclose(mystdout)==EOF||errout==EOF)&&!quiet)
  750. X         nlog(couldntw),elog(", continuing...\n"),split= -1;
  751. X          if(!nowait)
  752. X         waitforit();         /* wait till the child has finished */
  753. X          startprog((const char*Const*)argv);goto startover;
  754. X        }                        /* and there we go again */
  755. X     }
  756. X      }
  757. X     else if(eqFrom_(buf))             /* special case, From_ line */
  758. X      { addbuf();goto fromanyway;      /* add it manually, readhead() didn't */
  759. X      }
  760. X     else if(split&&digest&&(lnl||every)&&artheadr())
  761. X    goto accuhdr;
  762. X#ifdef MAILBOX_SEPARATOR
  763. X     if(!strncmp(emboxsep,buf,STRLEN(emboxsep)))         /* end of mail? */
  764. X      { if(split)               /* gobble up the next start separator */
  765. X     { buffilled=0;
  766. X#ifdef sMAILBOX_SEPARATOR
  767. X       getline();buffilled=0;         /* but only if it's defined */
  768. X#endif
  769. X       if(buflast!=EOF)                       /* if any */
  770. X          goto splitit;
  771. X       break;
  772. X     }
  773. X    else if(bogus)
  774. X       goto putsp;                   /* escape it with a space */
  775. X      }
  776. X     else if(!strncmp(smboxsep,buf,STRLEN(smboxsep)&&bogus))
  777. Xputsp:    lputcs(' ');
  778. X#endif /* MAILBOX_SEPARATOR */
  779. X     lnl=buffilled==1;              /* check if we just read an empty line */
  780. X     if(areply&&bogus)                      /* escape the body */
  781. X    if(fldp=rdheader)          /* we already read some "valid" fields */
  782. X     { register char*p;
  783. X       rdheader=0;
  784. X       do                   /* careful, they can contain newlines */
  785. X        { fp2=fldp->fld_next;chp=fldp->fld_text;
  786. X          do
  787. X           { lputssn(escap,escaplen);
  788. X         lputssn(chp,(p=strchr(chp,'\n')+1)-chp);
  789. X           }
  790. X          while((chp=p)<fldp->fld_text+fldp->tot_len);
  791. X          free(fldp);                    /* delete it */
  792. X        }
  793. X       while(fldp=fp2);               /* escape all fields we found */
  794. X     }
  795. X    else
  796. X     { if(buffilled>1)      /* we don't escape empty lines, looks neat */
  797. X          lputssn(escap,escaplen);
  798. X       goto flbuf;
  799. X     }
  800. X     else if(rdheader)
  801. X    flushfield(&rdheader); /* beware, after this buf can still be filled */
  802. X     else
  803. Xflbuf:    lputssn(buf,buffilled),buffilled=0;
  804. X   }                   /* make sure the mail ends with an empty line */
  805. X  logfolder();closemine();child= -1;waitforit();    /* wait for everyone */
  806. X  return split<0?EX_IOERR:EX_OK;
  807. X}
  808. X
  809. XeqFrom_(a)const char*const a;
  810. X{ return!strncmp(a,From_,STRLEN(From_));
  811. X}
  812. X
  813. Xbreakfield(line,len)const char*const line;size_t len;       /* look where the */
  814. X{ const char*p=line;               /* fieldname ends (RFC 822 specs) */
  815. X  if(eqFrom_(line))                      /* special case, From_ */
  816. X     return STRLEN(From_);
  817. X  while(len--&&!iscntrl(*p))            /* no control characters allowed */
  818. X   { switch(*p++)
  819. X      { default:continue;
  820. X    case HEAD_DELIMITER:len=p-line;return len==1?0:len;      /* eureka! */
  821. X    case ' ':;                    /* no spaces allowed */
  822. X      }
  823. X     break;
  824. X   }
  825. X  return 0;            /* sorry, does not seem to be a legitimate field */
  826. X}
  827. END_OF_FILE
  828. if test 18606 -ne `wc -c <'procmail/src/formail.c'`; then
  829.     echo shar: \"'procmail/src/formail.c'\" unpacked with wrong size!
  830. fi
  831. # end of 'procmail/src/formail.c'
  832. fi
  833. if test -f 'procmail/src/multigram.c' -a "${1}" != "-c" ; then 
  834.   echo shar: Will not clobber existing file \"'procmail/src/multigram.c'\"
  835. else
  836. echo shar: Extracting \"'procmail/src/multigram.c'\" \(18039 characters\)
  837. sed "s/^X//" >'procmail/src/multigram.c' <<'END_OF_FILE'
  838. X/************************************************************************
  839. X *    multigram - The human mail address reader            *
  840. X *                                    *
  841. X *    It uses multigrams to intelligently filter out mail addresses    *
  842. X *    from the garbage in any arbitrary mail.                *
  843. X *    Multigram is currently unable to pick out addresses that    *
  844. X *    contain embedded whitespace.                    *
  845. X *    This program also contains some swiss-army-knife mailinglist    *
  846. X *    support features.                        *
  847. X *                                    *
  848. X *    Most notably:    flist    A program that should be setuid root.    *
  849. X *                                    *
  850. X *    Seems to be relatively bug free.                *
  851. X *                                    *
  852. X *    Copyright (c) 1990-1992, S.R. van den Berg, The Netherlands    *
  853. X *    #include "README"                        *
  854. X ************************************************************************/
  855. X#ifdef RCS
  856. Xstatic /*const*/char rcsid[]=
  857. X "$Id: multigram.c,v 1.30 1993/06/07 12:37:18 berg Exp $";
  858. X#endif
  859. Xstatic /*const*/char rcsdate[]="$Date: 1993/06/07 12:37:18 $";
  860. X#include "includes.h"
  861. X#include "sublib.h"
  862. X#include "shell.h"
  863. X#include "ecommon.h"
  864. X
  865. X#include "targetdir.h"      /* see ../mailinglist/install.sh2 for more details */
  866. X
  867. X#define BUFSTEP        16
  868. X#define COPYBUF        16384
  869. X/*#define SPEEDBUF    COPYBUF           /* uncomment to get a speed increase? */
  870. X#define SCALE_WEIGHT    0x7fff
  871. X#define EXCL_THRESHOLD    30730
  872. X
  873. X#define DEFmaxgram    4
  874. X#define DEFminweight    (SCALE_WEIGHT/4)          /* sanity cutoff value */
  875. X#define DEFbest_matches 2
  876. X
  877. X#define DEFAULTS_DIR    ".etc"              /* some configurable paths */
  878. X#define GLOCKFILE    "../.etc/rc.lock"
  879. X#define RCMAIN        "./.etc/rc.main"
  880. X#define LLOCKFILE    "rc.lock"
  881. X#define REQUEST        "-request"
  882. X#define RCSUBMIT    "./rc.submit"
  883. X#define RCREQUEST    "./rc.request"
  884. X#define RCPOST        "./../.etc/rc.post"
  885. X#define RCINIT        "RC_INIT=rc.init"
  886. X#define XENVELOPETO    "X_ENVELOPE_TO="
  887. X#define LIST        "list="
  888. X
  889. X#define metoo_SENDMAIL        "-om"
  890. X#define nometoo_SENDMAIL    "-omF"
  891. X#define REMOV1_DELIM "(Only"
  892. X#define REMOV2_DELIM "addresses below this line can be automatically removed)"
  893. X#define NOT_METOO    "(-n)"
  894. X
  895. Xstruct string{char*text,*itext;size_t textlen,buflen;};
  896. X
  897. Xstatic remov_delim,maxgram;
  898. X
  899. XstrnIcmp(a,b,l)const char*a,*b;size_t l;                 /* stub */
  900. X{ return strncmp(a,b,l);
  901. X}
  902. X            /* read a string from a file into a struct string buffer */
  903. Xstatic size_t readstr(file,p,linewise)FILE*const file;struct string*p;
  904. X const int linewise;
  905. X{ size_t len;int i,firstspc;
  906. X  static const char rem1str[]=REMOV1_DELIM,rem2str[]=REMOV2_DELIM;
  907. X  for(len=firstspc=0;;)
  908. X   { switch(i=getc(file))
  909. X      { case ' ':case '\t':case '\n':
  910. X       if(!len)                  /* only skip leading space */
  911. X          continue;
  912. X       if(!linewise)              /* do we need a complete line? */
  913. X          break;                       /* no, a word will do */
  914. X       if(!firstspc)                 /* already seen spaces? */
  915. X        { if(i=='\n')                 /* no, so check for EOL */
  916. X           { p->text[len]='\0';      /* terminate the first word, split */
  917. X         if(++len==p->buflen)         /* still buffer space left? */
  918. X            p->text=realloc(p->text,p->buflen+=BUFSTEP);
  919. X         break;
  920. X           }
  921. X          i='\0';firstspc=1;
  922. X        }             /* not the first word on the line, continue */
  923. X       if(i=='\n')
  924. X          break;
  925. X    default:p->text[len]=i;              /* regular character, store it */
  926. X       if(++len==p->buflen)               /* watch our buffer space */
  927. X          p->text=realloc(p->text,p->buflen+=BUFSTEP);
  928. X       continue;                       /* next character */
  929. X    case EOF:;
  930. X      }
  931. X     p->text[len]='\0';             /* terminate the buffer in any case */
  932. X     if(linewise&&!remov_delim&&!strcmp(p->text,rem1str)&&
  933. X      !strcmp(p->text+sizeof rem1str,rem2str))           /* special delimiter? */
  934. X    remov_delim=1;
  935. X     return len;
  936. X   }
  937. X}
  938. X
  939. Xstatic char*tstrdup(p)const char*const p;
  940. X{ return strcpy(malloc(strlen(p)+1),p);
  941. X}
  942. X
  943. Xstatic void lowcase(str)struct string*const str;       /* make lowercase */
  944. X{ register char*p;
  945. X  for(p=str->itext=tstrdup(str->text);*p;p++)
  946. X     if((unsigned)*p-'A'<'Z'-'A')
  947. X    *p+='a'-'A';
  948. X}
  949. X
  950. Xstatic void elog(a)const char*const a;
  951. X{ fputs(a,stderr);
  952. X}
  953. X                            /* the program names */
  954. Xstatic const char idhash[]="idhash",flist[]="flist",senddigest[]="senddigest",
  955. X dirsep[]=DIRSEP;
  956. Xstatic const char*progname="multigram";
  957. X
  958. Xvoid nlog(a)const char*const a;            /* log error with identification */
  959. X{ elog(progname);elog(": ");elog(a);
  960. X}
  961. X                         /* finds the next character */
  962. Xstatic char*lastdirsep(filename)const char*filename;
  963. X{ const char*p;                    /* following the last DIRSEP */
  964. X  while(p=strpbrk(filename,dirsep))
  965. X     filename=p+1;
  966. X  return(char*)filename;
  967. X}
  968. X                           /* check rc.lock file age */
  969. Xstatic rclock(file,stbuf)const char*const file;struct stat*const stbuf;
  970. X{ int waited=0;
  971. X  while(!stat(file,stbuf)&&time((time_t*)0)-stbuf->st_mtime<DEFlocktimeout)
  972. X     waited=1,sleep(DEFlocksleep);             /* wait, if appropriate */
  973. X  return waited;
  974. X}
  975. X
  976. Xstatic char*argstr(first,last)const char*first,*last;        /* construct */
  977. X{ char*chp;size_t i;                   /* an argument assignment */
  978. X  strcpy(chp=malloc((i=strlen(first))+strlen(last)+1),first);
  979. X  strcpy(chp+i,last);return chp;
  980. X}
  981. X
  982. Xstatic PROGID;
  983. X
  984. Xstatic matchgram(fuzzstr,hardstr)
  985. Xconst struct string*const fuzzstr;struct string*const hardstr;
  986. X{ size_t minlen,maxlen;unsigned maxweight;int meter;
  987. X  register size_t gramsize;
  988. X  if((minlen=hardstr->textlen=strlen(hardstr->text))>(maxlen=fuzzstr->textlen))
  989. X     minlen=fuzzstr->textlen,maxlen=hardstr->textlen;
  990. X  if((gramsize=minlen-1)>maxgram)
  991. X     gramsize=maxgram;
  992. X  maxweight=SCALE_WEIGHT/(gramsize+1);
  993. X  meter=(int)((unsigned long)SCALE_WEIGHT/2*minlen/maxlen)-SCALE_WEIGHT/2;
  994. X  do                        /* reset local multigram counter */
  995. X   { register lmeter=0;size_t cmaxlen=maxlen;
  996. X     ;{ register const char*fzz,*hrd;
  997. X    fzz=fuzzstr->itext;
  998. X    do
  999. X     { for(hrd=fzz+1;hrd=strchr(hrd,*fzz);)         /* is it present in */
  1000. X          if(!strncmp(++hrd,fzz+1,gramsize))          /* own string? */
  1001. X           { if(cmaxlen>gramsize+1)
  1002. X            cmaxlen--;
  1003. X          goto dble_gram;             /* skip until it's last */
  1004. X           }
  1005. X       for(hrd=hardstr->itext;hrd=strchr(hrd,*fzz);)    /* otherwise */
  1006. X           if(!strncmp(++hrd,fzz+1,gramsize))     /* search it in the */
  1007. X        { lmeter++;break;                   /* dist entry */
  1008. X        }
  1009. Xdble_gram:;
  1010. X     }
  1011. X    while(*(++fzz+gramsize));                /* next gram */
  1012. X      }
  1013. X     if(lmeter)
  1014. X      { unsigned weight;
  1015. X    if(cmaxlen>minlen)
  1016. X       cmaxlen=minlen;
  1017. X    meter+=lmeter*(weight=maxweight/(unsigned)(cmaxlen-gramsize));
  1018. X    meter-=weight*
  1019. X     (unsigned long)((lmeter+=gramsize-cmaxlen)<0?-lmeter:lmeter)/cmaxlen;
  1020. X      }
  1021. X   }
  1022. X  while(gramsize--);             /* search all gramsizes down to one */
  1023. X  return meter;
  1024. X}
  1025. X
  1026. Xmain(minweight,argv)char*argv[];
  1027. X{ struct string fuzzstr,hardstr,excstr,exc2str;FILE*hardfile;
  1028. X  const char*addit=0;
  1029. X  struct match{char*fuzz,*hard;int metric;long lentry;off_t offs1,offs2;}
  1030. X   **best,*curmatch=0;
  1031. X  unsigned best_matches,charoffs=0,remov=0,renam=0,
  1032. X   chkmetoo=(char*)progid-(char*)progid;
  1033. X  int lastfrom;
  1034. X  static const char usage[]=
  1035. X"Usage: multigram [-cdmr] [-b nnn] [-l nnn] [-w nnn] [-ax address] filename\n";
  1036. X  if(minweight)                  /* sanity check, any arguments at all? */
  1037. X   { char*chp;
  1038. X     if(!strcmp(chp=lastdirsep(argv[0]),flist))         /* suid flist prog? */
  1039. X      { struct stat stbuf;char*arg;
  1040. X    static const char request[]=REQUEST,listid[]=LISTID,
  1041. X     rcrequest[]=RCREQUEST,rcpost[]=RCPOST,list[]=LIST,
  1042. X     defdir[]=DEFAULTS_DIR,targetdir[]=TARGETDIR,
  1043. X     *pmexec[]={PROCMAIL,RCSUBMIT,RCINIT,0,0,0,rcrequest,rcpost,0};
  1044. X#define Endpmexec(i)    (pmexec[maxindex(pmexec)-(i)])
  1045. X    progname=flist;*chp='\0';
  1046. X    if(chdir(targetdir))
  1047. X     { nlog("Couldn't chdir to \"");elog(targetdir);elog("\"\n");
  1048. X       return EX_NOPERM;
  1049. X     }
  1050. X    if(stat(defdir,&stbuf))
  1051. X     { nlog("Can't find \"");elog(defdir);elog("\" in \"");
  1052. X       elog(targetdir);elog("\"\n");return EX_NOINPUT;
  1053. X     }
  1054. X    if(minweight!=2)               /* wrong number of arguments? */
  1055. X     { elog("Usage: flist listname[-request]\n");return EX_USAGE;
  1056. X     }
  1057. X    chp=strchr(arg=argv[1],'\0');               /* check for -request */
  1058. X    if(chp-arg>STRLEN(request)&&!strcmp(chp-=STRLEN(request),request))
  1059. X       *chp='\0',pmexec[1]=rcrequest,Endpmexec(1)=0,Endpmexec(2)=rcpost;
  1060. X    else
  1061. X       chp=0;
  1062. X    if(!strcmp(arg,chPARDIR)||strpbrk(arg,dirsep))
  1063. X     { nlog("Bogus listname\n");return EX_NOPERM;
  1064. X     }
  1065. X    if(geteuid()==ROOT_uid)          /* continue as the list maintainer */
  1066. X     { struct passwd*pass;
  1067. X       if(!(pass=getpwnam(listid)))
  1068. X        { nlog("User \");elog(listid);elog(\" unknown\n");
  1069. X          return EX_NOUSER;
  1070. X        }
  1071. X       setgid(pass->pw_gid);initgroups(listid,pass->pw_gid);
  1072. X       setuid(pass->pw_uid);endpwent();
  1073. X     }
  1074. X    else
  1075. X       setgid(stbuf.st_gid),setuid(stbuf.st_uid);
  1076. X    if(chdir(arg))                 /* goto the list's subdirectory */
  1077. X       pmexec[1]=RCMAIN,Endpmexec(2)=0,chdir(defdir);
  1078. X    Endpmexec(5)=INIT_PATH;
  1079. X    Endpmexec(4)=argstr(list,arg);            /* pass on the list name */
  1080. X    if(chp)                      /* was it a -request list? */
  1081. X       *chp= *request;             /* then restore the leading '-' */
  1082. X    Endpmexec(3)=argstr(XENVELOPETO,arg);
  1083. X    while(rclock(GLOCKFILE,&stbuf)||rclock(LLOCKFILE,&stbuf));  /* stall */
  1084. X    execve(pmexec[0],(char*const*)pmexec,environ);nlog("Couldn't exec \"");
  1085. X    elog(pmexec[0]);elog("\"\n");return EX_UNAVAILABLE;        /* panic */
  1086. X      }
  1087. X     setgid(getgid());setuid(getuid());          /* revoke suid permissions */
  1088. X     if(!strcmp(chp,idhash))                  /* idhash program? */
  1089. X      { unsigned long hash=0;int i;
  1090. X    progname=idhash;
  1091. X    if(minweight!=1)
  1092. X     { elog("Usage: idhash\n");return EX_USAGE;
  1093. X     }
  1094. X    while(i=fgetc(stdin),!feof(stdin))               /* hash away! */
  1095. X       hash=hash*67067L+i;
  1096. X    printf("%lx",hash);return EX_OK;
  1097. X      }
  1098. X     if(!strcmp(chp,senddigest))              /* senddigest program? */
  1099. X      { struct stat stbuf;
  1100. X    progname=senddigest;
  1101. X    if(minweight<5)
  1102. X     { elog(
  1103. X     "Usage: senddigest maxage maxsize bodyfile trailerfile [file] ...\n");
  1104. X       return EX_USAGE;
  1105. X     }
  1106. X    if(!stat(argv[3],&stbuf))
  1107. X     { time_t newt;off_t size;
  1108. X       newt=stbuf.st_mtime;size=stbuf.st_size;
  1109. X       if(!stat(argv[minweight=4],&stbuf))
  1110. X        { off_t maxsize;
  1111. X          if(stbuf.st_mtime+strtol(argv[1],(char**)0,10)<newt)
  1112. X         return EX_OK;                   /* digest too old */
  1113. X          maxsize=strtol(argv[2],(char**)0,10);goto statd;
  1114. X          do
  1115. X           { if(!stat(argv[minweight],&stbuf))
  1116. Xstatd:            if((size+=stbuf.st_size)>maxsize)      /* digest too big? */
  1117. X               return EX_OK;
  1118. X           }
  1119. X          while(argv[++minweight]);
  1120. X        }
  1121. X     }
  1122. X    return 1;
  1123. X      }
  1124. X     minweight=SCALE_WEIGHT;best_matches=maxgram=0;exc2str.text=excstr.text=0;
  1125. X     while((chp= *++argv)&&*chp=='-')
  1126. X    for(chp++;;)
  1127. X     { int c;
  1128. X       switch(c= *chp++)
  1129. X        { case 'c':charoffs=1;continue;
  1130. X          case 'd':remov=1;continue;
  1131. X          case 'r':renam=1;continue;
  1132. X          case 'm':chkmetoo=1;continue;
  1133. X          case 'a':
  1134. X         if(!*chp&&!(chp= *++argv))
  1135. X            goto usg;
  1136. X         addit=chp;break;
  1137. X          case 'x':
  1138. X         if(!*chp&&!(chp= *++argv))
  1139. X            goto usg;
  1140. X         if(excstr.text)
  1141. X            exc2str.text=chp;
  1142. X         else
  1143. X            excstr.text=chp;
  1144. X         break;
  1145. X          case 'b':case 'l':case 'w':
  1146. X           { int i;
  1147. X         ;{ const char*ochp;
  1148. X            if(!*chp&&!(chp= *++argv)||
  1149. X             (i=strtol(ochp=chp,(char**)&chp,10),chp==ochp))
  1150. X               goto usg;
  1151. X          }
  1152. X         switch(c)
  1153. X          { case 'b':best_matches=i;continue;
  1154. X            case 'l':minweight=i;continue;
  1155. X            case 'w':maxgram=i;continue;
  1156. X          }
  1157. X           }
  1158. X          case HELPOPT1:case HELPOPT2:elog(usage);
  1159. X         elog(
  1160. X "\t-a address\tadd this address to the list\
  1161. X\n\t-b nnn\t\tmaximum no. of best matches shown\
  1162. X\n\t-c\t\tdisplay offsets in characters\
  1163. X\n\t-d\t\tdelete address from list\
  1164. X\n\t-m\t\tcheck for metoo\
  1165. X\n\t-l nnn\t\tlower bound metric\
  1166. X\n\t-r\t\trename address on list\
  1167. X\n\t-x address\texclude this address from the search (max. 2)\
  1168. X\n\t-w nnn\t\twindow width used when matching\n");return EX_USAGE;
  1169. X          case '-':
  1170. X         if(!*chp)
  1171. X          { chp= *++argv;goto lastopt;
  1172. X          }
  1173. X          default:goto usg;
  1174. X          case '\0':;
  1175. X        }
  1176. X       break;
  1177. X     }
  1178. Xlastopt:
  1179. X     if(!chp||*++argv||renam+remov+!!addit>1)
  1180. X    goto usg;
  1181. X     if(excstr.text)
  1182. X      { excstr.textlen=strlen(excstr.text);lowcase(&excstr);
  1183. X    if(exc2str.text)
  1184. X       exc2str.textlen=strlen(exc2str.text),lowcase(&exc2str);
  1185. X      }
  1186. X     if(!(hardfile=fopen(chp,remov||renam||addit?"r+":"r")))
  1187. X      { nlog("Couldn't open \"");elog(chp);elog("\"\n");return EX_IOERR;
  1188. X      }
  1189. X#ifdef SPEEDBUF                   /* allocate a bigger stdio buffer */
  1190. X     setvbuf(hardfile,malloc(SPEEDBUF),_IOFBF,(size_t)SPEEDBUF);
  1191. X#endif
  1192. X   }
  1193. X  else
  1194. Xusg:
  1195. X   { elog(usage);return EX_USAGE;
  1196. X   }
  1197. X  if(addit)                  /* special subfunction, to add entries */
  1198. X   { int lnl;off_t lasttell;                 /* to the dist file */
  1199. X     for(lnl=1,lasttell=0;;)
  1200. X      { switch(getc(hardfile))                /* step through the file */
  1201. X     { case '\n':
  1202. X          if(!lnl)                /* looking for trailing newlines */
  1203. X         lnl=1,lasttell=ftell(hardfile);
  1204. X          continue;
  1205. X       default:lnl=0;continue;
  1206. X       case EOF:;                   /* or the end of the file */
  1207. X     }
  1208. X    break;
  1209. X      }                     /* go back there, and add the new entry */
  1210. X     fseek(hardfile,lasttell,SEEK_SET);fprintf(hardfile,"%s\n",addit);
  1211. X     printf("Added: %s\n",addit);fclose(hardfile);return EX_OK;
  1212. X   }
  1213. X  if(!maxgram)
  1214. X     maxgram=DEFmaxgram;
  1215. X  maxgram--;
  1216. X  if(minweight==SCALE_WEIGHT)
  1217. X     minweight=DEFminweight;
  1218. X  if(!best_matches)
  1219. X     best_matches=DEFbest_matches;
  1220. X  fuzzstr.text=malloc(fuzzstr.buflen=BUFSTEP);
  1221. X  hardstr.text=malloc(hardstr.buflen=BUFSTEP);
  1222. X  ;{ int i;
  1223. X     best=malloc(best_matches--*sizeof*best);i=best_matches;
  1224. X     do
  1225. X      { best[i]=malloc(sizeof**best);best[i]->hard=malloc(1);
  1226. X    best[i]->fuzz=malloc(1);best[i]->metric= -SCALE_WEIGHT;
  1227. X      }
  1228. X     while(i--);
  1229. X   }
  1230. X  for(lastfrom= -1;readstr(stdin,&fuzzstr,0);)
  1231. X   { int meter,maxmetric;long linentry;off_t offs1,offs2;
  1232. X     ;{ char*chp,*echp;int parens;
  1233. X    echp=strchr(chp=fuzzstr.text,'\0')-1;
  1234. X    do
  1235. X     { switch(*echp)
  1236. X        { case '.':case ',':case ';':case ':':case '?':case '!':*echp='\0';
  1237. X         continue;
  1238. X        }
  1239. X       break;
  1240. X     }
  1241. X    while(--echp>chp);    /* roughly check if it could be a mail address */
  1242. X    if(lastfrom<=0&&!strpbrk(chp,"@/")&&(!strchr(chp,'!')||
  1243. X     strchr(chp,'|')||strchr(chp,',')||strstr(chp,"..")))
  1244. X     { if(lastfrom<0)
  1245. X          lastfrom=!strcmp(SHFROM,chp);
  1246. X       continue;              /* apparently not an email address */
  1247. X     }
  1248. X    lastfrom=0;
  1249. X    for(parens=0;chp=strchr(chp,'(');chp++,parens++);
  1250. X    for(chp=fuzzstr.text;chp=strchr(chp,')');chp++,parens--);
  1251. X    if(*(chp=fuzzstr.text)=='(')
  1252. X     { if(!parens&&*echp==')')
  1253. X        { *echp='\0';goto shftleft;
  1254. X        }
  1255. X       if(parens>0)
  1256. Xshftleft:     tmemmove(chp,chp+1,strlen(chp));
  1257. X     }
  1258. X    else if(parens<0&&*echp==')')
  1259. X       *echp='\0';
  1260. X    if(*(chp=fuzzstr.text)=='<'&&*(echp=strchr(chp,'\0')-1)=='>'
  1261. X     &&!strchr(chp,','))                  /* strip '<' and '>' ? */
  1262. X       *echp='\0',tmemmove(chp,chp+1,echp-chp);
  1263. X    if(!(fuzzstr.textlen=strlen(chp)))
  1264. X       continue;
  1265. X    lowcase(&fuzzstr);
  1266. X    if(excstr.text&&matchgram(&fuzzstr,&excstr)>=EXCL_THRESHOLD||
  1267. X     exc2str.text&&matchgram(&fuzzstr,&exc2str)>=EXCL_THRESHOLD)
  1268. X     { free(fuzzstr.itext);continue;
  1269. X     }
  1270. X    ;{ int i=0;
  1271. X       do
  1272. X        { if(best[i]->metric==-SCALE_WEIGHT&&!strcmp(best[i]->fuzz,chp))
  1273. X         break;
  1274. X          if(!strcmp(best[i]->fuzz,chp))    /* already matched this one? */
  1275. X         goto dupl_addr;
  1276. X        }
  1277. X       while(++i<=best_matches);
  1278. X     }
  1279. X    if(!curmatch)
  1280. X       curmatch=malloc(sizeof*curmatch);
  1281. X    curmatch->fuzz=tstrdup(chp);curmatch->hard=malloc(1);
  1282. X    curmatch->metric= -SCALE_WEIGHT;
  1283. X      }
  1284. X     fseek(hardfile,(off_t)0,SEEK_SET);maxmetric=best[best_matches]->metric;
  1285. X     for(remov_delim=offs2=linentry=0;
  1286. X      offs1=offs2,readstr(hardfile,&hardstr,1);)
  1287. X      { offs2=ftell(hardfile);linentry++;
  1288. X    if(*hardstr.text=='(')
  1289. X       continue;                   /* unsuitable for matches */
  1290. X    lowcase(&hardstr);meter=matchgram(&fuzzstr,&hardstr);
  1291. X    free(hardstr.itext);             /* check if we had any luck */
  1292. X    if(meter>maxmetric&&(remov_delim||!renam&&!remov))
  1293. X     { size_t hardlen;
  1294. X       curmatch->metric=maxmetric=meter;curmatch->lentry=linentry;
  1295. X       free(curmatch->hard);hardlen=hardstr.textlen+1;
  1296. X       hardlen+=strlen(hardstr.text+hardlen)+1;
  1297. X       curmatch->hard=malloc(hardlen+=strlen(hardstr.text+hardlen)+1);
  1298. X       tmemmove(curmatch->hard,hardstr.text,hardlen);
  1299. X       curmatch->offs1=offs1;curmatch->offs2=offs2;
  1300. X     }
  1301. X      }
  1302. X     free(fuzzstr.itext);     /* maybe this match can be put in the array */
  1303. X     if(curmatch->metric>-SCALE_WEIGHT)           /* of best matches so far */
  1304. X      { struct match*mp,**mmp;
  1305. X    free((mp= *(mmp=best+best_matches))->fuzz);free(mp->hard);free(mp);
  1306. X    while(--mmp>=best&&(mp= *mmp)->metric<curmatch->metric)
  1307. X       mmp[1]=mp;                       /* keep it sorted */
  1308. X    mmp[1]=curmatch;curmatch=0;
  1309. X      }
  1310. X     else
  1311. X    free(curmatch->fuzz),free(curmatch->hard);
  1312. Xdupl_addr:;
  1313. X   }
  1314. X  ;{ int i;struct match*mp;
  1315. X     for(i=0;i<=best_matches&&(mp=best[i++])->metric>=minweight;)
  1316. X    if(chkmetoo)
  1317. X       printf("%s\n",strcmp(mp->hard+strlen(mp->hard)+1,NOT_METOO)
  1318. X        ?metoo_SENDMAIL:nometoo_SENDMAIL);
  1319. X    else
  1320. X       printf("%3ld %-34s %5d %-34s\n",
  1321. X        charoffs?mp->offs1:mp->lentry,mp->hard,mp->metric,mp->fuzz);
  1322. X     if((mp= *best)->metric>=minweight)
  1323. X      { struct match*worse;
  1324. X    if(renam)
  1325. X     { long line;int i,w1;unsigned maxweight;
  1326. X       maxweight=SCALE_WEIGHT/(maxgram+1)>>1;;
  1327. X       for(i=1,line=mp->lentry,w1=mp->metric,worse=0;
  1328. X        i<=best_matches&&(mp=best[i++])->metric>=minweight;)
  1329. X          if(mp->lentry==line&&mp->metric+maxweight<w1)
  1330. X           { goto remv1;
  1331. X           }
  1332. X       for(i=1;i<=best_matches&&(mp=best[i++])->metric>=minweight;)
  1333. X          if(mp->metric+maxweight<w1)
  1334. Xremv1:           { worse=mp;mp= *best;goto remv;
  1335. X           }
  1336. X       nlog("Couldn't find a proper address pair\n");goto norenam;
  1337. X     }
  1338. X    if(remov)
  1339. Xremv:     { char*buf;off_t offs1,offs2;size_t readin;
  1340. X       buf=malloc(COPYBUF);offs1=mp->offs1;offs2=mp->offs2;
  1341. X       while(fseek(hardfile,offs2,SEEK_SET),
  1342. X        readin=fread(buf,1,COPYBUF,hardfile))
  1343. X        { offs2=ftell(hardfile);fseek(hardfile,offs1,SEEK_SET);
  1344. X          if(buf[readin-1]=='\n')      /* try and remove some empty lines */
  1345. X         while(readin>1&&buf[readin-2]=='\n')    /* at the end, since */
  1346. X            readin--;             /* every time could be the last */
  1347. X          fwrite(buf,1,readin,hardfile);offs1=ftell(hardfile);
  1348. X        }
  1349. X       free(buf);fseek(hardfile,offs1,SEEK_SET);
  1350. X       printf("Removed: %s\n",mp->hard);
  1351. X       if(renam)
  1352. X          fputs(worse->fuzz,hardfile),printf("Added: %s\n",worse->fuzz);
  1353. X       do putc('\n',hardfile);               /* erase the tail */
  1354. X       while(ftell(hardfile)<offs2);
  1355. X       fclose(hardfile);
  1356. X     }
  1357. X    return EX_OK;
  1358. X      }
  1359. X   }
  1360. X  if(remov||renam)
  1361. X   { nlog("Couldn't even find a single address\n");
  1362. X   }
  1363. Xnorenam:
  1364. X  return 1;
  1365. X}
  1366. END_OF_FILE
  1367. if test 18039 -ne `wc -c <'procmail/src/multigram.c'`; then
  1368.     echo shar: \"'procmail/src/multigram.c'\" unpacked with wrong size!
  1369. fi
  1370. # end of 'procmail/src/multigram.c'
  1371. fi
  1372. echo shar: End of archive 9 \(of 11\).
  1373. cp /dev/null ark9isdone
  1374. MISSING=""
  1375. for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
  1376.     if test ! -f ark${I}isdone ; then
  1377.     MISSING="${MISSING} ${I}"
  1378.     fi
  1379. done
  1380. if test "${MISSING}" = "" ; then
  1381.     echo You have unpacked all 11 archives.
  1382.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1383. else
  1384.     echo You still need to unpack the following archives:
  1385.     echo "        " ${MISSING}
  1386. fi
  1387. ##  End of shell archive.
  1388. exit 0
  1389. -- 
  1390. Sincerely,                                  berg@pool.informatik.rwth-aachen.de
  1391.            Stephen R. van den Berg (AKA BuGless).    berg@physik.tu-muenchen.de
  1392.  
  1393. "Always look on the bright side of life!"
  1394.  
  1395. exit 0 # Just in case...
  1396.