#! /usr/local/bin/perl
##---------------------------------------------------------------------------##
##  File:
##      MHonArc
##  Author:
##      Earl Hood       ehood@isogen.com
##  Contributers:
##	Steve Pacenka <sp17@cornell.edu>,
##	Achim Bohnet <ach@rosat.mpe-garching.mpg.de>,
##	Achille Petrilli <Achille.Petrilli@MACMAIL.CERN.CH>
##  Description:
##      MHonArc is a Perl program to convert mail to HTML.  See
##	accompany documentation for full details.
##---------------------------------------------------------------------------##
##    MHonArc -- Internet mail-to-HTML converter
##    Copyright (C) 1995,1996	Earl Hood, ehood@isogen.com
##
##    This program is free software; you can redistribute it and/or modify
##    it under the terms of the GNU General Public License as published by
##    the Free Software Foundation; either version 2 of the License, or
##    (at your option) any later version.
##
##    This program is distributed in the hope that it will be useful,
##    but WITHOUT ANY WARRANTY; without even the implied warranty of
##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##    GNU General Public License for more details.
##
##    You should have received a copy of the GNU General Public License
##    along with this program; if not, write to the Free Software
##    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
##---------------------------------------------------------------------------##

#############################################################################
#############################################################################
package main;

$VERSION = "1.2.3";		# Version number

##---------------------------------------------------------------------------##
##				Main routine				     ##
##---------------------------------------------------------------------------##

&prestart();
&get_cli_opts();
&doit();

