Newsgroups: comp.sources.unix
From: clay@monsta.metronet.com (Clay Luther)
Subject: v28i082: bmw-5 - Mailing List/Digest/Archive Mananger (in PERL), V5, Part01/01
Message-id: <1.773795973.8083@gw.home.vix.com>
Sender: unix-sources-moderator@gw.home.vix.com
Approved: vixie@gw.home.vix.com

Submitted-By: clay@monsta.metronet.com (Clay Luther)
Posting-Number: Volume 28, Issue 82
Archive-Name: bmw-5/part01

#! /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 1 (of 1)."
# Contents:  FILES MANIFEST README bmw bmw.cf bmwmail digestify help
#   kron ledit message mkarchive senddigest test test/digest test/help
#   test/log test/message test/subscribers test/subscribers.bak
#   test/subscribers.d test/subscribers.d.bak testmsg
# Wrapped by vixie@gw.home.vix.com on Sat Jul  9 16:19:15 1994
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'FILES' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'FILES'\"
else
echo shar: Extracting \"'FILES'\" \(622 characters\)
sed "s/^X//" >'FILES' <<'END_OF_FILE'
XYou should have the following files:
X
XREADME - instructions
XFILES - this file
Xbmw - the bmw script
Xbmw.cf - the bmw configuration file
Xbmwmail - the bmwmail script
Xdigestify - the digestify script
Xhelp - the global bmw help file
Xkron - a sample crontab
Xledit - a script to safely edit bmw list files
Xmessage - the global bmw message file
Xmkarchive - a script to make an archive out of a bmw monthly file
Xsenddigest - a script to send a digest file to subscribers
Xtestmsg - a simple test message you can edit
X
XFiles under directory test/:
Xdigest
Xhelp
Xlog
Xmessage
Xsubscribers
Xsubscribers.bak
Xsubscribers.d
Xsubscribers.d.bak
END_OF_FILE
if test 622 -ne `wc -c <'FILES'`; then
    echo shar: \"'FILES'\" unpacked with wrong size!