##---------------------------------------------------------------------------##
##                              SubRoutines                                  ##
##---------------------------------------------------------------------------##
##---------------------------------------------------------------------------
##	prestart() does some initialization stuff.
##
sub prestart {
    ##	Turn off buffered I/O to terminal
    select(STDOUT);  $| = 1;

    unshift(@INC, 'lib');	# Should I leave this line in?

    ##	Check what system we are executing under
    require 'osinit.pl'	   || &error("ERROR: Unable to require osinit.pl");
    &'OSinit();

    ##	Require key libraries
    require 'newgetopt.pl' || &error("ERROR: Unable to require newgetopt.pl");
    require 'timelocal.pl' || &error("ERROR: Unable to require timelocal.pl");
    require 'readmail.pl'  || &error("ERROR: Unable to require readmail.pl");
    require 'mhdb.pl'      || &error("ERROR: Unable to require mhdb.pl");
    require 'mhutil.pl'    || &error("ERROR: Unable to require mhutil.pl");
    require 'mhinit.pl'    || &error("ERROR: Unable to require mhinit.pl");
}
##---------------------------------------------------------------------------
##	get_cli_opts() is responsible for grabbing command-line options
##	and also settings the resource file.
##
sub get_cli_opts {
    local($tmp, @array);

    &error(qq{Try "$PROG -help" for usage information}) unless
    &NGetOpt(
	"add",		# Add a message to archive
	"dbfile=s",	# Database/state filename for mhonarc archive
	"docurl=s",	# URL to mhonarc documentation
	"editidx",	# Change index page layout only
	"footer=s",	# File containing user text for bottom of index page
	"force",	# Perform archive operation even if unable to lock
	"genidx",	# Generate an index based upon archive contents
	"header=s",	# File containing user text for top of index page
	"idxfname=s",	# File name of index page
	"idxsize=i",	# Maximum number of messages shown in indexes
	"lockdelay=i",	# Time delay in seconds between lock tries
	"locktries=i",	# Number of tries in locking an archive
	"mailtourl=s",	# URL to use for e-mail address hyperlinks
	"maxsize=i",	# Maximum number of messages allowed in archive
	"mbox",		# Use mailbox format		(ignored now)
	"mh",		# Use MH mail folders format	(ignored now)
	"msgsep=s",	# Message separator for mailbox files
	"nodoc",	# Do not print link to doc at end of index page
	"nomailto",	# Do not add in mailto links for e-mail addresses
	"nonews",	# Do not add links to newsgroups
	"noreverse",	# List messages in normal order
	"nosort",	# Do not sort
	"nothread",	# Do not create threaded index
	"notreverse",	# List oldest thread first
	"notsubsort",	# Do sort listed threads by subject; sort by date
	"nourl",	# Do not make URL hyperlinks
	"outdir=s",	# Destination of HTML files
	"quiet",	# No status messages while running
	"rcfile=s",	# Resource file for mhonarc
	"reverse",	# List messages in reverse order
	"revsort",	# Perform reverse sorting on dates
	"rmm",		# Remove messages from an archive
	"savemem",	# Write message data while processing
	"scan",		# List out archive contents to terminal
	"single",	# Convert a single message to HTML
	"sort",		# Sort messages in increasing date order
	"subsort",	# Sort message by subject
	"tidxfname=s",	# File name of threaded index page
	"time",		# Print processing time
	"title=s",	# Title of index page
	"ttitle=s",	# Title of threaded index page
	"thread",	# Create threaded index
	"tlevels=i",	# Maximum # of nested lists in threaded index
	"treverse",	# List most recent thread first
	"tsubsort",	# Sort listed threads by subject
	"umask=i",	# Set umask of process

	"help"		# A brief usage message
    );
    &usage() if defined($opt_help);

    ## These options have NO resource file equivalent.
    ##
    $ADD     = defined($opt_add);
    $RMM     = defined($opt_rmm);
    $SCAN    = defined($opt_scan);
    $QUIET   = defined($opt_quiet);
    $EDITIDX = defined($opt_editidx);
    if (defined($opt_genidx)) {
	$IDXONLY  = 1;  $QUIET = 1;
    } else {
	$IDXONLY  = 0;
    }
    if (defined($opt_single)) {
	$SINGLE  = 1;  $QUIET = 1;
    } else {
	$SINGLE = 0;
    }
    &usage() unless ($#ARGV >= 0) || $ADD || $SINGLE ||
		    $EDITIDX || $SCAN || $IDXONLY;
    $FMTFILE = $opt_rcfile   if $opt_rcfile;
    $LOCKTRIES = $opt_locktries  if ($opt_locktries > 0);
    $LOCKDELAY = $opt_lockdelay  if ($opt_lockdelay > 0);
    $FORCELOCK = defined($opt_force);

    ## These options must be grabbed before reading the database file
    ## since these options may tells us where the database file is.
    ##
    $OUTDIR  = $opt_outdir    if $opt_outdir;
	if (!(-r $OUTDIR) || !(-w $OUTDIR) || !(-x $OUTDIR)) {
	    &error("ERROR: Unable to access $OUTDIR");
	}
    $DBFILE  = $opt_dbfile    if $opt_dbfile;

    ## Create lockfile
    ##
    $LOCKFILE  = "${OUTDIR}${DIRSEP}${LOCKFILE}";
    if (!$SINGLE && !&create_lock_file($LOCKFILE, 1, 0, 0)) {
	print STDOUT "Trying to lock mail archive in $OUTDIR ...\n"
	    unless $QUIET;
	if (!&create_lock_file($LOCKFILE,
			       $LOCKTRIES-1,
			       $LOCKDELAY,
			       $FORCELOCK)) {
	    &error("ERROR: Unable to create $LOCKFILE after $LOCKTRIES tries");
	}
    }
    ## Race condition exists: if process is terminated before termination
    ## handlers set, lock file will not get removed.
    ##
    &set_handler();

    ## Check if we need to access database file
    ##
    if ($ADD || $EDITIDX || $RMM || $SCAN || $IDXONLY) {
	$DBFILE = ".mail2html.db"
	    unless (-e "${OUTDIR}${DIRSEP}${DBFILE}") ||
		   (!-e "${OUTDIR}${DIRSEP}.mail2html.db");
	if (-e "${OUTDIR}${DIRSEP}${DBFILE}") {
	    eval q%require "${OUTDIR}${DIRSEP}${DBFILE}"%;
	    &error("ERROR: Database read error of ",
		   "${OUTDIR}${DIRSEP}${DBFILE}:\n\t$@")  if $@;
	    $OldNOSORT   = $NOSORT;
	    $OldSUBSORT  = $SUBSORT;
	    $OldREVSORT	 = $REVSORT;
	    if ($VERSION ne $DbVERSION) {
		warn "Warning: Database ($DbVERSION) != ",
		     "program ($VERSION) version.\n";
	    }
	}
	if ($#ARGV < 0) { $ADDSINGLE = 1; }	# See if adding single mesg
	else { $ADDSINGLE = 0; }
	$ADD = 'STDIN';
    }
    $OldTITLE = $TITLE;
    $OldTHREAD = $THREAD;
    $OldTTITLE = $TTITLE;

    ## Get highest message number
    if ($ADD) {
	$LastMsgNum = &get_last_msg_num();
    } else {
	$LastMsgNum = -1;
    }

    ## Remove lock file if scanning messages
    ##
    if ($SCAN) {
	&clean_up();
    }

    ##	Read resource file (I initially used the term 'format file').
    ##	Look for resource in outdir if not absolute path or not
    ##	existing according to current value.
    ##
    if ($FMTFILE) {
	$FMTFILE = "${OUTDIR}${DIRSEP}$FMTFILE"
	    unless ($FMTFILE =~ m%^/%) || (-e $FMTFILE);
	&read_fmt_file($FMTFILE);
    }

    ## Require MIME filters and other libraries
    ##
    unshift(@INC, @PerlINC);
    if (!$EDITIDX && !$SCAN && !$RMM) {
	&remove_dups(*Requires);
	print STDOUT "Requiring MIME filter libraries ...\n"  unless $QUIET;
	foreach (@Requires) {
	    print STDOUT "\t$_\n"  unless $QUIET;
	    eval qq{require "$_"};
	    &error("ERROR: Unable to require ${_}:\n\t$@")  if $@;
	}
	## Register message header formatter to readmail library
	$readmail'FormatHeaderFunc = "main'htmlize_header";
    }

    ## Get other command-line options
    ##
    $DBFILE	= $opt_dbfile     if $opt_dbfile; # Set again to override db
    $DOCURL	= $opt_docurl     if $opt_docurl;
    $FOOTER	= $opt_footer     if $opt_footer;
    $FROM	= $opt_msgsep     if $opt_msgsep;
    $HEADER	= $opt_header     if $opt_header;
    $IDXNAME	= $opt_idxfname   if $opt_idxfname;
    $IDXSIZE	= $opt_idxsize    if $opt_idxsize;
	$IDXSIZE *= -1  if $IDXSIZE < 0;
    $OUTDIR	= $opt_outdir     if $opt_outdir; # Set again to override db
    $MAILTOURL	= $opt_mailtourl  if $opt_mailtourl;
    $MAXSIZE	= $opt_maxsize    if $opt_maxsize;
	$MAXSIZE = ""  if $MAXSIZE < 0;
    $TIDXNAME	= $opt_tidxfname  if $opt_tidxfname;
    $TITLE	= $opt_title      if $opt_title;
    $TLEVELS	= $opt_tlevels    if $opt_tlevels;
    $TTITLE	= $opt_ttitle     if $opt_ttitle;

    $NODOC	= 1  if defined($opt_nodoc);
    $NOMAILTO	= 1  if defined($opt_nomailto);
    $NONEWS	= 1  if defined($opt_nonews);
    $NOURL	= 1  if defined($opt_nourl);
    $SLOW	= 1  if defined($opt_savemem);
    $THREAD	= 1  if defined($opt_thread);
    $THREAD	= 0  if defined($opt_nothread);
    $TREVERSE	= 1  if defined($opt_treverse);
    $TREVERSE	= 0  if defined($opt_notreverse);
    $TSUBSORT	= 1  if defined($opt_tsubsort);
    $TSUBSORT	= 0  if defined($opt_notsubsort);

    ##	Set umask
    if ($UNIX) {
	$UMASK = $opt_umask      if $opt_umask;
	eval 'umask oct($UMASK)';
    }

    ##	Get sort method
    ##
    $SORTCHNG = 0;
    if (defined($opt_nosort)) {		# No sorting takes highest precedence
	$NOSORT = 1;  $SUBSORT = 0;
    } elsif (defined($opt_subsort)) {	# Subject sort
	$SUBSORT = 1;  $NOSORT = 0;
    } elsif (defined($opt_sort)) {	# Regular sort is last
	$NOSORT = 0;  $SUBSORT = 0;
    }
    ## Check for listing order
    ##
    if (defined($opt_noreverse)) {
	$REVSORT = 0;
    } elsif (defined($opt_reverse) || defined($opt_revsort)) {
	$REVSORT = 1;
    }
    $SORTCHNG = 1  if (($OldNOSORT != $NOSORT) ||
		       ($OldSUBSORT != $SUBSORT) ||
		       ($OldREVSORT != $REVSORT));

    ## Check if all messages must be updated
    ##
    if ($SORTCHNG || $RMM || $EDITIDX ||
	($OldTITLE ne $TITLE) ||
	($OldTTITLE ne $TTITLE) ||
	($THREAD != $OldTHREAD)) {
	$UPDATE_ALL = 1;
    } else {
	$UPDATE_ALL = 0;
    }

    ##	Check index resources
    $IDXPGBEG = join('',
		     '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">',
		     "\n",
		     "<HTML>\n",
		     "<HEAD>\n",
		     '<TITLE>$IDXTITLE$</TITLE>', "\n",
		     "</HEAD>\n",
		     "<BODY>\n",
		     '<H1>$IDXTITLE$</H1>', "\n")
		unless $IDXPGBEG;
    $IDXPGEND = join('',
		     "</BODY>\n",
		     "</HTML>\n")
		unless $IDXPGEND;

    if ($THREAD) {
	$LIBEG  = join('', "<UL>\n",
			   '<LI><A HREF="$TIDXFNAME$">Thread Index</A></LI>',
			   "\n",
			   "</UL>\n<HR>\n<UL>\n")
		    unless $LIBEG;
	$THEAD  = join('', "<UL>\n",
			   '<LI><A HREF="$IDXFNAME$">Main Index</A></LI>',
			   "\n",
			   "</UL>\n<HR>\n")
		    unless $THEAD;
	$TLITXT = '($NUMFOLUP$) <STRONG>$SUBJECT:40$</STRONG>, ' .
		  '<EM>$FROMNAME$</EM>'
		    unless $TLITXT;
	$TIDXPGBEG = join('',
			 "<!DOCTYPE HTML PUBLIC ",
			 qq{"-//IETF//DTD HTML 2.0//EN">\n},
			 "<HTML>\n",
			 "<HEAD>\n",
			 '<TITLE>$TIDXTITLE$</TITLE>', "\n",
			 "</HEAD>\n",
			 "<BODY>\n",
			 '<H1>$TIDXTITLE$</H1>', "\n")
		    unless $TIDXPGBEG;
	$TIDXPGEND = join('',
			 "</BODY>\n",
			 "</HTML>\n")
		    unless $TIDXPGEND;

    } else {
	$LIBEG = "<HR>\n<UL>\n"  unless $LIBEG;
    }
    $LIEND  = "</UL>\n"
		unless $LIEND;
    $LITMPL = join('', '<LI><STRONG>$SUBJECT$</STRONG>', "\n",
		       '<UL><LI><EM>From</EM>: $FROM$</LI></UL>' , "\n",
		       "</LI>\n")
		unless $LITMPL;

    ##	Message resources
    $MSGPGBEG = join('',
		     '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">',
		     "\n",
		     "<HTML>\n",
		     "<HEAD>\n",
		     '<TITLE>$SUBJECTNA:72$</TITLE>', "\n",
		     '<LINK REV="made" HREF="mailto:$FROMADDR$">', "\n",
		     "</HEAD>\n",
		     "<BODY>\n")
		unless $MSGPGBEG;
    $MSGPGEND = join('',
		     "</BODY>\n",
		     "</HTML>\n")
		unless $MSGPGEND;

    # 	Check for next/prev message link resources.  Must check for
    # 	older variables to preserve capatibility.
    $PREVBL	= '[Prev]'	unless $PREVBL;
    $NEXTBL	= '[Next]'	unless $NEXTBL;
    $IDXBL	= '[Index]'	unless $IDXBL;
    $TIDXBL	= '[Thread]'	unless $TIDXBL;
    $NEXTFL	= 'Next'	unless $NEXTFL;
    $PREVFL	= 'Prev'	unless $PREVFL;

    $NEXTBUTTON = '<A HREF="$NEXTMSG$">' . $NEXTBL . '</A>'
	unless $NEXTBUTTON;
    $PREVBUTTON = '<A HREF="$PREVMSG$">' . $PREVBL . '</A>'
	unless $PREVBUTTON;
    $NEXTBUTTONIA = ''
	unless $PREVBUTTONIA;
    $PREVBUTTONIA = ''
	unless $PREVBUTTONIA;

    $NEXTLINK	= join('',
		       "<LI>$NEXTFL: <STRONG>",
		       '<A HREF="$NEXTMSG$">$NEXTSUBJECT$</A>',
		       "</STRONG></LI>\n")  unless $NEXTLINK;
    $NEXTLINKIA = ''  unless $NEXTLINKIA;
    $PREVLINK	= join('',
		       "<LI>$PREVFL: <STRONG>",
		       '<A HREF="$PREVMSG$">$PREVSUBJECT$</A>',
		       "</STRONG></LI>\n")  unless $PREVLINK;
    $PREVLINKIA = ''  unless $PREVLINKIA;

    if (!$TOPLINKS) {
	$TOPLINKS  = join('',
			  "<HR>\n",
		          '$PREVBUTTON$$NEXTBUTTON$',
		          '<A HREF="$IDXFNAME$#$MSGNUM$">', $IDXBL, '</A>');
	$TOPLINKS .= join('',
			  '<A HREF="$TIDXFNAME$#$MSGNUM$">', $TIDXBL, '</A>')
		     if $THREAD;
    }

    if (!$BOTLINKS) {
	$BOTLINKS =  join('',
			  "<HR>\n",
			  "<UL>\n",
			  '$PREVLINK$',
			  '$NEXTLINK$',
			  "<LI>Index(es):\n",
			  "<UL>\n",
			  '<LI><A HREF="$IDXFNAME$#$MSGNUM$">',
			  "<STRONG>Main</STRONG></A></LI>\n");
	$BOTLINKS .= join('',
			  '<LI><A HREF="$TIDXFNAME$#$MSGNUM$">',
			  "<STRONG>Thread</STRONG></A></LI>\n")
		     if $THREAD;
	$BOTLINKS .= "</UL>\n</LI>\n</UL>\n";
    }

    ##	Set unknown icon
    $Icons{'unknown'} = $Icons{'text/plain'}  unless $Icons{'unknown'};

    ##	Set some other variables
    $IDXPATHNAME	= "${OUTDIR}${DIRSEP}${IDXNAME}";
    $TIDXPATHNAME	= "${OUTDIR}${DIRSEP}${TIDXNAME}";

    ##  Create dynamic subroutines.
    &create_routines();

    $TIME = defined($opt_time);
    $StartTime = (times)[0]  if ($TIME);
}
##---------------------------------------------------------------------------
sub doit {
    ## Check for non-archive modification modes.
    if ($SCAN) {
	&scan();
	&quit(0);
    } elsif ($SINGLE) {
	&single();
	&quit(0);
    }

    ## Following causes changes to an archive
    local($mesg, $tmp, $index, $sub, $from, $i, $date, @array,
	  @array2, $tmp2, %fields);

    $i = $NumOfMsgs;
    ##-------------------##
    ## Read mail folders ##
    ##-------------------##
    if ($EDITIDX || $IDXONLY) {
	print STDOUT "Editing $OUTDIR layout ...\n"  unless $QUIET;

    } elsif ($RMM) {		## Delete messages
	print STDOUT "Removing messages from $OUTDIR ...\n"
	    unless $QUIET;
	&rmm(*ARGV);

    } elsif ($ADDSINGLE) {		## Adding single message
	print STDOUT "Adding message to $OUTDIR\n"  unless $QUIET;
	$handle = $ADD;

	## Read mail head
	($index,$from,$date,$sub,$header) =
	    &read_mail_header($handle, *mesg, *fields);

	if ($index ne '') {
	    ($From{$index},$Date{$index},$Subject{$index}) =
		($from,$date,$sub);

	    $AddIndex{$index} = 1;
	    $IndexNum{$index} = &getNewMsgNum();

	    $MsgHead{$index} = $mesg;
	    $MsgHead{$index} .= "<HR>\n"  unless $mesg =~ /^\s*$/;

	    ## Read rest of message
	    $Message{$index} = &read_mail_body(
					$handle,
					$index,
					$header,
				        *fields);
	}

    } else {			## Adding/converting mail{boxes,folders}
	print STDOUT ($ADD ? "Adding" : "Converting"), " messages to $OUTDIR"
	    unless $QUIET;
	local($mbox, $mesgfile, @files);
	foreach $mbox (@ARGV) {
	    if (-d $mbox) {		# MH mail folder
		if (!opendir(MAILDIR, $mbox)) {
		    warn "\nWarning: Unable to open $mbox\n";
		    next;
		}
		$MBOX = 0;  $MH = 1;
		print STDOUT "\nReading $mbox "  unless $QUIET;
		@files = sort numerically grep(/^\d+$/, readdir(MAILDIR));
		closedir(MAILDIR);
		foreach (@files) {
		    $mesgfile = "${mbox}${DIRSEP}${_}";
		    if (!open(FILE, $mesgfile)) {
			warn "\nWarning: Unable to open message $mesgfile\n";
			next;
		    }
		    print STDOUT "."  unless $QUIET;
		    $mesg = '';
		    ($index,$from,$date,$sub,$header) =
			&read_mail_header(FILE, *mesg, *fields);

		    #  Process message if valid
		    if ($index ne '') {
			($From{$index},$Date{$index},$Subject{$index}) =
			    ($from,$date,$sub);
			$MsgHead{$index} = $mesg;
			$MsgHead{$index} .= "<HR>\n"  unless $mesg =~ /^\s*$/;

			if ($ADD && !$SLOW) { $AddIndex{$index} = 1; }
			$IndexNum{$index} = &getNewMsgNum();

			$Message{$index} = &read_mail_body(
						FILE,
						$index,
						$header,
					        *fields);
			#  Check if conserving memory
			if ($SLOW) {
			    &output_mail($index, 0, 0, *bogus, 1, 1);
			    $Update{$IndexNum{$index}} = 1;
			    undef $MsgHead{$index};
			    undef $Message{$index};
			}
		    }
		    close(FILE);
		}
	    } else {		# UUCP mail box file
		if (!open(FILE, $mbox)) {
		    warn "\nWarning: Unable to open $mbox\n";
		    next;
		}
		$MBOX = 1;  $MH = 0;
		print STDOUT "\nReading $mbox "  unless $QUIET;
		while (<FILE>) { last if /$FROM/o; }
		MBOX: while (!eof(FILE)) {
		    print STDOUT "."  unless $QUIET;
		    $mesg = '';
		    ($index,$from,$date,$sub,$header) =
			&read_mail_header(FILE, *mesg, *fields);

		    if ($index ne '') {
			($From{$index},$Date{$index},$Subject{$index}) =
			    ($from,$date,$sub);
			$MsgHead{$index} = $mesg;
			$MsgHead{$index} .= "<HR>\n"  unless $mesg =~ /^\s*$/;

			if ($ADD && !$SLOW) { $AddIndex{$index} = 1; }
			$IndexNum{$index} = &getNewMsgNum();

			$Message{$index} = &read_mail_body(
						FILE,
						$index,
						$header,
						*fields);
			if ($SLOW) {
			    &output_mail($index, 0, 0, *bogus, 1, 1);
			    $Update{$IndexNum{$index}} = 1;
			    undef $MsgHead{$index};
			    undef $Message{$index};
			}
		    } else {
			&read_mail_body(FILE, $index, $header, *fields, 1);
		    }
		}
		close(FILE);
	    }
	}
    }

    ## Check if there are any new messages
    if (!$EDITIDX && !$IDXONLY && $i == $NumOfMsgs) {
	print STDOUT "\nNo new messages\n"  unless $QUIET;
	&quit(0);
    }

    ##---------------------------------------------##
    ## Setup data structures for final HTML output ##
    ##---------------------------------------------##

    ## Remove old message if hit maximum size
    if (!$IDXONLY && $MAXSIZE && ($NumOfMsgs > $MAXSIZE)) {
	if ($REVSORT) {
	    @array = reverse &sort_messages();
	} else {
	    @array = &sort_messages();
	}
	&ign_signals();				# Ignore termination signals
	while ($NumOfMsgs > $MAXSIZE) {
	    $index = shift @array;
	    &delmsg($index);
	    $Update{$IndexNum{$array[0]}} = 1;		  # Update next
	    foreach (split(/$bs/o, $FollowOld{$index})) { # Update any replies
		$Update{$IndexNum{$_}} = 1;
	    }
	}
    }
    @array = &sort_messages();

    ## Compute follow up messages
    foreach $index (@array) {
	$FolCnt{$index} = 0  unless $FolCnt{$index};
	if (@array2 = split(/$'X/o, $Refs{$index})) {
	    $tmp2 = $array2[$#array2];
	    next unless defined($IndexNum{$MsgId{$tmp2}});
	    $tmp = $MsgId{$tmp2};
	    if ($Follow{$tmp}) { $Follow{$tmp} .= $bs . $index; }
	    else { $Follow{$tmp} = $index; }
	    $FolCnt{$tmp}++;
	}
    }

    ## Check for which messages to update when adding to archive
    if (!$IDXONLY && $ADD) {
	if ($UPDATE_ALL) {
	    foreach $index (@array) { $Update{$IndexNum{$index}} = 1; }
	} else {
	    $i = 0;
	    foreach $index (@array) {
		## Check for New follow-up links
		if ($FollowOld{$index} ne $Follow{$index}) {
		    $Update{$IndexNum{$index}} = 1;
		}
		## Check if new message; must update links in prev/next mesgs
		if ($AddIndex{$index}) {
		    $Update{$IndexNum{$array[$i-1]}} = 1  if $i > 0;
		    $Update{$IndexNum{$array[$i+1]}} = 1  if $i < $#array;
		}
		## Check for New reference links
		foreach (split(/$'X/o, $Refs{$index})) {
		    $tmp = $MsgId{$_};
		    if (defined($IndexNum{$tmp}) && $AddIndex{$tmp}) {
			$Update{$IndexNum{$index}} = 1;
		    }
		}
		$i++;
	    }
	}
    }

    ##------------##
    ## Write HTML ##
    ##------------##
    &ign_signals();				# Ignore termination signals
    print STDOUT "\n"  unless $QUIET;
    if (!$IDXONLY) {
	&write_mail(*array);
	&write_main_index();
	&write_thread_index()  if $THREAD;
    } elsif ($THREAD) {
	&write_thread_index();
    } else {
	&write_main_index();
    }

    ## Save archive state
    if (!$IDXONLY) {
	&output_db();
	foreach $tmp (@OtherIdxs) {
	    $THREAD = 0;
	    $tmp = "${OUTDIR}${DIRSEP}$tmp"
		unless ($tmp =~ m%^/%) || (-e $tmp);
	    if (&read_fmt_file($tmp)) {
		if ($THREAD) {
		    $TIDXPATHNAME = "${OUTDIR}${DIRSEP}${TIDXNAME}";
		    &write_thread_index();
		} else {
		    $IDXPATHNAME = "${OUTDIR}${DIRSEP}${IDXNAME}";
		    &write_main_index();
		}
	    }
	}
	print STDOUT "$NumOfMsgs messages\n"  unless $QUIET;
    }

    &quit(0);
}
##---------------------------------------------------------------------------
##	Function to do scan feature.
##
sub scan {
    local($key, $num, $index, $day, $mon, $year, $from, $date,
	  $subject, $time, @array);

    print STDOUT "$NumOfMsgs messages in $OUTDIR:\n\n";
    print STDOUT sprintf("%5s  %s  %-15s  %-45s\n",
			 "Msg #", "YY/MM/DD", "From", "Subject");
    print STDOUT sprintf("%5s  %s  %-15s  %-45s\n",
			 "-" x 5, "--------", "-" x 15, "-" x 45);

    @array = &sort_messages();
    foreach $index (@array) {
	$date = &time2mmddyy((split(/$X/o, $index))[0], 'yymmdd');
	$num = $IndexNum{$index};
	$from = substr(&dehtmlize(&extract_email_name($From{$index})), 0, 15);
	$subject = substr(&dehtmlize($Subject{$index}), 0, 45);
	print STDOUT sprintf("%5d  %s  %-15s  %-45s\n",
			     $num, $date, $from, $subject);
    }
}
##---------------------------------------------------------------------------
##	Routine to perform conversion of a single mail message to
##	HTML.
##
sub single {
    local($mhead,$index,$from,$date,$sub,$header,$handle,$mesg,
	  $template,$filename,%fields);

    ## Prevent any verbose output
    $QUIET = 1;

    ## See where input is coming from
    if ($ARGV[0]) {
	open(SINGLE, $ARGV[0]) || &error("ERROR: Unable to open $ARGV[0]");
	$handle = 'SINGLE';
	$filename = $ARGV[0];
    } else {
	$handle = 'STDIN';
    }

    ## Read header
    ($index,$from,$date,$sub,$header) =
	&read_mail_header($handle, *mhead, *fields);

    ($From{$index},$Date{$index},$Subject{$index}) = ($from,$date,$sub);

    ## Read rest of message
    $mesg = &read_mail_body($handle, $index, $header, *fields);

    ## Output to stdout
    $template = $MSGPGBEG;
    $template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
    print STDOUT $template;

    $template = $MSGHEAD;
    $template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
    print STDOUT $template;

    print STDOUT "<H1>$sub</H1>\n",
		 "<HR>\n",
		 $mhead;

    print STDOUT "<HR>\n"  unless $mhead =~ /^\s*$/;
    print STDOUT $mesg,
		 "<HR>\n";

    $template = $MSGFOOT;
    $template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
    print STDOUT $template;

    $template = $MSGPGEND;
    $template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
    print STDOUT $template;
}
##---------------------------------------------------------------------------
##	Function for removing messages.  *numbers points to an array
##	of message numbers to delete
##
sub rmm {
    local(*numbers) = shift;
    local($key, %Num2Index, $num, $didrmm, $filename);

    if ($#numbers < 0) {
	&error("Error: No message numbers specified");
    }
    $didrmm = 0;

    ## Make assoc arrays to perform deletions
    foreach $key (keys %IndexNum) {
	$Num2Index{$IndexNum{$key}} = $key;
    }
    ## Remove messages
    foreach $num (@numbers) {
	if ($num !~ /^\d+$/) {
	    print STDERR "`$num' is not a legal message number\n";
	}
	if ($key = $Num2Index{$num}) {
	    print STDOUT "\tRemoving message $num\n"  unless $QUIET;
	    &delmsg($key);
	    $didrmm = 1;
	} else {
	    print STDOUT "\tMessage $num does not exist\n"  unless $QUIET;
	}
    }
    if (!$didrmm) {
	&error("ERROR: Messages specified do not exist");
    }
}
##---------------------------------------------------------------------------
sub delmsg {
    local($key) = @_;
    local($filename);

    &defineIndex2MsgId();
    $msgnum = $IndexNum{$key};  return 0  if ($msgnum eq '');
    $filename = $OUTDIR . $DIRSEP . &msgnum_filename($msgnum);
    delete $ContentType{$key};
    delete $Date{$key};
    delete $From{$key};
    delete $IndexNum{$key};
    delete $Refs{$key};
    delete $Subject{$key};
    delete $MsgId{$Index2MsgId{$key}};
    unlink $filename;
    foreach $filename (split(/$'X/o, $Derived{$key})) {
	unlink "${OUTDIR}${DIRSEP}${filename}";
    }
    delete $Derived{$key};
    $NumOfMsgs--;
    1;
}
##---------------------------------------------------------------------------
##	write_mail outputs converted mail.  It takes a reference to an
##	array containing indexes of messages to output.
##
sub write_mail {
    local(*idxarray) = $_[0];
    local($max, $hack) = ($#idxarray, 0);
    print STDOUT "Writing mail ...\n"  unless $QUIET;
    if ($SLOW && !$ADD) {  $ADD = 1;  $hack = 1; }
    $i = 0;
    foreach $index (@idxarray) {
	&output_mail($index, $i, $max, *idxarray, $AddIndex{$index}, 0);
	$i++;
    }
    if ($hack) { $ADD = 0; }
}
##---------------------------------------------------------------------------
##	write_main_index outputs main index of archive
##
sub write_main_index {
    local(@array) = &sort_messages();
    local($outhandle, $i, $i_p0, $filename, $tmpl);

    ## Set messages that are shown in index
    if ($IDXSIZE && (($i = ($#array+1) - $IDXSIZE) > 0)) {
	if ($REVSORT) {
	    splice(@array, $IDXSIZE);
	} else {
	    splice(@array, 0, $i);
	}
    }

    ## Open/create index file
    if ($ADD) {
	if (-e $IDXPATHNAME) {
	    &cp($IDXPATHNAME, "${OUTDIR}${DIRSEP}tmp.$$");
	    open(MAILLISTIN, "${OUTDIR}${DIRSEP}tmp.$$")
		|| &error("ERROR: Unable to open ${OUTDIR}${DIRSEP}tmp.$$");
	    $MLCP = 1;
	} else {
	    $MLCP = 0;
	}
    }
    if ($IDXONLY) {
       $outhandle = STDOUT;
    } else {
	open(MAILLIST, "> $IDXPATHNAME") ||
	    &error("ERROR: Unable to create $IDXPATHNAME");
	$outhandle = 'MAILLIST';
    }
    print STDOUT "Writing $IDXPATHNAME ...\n"  unless $QUIET;

    ## Print top part of index
    &output_maillist_head($outhandle, MAILLISTIN);

    ## Output messages to HTML
    $i = 0;
    foreach $index (@array) {
	$msgnum = $IndexNum{$index};
	$i_p0 = &fmt_msgnum($msgnum);		# Var for replace_li_var
	$filename = &msgnum_filename($msgnum);	# Var for replace_li_var
	$tmpl = $LITMPL;
	$tmpl =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
	print $outhandle $tmpl;
	$i++;
    }

    ## Print bottom part of index
    &output_maillist_foot($outhandle, MAILLISTIN);
    close($outhandle)  unless $IDXONLY;
    close(MAILLISTIN), unlink("${OUTDIR}${DIRSEP}tmp.$$")  if $MLCP;
}
##---------------------------------------------------------------------------
##	write_thread_index outputs the thread index
##
sub write_thread_index {
    local($tmpl, $handle);

    if ($IDXONLY) {
	$handle = 'STDOUT';
    } else {
	open(THREAD, "> $TIDXPATHNAME") ||
	    &error("ERROR: Unable to create $TIDXPATHNAME");
	$handle = 'THREAD';
    }
    print STDOUT "Writing $TIDXPATHNAME ...\n"  unless $QUIET;

    $tmpl = $TIDXPGBEG;
    $tmpl =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
    print $handle $tmpl;

    $tmpl = $THEAD;
    $tmpl =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
    print $handle $tmpl;

    &output_thread_index($handle);

    $tmpl = $TFOOT;
    $tmpl =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
    print $handle $tmpl;

    &output_doclink($handle);

    $tmpl = $TIDXPGEND;
    $tmpl =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
    print $handle $tmpl;

    close($handle)  unless $IDXONLY;
}
##---------------------------------------------------------------------------
##	read_mail_header() is responsible for parsing the header of
##	a mail message.
##
sub read_mail_header {
    local($handle, *mesg, *fields) = @_;
    local(%l2o, $header, $index, $from, $sub, $date, $tmp, $msgid,
	  @refs, @array);

    $header = &'MAILread_file_header("main'$handle", *fields, *l2o);

    ##------------##
    ## Get Msg-ID ##
    ##------------##
    $msgid = $fields{'message-id'} || $fields{'msg-id'} || 
	     $fields{'content-id'};
    if (!($msgid =~ s/\s*<([^>]*)>\s*/$1/g)) {
	$msgid =~ s/^\s*//;
	$msgid =~ s/\s*$//;
    }

    #	Return if message already exists in archive
    #
    if ($msgid && defined($MsgId{$msgid})) {
	return ("", "", "", "", "");
    }

    ##----------##
    ## Get date ##
    ##----------##
    $date = '';
    if ($fields{'received'}) {
	@array = split(/$'FieldSep/o, $fields{'received'});
	$tmp = shift @array;
	@array = split(/;/, $tmp);
	$date = pop @array;
    } elsif ($fields{'date'}) {
	@array = split(/$'FieldSep/o, $fields{'date'});
	$date = shift @array;
    }
    if ($date =~ /\w/) {
	local($wday, $mday, $mon, $yr, $hr, $min, $sec, $zone) =
	    &parse_date($date);
	if ($zone) {
	    $index = &timegm($sec,$min,$hr,$mday,$mon,
			     ($yr > 1900 ? $yr-1900 : $yr));
	} else {
	    $index = &timelocal($sec,$min,$hr,$mday,$mon,
				($yr > 1900 ? $yr-1900 : $yr));
	}

	## Try to modify time/date based on timezone ##
	if ($zone =~ /^[\+-]\d+$/) {# Numeric timezone
	    $zone =~ s/0//g;
	    $index -= ($zone*3600);
	} else {                                # Timezone abbrev
	    warn qq|Warning: Undefined time zone: "$zone", Line $.\n|
		if $zone && !defined($Zone{$zone});
	    $index += ($Zone{$zone}*3600);      # %Zone defined above
	}
    } else {
	warn "Warning: Could not find date for message\n";
	$date = '';  $index = 0;
    }
    ##-------------##
    ## Get Subject ##
    ##-------------##
    if ($fields{'subject'} !~ /^\s*$/) {
	($sub = $fields{'subject'}) =~ s/\s*$//;
	&htmlize(*sub);
    } else {
	$sub = 'No Subject';
    }
    ##----------##
    ## Get From ##
    ##----------##
    $tmp = $fields{'from'} || $fields{'apparently-from'};
    $from = &convert_line($tmp);
    ##----------------##
    ## Get References ##
    ##----------------##
    $tmp = $fields{'references'};
    while ($tmp =~ s/<([^>]+)>//) {
	push(@refs, $1);
    }
    $tmp = $fields{'in-reply-to'};
    if ($tmp =~ s/^[^<]*<([^>]*)>.*$/$1/) {
	push(@refs, $tmp)  unless $tmp =~ /^\s*$/;
    }
    ##------------------------##
    ## Create HTML for header ##
    ##------------------------##
    $mesg .= &htmlize_header(*fields, *l2o);

    ## Insure uniqueness of msg-id
    $index .= $'X . sprintf("%d",$LastMsgNum+1);

    if ($fields{'content-type'}) {
	($tmp = $fields{'content-type'}) =~ m%^\s*([\w-/]+)%;
	$tmp = $1 || 'text/plain';
	$tmp =~ tr/A-Z/a-z/;
    } else {
	$tmp = 'text/plain';
    }
    $ContentType{$index} = $tmp;

    $MsgId{$msgid} = $index;
    &remove_dups(*refs);                # Remove duplicate msg-ids
    $Refs{$index} = join($'X, @refs)  if (@refs);

    ($index,$from,$date,$sub,$header);
}
##---------------------------------------------------------------------------
##	read_mail_body() reads in the body of a message.  The returned
##	filtered body is in $ret.
##
sub read_mail_body {
    local($handle, $index, $header, *fields, $skip) = @_;
    local($ret, $data, @files);

    while (<$handle>) {
	last  if $MBOX && /$FROM/o;
	$data .= $_;
    }
    return ''  if $skip;
    $fields{'content-type'} = 'text/plain'
	if $fields{'content-type'} =~ /^\s*$/;
    ($ret, @files) = &'MAILread_body($header, $data,
				    $fields{'content-type'},
				    $fields{'content-transfer-encoding'});
    $ret = join('',
		"<DL>\n",
		"<DT><STRONG>Warning</STRONG></DT>\n",
		"<DD>Could not process message with given Content-Type: \n",
		"<CODE>", $fields{'content-type'}, "</CODE>\n",
		"</DD>\n",
		"</DL>\n"
		)  unless $ret;
    if (@files) {
	$Derived{$index} = join($'X, @files);
    }
    $ret;
}
##---------------------------------------------------------------------------
##	Output/edit a mail message.
##	    $index	=> current index (== $array[$i])
##	    $i		=> current index into *array
##	    $maxnum	=> size of *array
##	    *array	=> reference to array of indexes
##	    $force	=> flag if mail is written and not editted, regardless
##	    $nocustom	=> ignore sections with user customization
##			   ($i, $maxnum, *array ignored if true)
##
sub output_mail {
    local($index, $i, $maxnum, *array, $force, $nocustom) = @_;
    local($msgi,$tmp,$tmp2,$template,@array2);
    local($filepathname, $tmppathname);
    local($adding) = ($ADD && !$force);

    # Variables for replace_li_var
    local($i_p0,$i_p1,$i_m1,$filename,$nextindex,$previndex);

    if (!$nocustom) {
	$nextindex = $array[$i+1];
	$previndex = $array[$i-1];
    }

    # Here $i is the current message count and not necessarily the
    # message number in the filename.

    $i_p0 = &fmt_msgnum($IndexNum{$index});
    if (!$nocustom) {
	$i_p1 = &fmt_msgnum($IndexNum{$nextindex});
	$i_m1 = &fmt_msgnum($IndexNum{$previndex});
    }

    $filename = &msgnum_filename($IndexNum{$index});
    $filepathname = $OUTDIR . $DIRSEP . $filename;
    $tmppathname = $OUTDIR . $DIRSEP . "msgtmp.$$";

    if ($adding) {
	return ($i_p0,$filename)  unless $Update{$IndexNum{$index}};
	&cp($filepathname, $tmppathname);
	open(MSGFILEIN, $tmppathname)
	    || &error("ERROR: Unable to open $tmppathname");
    }
    open(MSGFILE, "> $filepathname")
	|| &error("ERROR: Unable to create $filepathname");

    ## Output HTML header
    if ($adding) {
	while (<MSGFILEIN>) { last  if /<!--X-Body-Begin/; }
    }
    if (!$nocustom) {
	&defineIndex2MsgId();

	# Output comments -- more informative, but can be used for
	#		     error recovering.
	print MSGFILE "<!--X-Subject: $Subject{$index} -->\n",
		      "<!--X-From: $From{$index} -->\n",
		      "<!--X-Date: $Date{$index} -->\n",
		      "<!--X-Message-Id: $Index2MsgId{$index} -->\n",
		      "<!--X-ContentType: $ContentType{$index} -->\n";
	foreach (split(/$'X/o, $Refs{$index})) {
	    print MSGFILE
		      "<!--X-Reference-Id: $_ -->\n";
	}
	print MSGFILE "<!--X-Head-End-->\n";

	# Add in user defined markup
	$template = $MSGPGBEG;
	$template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
	print MSGFILE $template;
    }
    print MSGFILE "<!--X-Body-Begin-->\n";

    ## Output header
    if ($adding) {
	while (<MSGFILEIN>) {
	    last  if /<!--X-User-Header-End/ || /<!--X-TopPNI--/;
	}
    }
    print MSGFILE "<!--X-User-Header-->\n";
    if (!$nocustom) {
	$template = $MSGHEAD;
	$template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
	print MSGFILE $template;
    }
    print MSGFILE "<!--X-User-Header-End-->\n";

    ## Output Prev/Next/Index links at top
    if ($adding) {
	while (<MSGFILEIN>) { last  if /<!--X-TopPNI-End/; }
    }
    print MSGFILE "<!--X-TopPNI-->\n";
    if (!$nocustom) {
	$template = $TOPLINKS;
	$template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
	print MSGFILE $template;
    }
    print MSGFILE qq|\n|;
    print MSGFILE "<!--X-TopPNI-End-->\n";

    ## Output message body
    if ($adding) {
	$tmp2 = '';
	while (<MSGFILEIN>) {
	    $tmp2 .= $_;
	    last  if /<!--X-MsgBody-End/;
	}
	foreach (split(/$'X/o, $Refs{$index})) {# Convert msg-ids to hyperlinks
	    ($tmp = $_) =~ s/(\W)/\\$1/g;
	    if (defined($IndexNum{$MsgId{$_}}) &&
		$IndexNum{$MsgId{$_}} != $IndexNum{$index}) {
		$msgi = &fmt_msgnum($IndexNum{$MsgId{$_}});
		$tmp2 =~ s/$tmp/<A HREF="msg$msgi.html">$_<\/A>/g;
	    }
	}
	print MSGFILE $tmp2;
    } else {
	print MSGFILE "<!--X-MsgBody-->\n";
	print MSGFILE "<H1>", $Subject{$index}, "</H1>\n";
	print MSGFILE "<HR>\n";
	foreach (split(/$'X/o, $Refs{$index})) {# Convert msg-ids to hyperlinks
	    ($tmp = $_) =~ s/(\W)/\\$1/g;
	    if (defined($IndexNum{$MsgId{$_}}) &&
		$IndexNum{$MsgId{$_}} != $IndexNum{$index}) {

		$msgi = &fmt_msgnum($IndexNum{$MsgId{$_}});
		$MsgHead{$index} =~
		    s/$tmp/<A HREF="msg$msgi.html">$_<\/A>/g;
		$Message{$index} =~
		    s/$tmp/<A HREF="msg$msgi.html">$_<\/A>/g;
	    }
	}

	print MSGFILE $MsgHead{$index};
	print MSGFILE $Message{$index};
	print MSGFILE "<!--X-MsgBody-End-->\n";
    }

    ## Output any followup messages
    if ($adding) {
	while (<MSGFILEIN>) { last  if /<!--X-Follow-Ups-End/; }
    }
    print MSGFILE "<!--X-Follow-Ups-->\n";
    if (!$nocustom) {
	@array2 = split(/$bs/o, $Follow{$index});
	if ($#array2 >= 0) {
	    $tmp = 1;		# Here, $tmp a flag if <HR> printed
	    print MSGFILE "<HR>\n",
		       "<STRONG>Follow-Ups</STRONG>:\n",
		       "<UL>\n";
	    foreach (@array2) {
		print MSGFILE "<LI>",
		       qq|<STRONG><A HREF="|, &msgnum_filename($IndexNum{$_}),
		       qq|">$Subject{$_}</A></STRONG></LI>\n|,
		       "<UL>\n",
		       "<LI><EM>From</EM>: $From{$_}</LI>\n",
		       "</UL>\n";
	    }
	    print MSGFILE "</UL>\n";
	} else {
	    $tmp = 0;
	}
    }
    print MSGFILE "<!--X-Follow-Ups-End-->\n";

    ## Output any references
    if ($adding) {
	while (<MSGFILEIN>) { last  if /<!--X-References-End/; }
    }
    print MSGFILE "<!--X-References-->\n";
    if (!$nocustom) {
	@array2 = split(/$'X/o, $Refs{$index});  $tmp2 = 0;
	if ($#array2 >= 0) {
	    foreach (@array2) {
		if (defined($IndexNum{$MsgId{$_}})) {
		    if (!$tmp) { print MSGFILE "<HR>\n"; $tmp = 1; }
		    if (!$tmp2) {
			print MSGFILE "<STRONG>References</STRONG>:\n",
				   "<UL>\n";
			$tmp2 = 1;
		    }
		    print MSGFILE "<LI>",
			   qq|<STRONG><A HREF="|,
			   &msgnum_filename($IndexNum{$MsgId{$_}}),
			   qq|">$Subject{$MsgId{$_}}</A></STRONG></LI>\n|,
			   "<UL>\n",
			   "<LI><EM>From</EM>: $From{$MsgId{$_}}</LI>\n",
			   "</UL>\n";
		}
	    }
	    print MSGFILE "</UL>\n"  if $tmp2;
	}
    }
    print MSGFILE "<!--X-References-End-->\n";

    ## Output verbose links to prev/next message in list
    if ($adding) {
	while (<MSGFILEIN>) { last  if /<!--X-BotPNI-End/; }
    }
    print MSGFILE "<!--X-BotPNI-->\n";
    if (!$nocustom) {
	$template = $BOTLINKS;
	$template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
	print MSGFILE $template;
	print MSGFILE qq|\n|;
    }
    print MSGFILE "<!--X-BotPNI-End-->\n";

    ## Output footer
    if ($adding) {
	while (<MSGFILEIN>) {
	    last  if /<!--X-User-Footer-End/;
	}
    }
    print MSGFILE "<!--X-User-Footer-->\n";
    if (!$nocustom) {
	$template = $MSGFOOT;
	$template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
	print MSGFILE $template;
    }
    print MSGFILE "<!--X-User-Footer-End-->\n";

    if (!$nocustom) {
	$template = $MSGPGEND;
	$template =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
	print MSGFILE $template;
    }

    close(MSGFILE);
    close(MSGFILEIN), unlink($tmppathname)  if ($adding);

    ($i_p0, $filename);
}
##---------------------------------------------------------------------------
##	output_maillist_head() outputs the beginning of the index page.
##
sub output_maillist_head {
    local($handle, $cphandle) = @_;
    local($tmp);

    ## Output title
    $tmp = $IDXPGBEG;
    $tmp =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
    print $handle $tmp;
    print $handle "<!--X-ML-Title-H1-End-->\n";

    if ($MLCP) {
	while (<$cphandle>) { last  if /<!--X-ML-Title-H1-End/; }
    }

    ## Output header file
    if ($HEADER) {				# Read external header
	print $handle "<!--X-ML-Header-->\n";
	if (open(HEADER, $HEADER)) {
	    print $handle <HEADER>;
	} else {
	    warn "Warning: Unable to open header: $HEADER\n";
	}
	if ($MLCP) {
	    while (<$cphandle>) { last  if /<!--X-ML-Header-End/; }
	}
	print $handle "<!--X-ML-Header-End-->\n";
    } elsif ($MLCP) {				# Preserve maillist header
	while (<$cphandle>) {
	    print $handle $_;
	    last  if /<!--X-ML-Header-End/;
	}
    } else {					# No header
	print $handle "<!--X-ML-Header-->\n",
		      "<!--X-ML-Header-End-->\n";
    }

    print $handle "<!--X-ML-Index-->\n";
    $tmp = $LIBEG;
    $tmp =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
    print $handle $tmp;
}
##---------------------------------------------------------------------------
##	output_maillist_foot() outputs the end of the index page.
##
sub output_maillist_foot {
    local($handle, $cphandle) = @_;
    local($tmp);

    $tmp = $LIEND;
    $tmp =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
    print $handle $tmp;
    print $handle "<!--X-ML-Index-End-->\n";

    ## Skip past index in old maillist file
    if ($MLCP) {
	while (<$cphandle>) { last  if /<!--X-ML-Index-End/; }
    }

    ## Output footer file
    if ($FOOTER) {				# Read external footer
	print $handle "<!--X-ML-Footer-->\n";
	if (open(FOOTER, $FOOTER)) {
	    print $handle <FOOTER>;
	} else {
	    warn "Warning: Unable to open footer: $FOOTER\n";
	}
	if ($MLCP) {
	    while (<$cphandle>) { last  if /<!--X-ML-Footer-End/; }
	}
	print $handle "<!--X-ML-Footer-End-->\n";
    } elsif ($MLCP) {				# Preserve maillist footer
	while (<$cphandle>) {
	    print $handle $_;
	    last  if /<!--X-ML-Footer-End/;
	}
    } else {					# No footer
	print $handle "<!--X-ML-Footer-->\n",
		      "<!--X-ML-Footer-End-->\n";
    }

    &output_doclink($handle);

    ## Close document
    $tmp = $IDXPGEND;
    $tmp =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
    print $handle $tmp;
}
##---------------------------------------------------------------------------
##	Output link to documentation, if specified
##
sub output_doclink {
    local($handle) = ($_[0]);
    if (!$NODOC && $DOCURL) {
	print $handle "<HR>\n";
	print $handle
		"<ADDRESS>\n",
		"Mail converted by ",
		qq|<A HREF="$DOCURL"><CODE>MHonArc</CODE></A> $VERSION\n|,
		"</ADDRESS>\n";
    }
}
#############################################################################
## Miscellaneous routines
#############################################################################
##---------------------------------------------------------------------------
sub getNewMsgNum {
    $NumOfMsgs++; $LastMsgNum++;
    $LastMsgNum;
}
##---------------------------------------------------------------------------
##	replace_li_var() is used to substitute vars to current
##	values.  This routine relies on dynamic linking for $i,
##	$i_{p0,p1,m1}, $index, $maxnum and $filename.
##
sub replace_li_var {
    local($val) = $_[0];
    local($var,$len,$canclip,$raw,$isurl,$tmp,$ret) = ('',0,0,0,0,'','');
    local($expand) = (0);

    ##	Get length specifier (if defined)
    ($var, $len) = split(/:/, $val, 2);

    ##	Check if variable in a URL string
    $isurl = 1  if ($len =~ s/u//ig);	

    REPLACESW: {
	if ($var eq 'SUBJECT') {
	    $canclip = 1; $raw = 1; $isurl = 0;
	    $tmp = &dehtmlize($Subject{$index});
	    last REPLACESW;
	}
    	if ($var eq 'SUBJECTNA') {
	    $canclip = 1; $raw = 1;
	    $tmp = &dehtmlize($Subject{$index});
	    last REPLACESW;
	}
    	if ($var eq 'A_ATTR') {
	    $isurl = 0; $tmp = qq|NAME="$i_p0" HREF="$filename"|;
	    last REPLACESW;
	}
    	if ($var eq 'A_NAME')
	    { $isurl = 0; $tmp = qq|NAME="$i_p0"|; last REPLACESW; }
    	if ($var eq 'A_HREF')
	    { $isurl = 0; $tmp = qq|HREF="$filename"|; last REPLACESW; }
    	if ($var eq 'DATE')
	    { $tmp = $Date{$index}; last REPLACESW; }
    	if ($var eq 'DDMMYY') {
	    $tmp = &time2mmddyy((split(/$X/o, $index))[0], 'ddmmyy');
	    last REPLACESW;
	}
    	if ($var eq 'DOCURL')
	    { $isurl = 0; $tmp = $DOCURL; last REPLACESW; }
    	if ($var eq 'FROM') {
	    $canclip = 1; $raw = 1;
	    $tmp = &dehtmlize($From{$index});
	    last REPLACESW;
	}
    	if ($var eq 'FROMADDR') {
	    $canclip = 1; $raw = 1;
	    $tmp = &dehtmlize(&extract_email_address($From{$index}));
	    last REPLACESW;
	}
    	if ($var eq 'FROMNAME') {
	    $canclip = 1; $raw = 1;
	    $tmp = &dehtmlize(&extract_email_name($From{$index}));
	    last REPLACESW;
	}
    	if ($var eq 'GMTDATE')
	    { $tmp = $curdate; last REPLACESW; }
    	if ($var eq 'ICON') {
	    if ($Icons{$ContentType{$index}}) {
		$tmp = qq|<IMG SRC="$Icons{$ContentType{$index}}" | .
		       qq|ALT="[$ContentType{$index}]">|;
	    } else {
		$tmp = qq|<IMG SRC="$Icons{'unknown'}" ALT="[unknown]">|;
	    }
	    last REPLACESW;
	}
    	if ($var eq 'ICONURL') {
	    $isurl = 0;
	    if ($Icons{$ContentType{$index}}) {
		$tmp = $Icons{$ContentType{$index}};
	    } else {
		$tmp = $Icons{'unknown'};
	    }
	    last REPLACESW;
	}
    	if ($var eq 'IDXFNAME')
	    { $tmp = $IDXNAME; last REPLACESW; }
    	if ($var eq 'IDXSIZE')
	    { $tmp = $IDXSIZE; last REPLACESW; }
    	if ($var eq 'IDXTITLE')
	    { $canclip = 1; $tmp = $TITLE; last REPLACESW; }
    	if ($var eq 'LOCALDATE')
	    { $tmp = $locdate; last REPLACESW; }
    	if ($var eq 'MMDDYY') {
	    $tmp = &time2mmddyy((split(/$X/o, $index))[0], 'mmddyy');
	    last REPLACESW;
	}
    	if ($var eq 'MSGID') {
	    &defineIndex2MsgId();
	    $tmp = $Index2MsgId{$index};
	    last REPLACESW;
	}
    	if ($var eq 'MSGNUM')
	    { $tmp = $i_p0; last REPLACESW; }
    	if ($var eq 'NEXTFROM') {
	    $canclip = 1; $raw = 1;
	    $tmp = &dehtmlize($From{$nextindex});
	    last REPLACESW;
	}
    	if ($var eq 'NEXTFROMADDR') {
	    $canclip = 1; $raw = 1;
	    $tmp = &dehtmlize(&extract_email_address($From{$nextindex}));
	    last REPLACESW;
	}
    	if ($var eq 'NEXTFROMNAME') {
	    $canclip = 1; $raw = 1;
	    $tmp = &dehtmlize(&extract_email_name($From{$nextindex}));
	    last REPLACESW;
	}
    	if ($var eq 'NEXTMSG')
	    { $tmp = "msg${i_p1}.html"; last REPLACESW; }
    	if ($var eq 'NEXTMSGNUM')
	    { $tmp = $i_p1; last REPLACESW; }
	if ($var eq 'NEXTSUBJECT') {
	    $canclip = 1; $raw = 1;
	    $tmp = &dehtmlize($Subject{$nextindex});
	    last REPLACESW;
	}
    	if ($var eq 'NUMFOLUP')
	    { $tmp = $FolCnt{$index}; last REPLACESW; }
    	if ($var eq 'NUMOFIDXMSG') {
	    $tmp = ($NumOfMsgs > $IDXSIZE ? $IDXSIZE : $NumOfMsgs);
	    last REPLACESW;
	}
    	if ($var eq 'NUMOFMSG')
	    { $tmp = $NumOfMsgs; last REPLACESW; }
    	if ($var eq 'ORDNUM')
	    { $tmp = $i+1; last REPLACESW; }
    	if ($var eq 'OUTDIR')
	    { $tmp = $OUTDIR; last REPLACESW; }
    	if ($var eq 'PREVFROM') {
	    $canclip = 1; $raw = 1;
	    $tmp = &dehtmlize($From{$previndex});
	    last REPLACESW;
	}
    	if ($var eq 'PREVFROMADDR') {
	    $canclip = 1; $raw = 1;
	    $tmp = &dehtmlize(&extract_email_address($From{$previndex}));
	    last REPLACESW;
	}
    	if ($var eq 'PREVFROMNAME') {
	    $canclip = 1; $raw = 1;
	    $tmp = &dehtmlize(&extract_email_name($From{$previndex}));
	    last REPLACESW;
	}
    	if ($var eq 'PREVMSG')
	    { $tmp = "msg${i_m1}.html"; last REPLACESW; }
    	if ($var eq 'PREVMSGNUM')
	    { $tmp = $i_m1; last REPLACESW; }
	if ($var eq 'PREVSUBJECT') {
	    $canclip = 1; $raw = 1;
	    $tmp = &dehtmlize($Subject{$previndex});
	    last REPLACESW;
	}
    	if ($var eq 'PROG')
	    { $tmp = $PROG; last REPLACESW; }
    	if ($var eq 'TIDXFNAME')
	    { $tmp = $TIDXNAME; last REPLACESW; }
    	if ($var eq 'TIDXTITLE')
	    { $canclip = 1; $tmp = $TTITLE; last REPLACESW; }
    	if ($var eq 'VERSION')
	    { $tmp = $VERSION; last REPLACESW; }
    	if ($var eq '')
	    { $tmp = '$'; last REPLACESW; }
	if ($var eq 'NEXTBUTTON') {
	    $expand = 1;
	    $tmp = (($i < $maxnum) ? $NEXTBUTTON : $NEXTBUTTONIA);
	    last REPLACESW;
	}
	if ($var eq 'NEXTLINK') {
	    $expand = 1;
	    $tmp = (($i < $maxnum) ? $NEXTLINK : $NEXTLINKIA);
	    last REPLACESW;
	}
	if ($var eq 'PREVBUTTON') {
	    $expand = 1;
	    $tmp = (($i > 0) ? $PREVBUTTON : $PREVBUTTONIA);
	    last REPLACESW;
	}
	if ($var eq 'PREVLINK') {
	    $expand = 1;
	    $tmp = (($i > 0) ? $PREVLINK : $PREVLINKIA);
	    last REPLACESW;
	}
    	if ($var eq 'YYMMDD') {
	    $tmp = &time2mmddyy((split(/$X/o, $index))[0], 'yymmdd');
	    last REPLACESW;
	}
	warn qq|Warning: Unrecognized variable: "$val"\n|;
	return '';
    }

    ##	Check if string needs to expanded again
    if ($expand) {
	$tmp =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
    }

    ##	Check if clipping string
    if ($len > 0 && $canclip) {
	$ret = substr($tmp, 0, $len);
    } else {
	$ret = $tmp;
    }

    ##	Check if URL text specifier is set
    if ($isurl) {
	$ret = &urlize($ret);
    } else {
	&htmlize(*ret)  if $raw;
    }

    ##	Check for subject link
    $ret = qq|<A NAME="$i_p0" HREF="$filename">$ret</A>|  if $var eq 'SUBJECT';

    $ret;
}
##---------------------------------------------------------------------------
##	Add mailto URLs to $str.
##
sub mailto {
    local(*str) = shift;
    if ($MAILTOURL) {
	$str =~ s|([\!\%\w\.\-+=/]+@[\w\.\-]+)|&mailUrl($1)|ge;
    } else {
	$str =~ s|([\!\%\w\.\-+=/]+@[\w\.\-]+)|<A HREF="mailto:$1">$1</A>|g;
    }
}
##---------------------------------------------------------------------------
##	$sub, $msgid, $from come from read_mail_header() (ugly!!!!)
##
sub mailUrl {
    local($to) = (&urlize(shift));
    local($url) = ($MAILTOURL);
    local($subjectl, $froml, $msgidl) =
	 (&urlize($sub), &urlize($from), &urlize($msgid));
    $url =~ s/\$FROM\$/$froml/g;
    $url =~ s/\$MSGID\$/$msgidl/g;
    $url =~ s/\$SUBJECT\$/$subjectl/g;
    $url =~ s/\$SUBJECTNA\$/$subjectl/g;
    $url =~ s/\$TO\$/$to/g;
    qq|<A HREF="$url">$to</A>|;
}
##---------------------------------------------------------------------------
sub newsurl {
    local(*str) = shift;
    local($h, @groups);
    $str =~ s/^([^:]*:\s*)//;  $h = $1;
    $str =~ s/\s//g;			# Strip whitespace
    @groups = split(/,/, $str);		# Split groups
    foreach (@groups) {			# Make hyperlinks
	s|(.*)|<A HREF="news:$1">$1</A>|;
    }
    $str = $h . join(', ', @groups);	# Rejoin string
}
##---------------------------------------------------------------------------
sub get_header_tags {
    local($f) = shift;
    local($ftago, $ftagc, $tago, $tagc);

    ## Get user specified tags (this is one funcky looking code)
    $tag = (defined($HeadHeads{$f}) ?
	    $HeadHeads{$f} : $HeadHeads{"-default-"});
    $ftag = (defined($HeadFields{$f}) ?
	     $HeadFields{$f} : $HeadFields{"-default-"});
    if ($tag) { $tago = "<$tag>";  $tagc = "</$tag>"; }
    else { $tago = $tagc = ''; }
    if ($ftag) { $ftago = "<$ftag>";  $ftagc = "</$ftag>"; }
    else { $ftago = $ftagc = ''; }

    ($tago, $tagc, $ftago, $ftagc);
}
##---------------------------------------------------------------------------
sub field_add_links {
    local($label, *fld_text) = @_;
    &mailto(*fld_text)
	if !$NOMAILTO &&
	    $label =~ /^(to|from|cc|sender|reply-to)/i;
    &newsurl(*fld_text)
	if !$NONEWS && $label =~ /^newsgroup/i;

}
##---------------------------------------------------------------------------
##	convert_line() translates a line to HTML.  Checks are made for
##	embedded URLs.
##
sub convert_line {
    local($str) = $_[0];
    local($item, $item2, $item2h, @array);

    if (!$NOURL &&
	(@array = split(m%($Url[^\s\(\)\|<>"']*[^\.\?;,"'\|\[\]\(\)\s<>])%o,
		  $str))
       ) {
	    $str = '';
	    while($#array > 0) {
		$item = &entify(shift @array);      # Get non-URL text
		$item2 = shift @array;              # Get URL
		$item2h = &entify($item2);          # Variable for <A> content

		$str .= join('',
			     $item,
			     '<A HREF="', $item2, '">', $item2h, '</A>');

		# The next line is needed since Perl's split function also
		# returns extra entries for nested ()'s in the split pattern.
		shift @array  if $array[0] =~ m%^$Url$%o;
	    }
	    $item = &entify(shift @array);          # Last item in array
	    $str .= $item;
    } else {
	&htmlize(*str);
    }
    $str;
}
##---------------------------------------------------------------------------
##	ign_signals() sets mhonarc to ignore termination signals.  This
##	routine is called right before an archive is written/editted to
##	help prevent archive corruption.
##
sub ign_signals {
    $SIG{'ABRT'} = 'IGNORE';
    $SIG{'HUP'}  = 'IGNORE';
    $SIG{'INT'}  = 'IGNORE';
    $SIG{'PIPE'} = 'IGNORE';
    $SIG{'QUIT'} = 'IGNORE';
    $SIG{'TERM'} = 'IGNORE';
}
##---------------------------------------------------------------------------
##	set_handler() sets up the quit() routine to be called when
##	a termination signal is sent to mhonarc.
sub set_handler {
    $SIG{'ABRT'} = 'quit';
    $SIG{'HUP'}  = 'quit';
    $SIG{'INT'}  = 'quit';
    $SIG{'PIPE'} = 'quit';
    $SIG{'QUIT'} = 'quit';
    $SIG{'TERM'} = 'quit';
}
##---------------------------------------------------------------------------
##	create_lock_file() creates a file with zero permissions to act
##	as a lock.  Thanks to Walter_Hobbs@rand.org (Walt Hobbs) for
##	giving me a way to achieve this in Perl without possible race
##	conditions or the use of syscall.
##
##	Note: There is yet to be a way to a single locking capability
##	that works across mutliple operating systems: Unix, DOS, etc.
##
sub create_lock_file {
    local($file, $tries, $sleep, $force) = @_;
    local($umask, $ret);
    $ret = 0;
    eval '$umask = umask(0777)'  if $UNIX;
    while ($tries > 0) {
	if (open(LCK_FILE, "> $file")) {
	    $ISLOCK = 1;
	    $ret = 1;
	    last;
	}
	sleep($sleep)  if $sleep > 0;
	$tries--;
    }
    if ($force) {		# Set lock files if force option set
	$ISLOCK = 1;  $ret = 1;
    }
    eval 'umask($umask)'  if $UNIX;
    $ret;
}
##---------------------------------------------------------------------------
sub clean_up {
    if ($ISLOCK) {
	unlink ($LOCKFILE);
	$ISLOCK = 0;
    }
}
##---------------------------------------------------------------------------
sub error {
    &clean_up();
    die @_, "\n";
}
##---------------------------------------------------------------------------
sub quit {
    local($status) = shift;
    &clean_up();
    if ($TIME) {
	$EndTime = (times)[0];
	printf(STDERR "\nTime: %.4f CPU seconds\n", $EndTime - $StartTime);
    }
    exit $status;
}
##---------------------------------------------------------------------------
##	Create HTML for header
sub htmlize_header {
    local(*fields, *l2o) = @_;
    local($tmp, $key, $tago, $tagc, $ftago, $ftagc, $mesg, $item, @array, %hf);
    %hf = %fields;
    foreach $item (@FieldOrder) {
	if ($item eq '-extra-') {
	    foreach $key (sort keys %hf) {
		next  if $FieldODefs{$key};
		delete $hf{$key}, next  if &exclude_field($key);

		@array = split(/$'FieldSep/o, $hf{$key});
		foreach $tmp (@array) {
		    $tmp = &convert_line($tmp);
		    &field_add_links($key, *tmp);
		    ($tago, $tagc, $ftago, $ftagc) = &get_header_tags($key);
		    $mesg .= join('', "<LI>\n",
				  $tago, $l2o{$key}, $tagc, ": ",
				  $ftago, $tmp, $ftagc, "\n",
				  "</LI>\n");
		}
		delete $hf{$key};
	    }
	} else {
	    if (!&exclude_field($item) && $hf{$item}) {
		@array = (split(/$'FieldSep/o, $hf{$item}));
		foreach $tmp (@array) {
		    $tmp = &convert_line($tmp);
		    &field_add_links($item, *tmp);
		    ($tago, $tagc, $ftago, $ftagc) = &get_header_tags($item);
		    $mesg .= join('', "<LI>\n",
				  $tago, $l2o{$item}, $tagc, ": ",
				  $ftago, $tmp, $ftagc, "\n",
				  "</LI>\n");
		}
	    }
	    delete $hf{$item};
	}
    }
    if ($mesg) { $mesg = "<UL>\n" . $mesg . "</UL>\n"; }
    $mesg;
}
##---------------------------------------------------------------------------
sub output_thread_index {
    local($handle) = $_[0];
    local(%HasRef, %Replies, %Printed);
    local(@array, @refs);
    local($index, $msgid, $refindex, $level);

    ## Routine to print thread
    ##
    sub print_thread {
	local($i) = @_;
	local(@repls);

	&print_thread_entry($handle, $i);
	$Printed{$i} = 1;
	if (@repls = sort increase_index split(/$bs/o, $Replies{$i})) {
	    $level++;
	    print $handle "<UL>\n"  if $level <= $TLEVELS;
	    foreach (@repls) {
		&print_thread($_);
	    }
	    print $handle "</UL>\n"  if $level <= $TLEVELS;
	    $level--;
	}
    }

    ## Compute threads
    ##
    foreach $index (keys %Subject) {
	next  unless $Refs{$index};
	@refs = split(/$X/o, $Refs{$index});
	$msgid = $refs[$#refs];		## get last (rfc1036)
	if ($refindex = $MsgId{$msgid}) {
	    $HasRef{$index} = 1;
	    if ($Replies{$refindex}) {
		$Replies{$refindex} .= $bs . $index;
	    } else {
		$Replies{$refindex} = $index;
	    }
	}
    }

    ## Print index
    ##
    if ($TREVERSE) {
	@array = sort decrease_index keys %Subject;
    } else {
	@array = sort increase_index keys %Subject;
    }
    #	Set messages that are shown in index
    if ($IDXSIZE && (($i = ($#array+1) - $IDXSIZE) > 0)) {
	if ($TREVERSE) {
	    splice(@array, $IDXSIZE);
	} else {
	    splice(@array, 0, $i);
	}
    }
    if ($TSUBSORT) {
	@array = sort increase_subject @array;
    }
    print $handle "<UL>\n";
    foreach $index (@array) {
	&print_thread($index) unless $Printed{$index} || $HasRef{$index};
    }
    print $handle "</UL>\n";
}
##---------------------------------------------------------------------------
sub print_thread_entry {
    local($handle, $index) = @_;
    local($i_p0, $filename, $tmpl, $msgnum);

    $msgnum = $IndexNum{$index};
    $i_p0 = &fmt_msgnum($msgnum);		# Var for replace_li_var
    $filename = &msgnum_filename($msgnum);	# Var for replace_li_var

    $tmpl = $TLITXT;
    $tmpl =~ s/\$([^\$]*)\$/&replace_li_var($1)/ge;
    print $handle "<LI>", $tmpl, "</LI>\n";
}
##---------------------------------------------------------------------------
##	Create Index2MsgId if not defined
##
sub defineIndex2MsgId {
    if (!defined(%Index2MsgId)) {
	foreach (keys %MsgId) {
	    $Index2MsgId{$MsgId{$_}} = $_;
	}
    }
}
##---------------------------------------------------------------------------
##	create_routines is used to dynamically create routines that
##	would benefit from being create at run-time.  Routines
##	that have to check against several regular expressions
##	are candidates.
##
sub create_routines {
    local($sub) = '';

    ##-----------------------------------------------------------------------
    ## exclude_field: Used to determine if field should be excluded from
    ## message header
    ##
    $sub  =<<'EndOfRoutine';
	sub exclude_field {
	    local($f) = shift;
	    local($pat, $ret);
	    $ret = 0;
	    EXC_FIELD_SW: {
EndOfRoutine

	# Create switch block for checking field against regular
	# expressions (an large || statement could also work).
	foreach $pat (keys %HFieldsExc) {
	    $sub .= join('',
			 'if ($f =~ /^',
			 $pat,
			 '/i) { $ret = 1;  last EXC_FIELD_SW; }',
			 "\n");
	}

    $sub .=<<'EndOfRoutine';
	    }
	    $ret;
	}
EndOfRoutine

    eval $sub;
    &error("ERROR: Unable to create exclude_field routine:\n\t$@") if $@;
}
##---------------------------------------------------------------------------
##	Usage routine
##
sub usage {
    select(STDOUT);
    print <<EndOfUsage;
Usage:  $PROG [<options>] <file> ... 
        $PROG [<options>] -rmm <msg #> ...
Options:
  -add                  : Add message(s) to archive
  -dbfile <name>        : Name of MHonArc database file
                            (def: ".mhonarc.db")
  -docurl <url>         : URL to MHonArc documentation
                            (def: "http://www.oac.uci.edu/indiv/ehood/
                                   mhonarc.html")
  -editidx              : Only edit/change index page and messages
  -force                : Perform archive operation even if unable to lock
  -footer <file>        : File containing user text for bottom of index page
  -genidx               : Output index to stdout based upon archive contents
  -header <file>        : User text to include at top of index page
  -help                 : This message
  -idxfname <name>      : Name of index page
                            (def: "maillist.html")
  -idxsize <#>          : Maximum number of messages shown in indexes
  -lockdelay <#>        : Time delay, in seconds, between lock tries
                            (def: "3")
  -locktries <#>        : Maximum number of tries in locking an archive
                            (def: "10")
  -mailtourl <url>      : URL to use for e-mail address hyperlinks
                            (def: "mailto:\$TO\$")
  -maxsize <#>          : Maximum number of messages allowed in archive
  -msgsep <exp>         : Message separator expression for mailbox files
                            (def: "^From ")
  -nodoc                : Do not print link to doc at end of index page
  -nomailto             : Do not add in mailto links for e-mail addresses
  -nonews               : Do not add links to newsgroups
  -noreverse            : List messages in normal order
  -nosort               : Do not sort messages
  -nothread             : Do not create threaded index
  -notsubsort           : Do not sort threads by subject
  -outdir <path>        : Destination/location of HTML mail archive
                            (def: ".")
  -quiet                : Suppress status messages during execution
  -rcfile <file>        : Resource file for MHonArc
  -reverse              : List messages in reverse order
  -rmm                  : Remove messages from archive
  -savemem              : Write message data while processing
  -scan                 : List out archive contents to stdout
  -single               : Convert a single message to HTML
  -sort                 : Sort by dates (this is the default)
  -subsort              : Sort message by subject
  -thread               : Create threaded index
  -tidxfname <name>     : File name of threaded index page
                            (def: "threads.html")
  -time                 : Print to stderr CPU time used to process mail
  -title <string>       : Title of main index page
                            (def: "Mail Index")
  -tlevels <#>          : Maximum # of nested lists in threaded index
                            (def: "3")
  -treverse             : List threads with newest thread first
  -tsubsort             : Sort threads by subject
  -ttitle <string>      : Title of thread index page
                            (def: "Mail Thread Index")
  -umask <umask>        : Umask of MHonArc process

Description:
  MHonArc is a highly customizable Perl program for converting e-mail into
  HTML.  MHonArc will convert UUCP style mailbox files or MH mail folders
  into HTML with an index linking to each mail message.  The -single option
  can be used to convert a single mail message.

  Read the documentation for more complete usage information.

Version:
  $VERSION
  Copyright (C) 1995,1996  Earl Hood, ehood\@isogen.com
  MHonArc comes with ABSOLUTELY NO WARRANTY and MHonArc may be copied only
  under the terms of the GNU General Public License, which may be found in
  the MHonArc distribution.

EndOfUsage
    exit 0;
}