fi
# end of 'FILES'
fi
if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(827 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X FILES                      1	
X MANIFEST                   1	This shipping list
X README                     1	
X bmw                        1	
X bmw.cf                     1	
X bmwmail                    1	
X digestify                  1	
X help                       1	
X kron                       1	
X ledit                      1	
X message                    1	
X mkarchive                  1	
X senddigest                 1	
X test                       1	
X test/digest                1	
X test/help                  1	
X test/log                   1	
X test/message               1	
X test/subscribers           1	
X test/subscribers.bak       1	
X test/subscribers.d         1	
X test/subscribers.d.bak     1	
X testmsg                    1	
END_OF_FILE
if test 827 -ne `wc -c <'MANIFEST'`; then
    echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(8962 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XBMW 5.0
XCOPYRIGHT 1994 CLAY LUTHER
X
XTHIS SOFTWARE IS FREE.  YOU MAY DISTRIBUTE THIS SOFTWARE AS YOU WANT, BUT
XTHIS AND ALL OTHER COPYRIGHT NOTICES MUST REMAIN INTACT.
XSETTING UP A THE BMW SOFTWARE TO MANAGE LISTS.
X
XTHIS SOFTWARE MAY BE DISTRIBUTED UNDER THE GNU SOFTWARE LICENSE.
X
X
XBMW is written in perl and your system must have perl installed to run it.
XPerl was selected because it is fast AND interpreted.  The user can more
Xeasily change the scripts to suit his site than if they were written in a
Xprecompiled language.  So, perl was choosen.
X
XLook over the FILES file and make sure that your tarball unpack correctly.
X
XThere is no Makefile to execute, but you may have to edit the bmw scripts to
Xcorrectly point at your perl executable.  Look at the first line of each
Xscript and make sure that the #! path is correct.  For example, on my system
Xit is #!/usr/bin/perl, but serveral systems install perl in /usr/local/bin;
Xsuch a system would change the #! path to #!/usr/local/bin/perl. 
X
XBMW relies on sendmail to handle incoming and outgoing messages.  In general,
Xall the BMW programs accept information on standard input (the mail message).
XIt is assumed that the mail message is piped in from sendmail.
X
XThis means that you must be able to add aliases to your sendmail aliases file.
XOn most systems only the superuser can do this.  If you do not have superuser
Xpermission, you will probably have to get it before you can operate a 
XBMW mailing list.
X
X
XCREATE THE BMW DIRECTORY
X
XCreate a directory that is owned by the bmw owner (on my system, I actually
Xcreated a user specifically for owning and operating the mailing lists).
XI'll call this directory $BMW.  Unpack the bmw scripts into $BMW.
X
XYou may have permission problems if the $BMW subdirectories are not in the
Xsame group as what sendmail runs under.  On most systems, this is bin.  On
Xmy system, the user bmw is in group bin (same as sendmail).  I've set the
Xpermissions on $BMW and all its subdirectories to 775.  Furthermore, the
Xedited files under each $BMW/$LIST directory (subscribers, subscribers.d and
Xlog) should be set to 774.
X
X
XWHAT ALIASES DO I NEED?
X
XThe BMW requires 7 aliases per list to operate.   If your mailing list is
Xnamed $LIST then these are:
X
X$LIST - alias to a pipe to bmwmail; the address that members post messages to
X$LIST-mail - alias to an included file, the actual list of subscribers
X$LIST-owner - alias to the list's owner
X$LIST-request - alias to a pipe to the bmw program for handling server requests
X$LIST-log - alias to the log of the mailing list
X$LIST-digest - alias to the digest log of the mailing list
X$LIST-digest-mail - alias to the included list of digest subscribers
X
XEdit your sendmail aliases file, and create the following aliases for your
Xlist:
X
X$LIST: "|$BMW/bmwmail $LIST"
X$LIST-mail: ":include:$BMW/$LIST/subscribers"
X$LIST-owner: <put your address here!>
X$LIST-request: "|$BMW/bmw $LIST"
X$LIST-digest: "$BMW/$LIST/digest"
X$LIST-digest-mail: ":include:$BMW/$LIST/subscribers.d"
X
XSave the aliases file and run newaliases.  Check your aliases with
X/usr/lib/sendmail -bv -v <one of the aliases goes here!>.
X
XNOTE:  Your users should only send messages to $LIST, $LIST-owner, and 
X$LIST-request.  The other addresses are used internally by bmw.
X
X
XCREATE YOUR $LIST SUBDIRECTORY
X
XCreate a directory under $BMW named for your $LIST (lower-case).
X
X
XEDIT THE BMW SCRIPTS AND FIX BMWCF
X
XEdit bmw, bmwmail, digestify, senddigest, and mkarchive.  Search for the
Xfirst occurence of BMWCF.  Change the value of BMWCF to point to whereever
Xyou are putting the bmw.cf file.  This should be all you need to change 
Xin the scripts themselves (well, besides the perl #! path).
X
X
XEDIT bmw.cf
X
XAfter putting bmw.cf whereever you want, edit it.  This allows you to set
Xbmw operating globals to whatever you want.  Examine the bmw script and look
Xfor the initialization of the GLOBALS array.  If there is a value you don't
Xlike here (certainly any the the ...DIR directory globals will need your 
Xattention), put them in your bmw.cf file.
X
XLook at the bmw.cf file that came with the bmw distribution:
X
Xbasedir = /home/bmw
Xdigest = 1
Xlog = 1
Xdebug = 0
X
XThis sets the BASEDIR value to /home/bmw, tells bmw that there is a digest
Xform of the mailing list, tells bmw to log requests, and tells bmw not to
Xprint debugging information during execution.
X
X
XRUN A TEST
X
XThe easiest way to run a test is to execute bmw by hand.  Type in the 
Xfollowing, substituting your list's name for $LIST and your mail address for
X$ADDRESS.
X
X% bmw $LIST
XSubject: ping
XFrom: $ADDRESS
X
Xwho
X^D
X
XIf bmw prints out an error, try to solve the problem and run the test again.
X
XIf no error is printed, check your mailbox.  Also, check the mail mailbox of
X$LIST-owner.  You should have a response from bmw for the ping and who request.
X
XIf there is an error message in $LIST-owner's mailbox, try to fix the error
X(perhaps a permission error?) and run the test again.
X
X
XTEST ALIASES
X
XNow comes a bigger test.  Type in the following with appropriate substitutions:
X
X% /usr/lib/sendmail -v $LIST-request
XSubject: ping
XFrom: $ADDRESS
X
Xwho
Xsub
Xuns
Xdig
Xund
Xdir
Xhelp
X.
X
XWatch sendmail execute your message closely; did it error?  If it does at 
Xthis level, it is most likely a permissions problem.  Go check your permissions
Xon the $LIST directory and its files and try again.
X
XNext, check your mailbox.  Confirm that bmw was able to execute all of those
Xcommands.
X
XCheck the $LIST-owner's mailbox.  Did bmw send you a error messsage?  If so,
Xsee if you can fix it and run the test again.
X
XCheck the $LIST directory.  If you have bmw logging on, check the log file.
XAre the requests in the log file, especially the sub/uns/dig/und requests?
X
X
XMAIL A TEST
X
XNow, mail a message to $LIST-request using your favorite mail program.  Make
Xit a sub request.  Wait for the reponse and check the $BMW/$LIST/subsribers
Xfile to make sure you were added.
X
XNow, mail a test message to $LIST.   Wait a little while.  Do you get the
Xmessage back?  If not, try mailing to the $LIST with
X
X% /usr/lib/sendmail -v $LIST
XSubject: testing
X
Xtesting
X.
X
XAnd see what it says.  Is there an error?  Try to fix the error and repeat
Xthe test.  It will usually be a permissions error.
X
X
XIf you can subscribe, unsubscribe, and mail messages to the $LIST and get
Xreponses, you have probably set bmw up correctly!
X
X
XSETTING UP THE DIGEST AND LOG
X
XIf you are running a digest and log of the mailing list (not a bmw log, but
Xa monthly log for archiving later), there are a few extra steps you must 
Xtake.
X
XFirst, place the aliases to the digest and the log in the subscribers file.
XThis will allow them to be sent automatically when the mailing list is mailed
Xto.  Send a test message to the list with /usr/lib/sendmail -v and make sure
Xthat a copy of the message appears in $LIST/monthly and $LIST/digest.  If
Xthe digest file does not appear, try piping a test message by hand through
Xdigestify.  Repair any reported errors.
X
X
XSend a DIGEST request to $LIST-request.  Check the $LIST/subscribers.d file -
Xis your address in there?  If not, check for permission problems and make sure
Xthat your bmw.cf file has "digest = 1" in it.
X
XNext, run senddigest:
X
X% senddigest $LIST
X
XDid you get a copy of the digest?  If so, congratulations!  If not, look
Xat the errors reported, check your postmaster's mailbox, and check the
Xlist owner's mailbox for errors.
X
X
XCHECK MKARCHIVE
X
XIf you are doing list logging (a monthly file exists in the $LIST directory
Xafter sending a message to the list [$LIST-log is in your subscribers file,
Xright?]), run mkarchive to make sure that the monthly file can be archived:
X
X% mkarchive $LIST
X
XIf there are errors (did you set your ARCDIR with bmw.cf?), fix them and
Xrepeat the process with another monthly file in place.
X
X
XSET UP CRON
X
XIf everything is working, set up your crontab.  The distribution comes with
Xan example crontab named kron.  Edit this file to suit your system and
Xsubmit it to cron.
X
XThe crontab should have a senddigest entry, which bundles up and sends out
Xthe $LIST/digest file to the addresses listed in $LIST/subscribers.d.
X
XThe crontab should have an entry for mkarchive, which moves the $LIST/monthly
Xfile to the $ARCDIR (specified in bmw.cf) and compresses it.
X
X
XEDIT YOUR HELP AND MESSAGE FILES
X
XChange the $BMW/help and $BMW/message files to suit your site.  You can also
Xcreate $LIST/help and $LIST/message files which override the $BMW files when
Xa that $LIST is mailed to.
X
X
XANNOUNCE YOUR MAILING LIST
X
XTell people about your list and have fun.
X
X
XOTHER PROGRAMS - ledit
X
XLedit is a program that allows your edit the subscribers and subscribers.d
Xfile of a list safely (it does some primitive file locking).  It does not
Xuse the bmw.cf file, so you will need to change one value in it, SDIR,
Xand possibly your EDITOR command, to suit your system.  To edit a list
Xwith ledit:
X
X% ledit $LIST
X
Xto edit the subscribers file, or
X
X% ledit -d $LIST
X
Xto edit the subscribers.d file.
X
END_OF_FILE
if test 8962 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'bmw' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'bmw'\"
else
echo shar: Extracting \"'bmw'\" \(26042 characters\)
sed "s/^X//" >'bmw' <<'END_OF_FILE'
X#! /usr/bin/perl
X## Set the above path to your path to perl.
X##
X## BMW - The Black Marble Wombat Mailing List Manager
X## Copyright (c) 1994 by Clay Luther, All rights reserved.
X## THIS SOFTWARE IS COVERED BY THE GNU SOFTWARE LICENSE.
X## YOU MAY USE THIS SOFTWARE AS YOU PLEASE SO LONG AS YOU DO NOT REMOVE
X## THE COPYRIGHT NOTICES.
X##
X
X$USAGE = "$0 <listname>";
X
X##
X## General operation.
X## BMW accepts a single mail message on standard input.  It expects the
X## message to obey standard UN*X mail format, that is:
X##
X## FROMSPACE line
X## HEADERS
X## <blank line>
X## DATA
X##
X## BMW will attempt to extract the user's address from the message.  However,
X## if the environment variable SENDER is set, BMW will use it instead.
X##
X## BMW will parse the mail message looking for commands from the user.
X## Commands should appear either on the subject line or at the beginning of
X## a line in the DATA section.  The user may issue an unlimited number of
X## commands.
X##
X## Commands are significant to 3-letters and are not case-sensitive.
X## The commands are:
X##
X##  subscribe
X##  unsubscribe
X##  who
X##  dir
X##  cd
X##  get
X##  ping
X##  help
X##  digest
X##  undigest
X##
X
X# BEGIN
X
X$LF = "\n";
X$TRACE = 0;          # Trace debug.  This will be set by the global routines.
X
Xsub trace            # The trace routine.
X{
X  local($i) = @_;
X  print $i,$LF if $TRACE;
X}
X
X
Xsrand;
Xumask("000");
X
Xif ($#ARGV != 0)
X{
X# We must have at least one argument!
X  die "USAGE: ",$USAGE,$LF;
X}
X
X$LISTNAME = $ARGV[0];
X$LISTNAME =~ tr/A-Z/a-z/;  # Convert everything to lower case for now.
X
X%GLOBALS = ();
X
X## We need to set the hostname.  You this is done by querying hostname and
X## domainname.  You can override this here or set your FQDN global explicitly
X## in the bmw.cf file.
X
X$HOSTNAME = `hostname`; chop($HOSTNAME);
X$DOMAINNAME = `domainname`; chop($DOMAINNAME);
X
X## Path to bmw.cf - CHANGE THIS FOR YOUR SYSTEM
X#$BMWCF = "/etc/bmw.cf";
X$BMWCF = "/home/bmw/bmw.cf";
X
X## Process the config file and learn about all the globals.
X## FORMAT OF CONFIG FILE:
X##   Each line of the config file should have the format
X##     <globalname> = <value>
X##   for example,
X##     BASEDIR = /usr/local/lib/bmw
X##
X## I now set the defaults.  You should override these in your bmw.cf file.
X##
X$GLOBALS{'SENDMAIL'} = "/usr/lib/sendmail";
X$GLOBALS{'SENDMAILOPTS'} = "";
X$GLOBALS{'ENCODE'} = "/usr/bin/uuencode";
X$GLOBALS{'COMPRESS'} = "/bin/gzip";
X$GLOBALS{'COMPSUFFIX'} = ".gz";
X$GLOBALS{'ARCDIR'} = "/home/ftp/pub/lists";
X$GLOBALS{'ARCOWNER'} = "ftp";
X$GLOBALS{'OWNER'} = "postmaster";
X$GLOBALS{'DEBUG'} = 0;
X$GLOBALS{'LOG'} = 0;
X$GLOBALS{'MAXGETS'} = 5;
X$GLOBALS{'PREFERFTP'} = 0;
X$GLOBALS{'TRACE'} = 0;
X$GLOBALS{'BASEDIR'} = "/usr/local/lib/bmw";
X$GLOBALS{'FQDN'} = $HOSTNAME . "." . $DOMAINNAME;
X$GLOBALS{'DIGEST'} = 0;
X$GLOBALS{'TMPDIR'} = "/var/tmp";
X$GLOBALS{'USER'} = "bmw";
X$GLOBALS{'GROUP'} = "bin";
X
X
X@VALIDGLOBALS = ("SENDMAIL", "SENDMAILOPTS", "TMPDIR", "ENCODE",
X                 "COMPRESS", "COMPSUFFIX", "ARCDIR", "ARCOWNER",
X		 "OWNER", "DEBUG", "LOG", "MAXGETS", "PREFERFTP",
X		 "TRACE", "BASEDIR", "FQDN", "DIGEST", "USER", "GROUP");
X
X
X
Xsub validGlobal {
X## Determine if the string is a valid global reference.
X  local($s) = @_;
X  local($i);
X  for ($i=0; $i<=$#VALIDGLOBALS; $i++) {
X    if ($s eq $VALIDGLOBALS[$i]) { return 1; }
X  }
X  return 0;
X}
X
X
X
X## Load the globals from the cf file.
Xif (-e $BMWCF) {
X  local($lno) = 0;
X  local(@LINE);
X  open(CF, "<$BMWCF") || die "Cannot open $BMWCF: $!\n";
X  while (<CF>) {
X    $lno++;
X    chop;
X    tr/\t //d;
X    @LINE = split("=");
X    if (!$LINE[0]) { die "ERROR in $BMWCF line $lno; $_\n"; }
X    $LINE[0] =~ tr/a-z/A-Z/;
X    if (!&validGlobal($LINE[0])) { 
X      die "ERROR in $BMWCF line $lno; Unknown global \"$LINE[0]\"\n"; 
X    }
X## Good.  We have a valid global
X    $GLOBALS{$LINE[0]} = $LINE[1];
X  }
X}
Xelse {
X  die "$BMWCF does not exist!\n";
X}
X
Xif ($GLOBALS{'DEBUG'}) {
X  local($key);
X  foreach $key (keys %GLOBALS) {
X    print "$key = $GLOBALS{$key}",$LF;
X  }
X}
X
X
X## Internal global values.  Do not fiddle with these without good reason.
X$VERSION = "5.0";
X$LISTDIR = $GLOBALS{'BASEDIR'} . "/$LISTNAME";
X$LISTARC = $GLOBALS{'ARCDIR'} . "/$LISTNAME";
X$LISTFILE = "$LISTDIR/subscribers";
X$DIGESTFILE = "$LISTDIR/subscribers.d";
X$LOGFILE = "$LISTDIR/log";
X$MESSAGE = $GLOBALS{'BASEDIR'} . "/message";
X$MESSAGE = "$LISTDIR/message" if (-e "$LISTDIR/message");
X$HELP = $GLOBALS{'BASEDIR'} . "/help";
X$HELP = "$LISTDIR/help" if (-e "$LISTDIR/help");
X$WDIR = "";                   # working directory suffix for gets
X
X$TMPFILE = $GLOBALS{'TMPDIR'} . "/bmw$$";
X$REPLYFILE = "$TMPFILE.reply";
X$TMPLOGFILE = "$TMPFILE.log";
X
X$LISTOWNER = "$LISTNAME-owner";
X$LISTREQUEST = "$LISTNAME-request";
X
X@ERRORS = ();
X%LOCKS = ();
X
X##
X## Exit routines.  These make sure everything is cleaned up.
X##
Xsub finish {
X  local($rc) = @_;
X  local($i);
X  system("rm -f $TMPFILE $REPLYFILE $TMPLOGFILE");
X  if ($#ERRORS > -1) {
X    open(T, ">$TMPFILE");
X    print T "From: $LISTOWNER\n";
X    print T "Subject: Errors from $LISTREQUEST\n";
X    print T "To: $LISTOWNER\n\n";
X    for ($i = 0; $i <= $#ERRORS; $i++) {
X      print T $ERRORS[$i],$LF;
X    }
X    close(T);
X    system("cat $TMPFILE | $GLOBALS{'SENDMAIL'} \'$LISTOWNER\'");
X    system("rm -f $TMPFILE")
X  }
X  exit($rc);
X}
X
X
X
Xsub DIE {
X  local($msg) = @_;
X  print STDERR "$msg",$LF;
X  $ERRORS[$#ERRORS+1] = $msg;
X  local($key);
X  foreach $key (keys %LOCKS) {
X    system("rm -f $key");
X  }
X  &finish(-1);
X}
X
X
X# Test for things that must exist.
X
X&DIE("$LISTDIR does not exist!\n") if (! -d $LISTDIR);
X
Xif (! -e $LISTFILE) {
X## Create a list file.
X  open(LF,">$LISTFILE") || &DIE("Could not create $LISTFILE: $!\n");
X  print LF "$LISTOWNER\n";
X  close(LF);
X  chown $GLOBALS{'USER'}, $GLOBALS{'GROUP'}, $LISTFILE;
X  chmod 0664,$LISTFILE;
X}
X
Xif (! -e $DIGESTFILE) {
X## Create a digest file.
X  open(LF,">$DIGESTFILE") || &DIE("Could not create $DIGESTFILE: $!\n");
X  print LF "$LISTOWNER\n";
X  close(LF);
X  chown $GLOBALS{'USER'}, $GLOBALS{'GROUP'}, $DIGESTFILE;
X  chmod 0664,$DIGESTFILE;
X}
X
Xif (! -d $LISTARC) {
X  $ARCTHERE = 0;
X}
Xelse {
X  $ARCTHERE = 1;
X}
X
X## Start parsing the message.
X
X$FROM = "";      ## who from?
X$OFROM = "";     ## original from? overrides From:
X$REPLYTO = "";   ## reply to? overrides Originally-From:
X$SUBJECT = "";   ## subject line
X$REPLYOPEN = 0;  ## is the reply file open?
X$STOPEN = 0;     ## used for sending text files
X$LOGOPEN = 0;    ## is the logfile open?
X$NUMGETS = 0;    ## how many gets so far?
X$HEADERS = 1;    ## location of the parse
X$INFROM = 0;     ## deal with problem Froms:
X
X@COMMANDS = ();
X
Xshift;
X
Xwhile (<>) {
X  if ($HEADERS) {
X    if ($INFROM && /^\s/) {
X      local($morefrom);
X      $morefrom = $1 if m/^\s*(.*)\n/;
X      $FROM = $FROM . $morefrom;
X    }
X    else {
X      $INFROM = 0;
X      if (/^Subject:/) { $SUBJECT = $1 if m/^Subject:\s*(.*)\n/; }
X      if (/^From:/) { $FROM = $1 if m/^From:\s*(.*)\n/; $INFROM = 1; }
X      if (/^Original-From:/) { $OFROM = $1 if m/^Original-From:\s*(.*)\n/; }
X      if (/^Reply-To:/) { $REPLYTO = $1 if m/^Reply-To:\s*(.*)\n/; }
X      
X      if ($_ eq "\n") {
X## Blank line ends headers.
X	$HEADERS = 0;
X	&debug("Subject = $SUBJECT\n") if $SUBJECT;
X        &loadCommand($SUBJECT) if $SUBJECT;
X      }
X    }
X  }
X  else {
X    &loadCommand($_);
X  }
X}
X
X
X## We have attempted to parse the entire message.  First, let's fix up
X## set the $ADDRESS, which will be used in the rest of the program.
X## $ADDRESS is the address of the person using the bmw at the moment.
X
X$ADDRESS = "";
Xif ($ENV{'SENDER'}) {
X  $ADDRESS = $ENV{'SENDER'};
X}
Xelse {
X  $ADDRESS = $FROM if $FROM;
X  $ADDRESS = $OFROM if $OFROM;
X  $ADDRESS = $REPLYTO if $REPLYTO;
X  $ADDRESS = &fixAddress($ADDRESS);
X}
X
X&debug("Address = $ADDRESS\n");
X
Xif (!$ADDRESS) {
X  &DIE("Invalid address: $ADDRESS\n");
X}
X
X## Commands loaded, a good address.  What more could we want?
X
X&processCommands();
X
X&finish(0);
X
X## WE'RE DONE!
X
X#### SUBROUTINES BELOW ####
X
Xsub fixAddress {
X## fixAddress attempts to expunge an address string of all extraneous
X## text except for the actual address.  It assumes that addresses have the
X## forms:
X##
X##  <address> username
X##  username <address>
X##  (username) address
X##  address (username)
X  local($a) = @_;
X  $_ = $a;
X  if ($a =~ /.*\<.*\>.*/) {
X## <> format?  pretty easy.
X    $a = $1 if m/.*\<(.*)\>.*/;
X  }
X  elsif ($a =~ /.*\(.*\).*/) {
X    $a = ($1 . $2) if m/(.*)\(.*\)(.*)/;
X  }
X  $_ = $a;
X  tr/ \t//d;
X  return $_;
X}
X
X
Xsub loadCommand {
X## parse the parameter.
X  local($_) = @_;
X  $COMMANDS[$#COMMANDS+1] = "SUB" if m/^\s*sub.*/i;
X  $COMMANDS[$#COMMANDS+1] = "UNS" if m/^\s*uns.*/i;
X  $COMMANDS[$#COMMANDS+1] = "WHO" if m/^\s*who.*/i;
X  $COMMANDS[$#COMMANDS+1] = "DIR $1" if m/^\s*dir\s*(\S*)/i;
X  $COMMANDS[$#COMMANDS+1] = "GET $1" if m/^\s*get\s*(\S*)/i;
X  $COMMANDS[$#COMMANDS+1] = "HEL" if m/^\s*hel.*/i;
X  $COMMANDS[$#COMMANDS+1] = "PIN" if m/^\s*pin.*/i;
X  $COMMANDS[$#COMMANDS+1] = "DIG" if m/^\s*dig.*/i;
X  $COMMANDS[$#COMMANDS+1] = "UND" if m/^\s*und.*/i;
X  $COMMANDS[$#COMMANDS+1] = "CD $1" if m/^\s*cd\s*(\S*)/i;
X}
X
X
Xsub processCommands {
X  if ($#COMMANDS > -1) {
X    local($i);
X    &startReply();
X    for ($i=0; $i <= $#COMMANDS; $i++)
X    {
X      $_ = $COMMANDS[$i];
X      if (/^SUB/) { &doSub($_); }
X      elsif (/^UNS/) { &doUns($_); }
X      elsif (/^WHO/) { &doWho($_); }
X      elsif (/^DIR/) { &doDir($_); }
X      elsif (/^GET/) { &doGet($_); }
X      elsif (/^HEL/) { &doHel($_); }
X      elsif (/^PIN/) { &doPin($_); }
X      elsif (/^DIG/) { &doDig($_); }
X      elsif (/^UND/) { &doUnd($_); }
X      elsif (/^CD/)  { &doCD($_);  }
X      else {
X        &reply("Unknown command: $_.\nNow how'd that happen?\n");
X        &error("Unknown command: $_\n");
X      }
X    }
X  }
X  else {
X    &standardReply();
X  }
X  &sendReply();
X}
X
Xsub standardReply {
X  &startReply();
X  &reply("\nI could not detect any commands in your mail to me.\n" .
X         "Need help?  Set your subject line to HELP.\n");
X  &sendReply();
X}
X
Xsub startReply {
X  local($FQDN) = $GLOBALS{'FQDN'};
X  if ($REPLYOPEN) { return; }
X  open(REPLY, ">$REPLYFILE") || &DIE("Cannot open $REPLYFILE: $!\n");
X  print REPLY "To: $ADDRESS\n";
X  print REPLY "Subject: Reply from $LISTREQUEST\n";
X  print REPLY "Errors-To: $LISTOWNER@$FQDN\n";
X  print REPLY "Reply-To: $LISTREQUEST@$FQDN\n";
X  print REPLY "From: $LISTOWNER@$FQDN\n";
X  print REPLY "Sender: $LISTOWNER@$FQDN\n";
X  print REPLY "X-bmw: Black Marble Wombat Version $VERSION\n";
X  print REPLY "X-list: $LISTNAME@$FQDN\n\n";
X  if (-e $MESSAGE)
X  {
X    local($listname) = $LISTNAME;
X    $listname =~ tr/a-z/A-Z/;
X    open(MESSAGE, "<$MESSAGE") || &DIE("Cannot open $MESSAGE: $!\n");
X    while (<MESSAGE>) 
X    { 
X      s/LISTNAME/$listname/g;
X      s/LISTOWNER/$LISTOWNER@$FQDN/g;
X      s/LISTREQUEST/$LISTREQUEST@$FQDN/g;
X      s/LISTADDR/$LISTNAME@$FQDN/g;
X      print REPLY $_; 
X    }
X    close(MESSAGE);
X  }
X  else {
X    &error("No message file for $LISTNAME.\n");
X    print REPLY "Black Marble Wombat, Version $VERSION\n";
X  }
X  print REPLY "\n" . 
X              "List addresses:\n" . 
X              "  Post messsages to        -> $LISTNAME@$FQDN\n" .
X              "  Automatic administration -> $LISTREQUEST@$FQDN\n" .
X              "  List owner (a person)    -> $LISTOWNER@$FQDN\n";
X  print REPLY "\nHello $ADDRESS.\n";
X  $REPLYOPEN = 1;
X}
X
Xsub sendReply {
X  if (!$REPLYOPEN) { &startReply(); }
X  &reply("\nThe Black Marble Wombat Mailing List Manager, " .
X         "Version $VERSION\n" .
X         "By Clay Luther, clay@gojira.monsta.com\n" . 
X         "Copyright (c) 1994 Monsta, Inc.\n");
X  close(REPLY);
X  $REPLYOPEN = 0;
X  &Mail($REPLYFILE);
X}
X
Xsub reply {
X  local($s) = @_;
X  if (!$REPLYOPEN) { &startReply(); }
X  print REPLY $s;
X}
X
Xsub Mail {
X## Send a file by mail.
X  local($f) = @_;
X  local($mailstr) = $GLOBALS{'SENDMAIL'} . " " .
X                    $GLOBALS{'SENDMAILOPTS'} . " " .
X                    $ADDRESS;
X  if (-r $f) {
X    local($cmd) = `cat $f | $mailstr`;
X    $cmd =~ tr/\s//d;
X    &error("Mail command ($mailstr) returned $cmd\n") if $cmd;
X  }
X  else {
X    &error("Could not read $r\n");
X  }
X}
X
Xsub error {
X  local($e) = @_;
X  $ERRORS[$#ERRORS+1] = $e;
X}
X
X##
X## Command processing
X##
X
Xsub doPin {
X  &reply("\nCommand: PING\n");
X  &reply("The Black Marble Wombat, Version $VERSION\n");
X  &log("PIN $ADDRESS");
X}
X
Xsub doWho {
X## Attempt to display the list (and digest list) subscribers.
X  local($cnt) = 0;
X  &reply("\nCommand: WHO\n");
X  if (open(LF, "<$LISTFILE")) {
X     while (<LF>) { &reply($_) && $cnt++ if (!/$LISTOWNER/); }
X     close(LF);
X   }
X   else {
X     &error("doWho: could not open $LISTFILE: $!\n");
X   }
X
X   if ($GLOBALS{'DIGEST'} && -e $DIGESTFILE)
X   {
X     if (open(LF, "<$DIGESTFILE")) {
X       while (<LF>) { &reply($_) && $cnt++ if (!/$LISTOWNER/); }
X       close(LF);
X     }
X     else {
X       &error("doWho: could not open $DIGESTFILE: $!\n");
X     }
X   }
X
X   &reply("\n$cnt subscriber(s).\n");
X}
X
X
Xsub doSub {
X## Allow the user to subscribe to the mailing list or its digest.
X  &reply("\nCommand: SUBSCRIBE\n");
X  if ($GLOBALS{'DIGEST'} && &inList($DIGESTFILE)) {
X    if (&removeFromList($DIGESTFILE)) {
X      &reply("You have been removed from the digest list.\n")
X    }
X    else {
X      &reply("There was an error on this end and you were not removed\n" .
X             "from the digest list.\n");
X    }
X  }
X  if (!&inList($LISTFILE)) {
X    if (&addToList($LISTFILE)) {
X      &reply("You have been added to the mailing list.\n");
X      &log("SUB $ADDRESS");
X    }
X    else {
X      &reply("There was an error on this end and you were not added\n" .
X             "to the mailing list.\n");
X    }
X  }
X  else {
X    &reply("You are already subscribed to the mailing list.\n");
X  }
X}
X
X
Xsub doDig {
X  &reply("\nCommand: DIGEST\n");
X  if ($GLOBALS{'DIGEST'}) {
X    if (&inList($LISTFILE)) {
X      if (&removeFromList($LISTFILE)) {
X        &reply("You have been removed from the mailing list.\n");
X      }
X      else {
X        &reply("There was an error on this end and you were not removed\n" .
X               "from the mailing list file.\n");
X      }
X    }
X    if (!&inList($DIGESTFILE)) {
X      if (&addToList($DIGESTFILE)) {
X        &reply("You have been added to the digest list.\n");
X        &log("DIG $ADDRESS");
X      }
X      else {
X        &reply("There was an error on this end and you were not added\n" .
X               "to the digest list.\n");
X      }
X    }
X    else {
X      &reply("You are already on the digest list.\n");
X    }
X  }
X  else {
X    &reply("This list does not currently support a digest format.\n");
X  }
X}
X
X
X
Xsub doUns {
X  &reply("\nCommand: UNSUBSCRIBE\n");
X  if (&inList($LISTFILE)) {
X    if (&removeFromList($LISTFILE)) {
X      &reply("You have been removed from the mailing list.\n");
X      &log("UNS $ADDRESS");
X    }
X    else {
X      &reply("There was an error on this end and you were not removed\n" .
X             "from the mailing list.\n");
X    }
X  }
X  else {
X    &reply("I could not find your address:\n" .
X           "  $ADDRESS\n" .
X           "in the mailing list file.  Perhaps you subscribed from a\n" .
X           "different address or you need to send an UNDIGEST request\n" .
X           "instead.\n");
X    &doOther($DIGESTFILE);
X  }
X}
X
X
X
Xsub doUnd {
X  &reply("\nCommand: UNDIGEST\n");
X  if ($GLOBALS{'DIGEST'}) {
X    if (&inList($DIGESTFILE)) {
X      if (&removeFromList($DIGESTFILE)) {
X        &reply("You have been removed from the digest list.\n");
X        &log("UND $ADDRESS");
X      }
X      else {
X        &reply("There was an error on this end and you were not removed\n" .
X               "from the digest list.\n");
X      }
X    }
X    else {
X      &reply("I could not find your address:\n" .
X             "  $ADDRESS\n" .
X             "in the digest list file.  Perhaps you subscribed from a\n" .
X             "different address or you need to send a UNSUBSCRIBE request\n" .
X             "instead.\n");
X      &doOther($LISTFILE);
X    }
X  }
X  else {
X    &reply("The mailing list does not support a digest format.\n");
X  }
X}
X
Xsub doOther {
X  local($OTHERFILE) = @_;
X  if (-e $OTHERFILE) {
X    &reply("\nAttempting to find you in the other list...\n");
X    if (&inList($OTHERFILE)) {
X      if (&removeFromList($OTHERFILE)) {
X        &reply("\nYou have been removed from the other list.\n");
X        &log("OTH $ADDRESS");
X      }
X      else {
X        &reply("There was an error on this end and you were not removed\n" .
X               "from the other list.\n");
X      }
X    }
X    else {
X      &reply("\nI could not find your address:\n" .
X             "  $ADDRESS\n" .
X             "in the other list file.  Please feel free to contact the list owner,\n" .
X             "  $LISTOWNER@$FQDN\n" .
X             "for help.\n");
X    }
X  }
X  else {
X    &reply("The mailing list does not support the other format.\n");
X  }
X}
X
X
X
Xsub inList {
X  local($l) = @_;
X  if (open(LF,"<$l")) {
X    while (<LF>) {
X      if (/$ADDRESS/i) {
X        close(LF);
X        return 1;
X      }
X    }
X    close(LF);
X  }
X  else {
X    &error("inList: could not open $l: $!\n");
X  }
X  return 0;
X}
X
X
X
Xsub addToList {
X  local($l) = @_;
X  local($ret) = 0;
X  local($cmd);
X  if (&lockFile($l)) {
X    $cmd = `cp $l $l.bak`; chop($cmd); 
X    chmod 0664, "$l.bak";
X    chown $GLOBALS{'USER'},$GLOBALS{'GROUP'}, "$l.bak";
X
X    &error("addToList: Cmd returned $cmd\n") if $cmd;
X    if (open(LF,">>$l")) {
X      print LF $ADDRESS . $LF;
X      close(LF);
X      $ret = 1;
X    }
X    else {
X      &error("addToList: could not open $l: $!\n");
X    }
X    &unlockFile($l);
X  }
X  else {
X    &error("addToList: $l locked!\n");
X  }
X  return $ret;
X}
X
X
Xsub removeFromList {
X  local($l) = @_;
X  local($ret) = 0;
X  if (&lockFile($l)) {
X## move the list to a temp file, then open it and copy it back, except for
X## the user we want to delete.
X    system("cp $l $TMPFILE");
X    if (open(TMPFILE, "<$TMPFILE")) {
X      if (open(LF, ">$l")) {
X        while (<TMPFILE>) {
X          print LF $_ if (!/$ADDRESS/i)
X        }
X        close(TMPFILE);
X        close(LF);
X        $ret = 1;
X      }
X      else {
X        &error("removeFromList: could not open $l: $!\n");
X        close(TMPFILE);
X      }
X    }
X    else {
X      &error("removeFromList: could not open $TMPFILE: $!\n");
X    }
X    &unlockFile($l);
X  }
X  else {
X    &error("removeFromList: $l locked!\n");
X  }
X  return $ret;
X}
X
X
X
Xsub doDir {
X  local($v) = @_;
X  local($cmd, $p) = split(/ /,$v);
X## Show the user a directory listing.
X  &reply("\nCommand: DIR $p\n");
X  if (-d $LISTARC) {
X    local($dir);
X    if ($WDIR) {
X      $dir = $LISTARC . "/" . $WDIR . "/" . $p;
X    }
X    else {
X      $dir = $LISTARC . "/" . $p;
X    }
X    if ($dir =~ /\.\./) {
X      &reply("Invalid directory.\n");
X      &log("INVDIR $ADDRESS, $p");
X      &error("$ADDRESS attempted a dir of $p.\n");
X    }
X    elsif (-d $dir) {
X## Good directory case
X      if (opendir(DIR, $dir)) {
X        rewinddir(DIR);
X        local(@dirl) = readdir(DIR);
X        local($fpath);
X        @dirl = sort(@dirl);
X        while ($dirl[0]) {
X          if ($dirl[0] !~ /^\./ && $dirl[0] !~ /^\.\./) {
X            $fpath = $dir . "/" . $dirl[0];
X            if (-d $fpath) { &reply("d    $dirl[0]\n"); }
X            else { &reply("f    $dirl[0]\n"); }
X          }
X          shift(@dirl);
X        }
X        closedir(DIR);
X        &reply("--\n");
X        &log("DIR $ADDRESS");
X      }
X      else {
X        &reply("There was an error on this end.\n");
X        &error("doDir: could not opendir $dir: $!\n");
X      }
X    }
X    else {
X      &reply("Invalid directory.\n");
X    }
X  }
X  else {
X    &reply("The list does not currently support archives.\n");
X  }
X}
X
X
Xsub doCD {
X  local($v) = @_;
X  local($cmd, $cdir) = split(/ /, $v);
X  &reply("\nCommand: CD $cdir\n");
X  if (-d $LISTARC) {
X    if ($cdir) {
X      if ($cdir !~ /\.\./) {
X        local($wpath);
X        if ($WDIR) {
X          $wpath = $LISTARC . "/" . $WDIR . "/" . $cdir;
X        }
X        else {
X          $wpath = $LISTARC . "/" . $cdir;
X        }
X        if (-d $wpath) {
X          $WDIR = $cdir;
X          &reply("Working directory set to $WDIR\n");
X        }
X        else {
X          &reply("Invalid directory.\n");
X        }
X      }
X      else {
X        &reply("Invalid directory specification.\n");
X        &log("INVDIR $ADDRESS, $cdir");
X        &error("$ADDRESS attempted a CD $cdir.\n");
X      }
X    }
X    else {
X      $WDIR = "";
X      &reply("Working directory reset.\n");
X    }
X  }
X  else {
X    &reply("The list does not currently support archives.\n");
X  }
X}
X
X
X
Xsub doGet {
X## Get a file from the archives.
X  local($v) = @_;
X  local($cmd, $f) = split(/ /, $v);
X  &reply("\nCommand: GET $f\n");
X  if (-d $LISTARC) {
X    local($fname);
X    if ($WDIR) {
X      $fname = $LISTARC . "/" . $WDIR . "/" . $f;
X    }
X    else {
X      $fname = $LISTARC . "/" . $f;
X    }
X    local(@ff) = split(/\//, $f);
X    local($ffname) = $ff[$#ff];   # This is the "absolute" name of the file
X    if ($f !~ /\.\./ && $f !~ /^\./) {
X      if (-e $fname && ! -d $fname && -r $fname) {
X        &reply("Sending file '$ffname'...\n");
X        &sendFile($fname, $ffname);
X        &reply("--\n");
X        &log("GET $ADDRESS, $f");
X      }
X      else {
X        &reply("That file does not exist.\n");
X      }
X    }
X    else {
X      &reply("Invalid file specification.\n");
X      &error("$ADDRESS attempted GET $f\n");
X      &log("INVFIL $ADDRESS, $f");
X    }
X  }
X  else {
X    &reply("The list does not currently support archives.\n");
X  }
X  system("rm -f $TMPFILE.st");
X}
X
X
Xsub sendFile {
X  local($f, $ff) = @_;
X  if (-T $f) {
X    &sendTextFile($f, $ff);
X  }
X  else {
X    &sendBinaryFile($f, $ff);
X  }
X}
X
Xsub sendTextFile {
X# We have been passed the name of a text file to send.  This text file
X# might be very large (greater than 30000 bytes).  If so, break it up
X# and send the chunks.
X  local($f, $ff) = @_;
X  local($fsize) = -s $f;
X  &debug("$f, $ff, $fsize\n");
X  if ($fsize > 30000) {
X    local($pnum) = 1;
X    local($maxnum) = int(0.5 + ($fsize / 30000));
X    local($psize) = -1;
X    if (open(TF, "<$f")) {
X      while (<TF>) {
X        if ($psize < 0) { # first time through
X          &debug("psize == -1\n");
X          &startST($pnum, $maxnum, $ff);
X          &printST("---cut here---\n");
X          $psize = 0;
X        }
X        $psize += length();
X        &printST($_);
X        if ($psize > 30000) {
X          &printST("---cut here---\n");
X          &sendST();
X          &reply("Package $pnum/$maxnum sent.\n");
X          $pnum += 1;
X          $psize = -1;
X          &debug("Sending $ff $pnum/$maxnum $psize\n");
X        }
X      }
X      close(TF);
X      if ($STOPEN) {
X        &sendST();
X        &reply("Package $pnum/$maxnum sent.\n");
X      }
X    }
X    else {
X      &reply("There was an error on this end.\n");
X      &error("sendTextFile: could not open $f: $!\n");
X    }
X  }
X  else {
X    &startST(1, 1, $ff);
X    if (open(TF,"<$f")) {
X      while (<TF>) { &printST($_) };
X      close(TF);
X      &sendST();
X    }
X    else {
X      &error("sendTextFile: could not open $f: $!\n");
X      &reply("There was an error on this end.\n");
X    }
X  }
X}
X
X
Xsub startST {
X  local($n1, $n2, $ff) = @_;
X  local($FQDN) = $GLOBALS{'FQDN'};
X  if ($STOPEN) { return 1; }
X  if (open(ST, ">$TMPFILE.st")) {
X    &debug("$TMPFILE.st opened.\n");
X    print ST "From: $LISTREQUEST@$FQDN\n";
X    print ST "To: $ADDRESS\n";
X    print ST "Sender: $LISTOWNER@$FQDN\n";
X    print ST "Errors-To: $LISTOWNER@$FQDN\n";
X    print ST "Subject: $ff: Part $n1 of $n2\n\n";
X    $STOPEN = 1;
X  }
X  else {
X    &error("startST: could not open $TMPFILE.st: $!\n");
X  }
X  return $STOPEN;
X}
X
X
X
Xsub printST {
X  local($s) = @_;
X  if ($STOPEN) {
X    print ST $s;
X  }
X}
X
X
Xsub sendST {
X  local($cmd, $ret);
X  if ($STOPEN) {
X    close(ST);
X    $cmd = $GLOBALS{'SENDMAIL'} . "  " . $ADDRESS;
X    $ret = `cat $TMPFILE.st | $cmd`; chop($ret);
X    if ($ret) {
X      &error("sendST: cmd returned $ret.\n");
X    }
X    &debug("Sent text file.\n");
X  }
X  system("rm -f $TMPFILE.st");
X  $STOPEN = 0;
X}
X
X
Xsub sendBinaryFile {
X# When sending a binary file, we must convert it to text with an encoding
X# program, then send it to the user as text.
X  local($f, $ff) = @_;
X  local($cmd, $ret);
X  &reply("This file was converted with uuencode.\n");
X  $cmd = $GLOBALS{'ENCODE'} . " " . $f . " " . $ff . " > " . $TMPFILE;
X# The cmd should look like "uuencode /dir/dir/filename filename > tmpfile"
X  $ret = `$cmd`; chop($ret);
X  if ($ret) {
X    &error($GLOBALS{'ENCODE'} . " returned $ret.\n");
X    &reply("There was an error on this end.\n");
X  }
X  &sendTextFile($TMPFILE, $ff);
X}
X
X
X
Xsub doHel {
X  &reply("\nCommand: HELP\n");
X  if (-e $HELP) {
X    if (open(HELP,"<$HELP")) {
X      while (<HELP>) { &reply($_); }
X      close(HELP);
X      &log("HEL $ADDRESS");
X    }
X    else {
X      &reply("There was an error on this end.\n");
X      &error("doHel: could not open $HELP: $!\n");
X    }
X  }
X  else {
X    &reply("Sorry, there is no help currently available.\n");
X  }
X}
X
X
X
Xsub lockFile {
X  local($f) = @_;
X  local($lockf) = $f . ".LOCK";
X  local($backoff) = 5;
X  local($boc) = 0;
X
X  while (-e $lockf) {
X    sleep(5);
X    $boc++;
X    if ($boc > $backoff) {
X      &debug("Lock file collision!\n");
X      return 0;
X    }
X  }
X  system("touch $lockf");
X  chmod 0664, $lockf;
X  chown $GLOBALS{'USER'}, $GLOBALS{'GROUP'}, $lockf;
X  $LOCKS{$lockf} = 1;
X  &debug("$f locked.\n");
X  return 1;
X}
X
X
Xsub unlockFile {
X  local($f) = @_;
X  local($lockf) = $f . ".LOCK";
X  if (-e $lockf) { system("rm -f $lockf"); }
X  delete $LOCKS{$lockf};
X  &debug("$f unlocked.\n");
X}
X
Xsub log {
X  local($s) = @_;
X  return if (!$GLOBALS{'LOG'});
X  if (&lockFile($LOGFILE)) {
X    if (! -e $LOGFILE) {
X      system("touch $LOGFILE");
X      chown $GLOBALS{'USER'}, $GLOBALS{'GROUP'}, $LOGFILE;
X      chmod 0664,$LOGFILE;
X    }
X    if (open(LOGFILE, ">>$LOGFILE")) {
X      local($date) = `date`; chop($date);
X      print LOGFILE $date . ": " . $s . "\n";
X      close(LOGFILE);
X    }
X    else {
X      &error("log: could not open $LOGFILE: $!\n");
X    }
X    &unlockFile($LOGFILE);
X  }
X  else {
X    &error("log: $LOGFILE locked!\n");
X  }
X}
X
X
Xsub debug {
X  local($s) = @_;
X  print $s if $GLOBALS{'DEBUG'};
X}
END_OF_FILE
if test 26042 -ne `wc -c <'bmw'`; then
    echo shar: \"'bmw'\" unpacked with wrong size!
fi
chmod +x 'bmw'
# end of 'bmw'
fi
if test -f 'bmw.cf' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'bmw.cf'\"
else
echo shar: Extracting \"'bmw.cf'\" \(49 characters\)
sed "s/^X//" >'bmw.cf' <<'END_OF_FILE'
Xbasedir = /home/bmw
Xdigest = 1
Xlog = 1
Xdebug = 0
END_OF_FILE
if test 49 -ne `wc -c <'bmw.cf'`; then
    echo shar: \"'bmw.cf'\" unpacked with wrong size!
fi
# end of 'bmw.cf'
fi
if test -f 'bmwmail' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'bmwmail'\"
else
echo shar: Extracting \"'bmwmail'\" \(4867 characters\)
sed "s/^X//" >'bmwmail' <<'END_OF_FILE'
X#! /usr/bin/perl
X#
X# bmwmail - send a message to the mailing list
X#
X#
X
X$USAGE = "$0 <listname>\n";
X# This program expects a mail-format message on standard input.
X
Xif ($#ARGV != 0) { die ("$USAGE"); }
X
X$LISTNAME = $ARGV[0];
X$LISTNAME =~ tr/A-Z/a-z/;  # Convert everything to lower case for now.
X
X%GLOBALS = ();
X
X## We need to set the hostname.  You this is done by querying hostname and
X## domainname.  You can override this here or set your FQDN global explicitly
X## in the bmw.cf file.
X
X$HOSTNAME = `hostname`; chop($HOSTNAME);
X$DOMAINNAME = `domainname`; chop($DOMAINNAME);
X
X## Path to bmw.cf - CHANGE THIS FOR YOUR SYSTEM
X#$BMWCF = "/etc/bmw.cf";
X$BMWCF = "/home/bmw/bmw.cf";
X
X## Process the config file and learn about all the globals.
X## FORMAT OF CONFIG FILE:
X##   Each line of the config file should have the format
X##     <globalname> = <value>
X##   for example,
X##     BASEDIR = /usr/local/lib/bmw
X##
X## I now set the defaults.  You should override these in your bmw.cf file.
X##
X$GLOBALS{'SENDMAIL'} = "/usr/lib/sendmail";
X$GLOBALS{'SENDMAILOPTS'} = "";
X$GLOBALS{'ENCODE'} = "/usr/bin/uuencode";
X$GLOBALS{'COMPRESS'} = "/bin/gzip";
X$GLOBALS{'COMPSUFFIX'} = ".gz";
X$GLOBALS{'ARCDIR'} = "/home/ftp/pub/lists";
X$GLOBALS{'ARCOWNER'} = "ftp";
X$GLOBALS{'OWNER'} = "postmaster";
X$GLOBALS{'DEBUG'} = 0;
X$GLOBALS{'LOG'} = 0;
X$GLOBALS{'MAXGETS'} = 5;
X$GLOBALS{'PREFERFTP'} = 0;
X$GLOBALS{'TRACE'} = 0;
X$GLOBALS{'BASEDIR'} = "/home/bmw";
X$GLOBALS{'FQDN'} = $HOSTNAME . "." . $DOMAINNAME;
X$GLOBALS{'DIGEST'} = 0;
X$GLOBALS{'TMPDIR'} = "/var/tmp";
X$GLOBALS{'USER'} = "bmw";
X$GLOBALS{'GROUP'} = "bin";
X
X
X@VALIDGLOBALS = ("SENDMAIL", "SENDMAILOPTS", "TMPDIR", "ENCODE",
X                 "COMPRESS", "COMPSUFFIX", "ARCDIR", "ARCOWNER",
X                 "OWNER", "DEBUG", "LOG", "MAXGETS", "PREFERFTP",
X                 "TRACE", "BASEDIR", "FQDN", "DIGEST", "USER", "GROUP");
X
X
X
Xsub validGlobal {
X## Determine if the string is a valid global reference.
X  local($s) = @_;
X  local($i);
X  for ($i=0; $i<=$#VALIDGLOBALS; $i++) {
X    if ($s eq $VALIDGLOBALS[$i]) { return 1; }
X  }
X  return 0;
X}
X
X
X
X## Load the globals from the cf file.
Xif (-e $BMWCF) {
X  local($lno) = 0;
X  local(@LINE);
X  open(CF, "<$BMWCF") || die "Cannot open $BMWCF: $!\n";
X  while (<CF>) {
X    $lno++;
X    chop;
X    tr/\t //d;
X    @LINE = split("=");
X    if (!$LINE[0]) { die "ERROR in $BMWCF line $lno; $_\n"; }
X    $LINE[0] =~ tr/a-z/A-Z/;
X    if (!&validGlobal($LINE[0])) {
X      die "ERROR in $BMWCF line $lno; Unknown global \"$LINE[0]\"\n";
X    }
X## Good.  We have a valid global
X    $GLOBALS{$LINE[0]} = $LINE[1];
X  }
X}
Xelse {
X  die "$BMWCF does not exist!\n";
X}
X
Xif ($GLOBALS{'DEBUG'}) {
X  local($key);
X  foreach $key (keys %GLOBALS) {
X    print "$key = $GLOBALS{$key}",$LF;
X  }
X}
X
X
X## Internal global values.  Do not fiddle with these without good reason.
X$VERSION = "5.0";
X$LISTDIR = $GLOBALS{'BASEDIR'} . "/$LISTNAME";
X$DIGESTFILE = "$LISTDIR/digest";
X$TOCFILE = "$LISTDIR/digesttoc";
X$LOGFILE = "$LISTDIR/log";
X$MESSAGE = "$LISTDIR/message";
X$HELP = $GLOBALS{'BASEDIR'} . "/help";
X
X$TMPFILE = $GLOBALS{'TMPDIR'} . "/bmw$$";
X$REPLYFILE = "$TMPFILE.reply";
X$TMPLOGFILE = "$TMPFILE.log";
X
X$LISTOWNER = "$LISTNAME-owner";
X$LISTREQUEST = "$LISTNAME-request";
X
X@ERRORS = ();
X%LOCKS = ();
X
X##
X## Exit routines.  These make sure everything is cleaned up.
X##
Xsub finish {
X  local($rc) = @_;
X  local($i);
X  system("rm -f $TMPFILE $REPLYFILE");
X  if ($#ERRORS > -1) {
X    open(T, ">$TMPFILE");
X    print T "From: $LISTOWNER\n";
X    print T "Subject: Errors from $LISTREQUEST\n";
X    print T "To: $LISTOWNER\n\n";
X    for ($i = 0; $i <= $#ERRORS; $i++) {
X      print T $ERRORS[$i],$LF;
X    }
X    close(T);
X    system("cat $TMPFILE | $GLOBALS{'SENDMAIL'} \'$LISTOWNER\'");
X    system("rm -f $TMPFILE")
X  }
X  exit($rc);
X}
X
X
Xsub DIE {
X  local($msg) = @_;
X  print STDERR "$msg",$LF;
X  $ERRORS[$#ERRORS+1] = $msg;
X  local($key);
X  foreach $key (keys %LOCKS) {
X    system("rm -f $key");
X  }
X  &finish(-1);
X}
X
X
X# Test for things that must exist.
X
X&DIE("$LISTDIR does not exist!\n") if (! -d $LISTDIR);
X
X$FQDN = $GLOBALS{'FQDN'};
X
X$DATE = `date`; chop($DATE);
X
X@TEXT = ();
X
X$LISTADDR = "$LISTNAME@$FQDN (The $LISTNAME mailing list)";
X$MAILADDR = "$LISTNAME-mail";
X
X$MAILCMD = "$GLOBALS{'SENDMAIL'} $MAILADDR";
X
Xopen(T,">$TMPFILE") || &DIE("$TMPFILE: $!\n");
X
Xshift;
X
X$HEADERS = 1;
X$l = 0;
Xwhile (<>)
X{
X  if ($HEADERS)
X  {
X    $l = length;
X    if ($l > 1)
X    {
X      if (!/^Reply-To:/ && !/^Errors-To:/)
X      {
X        print T $_;
X      }
X    }
X    else
X    {
X      print T "Reply-To: $LISTADDR\n";
X      print T "Errors-To: $LISTOWNER@$FQDN\n";
X      print T "X-bmw: Black Marble Wombat Version $VERSION\n";
X      print T $_;
X      $HEADERS = 0;
X    }
X  }
X  else
X  {
X    print T $_;
X  }
X}
X
Xclose(T);
X
X#open(T,"<$TMPFILE");
X#while (<T>) { print "3 " . $_; }
X#close(T);
Xsystem("cat $TMPFILE | $MAILCMD");
X
X&finish();
END_OF_FILE
if test 4867 -ne `wc -c <'bmwmail'`; then
    echo shar: \"'bmwmail'\" unpacked with wrong size!
fi
chmod +x 'bmwmail'
# end of 'bmwmail'
fi
if test -f 'digestify' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'digestify'\"
else
echo shar: Extracting \"'digestify'\" \(5114 characters\)
sed "s/^X//" >'digestify' <<'END_OF_FILE'
X#! /usr/bin/perl
X
X# Digestify -- accept a mail message on standard input.  Strip the headers
X# and cat into the digest file.  Build the digest table of contents file
X# as well.
X
X$USAGE = "$0 <listname>\n";
X
Xif ($#ARGV != 0) { die ("$USAGE"); }
X
X$LISTNAME = $ARGV[0];
X$LISTNAME =~ tr/A-Z/a-z/;  # Convert everything to lower case for now.
X
X%GLOBALS = ();
X
X## We need to set the hostname.  You this is done by querying hostname and
X## domainname.  You can override this here or set your FQDN global explicitly
X## in the bmw.cf file.
X
X$HOSTNAME = `hostname`; chop($HOSTNAME);
X$DOMAINNAME = `domainname`; chop($DOMAINNAME);
X
X## Path to bmw.cf - CHANGE THIS FOR YOUR SYSTEM
X#$BMWCF = "/etc/bmw.cf";
X$BMWCF = "/home/bmw/bmw.cf";
X
X## Process the config file and learn about all the globals.
X## FORMAT OF CONFIG FILE:
X##   Each line of the config file should have the format
X##     <globalname> = <value>
X##   for example,
X##     BASEDIR = /usr/local/lib/bmw
X##
X## I now set the defaults.  You should override these in your bmw.cf file.
X##
X$GLOBALS{'SENDMAIL'} = "/usr/lib/sendmail";
X$GLOBALS{'SENDMAILOPTS'} = "";
X$GLOBALS{'ENCODE'} = "/usr/bin/uuencode";
X$GLOBALS{'COMPRESS'} = "/bin/gzip";
X$GLOBALS{'COMPSUFFIX'} = ".gz";
X$GLOBALS{'ARCDIR'} = "/home/ftp/pub/lists";
X$GLOBALS{'ARCOWNER'} = "ftp";
X$GLOBALS{'OWNER'} = "postmaster";
X$GLOBALS{'DEBUG'} = 0;
X$GLOBALS{'LOG'} = 0;
X$GLOBALS{'MAXGETS'} = 5;
X$GLOBALS{'PREFERFTP'} = 0;
X$GLOBALS{'TRACE'} = 0;
X$GLOBALS{'BASEDIR'} = "/usr/local/lib/bmw";
X$GLOBALS{'FQDN'} = $HOSTNAME . "." . $DOMAINNAME;
X$GLOBALS{'DIGEST'} = 0;
X$GLOBALS{'TMPDIR'} = "/var/tmp";
X$GLOBALS{'USER'} = "bmw";
X$GLOBALS{'GROUP'} = "bin";
X
X
X@VALIDGLOBALS = ("SENDMAIL", "SENDMAILOPTS", "TMPDIR", "ENCODE",
X                 "COMPRESS", "COMPSUFFIX", "ARCDIR", "ARCOWNER",
X                 "OWNER", "DEBUG", "LOG", "MAXGETS", "PREFERFTP",
X                 "TRACE", "BASEDIR", "FQDN", "DIGEST", "USER", "GROUP");
X
X
X
Xsub validGlobal {
X## Determine if the string is a valid global reference.
X  local($s) = @_;
X  local($i);
X  for ($i=0; $i<=$#VALIDGLOBALS; $i++) {
X    if ($s eq $VALIDGLOBALS[$i]) { return 1; }
X  }
X  return 0;
X}
X
X
X
X## Load the globals from the cf file.
Xif (-e $BMWCF) {
X  local($lno) = 0;
X  local(@LINE);
X  open(CF, "<$BMWCF") || die "Cannot open $BMWCF: $!\n";
X  while (<CF>) {
X    $lno++;
X    chop;
X    tr/\t //d;
X    @LINE = split("=");
X    if (!$LINE[0]) { die "ERROR in $BMWCF line $lno; $_\n"; }
X    $LINE[0] =~ tr/a-z/A-Z/;
X    if (!&validGlobal($LINE[0])) {
X      die "ERROR in $BMWCF line $lno; Unknown global \"$LINE[0]\"\n";
X    }
X## Good.  We have a valid global
X    $GLOBALS{$LINE[0]} = $LINE[1];
X  }
X}
Xelse {
X  die "$BMWCF does not exist!\n";
X}
X
Xif ($GLOBALS{'DEBUG'}) {
X  local($key);
X  foreach $key (keys %GLOBALS) {
X    print "$key = $GLOBALS{$key}",$LF;
X  }
X}
X
X
X## Internal global values.  Do not fiddle with these without good reason.
X$VERSION = "5.0";
X$LISTDIR = $GLOBALS{'BASEDIR'} . "/$LISTNAME";
X$DIGESTFILE = "$LISTDIR/digest";
X$TOCFILE = "$LISTDIR/digesttoc";
X$LOGFILE = "$LISTDIR/log";
X$MESSAGE = "$LISTDIR/message";
X$HELP = "$LISTDIR/help";
X$WDIR = "";                   # working directory suffix for gets
X
X$TMPFILE = $GLOBALS{'TMPDIR'} . "/bmw$$";
X$REPLYFILE = "$TMPFILE.reply";
X$TMPLOGFILE = "$TMPFILE.log";
X
X$LISTOWNER = "$LISTNAME-owner";
X$LISTREQUEST = "$LISTNAME-request";
X
X@ERRORS = ();
X%LOCKS = ();
X
X##
X## Exit routines.  These make sure everything is cleaned up.
X##
Xsub finish {
X  local($rc) = @_;
X  local($i);
X  system("rm -f $TMPFILE");
X  if ($#ERRORS > -1) {
X    open(T, ">$TMPFILE");
X    print T "Subject: Errors from $LISTREQUEST\n";
X    print T "From: $LISTOWNER\n";
X    print T "To: $LISTOWNER\n\n";
X    for ($i = 0; $i <= $#ERRORS; $i++) {
X      print T $ERRORS[$i],$LF;
X    }
X    close(T);
X    system("cat $TMPFILE | $GLOBALS{'SENDMAIL'} \'$LISTOWNER\'");
X    system("rm -f $TMPFILE")
X  }
X  exit($rc);
X}
X
X
Xsub DIE {
X  local($msg) = @_;
X  print STDERR "$msg",$LF;
X  $ERRORS[$#ERRORS+1] = $msg;
X  local($key);
X  foreach $key (keys %LOCKS) {
X    system("rm -f $key");
X  }
X  &finish(-1);
X}
X
X
X# Test for things that must exist.
X
X&DIE("$LISTDIR does not exist!\n") if (! -d $LISTDIR);
X
X
X$FROM = "";
X$SUBJECT = "";
X$MID = "";
X$DATE = `date`; chop($DATE);
X
Xif (! -e $DIGESTFILE)
X{
X  system("touch $DIGESTFILE");
X  chown $GLOBALS{'USER'}, $GLOBALS{'GROUP'}, $DIGESTFILE;
X  chmod 0664 , $DIGESTFILE;
X}
X
Xshift;
X
Xopen(TF,">>$DIGESTFILE") || &DIE("Could not open $DIGESTFILE: $!\n");
X
X$HEADERS = 1;
X$INFROM = 0;
Xwhile (<>)
X{
X if ($HEADERS) {
X    if ($INFROM && /^\s/) {
X      local($morefrom);
X      $morefrom = $1 if m/^\s*(.*)\n/;
X      $FROM = $FROM . $morefrom . "\n";
X    }
X    else {
X      $INFROM = 0;
X      if (/^Subject:/) { $SUBJECT = $_; }
X      if (/^From:/) { $FROM = $_; }
X      if (/^Message-Id:/) { $MID = $_; }
X      if ($_ eq "\n") {
X## Blank line ends headers.
X        $HEADERS = 0;
X        print TF "\n" . $SUBJECT;
X        print TF $FROM;
X        print TF "Date: " . $DATE . "\n";
X        print TF $MID;
X        print TF "Reply-To: $LISTNAME@$GLOBALS{'FQDN'}\n\n";
X      }
X    }
X  }
X  else {
X    print TF $_;
X  }
X}
X
Xclose(TF);
X&finish();
X
END_OF_FILE
if test 5114 -ne `wc -c <'digestify'`; then
    echo shar: \"'digestify'\" unpacked with wrong size!
fi
chmod +x 'digestify'
# end of 'digestify'
fi
if test -f 'help' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'help'\"
else
echo shar: Extracting \"'help'\" \(663 characters\)
sed "s/^X//" >'help' <<'END_OF_FILE'
X*********
X* HELP! *
X*********
X
XThe Black Marble Wombat mailing list manager program (version 5.0).
X
XThe BMW will accept the following commands:
X
XSUBscribe
X  -- subscribe to the mailing list.
X
XUNSUBscribe
X  -- unsubscribe from the mailing list.
X
XDIGEST
X  -- subscribe to the digest list.  This will automatically remove you from 
X     the mailing list.
X
XUNDIGEST
X  -- unsubscribe from the digest list.
X
XWHO
X  -- list all the subscribers to the mailing list.
X
XDIRectory
X  -- list the files currently in the list archive.
X
XCD
X  -- change your current working directory.
X
XGET file
X  -- get a file (via mail) from the list archive.
X
XPING
X  -- pings the list manager.
X
END_OF_FILE
if test 663 -ne `wc -c <'help'`; then
    echo shar: \"'help'\" unpacked with wrong size!
fi
# end of 'help'
fi
if test -f 'kron' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'kron'\"
else
echo shar: Extracting \"'kron'\" \(756 characters\)
sed "s/^X//" >'kron' <<'END_OF_FILE'
X# (kron installed on Tue Jun 28 22:58:37 1994)
X# (Cron version -- $Header: crontab.c,v 2.2 90/07/18 00:23:56 vixie Exp $)
X# (cron.bmw installed on Mon Jun 27 21:11:22 1994)
X# (Cron version -- $Header: crontab.c,v 2.2 90/07/18 00:23:56 vixie Exp $)
X#5 0 1  * * /usr/export/bmw/bmwarc torg
X0 0 * * * /home/bmw/senddigest masterbook
X0 0 * * * /home/bmw/senddigest wfrp
X0 0 * * * /home/bmw/senddigest edge
X0 0 * * * /home/bmw/senddigest capra
X0 0 * * * /home/bmw/senddigest arm
X0 0 * * * /home/bmw/senddigest alex
X
X# Do the archive thang!
X5 0 1 * * /home/bmw/mkarchive masterbook
X10 0 1 * * /home/bmw/mkarchive wfrp
X15 0 1 * * /home/bmw/mkarchive edge
X20 0 1 * * /home/bmw/mkarchive capra
X25 0 1 * * /home/bmw/mkarchive arm
X30 0 1 * * /home/bmw/mkarchive alex
END_OF_FILE
if test 756 -ne `wc -c <'kron'`; then
    echo shar: \"'kron'\" unpacked with wrong size!
fi
# end of 'kron'
fi
if test -f 'ledit' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ledit'\"
else
echo shar: Extracting \"'ledit'\" \(887 characters\)
sed "s/^X//" >'ledit' <<'END_OF_FILE'
X#! /usr/bin/perl
X#
X# List Editor ledit/dedit
X#
X# Edit the mailing list in a safe manner.
X#
X
X$USAGE = "$0 <list>";
X
Xif ($#ARGV < 0) {
X  print "USAGE: $USAGE\n";
X  exit(-1);
X}
X
X$EDITOR = "/usr/bin/vi";
X$SDIR = "/home/bmw";
X$EDITDIGEST = 0;
X$LISTNAME = $ARGV[0];
Xif ($LISTNAME eq "-d") {
X  $EDITDIGEST = 1;
X  shift;
X  $LISTNAME = $ARGV[0];
X}
X$LISTNAME =~ tr/A-Z/a-z/;
X
X$LISTDIR = $SDIR . "/" . $LISTNAME;
Xif (! -d $LISTDIR) {
X  print "I can't find $LISTDIR\n";
X  exit(-2);
X}
X
X$LISTFILE = $LISTDIR . "/subscribers";
X$LISTFILE = $LISTFILE . ".d" if ($EDITDIGEST);
X
Xif (! -e $LISTFILE) {
X  print "I can't file $LISTFILE\n";
X  exit(-3);
X}
X
X$LOCKFILE = $LISTFILE . ".LOCK";
X
Xif (-e $LOCKFILE) {
X  print "$LISTFILE is currently locked by some other process.\n";
X  exit(-4);
X}
X
Xsystem("touch $LOCKFILE");
Xsystem("$EDITOR $LISTFILE");
Xsystem("rm $LOCKFILE");
Xprint "Have a nice day.\n";
Xexit(0);
X
X
END_OF_FILE
if test 887 -ne `wc -c <'ledit'`; then
    echo shar: \"'ledit'\" unpacked with wrong size!
fi
chmod +x 'ledit'
# end of 'ledit'
fi
if test -f 'message' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'message'\"
else
echo shar: Extracting \"'message'\" \(120 characters\)
sed "s/^X//" >'message' <<'END_OF_FILE'
XYou have connected with the LISTNAME mailing list manager.
X
XFor more information, send a HELP message to 
XLISTREQUEST.
X
END_OF_FILE
if test 120 -ne `wc -c <'message'`; then
    echo shar: \"'message'\" unpacked with wrong size!
fi
# end of 'message'
fi
if test -f 'mkarchive' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'mkarchive'\"
else
echo shar: Extracting \"'mkarchive'\" \(4951 characters\)
sed "s/^X//" >'mkarchive' <<'END_OF_FILE'
X#! /usr/bin/perl
X
X# mkarchive -- move the currently monthly file to the archive.
X
X$USAGE = "$0 <listname>\n";
X
Xif ($#ARGV != 0) { die ("$USAGE"); }
X
X$LISTNAME = $ARGV[0];
X$LISTNAME =~ tr/A-Z/a-z/;  # Convert everything to lower case for now.
X
X%GLOBALS = ();
X
X## We need to set the hostname.  You this is done by querying hostname and
X## domainname.  You can override this here or set your FQDN global explicitly
X## in the bmw.cf file.
X
X$HOSTNAME = `hostname`; chop($HOSTNAME);
X$DOMAINNAME = `domainname`; chop($DOMAINNAME);
X
X## Path to bmw.cf - CHANGE THIS FOR YOUR SYSTEM
X#$BMWCF = "/etc/bmw.cf";
X$BMWCF = "/home/bmw/bmw.cf";
X
X## Process the config file and learn about all the globals.
X## FORMAT OF CONFIG FILE:
X##   Each line of the config file should have the format
X##     <globalname> = <value>
X##   for example,
X##     BASEDIR = /usr/local/lib/bmw
X##
X## I now set the defaults.  You should override these in your bmw.cf file.
X##
X$GLOBALS{'SENDMAIL'} = "/usr/lib/sendmail";
X$GLOBALS{'SENDMAILOPTS'} = "";
X$GLOBALS{'ENCODE'} = "/usr/bin/uuencode";
X$GLOBALS{'COMPRESS'} = "/bin/gzip";
X$GLOBALS{'COMPSUFFIX'} = ".gz";
X$GLOBALS{'ARCDIR'} = "/home/ftp/pub/lists";
X$GLOBALS{'ARCOWNER'} = "ftp";
X$GLOBALS{'OWNER'} = "postmaster";
X$GLOBALS{'DEBUG'} = 0;
X$GLOBALS{'LOG'} = 0;
X$GLOBALS{'MAXGETS'} = 5;
X$GLOBALS{'PREFERFTP'} = 0;
X$GLOBALS{'TRACE'} = 0;
X$GLOBALS{'BASEDIR'} = "/usr/local/lib/bmw";
X$GLOBALS{'FQDN'} = $HOSTNAME . "." . $DOMAINNAME;
X$GLOBALS{'DIGEST'} = 0;
X$GLOBALS{'TMPDIR'} = "/var/tmp";
X$GLOBALS{'USER'} = "bmw";
X$GLOBALS{'GROUP'} = "bin";
X
X
X@VALIDGLOBALS = ("SENDMAIL", "SENDMAILOPTS", "TMPDIR", "ENCODE",
X                 "COMPRESS", "COMPSUFFIX", "ARCDIR", "ARCOWNER",
X                 "OWNER", "DEBUG", "LOG", "MAXGETS", "PREFERFTP",
X                 "TRACE", "BASEDIR", "FQDN", "DIGEST", "USER", "GROUP");
X
X
X
Xsub validGlobal {
X## Determine if the string is a valid global reference.
X  local($s) = @_;
X  local($i);
X  for ($i=0; $i<=$#VALIDGLOBALS; $i++) {
X    if ($s eq $VALIDGLOBALS[$i]) { return 1; }
X  }
X  return 0;
X}
X
X
X
X## Load the globals from the cf file.
Xif (-e $BMWCF) {
X  local($lno) = 0;
X  local(@LINE);
X  open(CF, "<$BMWCF") || die "Cannot open $BMWCF: $!\n";
X  while (<CF>) {
X    $lno++;
X    chop;
X    tr/\t //d;
X    @LINE = split("=");
X    if (!$LINE[0]) { die "ERROR in $BMWCF line $lno; $_\n"; }
X    $LINE[0] =~ tr/a-z/A-Z/;
X    if (!&validGlobal($LINE[0])) {
X      die "ERROR in $BMWCF line $lno; Unknown global \"$LINE[0]\"\n";
X    }
X## Good.  We have a valid global
X    $GLOBALS{$LINE[0]} = $LINE[1];
X  }
X}
Xelse {
X  die "$BMWCF does not exist!\n";
X}
X
Xif ($GLOBALS{'DEBUG'}) {
X  local($key);
X  foreach $key (keys %GLOBALS) {
X    print "$key = $GLOBALS{$key}",$LF;
X  }
X}
X
X
X## Internal global values.  Do not fiddle with these without good reason.
X$VERSION = "5.0";
X$LISTDIR = $GLOBALS{'BASEDIR'} . "/$LISTNAME";
X$LISTARCDIR = $GLOBALS{'ARCDIR'} . "/$LISTNAME";
X$DIGESTFILE = "$LISTDIR/digest";
X$TOCFILE = "$LISTDIR/digesttoc";
X$LOGFILE = "$LISTDIR/log";
X$MONTHLY = "$LISTDIR/monthly";
X$MESSAGE = "$LISTDIR/message";
X$HELP = "$LISTDIR/help";
X$WDIR = "";                   # working directory suffix for gets
X
X$TMPFILE = $GLOBALS{'TMPDIR'} . "/bmw$$";
X$REPLYFILE = "$TMPFILE.reply";
X$TMPLOGFILE = "$TMPFILE.log";
X
X$LISTOWNER = "$LISTNAME-owner";
X$LISTREQUEST = "$LISTNAME-request";
X
X@ERRORS = ();
X%LOCKS = ();
X
X##
X## Exit routines.  These make sure everything is cleaned up.
X##
Xsub finish {
X  local($rc) = @_;
X  local($i);
X  system("rm -f $TMPFILE");
X  if ($#ERRORS > -1) {
X    open(T, ">$TMPFILE");
X    print T "Subject: Errors from $LISTREQUEST\n";
X    print T "From: $LISTOWNER\n";
X    print T "To: $LISTOWNER\n\n";
X    for ($i = 0; $i <= $#ERRORS; $i++) {
X      print T $ERRORS[$i],$LF;
X    }
X    close(T);
X    system("cat $TMPFILE | $GLOBALS{'SENDMAIL'} \'$LISTOWNER\'");
X    system("rm -f $TMPFILE")
X  }
X  exit($rc);
X}
X
X
Xsub DIE {
X  local($msg) = @_;
X  print STDERR "$msg",$LF;
X  $ERRORS[$#ERRORS+1] = $msg;
X  local($key);
X  foreach $key (keys %LOCKS) {
X    system("rm -f $key");
X  }
X  &finish(-1);
X}
X
X
X# Test for things that must exist.
X
X&DIE("$LISTDIR does not exist!\n") if (! -d $LISTDIR);
X&DIE("$LISTARCDIR does not exist!\n") if (! -d $LISTARCDIR);
X
X
X$FROM = "";
X$SUBJECT = "";
X$MID = "";
X$DATE = `date`; chop($DATE);
X
Xif (! -e $MONTHLY)
X{
X  system("touch $MONTHLY");
X  chown $GLOBALS{'USER'}, $GLOBALS{'GROUP'}, $DIGESTFILE;
X  chmod 0664 , $MONTHLY;
X  &finish(0);
X}
X
Xshift;
X
X
X# Copy monthly to tmpdir with a new name
X$thismonth = (Jan,Feb,Mar,Apr,Jun,Jul,Aug,Sep,Oct,Nov,Dec)[(localtime)[4]];
X$thisyear = (localtime)[5];
X
X$FNAME = $LISTNAME . "-" . $thismonth . "-" . $thisyear;
X$FNAMECMP = $LISTARCDIR . "/" . $FNAME . $GLOBALS{'COMPSUFFIX'};
X$TMPF = $TMPFILE . $GLOBALS{'COMPSUFFIX'};
X
X$COMPRESSOR = $GLOBALS{'COMPRESS'} . " " . $TMPFILE;
X
Xsystem("mv $MONTHLY $TMPFILE");
X#print $COMPRESSOR . "\n";
Xsystem("$GLOBALS{'COMPRESS'} $TMPFILE");
Xsystem("mv $TMPF $FNAMECMP");
Xchmod 0444, $FNAMECMP;
X
X&finish();
X
END_OF_FILE
if test 4951 -ne `wc -c <'mkarchive'`; then
    echo shar: \"'mkarchive'\" unpacked with wrong size!
fi
chmod +x 'mkarchive'
# end of 'mkarchive'
fi
if test -f 'senddigest' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'senddigest'\"
else
echo shar: Extracting \"'senddigest'\" \(6331 characters\)
sed "s/^X//" >'senddigest' <<'END_OF_FILE'
X#! /usr/bin/perl
X#
X# senddigest - send a mailing list digest file
X#
X#
X
X$USAGE = "$0 <listname>\n";
X
Xif ($#ARGV != 0) { die ("$USAGE"); }
X
X$LISTNAME = $ARGV[0];
X$LISTNAME =~ tr/A-Z/a-z/;  # Convert everything to lower case for now.
X
X%GLOBALS = ();
X
X## We need to set the hostname.  You this is done by querying hostname and
X## domainname.  You can override this here or set your FQDN global explicitly
X## in the bmw.cf file.
X
X$HOSTNAME = `hostname`; chop($HOSTNAME);
X$DOMAINNAME = `domainname`; chop($DOMAINNAME);
X
X## Path to bmw.cf - CHANGE THIS FOR YOUR SYSTEM
X#$BMWCF = "/etc/bmw.cf";
X$BMWCF = "/home/bmw/bmw.cf";
X
X## Process the config file and learn about all the globals.
X## FORMAT OF CONFIG FILE:
X##   Each line of the config file should have the format
X##     <globalname> = <value>
X##   for example,
X##     BASEDIR = /usr/local/lib/bmw
X##
X## I now set the defaults.  You should override these in your bmw.cf file.
X##
X$GLOBALS{'SENDMAIL'} = "/usr/lib/sendmail";
X$GLOBALS{'SENDMAILOPTS'} = "";
X$GLOBALS{'ENCODE'} = "/usr/bin/uuencode";
X$GLOBALS{'COMPRESS'} = "/bin/gzip";
X$GLOBALS{'COMPSUFFIX'} = ".gz";
X$GLOBALS{'ARCDIR'} = "/home/ftp/pub/lists";
X$GLOBALS{'ARCOWNER'} = "ftp";
X$GLOBALS{'OWNER'} = "postmaster";
X$GLOBALS{'DEBUG'} = 0;
X$GLOBALS{'LOG'} = 0;
X$GLOBALS{'MAXGETS'} = 5;
X$GLOBALS{'PREFERFTP'} = 0;
X$GLOBALS{'TRACE'} = 0;
X$GLOBALS{'BASEDIR'} = "/usr/local/lib/bmw";
X$GLOBALS{'FQDN'} = $HOSTNAME . "." . $DOMAINNAME;
X$GLOBALS{'DIGEST'} = 0;
X$GLOBALS{'TMPDIR'} = "/var/tmp";
X$GLOBALS{'USER'} = "bmw";
X$GLOBALS{'GROUP'} = "bin";
X
X
X@VALIDGLOBALS = ("SENDMAIL", "SENDMAILOPTS", "TMPDIR", "ENCODE",
X                 "COMPRESS", "COMPSUFFIX", "ARCDIR", "ARCOWNER",
X                 "OWNER", "DEBUG", "LOG", "MAXGETS", "PREFERFTP",
X                 "TRACE", "BASEDIR", "FQDN", "DIGEST", "USER", "GROUP");
X
X
X
Xsub validGlobal {
X## Determine if the string is a valid global reference.
X  local($s) = @_;
X  local($i);
X  for ($i=0; $i<=$#VALIDGLOBALS; $i++) {
X    if ($s eq $VALIDGLOBALS[$i]) { return 1; }
X  }
X  return 0;
X}
X
X
X
X## Load the globals from the cf file.
Xif (-e $BMWCF) {
X  local($lno) = 0;
X  local(@LINE);
X  open(CF, "<$BMWCF") || die "Cannot open $BMWCF: $!\n";
X  while (<CF>) {
X    $lno++;
X    chop;
X    tr/\t //d;
X    @LINE = split("=");
X    if (!$LINE[0]) { die "ERROR in $BMWCF line $lno; $_\n"; }
X    $LINE[0] =~ tr/a-z/A-Z/;
X    if (!&validGlobal($LINE[0])) {
X      die "ERROR in $BMWCF line $lno; Unknown global \"$LINE[0]\"\n";
X    }
X## Good.  We have a valid global
X    $GLOBALS{$LINE[0]} = $LINE[1];
X  }
X}
Xelse {
X  die "$BMWCF does not exist!\n";
X}
X
Xif ($GLOBALS{'DEBUG'}) {
X  local($key);
X  foreach $key (keys %GLOBALS) {
X    print "$key = $GLOBALS{$key}",$LF;
X  }
X}
X
X
X## Internal global values.  Do not fiddle with these without good reason.
X$VERSION = "5.0";
X$LISTDIR = $GLOBALS{'BASEDIR'} . "/$LISTNAME";
X$DIGESTFILE = "$LISTDIR/digest";
X$TOCFILE = "$LISTDIR/digesttoc";
X$LOGFILE = "$LISTDIR/log";
X$MESSAGE = "$LISTDIR/message";
X$HELP = "$LISTDIR/help";
X$WDIR = "";                   # working directory suffix for gets
X
X$TMPFILE = $GLOBALS{'TMPDIR'} . "/bmw$$";
X$REPLYFILE = "$TMPFILE.reply";
X$TMPLOGFILE = "$TMPFILE.log";
X
X$LISTOWNER = "$LISTNAME-owner";
X$LISTREQUEST = "$LISTNAME-request";
X
X@ERRORS = ();
X%LOCKS = ();
X
X##
X## Exit routines.  These make sure everything is cleaned up.
X##
Xsub finish {
X  local($rc) = @_;
X  local($i);
X  system("rm -f $TMPFILE $REPLYFILE");
X  if ($#ERRORS > -1) {
X    open(T, ">$TMPFILE");
X    print T "From: $LISTOWNER\n";
X    print T "Subject: Errors from $LISTREQUEST\n";
X    print T "To: $LISTOWNER\n\n";
X    for ($i = 0; $i <= $#ERRORS; $i++) {
X      print T $ERRORS[$i],$LF;
X    }
X    close(T);
X    system("cat $TMPFILE | $GLOBALS{'SENDMAIL'} \'$LISTOWNER\'");
X    system("rm -f $TMPFILE")
X  }
X  exit($rc);
X}
X
X
Xsub DIE {
X  local($msg) = @_;
X  print STDERR "$msg",$LF;
X  $ERRORS[$#ERRORS+1] = $msg;
X  local($key);
X  foreach $key (keys %LOCKS) {
X    system("rm -f $key");
X  }
X  &finish(-1);
X}
X
X
X# Test for things that must exist.
X
X&DIE("$LISTDIR does not exist!\n") if (! -d $LISTDIR);
X
X## If no digest, then no send
X&finish() if (! -e $DIGESTFILE);
X
X$FQDN = $GLOBALS{'FQDN'};
X
X$DATE = `date`; chop($DATE);
X
X@TEXT = ();
X
X$LISTADDR = "$LISTNAME@$FQDN (The $LISTNAME digest)";
X$DIGESTADDR = "$LISTNAME-digest-mail";
X
X$MAILCMD = "$GLOBALS{'SENDMAIL'} $DIGESTADDR";
X$MAXSIZE = 30000;
X
Xsystem("mv $DIGESTFILE $TMPFILE");
Xopen(D,"<$TMPFILE") || &DIE("$TMPFILE: $!\n");
Xopen(T,">$REPLYFILE") || &DIE("$REPLYFILE: $!\n");
X
Xsub Headers
X{
X  local($p) = @_;
X  print T "To: $DIGESTADDR@$FQDN\n";
X  print T "From: $LISTADDR\n";
X  print T "Reply-To: $LISTADDR\n";
X  print T "Errors-To: $LISTOWNER@$FQDN\n";
X#  print T "Sender: $LISTOWNER@$FQDN\n";
X  print T "Subject: Digest: $LISTNAME for $DATE ($p)\n";
X  print T "\nThis is the automated digest of the $LISTNAME mailing list.\n";
X  print T "You may send replies to $LISTADDR.\n";
X  print T "Your replies will be automatically included in the next digest.\n";
X  print T "If you experience problems, please contact $LISTOWNER@$FQDN.\n";
X}
X
X$parts = 1;
X&Headers($parts);
Xprint T "\nTABLE OF CONTENTS:\n";
X
X$subjects = 0;
X
Xwhile (<D>)
X{
X  if (/^Subject:\s*/)
X  {
X    local($s) = $_;
X    chop($s);
X    $s =~ s/^Subject:\s*//;
X    $subjects += 1;
X    if (length($s) > 50)
X    {
X      $s = substr($s,0,50) . "...";
X    }
X    $s = sprintf("%3d. %s", $subjects, $s);
X    print T $s,"\n";
X    $subjects = sprintf("%3d", $subjects);
X#    $TEXT[$#TEXT+1] = "\n------- TOPIC: $subjects -------\n";
X     $TEXT[$#TEXT+1] = "\n";
X  }
X#print "1 " . $_;
X  $TEXT[$#TEXT+1] = $_;
X}
Xclose(D);
X#print "#######\n";
Xsystem("rm -f $TMPFILE");
X
X$size = 0;
X
Xif ($subjects)
X{
X  local($i);
X  print T "\n";
X  for($i=0;$i<=$#TEXT;$i++) {
X    $size += length($TEXT[$i]);
X#print "2 " . $TEXT[$i];
X    if ($size >= $MAXSIZE)
X    {
X      # Look for the next topic line
X      $_ = $TEXT[$i];
X      if (/^From:/)
X      {
X	# Close down, and send...
X        close(T);
X	system("cat $REPLYFILE | $MAILCMD");
X	open(T,">$REPLYFILE") || &DIE("$REPLYFILE: $!\n");
X	$parts += 1;
X	$size = 0;
X	&Headers($parts);
X	print T "\n";
X      }
X    }
X    print T $TEXT[$i]; 
X  }
X  close(T);
X
X  # The digested file 
X#print "-----------------\n";
X#open(T,"<$REPLYFILE");
X#while (<T>) { print "3 " . $_; }
X#close(T);
X  system("cat $REPLYFILE | $MAILCMD");
X}
Xelse
X{
X  close(T);
X}
X&finish();
END_OF_FILE
if test 6331 -ne `wc -c <'senddigest'`; then
    echo shar: \"'senddigest'\" unpacked with wrong size!
fi
chmod +x 'senddigest'
# end of 'senddigest'
fi
if test ! -d 'test' ; then
    echo shar: Creating directory \"'test'\"
    mkdir 'test'
fi
if test -f 'test/digest' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'test/digest'\"
else
echo shar: Extracting \"'test/digest'\" \(1713 characters\)
sed "s/^X//" >'test/digest' <<'END_OF_FILE'
X
XSubject: testing 3
XFrom: clay
XDate: Mon Jun 27 16:09:47 CDT 1994
XMessage-Id: <m0qINw2-0006IQC@monsta.metronet.com>
XReply-To: test@monsta.metronet.com
X
XTESTING 3
X
XSubject: test4
XFrom: caly
XDate: Mon Jun 27 16:13:30 CDT 1994
XMessage-Id: <m0qINzd-0006IQC@monsta.metronet.com>
XReply-To: test@monsta.metronet.com
X
Xtest 4
X
XSubject: test 2
XFrom: bmw
XDate: Mon Jun 27 16:22:15 CDT 1994
XMessage-Id: <m0qINkH-0006IPC@monsta.metronet.com>
XReply-To: test@monsta.metronet.com
X
Xtesting 2
X
XSubject: This is a test!
XFrom: clay (Clay Luther)
XDate: Mon Jun 27 17:48:05 CDT 1994
XMessage-Id: <m0qIPT3-0006ILC@monsta.metronet.com>
XReply-To: test@monsta.metronet.com
X
XTESTING!
X
X-- 
XClay Luther                           clay@monsta.metronet.com
XSystems Administrator                 clay@gojira.monsta.com
XMonsta, Inc.                          (214) 407-0029
X
XSubject: Testing
XFrom: bmw (Black Marble Wombat)
XDate: Mon Jun 27 17:48:34 CDT 1994
XMessage-Id: <m0qIPTX-0006ILC@monsta.metronet.com>
XReply-To: test@monsta.metronet.com
X
XTesting!
X
XSubject: testing
XFrom: clay (Clay Luther)
XDate: Mon Jun 27 17:57:36 CDT 1994
XMessage-Id: <m0qIPcD-0006INC@monsta.metronet.com>
XReply-To: test@monsta.metronet.com
X
Xtest 4
X
XSubject: I am TESTING!
XFrom: root (root)
XDate: Mon Jun 27 20:29:09 CDT 1994
XMessage-Id: <m0qIRyo-0006IPC@monsta.metronet.com>
XReply-To: test@monsta.metronet.com
X
XThis is a test!
X
XSubject: testing
XFrom: root (root)
XDate: Mon Jun 27 20:35:23 CDT 1994
XMessage-Id: <m0qIS4u-0006IRC@monsta.metronet.com>
XReply-To: test@monsta.metronet.com
X
Xtesting mail.
X
XSubject: testing
XFrom: root (root)
XDate: Mon Jun 27 20:37:16 CDT 1994
XMessage-Id: <m0qIS6l-0006ISC@monsta.metronet.com>
XReply-To: test@monsta.metronet.com
X
Xtesting forward.
END_OF_FILE
if test 1713 -ne `wc -c <'test/digest'`; then
    echo shar: \"'test/digest'\" unpacked with wrong size!
fi
# end of 'test/digest'
fi
if test -f 'test/help' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'test/help'\"
else
echo shar: Extracting \"'test/help'\" \(39 characters\)
sed "s/^X//" >'test/help' <<'END_OF_FILE'
XHELP!
X
Xsub
Xuns
Xdir
Xcd
Xget
Xping
Xdig
Xund
END_OF_FILE
if test 39 -ne `wc -c <'test/help'`; then
    echo shar: \"'test/help'\" unpacked with wrong size!
fi
# end of 'test/help'
fi
if test -f 'test/log' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'test/log'\"
else
echo shar: Extracting \"'test/log'\" \(273 characters\)
sed "s/^X//" >'test/log' <<'END_OF_FILE'
XSun May 22 02:28:31 CDT 1994: PIN clay
XSun May 22 02:34:21 CDT 1994: SUB clay
XSun May 22 02:34:22 CDT 1994: DIG clay
XSun May 22 02:34:23 CDT 1994: SUB clay
XSun May 22 02:34:23 CDT 1994: UNS clay
XSun May 22 02:43:39 CDT 1994: PIN clay
XSun May 22 02:48:50 CDT 1994: PIN clay
END_OF_FILE
if test 273 -ne `wc -c <'test/log'`; then
    echo shar: \"'test/log'\" unpacked with wrong size!
fi
# end of 'test/log'
fi
if test -f 'test/message' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'test/message'\"
else
echo shar: Extracting \"'test/message'\" \(323 characters\)
sed "s/^X//" >'test/message' <<'END_OF_FILE'
XYou have connected with the TEST mailing list manager.
X
XFor more information, send a HELP message to this address.
X
XImportant Addresses:
X  Posting address              -> test@gojira.monsta.com
X  Subscription/Archive address -> test-request@gojira.monsta.com
X  Owner's address              -> test-owner@gojira.monsta.com
X
END_OF_FILE
if test 323 -ne `wc -c <'test/message'`; then
    echo shar: \"'test/message'\" unpacked with wrong size!
fi
# end of 'test/message'
fi
if test -f 'test/subscribers' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'test/subscribers'\"
else
echo shar: Extracting \"'test/subscribers'\" \(23 characters\)
sed "s/^X//" >'test/subscribers' <<'END_OF_FILE'
Xtest-owner
Xtest-digest
END_OF_FILE
if test 23 -ne `wc -c <'test/subscribers'`; then
    echo shar: \"'test/subscribers'\" unpacked with wrong size!
fi
# end of 'test/subscribers'
fi
if test -f 'test/subscribers.bak' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'test/subscribers.bak'\"
else
echo shar: Extracting \"'test/subscribers.bak'\" \(11 characters\)
sed "s/^X//" >'test/subscribers.bak' <<'END_OF_FILE'
Xtest-owner
END_OF_FILE
if test 11 -ne `wc -c <'test/subscribers.bak'`; then
    echo shar: \"'test/subscribers.bak'\" unpacked with wrong size!
fi
# end of 'test/subscribers.bak'
fi
if test -f 'test/subscribers.d' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'test/subscribers.d'\"
else
echo shar: Extracting \"'test/subscribers.d'\" \(11 characters\)
sed "s/^X//" >'test/subscribers.d' <<'END_OF_FILE'
Xtest-owner
END_OF_FILE
if test 11 -ne `wc -c <'test/subscribers.d'`; then
    echo shar: \"'test/subscribers.d'\" unpacked with wrong size!
fi
# end of 'test/subscribers.d'
fi
if test -f 'test/subscribers.d.bak' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'test/subscribers.d.bak'\"
else
echo shar: Extracting \"'test/subscribers.d.bak'\" \(11 characters\)
sed "s/^X//" >'test/subscribers.d.bak' <<'END_OF_FILE'
Xtest-owner
END_OF_FILE
if test 11 -ne `wc -c <'test/subscribers.d.bak'`; then
    echo shar: \"'test/subscribers.d.bak'\" unpacked with wrong size!
fi
# end of 'test/subscribers.d.bak'
fi
if test -f 'testmsg' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'testmsg'\"
else
echo shar: Extracting \"'testmsg'\" \(181 characters\)
sed "s/^X//" >'testmsg' <<'END_OF_FILE'
X
X
XSubject: TESTING FIX
XFrom: clay (Clay Luther)
XDate: Tue Jun  7 16:29:13 CDT 1994
XMessage-Id: <m0qB8hr-0006ILC@monsta.metronet.com>
XReply-To: test@monsta.metronet.com
X
XTESTING FIX
END_OF_FILE
if test 181 -ne `wc -c <'testmsg'`; then
    echo shar: \"'testmsg'\" unpacked with wrong size!
fi
# end of 'testmsg'
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have the archive.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
