[comp.sources.misc] v09i091: brkdig - break mailing list digest into USENET messages

chip@chinacat.Lonestar.ORG (Chip Rosenthal) (12/28/89)

Posting-number: Volume 9, Issue 91
Submitted-by: chip@chinacat.Lonestar.ORG (Chip Rosenthal)
Archive-name: brkdig

"brkdig" takes a mailing list digest, breaks it into individual messages,
and provides headers appropriate for direct posting into USENET.  I developed
"brkdig" when I administered the [TELECOM Digest]->comp.dcom.telecom gateway.
It has been well tested under SCO XENIX.

There is one major portability issue:  "brkdig" uses the System V regex(3)
routine, and takes advantage of its ability to extract information from the
string.  I don't believe there is a direct BSD analog for this function.

#! /bin/sh
# this is a "shar" archive - run through "/bin/sh" to extract 12 files:
#   README Defs-example brkdig.h brkdig.c loaddefs.c block.c header.c cleanhdr.c fixfrom.c getdate.y Makefile testaddr
# Wrapped by news@chinacat on Mon Dec 25 09:51:44 CST 1989
# Unpacking this archive requires:  sed test wc (possibly mkdir)
# Existing files will not be clobbered unless "-c" is specified on the cmd line.
if test -f README -a "$1" != "-c" ; then
    echo "README: file exists - will not be overwritten"
else
    echo "x - README (file 1 of 12, 2546 chars)"
    sed -e 's/^X//' << 'END_OF_FILE_README' > README
X
X"brkdig" processes a mailing list digest into individual messages suitable
Xfor posting to USENET.  There are two parts to this task:  slicing the
Xdigest and generating the USENET headers.  The usage is:
X
X    brkdig [ -d max_days ] [ -f defs_file ] [ -I ] [ file ]
X
X        -d max_days	The "Date" of all messages is compared to a threshold,
X			is stripped if too old.  This prevents inews from
X			rejecting articles for being too old.  This switch
X			overrides the default given in "brkdig.h".  A zero
X			"max_days" value may be used to suppress the check.
X
X        -f defs_file	The digest definitions file which directs the
X			gateway procedure.  This switch overrides the default
X			given in "brkdig.h".
X
X        -I		This switch inhibits the creation of the messages, for
X			debugging purposes.
X
X        file		Pathname to the digest to process.  If not specified,
X			the standard input is processed.
X
X"brkdig" will generate a set of files, each containing an individual message
Xfrom the digest, with headers ready to pass to "inews".  A log of the gateway
Xprocessing is written to the standard error.  At successful conclusion, a
Xsingle line is written to the standard output in the following format:
X
X    volume_num issue_num num_mssgs mssg_1_file ... mssg_N_file
X
X        volume_num	Volume number of the digest, padded to two digits
X                        with leading zeros.
X
X        issue_num	Issue number of the digest, padded to three digits
X                        with leading zeros.
X
X        num_mssgs	Number of messages in the digest, padded to two
X                        digits with leading zeros.
X
X        mssg_1_file	Pathname to the first message extracted from the
X                        digest -- all prepared for posting.
X
X	     .
X	     .
X	     .
X
X	mssg_N_file	Pathname to the last message extracted.
X
XTherefore, to process and post the messages you could use a command
Xsequence such as:
X
X    if set -- `brkdig $digest_file` ; then
X	: brkdig worked
X    else
X	exit 1
X    fi
X    shift ; shift ; shift	# throw away first three fields
X    for file in $* ; do
X	/usr/lib/news/inews -h < $file
X	rm $file
X    done
X
XThe key to the digest processing is the definitions file.  Definitions
Xin this file are generally a keyword, followed by a parameter.  Comments
Xare introduced with a "#" and are ignored.  The "Defs-example" file
X(included in this distribution) is an example definitions file.  It also
Xcontains comments which describe the required format.
X
X
XChip Rosenthal
X<chip@vector.Dallas.TX.US>
X@(#) README 1.2 89/12/25 09:48:05
X
END_OF_FILE_README
    size="`wc -c < README`"
    if test 2546 -ne "$size" ; then
	echo "README: extraction error - got $size chars"
    fi
fi
if test -f Defs-example -a "$1" != "-c" ; then
    echo "Defs-example: file exists - will not be overwritten"
else
    echo "x - Defs-example (file 2 of 12, 7240 chars)"
    sed -e 's/^X//' << 'END_OF_FILE_Defs-example' > Defs-example
X##############################################################################
X#
X# @(#) Defs-example 1.1 89/12/25 09:45:30
X#
X# Defs-example - Sample "brkdig" definitions file.
X#
X# This example file is configured for the TELECOM Digest.  It is based
X# on the actual file used when I gatewayed the TELECOM Digest into
X# comp.dcom.telecom.
X#
X# Mon Dec  4 08:40:23 1989 - Chip Rosenthal <chip@chinacat.Lonestar.ORG>
X#	Commented for inclusion in "brkdig" release.
X#
X##############################################################################
X
X
X##############################################################################
X#
X# DIGEST_NAME - Name displayed on log output.
X#	The parameter is a name displayed in the log output just to identify
X#	the type of digest processed, and not is used anywhere else.
X
XDIGEST_NAME	TELECOM
X
X
X##############################################################################
X#
X# TEMP_NAME - Pattern for creating temporary file names.
X#	The parameter is a sprintf(3) pattern for generating names for the
X#	individual message files.  The parameter will be formatted with
X#	three numeric arguments - the digest volume number, issue number,
X#	and message number.
X
XTEMP_NAME	/tmp/msg-v%02di%03dm%02d
X
X
X##############################################################################
X#
X# ADDR_CHECK - Command to check e-mail addresses.
X#	The parameter is a command which verifies whether an e-mail address
X#	is valid.  This is used *only* when the From: address in a messaage
X#	is a bang path rather than an RFC-822 address.  "brkdig" will attempt
X#	to build a valid address from the path and use this command to check
X#	it out.  This is important because inews will step on bad addresses.
X#	The command must return a zero result on a valid address and a nonzero
X#	result on a bad address.  The parameter is a sprintf(3) pattern and
X#	will be formatted with one argument - the address to check.
X
XADDR_CHECK	smail -bv '%s' </dev/null >/dev/null 2>&1	# smail3.1
X#ADDR_CHECK	/usenet/telecom-digest/work/testaddr '%s'	# smail2.5
X
X
X##############################################################################
X#
X# Patterns within the digest, specified as regex(3) regular expressions.
X#
X#   PAT_TITLE	The parameter is a regex(3) format regular expression which
X#		identifies the title line in the digest.  This line must
X#		include the digest volume and issue numbers, and the parameter
X#		must include the commands to extract these two parameters.
X#		The volume number will be extracted as "$0" and the issue
X#		number will be extracted as "$1".  Please refer to the
X#		regex(3) manual page for information on forming such regular
X#		expressions.
X#
X#   PAT_HDRSEP	This parameter is a regular expression which describes the
X#		line which seperates the digest header from the first message
X#		in the digest.
X#
X#   PAT_MSSGSEP	This parameter is a regular expression which describes the
X#		line which seperates messages in the digest.
X#
X#   PAT_DIGEND	This parameter is a regular expression which describes any
X#		line in the digest trailer.  This is used to confirm that an
X#		entire, untruncated digest was received and processed
X#		correctly.  Note that an actual telecom digest ends with a
X#		line something like: "End of TELECOM Digest V9 #183", but we
X#		have to use '.' instead of '#' in the pattern because '#' is
X#		the comment character within this file.
X
X
XPAT_TITLE	^TELECOM Digest .* Volume ([0-9]{1,6})$0 : Issue ([0-9]{1,6})$1
X
XPAT_HDRSEP	^------------------------------------------------------------------{1,9}$
X
XPAT_MSSGSEP	^----------------------------{1,5}$
X
XPAT_DIGEND	^End of TELECOM Digest V[0-9]* .[0-9]*$
X
X
X##############################################################################
X#
X# Message header definitions.
X#
X#	The USENET headers are generated according to the table specified
X#	below.  Headers are generated in the order given, and are controlled
X#	by the "flag" type.  Some header types need a value to be specified
X#	in the definition.  The flags, whether they need a value, and a
X#	description are as follows:
X#
X#	flag     value?	description
X#	-------- ------	----------------------------------------------------
X#	HDR_REQ	 yes	Must appear - use our value if user doesn't give one.
X#	HDR_OPT  no	Optional - generate only if the user provided one.
X#	HDR_IGN	 no	Silently ignore this if the user provided one.
X#	HDR_ADD	 yes	Ignore any user-supplied value and insert our own.
X#	HDR_FMT	 yes	Like "HDR_ADD", but format value with vol/iss/mssg no.
X#	HDR_FROM yes	Like "HDR_REQ", but perform special "From:" checks.
X#	HDR_DATE no	Like "HDR_OPT", but perform special "Date:" checks.
X#
X# More detailed explainations of the header flags are as follows:
X#
X# HDR_REQ	This header must appear in the news article header.  If
X#		the original message contains this header, then its value
X#		will be used.  Otherwise, we will fill in a default value.
X# 
X# HDR_OPT	This header may appear in the news article header.  If
X#		the original message contains this header, then its value
X#		will be used.  Otherwise, this header will not appear in
X#		the news article.
X# 
X# HDR_ADD	The news article will contain this header with the value
X#		we specify.  If the original message contains this header
X#		then it will be silently ignored.
X# 
X# HDR_IGN	This header will never be placed in the news article.
X#		If the original message contains this header then it will
X#		be silently ignored.
X# 
X# HDR_FMT	This is similar to a "HDR_ADD" field, except the header
X#		value given in the specifications file should be a
X#		sprintf(3) pattern  The pattern will be formatted with
X#		four numeric fields:  the digest volume number, the digest
X#		issue number, a message number, and the total number of
X#		messages in the digest.  The message number starts at 1
X#		and increments with each message in the digest.
X# 
X# HDR_FROM	This is similar to a "HDR_REQ" field, except the header
X#		value is subject to special address processing.  That is,
X#		the value used will be guaranteed to be an RFC822ish
X#		looking address.
X# 
X# HDR_DATE	This is similar to a "HDR_OPT" field, except the header
X#		value is subject to special date processing.  That is,
X#		the value will be compared to a threshold, and if found
X#		to be too old will be suppressed.  The threshold is defined
X#		by MAX_AGE in the "brkdig.h" file, and may be changed
X#		with the "-d" command line option.
X#
X# NOTE:	In the following, I replaced "vector.dallas.tx.us", the site
X#	upon which this definitions file was used with "BOGUS.ADDRESS".
X
X# type	 header			value
X#------	 ---------------------	---------------------------------------------
XHDR_ADD	 Path			telecom-gateway
XHDR_FROM From			nobody@nowhere.UUCP (this is a bogus address)
XHDR_ADD	 Newsgroups		comp.dcom.telecom
XHDR_REQ	 Subject		(none)
XHDR_FMT	 Message-ID		<telecom-v%02di%04dm%02d@BOGUS.ADDRESS>
XHDR_DATE Date
XHDR_OPT	 References
XHDR_OPT	 Keywords
XHDR_OPT	 Summary
XHDR_OPT	 Reply-To
XHDR_OPT	 Followup-To
XHDR_OPT	 Organization
XHDR_ADD	 Approved		telecom-request@BOGUS.ADDRESS
XHDR_ADD	 X-Submissions-To	telecom@eecs.nwu.edu
XHDR_ADD	 X-Administrivia-To	telecom-request@BOGUS.ADDRESS
XHDR_FMT	 X-TELECOM-Digest	volume %d, issue %d, message %d of %d
XHDR_IGN	 To
XHDR_IGN	 Cc
XHDR_IGN	 CC
XHDR_IGN	 Lines
X
END_OF_FILE_Defs-example
    size="`wc -c < Defs-example`"
    if test 7240 -ne "$size" ; then
	echo "Defs-example: extraction error - got $size chars"
    fi
fi
if test -f brkdig.h -a "$1" != "-c" ; then
    echo "brkdig.h: file exists - will not be overwritten"
else
    echo "x - brkdig.h (file 3 of 12, 3331 chars)"
    sed -e 's/^X//' << 'END_OF_FILE_brkdig.h' > brkdig.h
X/* @(#) brkdig.h 1.2 89/09/21 20:25:59
X *
X * Module:	brkdig.h - global definitions
X * Package:	brkdig - digest processor
X *
X * Thu Sep 21 20:16:47 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
X *	Added "clean_header()".
X * Fri Aug 25 20:46:32 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
X *	Cleanup and beta release.
X * Sun Jul 16 03:13:45 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
X *	Original composition.
X */
X
X
X/*
X * DEFS_FILE - Default file with digest configuration definitions.  May be
X * overridden with "-f" command line option.
X */
X#define DEFS_FILE	"Defs"
X
X
X/*
X * MAX_AGE - Maximum message age in seconds.  The "Date" field will be
X * stripped from messages if they are over this value to prevent inews
X * from rejecting the message.  A non-positive (e.g. zero) value suppresses
X * this check.  May be overriden by "-d".
X */
X#define MAX_AGE		( 7 * (24*60*60) )
X
X
X#define BUFLEN		1024	/* input line buffer sizes		*/
X#define MAXMSSGS	128	/* maximum messages in a digest		*/
X#define MAXHDRS		64	/* maximum number of headers defined	*/
X#define TRUE		1
X#define FALSE		0
X
X
X/*
X * USENET header types - used in (struct header_definitions)
X */
X#define HDR_REQ		1	/* this header must be in the message	*/
X#define HDR_FROM	2	/* special handling of "From:" header	*/
X#define HDR_DATE	3	/* special handling of "Date:" header	*/
X#define HDR_ADD		4	/* add this header; ignore user's value	*/
X#define HDR_FMT		5	/* like HDR_ADD, but format it first	*/
X#define HDR_OPT		6	/* this header may be used if present	*/
X#define HDR_IGN		7	/* this header should be suppressed	*/
X
X
X#ifndef LINT
X# define SCCSID(STR) static char sccsid[] = STR;
X#else
X# define SCCSID(STR)
X#endif
X
X
X#ifdef M_XENIX
X# ifdef NULL
X#   undef NULL
X#   define NULL 0
X# endif
X#endif
X
X
X#ifdef INTERN
X# define EXTERN
X# define INIT(X)	= X
X#else
X# define EXTERN		extern
X# define INIT(X)
X#endif
X
X
X/*
X * Structure of a block of text.
X */
Xstruct text_block {
X    char **line;		/* dynamically allocated list of lines	*/
X    int nlines;			/* number of lines in the list		*/
X    int max;			/* list will hold this many lines	*/
X};
X
Xstruct header_definition {
X    int type;			/* code to define header processing	*/
X    char *name;			/* name of this header			*/
X    char *value;		/* value we will supply			*/
X};
X
X
X/*
X * Globals
X */
XEXTERN int Inhibit		INIT(FALSE);	/* inhibit file creation      */
XEXTERN int Max_age		INIT(MAX_AGE);	/* limit on "Date" acceptance */
XEXTERN int Num_hdrs		INIT(0);	/* num "Hdr_defs[]" defined   */
XEXTERN char *Defs_file		INIT(DEFS_FILE);/* file with header defs      */
XEXTERN char *Dig_name		INIT(NULL);	/* name of this digest	      */
XEXTERN char *Tmp_name		INIT(NULL);	/* pattern for temp files     */
XEXTERN char *Addrchk_cmd	INIT(NULL);	/* cmd to check email addr    */
XEXTERN char *Pat_title		INIT(NULL);	/* regex for digest header    */
XEXTERN char *Pat_hdrsep		INIT(NULL);	/* re for end of header	      */
XEXTERN char *Pat_mssgsep	INIT(NULL);	/* re for message seperator   */
XEXTERN char *Pat_digend		INIT(NULL);	/* re for end of digest	      */
XEXTERN struct header_definition Hdr_defs[MAXHDRS]; /* hdr generation specs    */
X
X
X/*
X * Global procedures.
X */
Xint block_load();
Xint block_extract();
Xint block_find();
Xvoid clean_header();
Xvoid output_header();
Xchar *fix_bang_from();
Xchar *Sstrdup();
Xchar *Smalloc();
Xchar *Srealloc();
X
END_OF_FILE_brkdig.h
    size="`wc -c < brkdig.h`"
    if test 3331 -ne "$size" ; then
	echo "brkdig.h: extraction error - got $size chars"
    fi
fi
if test -f brkdig.c -a "$1" != "-c" ; then
    echo "brkdig.c: file exists - will not be overwritten"
else
    echo "x - brkdig.c (file 4 of 12, 6458 chars)"
    sed -e 's/^X//' << 'END_OF_FILE_brkdig.c' > brkdig.c
X/* @(#) brkdig.c 1.2 89/09/21 20:25:58
X *
X * Module:	brkdig.c - digest processor main program
X * Package:	brkdig - digest processor
X *
X * Thu Sep 21 20:16:47 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
X *	Added "clean_header()".
X * Fri Aug 25 20:46:32 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
X *	Cleanup and beta release.
X * Sun Jul 16 03:13:45 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
X *	Original composition.
X */
X
X#include <stdio.h>
X#include <errno.h>
X#define INTERN
X#include "brkdig.h"
X
XSCCSID("@(#) brkdig.c 1.2 89/09/21 20:25:58")
X
X#define USAGE "usage: %s [-d max_days] [-f defs_file] [-I] [file]\n"
X
X/*
X * There are two phases to the digest processor.  In the first phase, we
X * break up the digest into all of its individual pieces, specifically the
X * digest header, all of the message headers, and all of the message bodies.
X * In the second phase, we run through all of the message headers and bodies
X * and generate news articles.
X *
X * The digest text is manipulated in a (struct text_block).  We start by
X * loading the entire digest into a "Digest" block, and then pull out the
X * pieces.  Two procedures, block_find() and block_extract() are used.  The
X * block_find() procedure takes a (struct text_block) and a regular expression,
X * and returns the index of the first line in the block containing the RE.
X * the block_extract() procedure pulls a range of lines out of one block,
X * and places them into another.
X */
X
Xstruct text_block Digest;		/* entire digest loaded here	*/
Xstruct text_block Digheader;		/* the digest header		*/
Xstruct text_block Mssgheader[MAXMSSGS];	/* all the message headers	*/
Xstruct text_block Mssgbody[MAXMSSGS];	/* all the message bodies	*/
X
Xextern int errno;
Xextern char *sys_errlist[];
X
X
X/*VARARGS*/
Xstatic void abort(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9)
Xchar *fmt, *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9;
X{
X    fputs("  ", stderr);
X    fprintf(stderr, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
X    fputs("\n  ABORTING - please process digest manually\n", stderr);
X    (void) exit(1);
X    /*NOTREACHED*/
X}
X
X
Xmain(argc,argv)
Xint argc;
Xchar *argv[];
X{
X    char volno_str[8], issno_str[8], *fname;
X    int volno, issno, nmssgs, n, i;
X    FILE *fp;
X    static char tmpfname[BUFLEN];
X    extern char *optarg;
X    extern int optind;
X    extern char *regcmp();
X
X    /*
X     * Process command line arguments.
X     */
X    while ( (i=getopt(argc,argv,"d:f:I")) != EOF ) {
X	switch ( i ) {
X	case 'd':
X	    Max_age = atoi(optarg) * (24*60*60);
X	    break;
X	case 'f':
X	    Defs_file = optarg;
X	    break;
X	case 'I':
X	    Inhibit = TRUE;
X	    break;
X	default:
X	    fprintf(stderr, USAGE, argv[0]);
X	    (void) exit(1);
X	}
X    }
X    switch ( argc-optind ) {
X	case 0:  fname = NULL; break;
X	case 1:  fname = argv[optind]; break;
X	default: fprintf(stderr, USAGE, argv[0]); (void) exit(1);
X    }
X
X    /*
X     * Load the definitions.
X     */
X    (void) load_defs(Defs_file);
X    fprintf(stderr, "%s: receiving %s digest...\n", argv[0], Dig_name);
X
X    /*
X     * Load in the digest.
X     */
X    if ( block_load(&Digest, fname) != 0 )
X	abort("couldn't read file \"%s\" (%s)", fname, sys_errlist[errno]);
X
X    /*
X     * Locate the end of the digest header.
X     */
X    if ( ( i = block_find(&Digest, Pat_hdrsep, (char *)0) ) < 0 )
X	abort("couldn't locate end of digest header");
X
X    /*
X     * Pull the header out of the digest.
X     */
X    if ( block_extract(&Digheader, &Digest, 0, i) < 0 )
X	abort("couldn't extract header from digest");
X
X    /*
X     * Locate the digest title within the header.
X     */
X    if (block_find(&Digheader, Pat_title, volno_str, issno_str, (char *)0) < 0)
X	abort("couldn't locate digest title line");
X    volno = atoi(volno_str);
X    issno = atoi(issno_str);
X    fprintf(stderr,"  processing volume %d issue %d\n",volno,issno);
X
X    /*
X     * Extract all of the mesasges.
X     */
X    for (
X	nmssgs = 0 ;
X	( i=block_find(&Digest, Pat_mssgsep, (char *)0) ) >= 0 ;
X	++nmssgs
X    ) {
X	if ( block_extract(&Mssgbody[nmssgs], &Digest, 0, i) < 0 )
X	    abort("mssg %d - couldn't extract from digest", nmssgs+1);
X	(void) block_extract((struct text_block *)0, &Mssgbody[nmssgs], i, i);
X    }
X    fprintf(stderr, "  extracted %d messages from digest\n", nmssgs);
X
X    /*
X     * Perform some sanity checks on the message extraction.
X     */
X    if ( nmssgs == 0 )
X	abort("didn't extract any messages");
X    if ( block_find(&Digest, Pat_digend, (char *)0) < 0 )
X	abort("couldn't locate digest trailer");
X    if ( block_find(&Digest, "^From ", (char *)0) >= 0 )
X	abort("looks like unextracted messages remain");
X
X    /*
X     * Split up the message headers and bodies.
X     */
X    for ( n = 0 ; n < nmssgs ; ++n ) {
X	if ( ( i = block_find(&Mssgbody[n], "^$", (char *)0) ) < 0 )
X	    abort("mssg %d - couldn't find end of header", n+1);
X	if ( block_extract(&Mssgheader[n], &Mssgbody[n], 0, i) < 0 )
X	    abort("mssg %d - couldn't extract message header", n+1);
X	clean_header(&Mssgheader[n], n+1);
X    }
X
X    /*
X     * Process the messages.
X     */
X    for ( n = 0 ; n < nmssgs ; ++n ) {
X
X	/*
X	 * Open message file.
X	 */
X	(void) sprintf(tmpfname, Tmp_name, volno, issno, n+1);
X	if ( (fp=fopen((Inhibit ? "/dev/null" : tmpfname), "w")) == NULL ) {
X	    abort("mssg %d - couldn't create \"%s\" (%s)",
X		n+1, tmpfname, sys_errlist[errno]);
X	}
X
X	/*
X	 * Dump the article.
X	 */
X	output_header(fp, &Mssgheader[n], volno, issno, n+1, nmssgs);
X	putc('\n', fp);
X	for ( i = 0 ; i < Mssgbody[n].nlines ; ++i ) {
X	    fputs(Mssgbody[n].line[i], fp);
X	    putc('\n', fp);
X	}
X
X	(void) fclose(fp);
X
X    }
X
X    fprintf(stderr, "  extraction complete.\n");
X
X    /*
X     * Output information for poster.
X     */
X    (void) printf("%02d %03d %02d", volno, issno, nmssgs);
X    for ( i = 0 ; i < nmssgs ; ++i ) {
X	(void) putchar(' ');
X	(void) printf(Tmp_name, volno, issno, i+1);
X    }
X    (void) putchar('\n');
X
X    (void) exit(0);
X    /*NOTREACHED*/
X}
X
X
Xstatic char malloc_error[] = "malloc: out of space\n";
X
Xchar *Sstrdup(s)
Xchar *s;
X{
X    extern char *strcpy();
X    return strcpy( Smalloc((unsigned)strlen(s)+1), s );
X}
X
Xchar *Smalloc(n)
Xunsigned n;
X{
X    char *s;
X    extern char *malloc();
X    if ( (s=malloc(n)) == NULL ) {
X	fputs(malloc_error,stderr);
X	(void) exit(2);
X    }
X    return s;
X}
X
Xchar *Srealloc(s,n)
Xchar *s;
Xunsigned n;
X{
X    extern char *realloc();
X    if ( s == NULL )
X	return Smalloc(n);
X    if ( (s=realloc(s,n)) == NULL ) {
X	fputs(malloc_error,stderr);
X	(void) exit(2);
X    }
X    return s;
X}
X
X
END_OF_FILE_brkdig.c
    size="`wc -c < brkdig.c`"
    if test 6458 -ne "$size" ; then
	echo "brkdig.c: extraction error - got $size chars"
    fi
fi
if test -f loaddefs.c -a "$1" != "-c" ; then
    echo "loaddefs.c: file exists - will not be overwritten"
else
    echo "x - loaddefs.c (file 5 of 12, 3732 chars)"
    sed -e 's/^X//' << 'END_OF_FILE_loaddefs.c' > loaddefs.c
X/* @(#) loaddefs.c 1.1 89/08/25 20:47:41
X *
X * Module:	loaddefs.c - definitions file processing
X * Package:	brkdig - digest processor
X *
X * Fri Aug 25 20:46:32 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
X *	Original composition.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <string.h>
X#include "brkdig.h"
X
X#define Strmatch(S1,S2)		( strcmp((S1),(S2)) == 0 )
X
X
Xvoid load_defs(fname)
Xchar *fname;
X{
X    char **defp, *cmd, *arg, *s;
X    int lineno, htype;
X    FILE *fp;
X    static char buf[BUFLEN];
X
X    if ( (fp=fopen(fname,"r")) == NULL ) {
X	perror(fname);
X	(void) exit(1);
X    }
X
X    for ( lineno = 1 ; fgets(buf,sizeof(buf),fp) != NULL ; ++lineno ) {
X
X	/*
X	 * Trim comments, trailing spaces.
X	 */
X	if ( (s=strchr(buf,'#')) != NULL )
X	    *s = '\0';
X	for ( s = buf+strlen(buf)-1 ; s >= buf && isspace(*s) ; --s ) ;
X	*(s+1) = '\0';
X	if ( buf[0] == '\0' )
X	    continue;
X
X	/*
X	 * Split the line into a command and argument.
X	 */
X	for ( cmd = buf ; isspace(*cmd) ; ++cmd ) ;
X	for ( arg = cmd ; *arg != '\0' && !isspace(*arg) ; ++arg ) ;
X	if ( *arg == '\0' ) {
X	    fprintf(stderr,"%s(%d): missing argument to '%s'\n",
X		fname,lineno,cmd);
X	    (void) exit(1);
X	}
X	for ( *arg++ = '\0' ; isspace(*arg) ; ++arg ) ;
X
X	/*
X	 * Process the command.
X	 */
X	defp = NULL;
X	htype = -1;
X	if ( Strmatch(cmd,"DIGEST_NAME") )	defp = &Dig_name;
X	else if ( Strmatch(cmd,"TEMP_NAME") )	defp = &Tmp_name;
X	else if ( Strmatch(cmd,"ADDR_CHECK") )	defp = &Addrchk_cmd;
X	else if ( Strmatch(cmd,"PAT_TITLE") )	defp = &Pat_title;
X	else if ( Strmatch(cmd,"PAT_HDRSEP") )	defp = &Pat_hdrsep;
X	else if ( Strmatch(cmd,"PAT_MSSGSEP") )	defp = &Pat_mssgsep;
X	else if ( Strmatch(cmd,"PAT_DIGEND") )	defp = &Pat_digend;
X	else if ( Strmatch(cmd,"HDR_ADD") )	htype = HDR_ADD;
X	else if ( Strmatch(cmd,"HDR_DATE") )	htype = HDR_DATE;
X	else if ( Strmatch(cmd,"HDR_FMT") )	htype = HDR_FMT;
X	else if ( Strmatch(cmd,"HDR_FROM") )	htype = HDR_FROM;
X	else if ( Strmatch(cmd,"HDR_IGN") )	htype = HDR_IGN;
X	else if ( Strmatch(cmd,"HDR_OPT") )	htype = HDR_OPT;
X	else if ( Strmatch(cmd,"HDR_REQ") )	htype = HDR_REQ;
X	else {
X	    fprintf(stderr,"%s(%d): unknown command '%s'\n",
X		fname,lineno,cmd);
X	    (void) exit(1);
X	}
X
X	if ( defp != NULL ) {
X	    if ( *defp != NULL ) {
X		fprintf(stderr,"%s(%d): duplicate definition of '%s'\n",
X		    fname,lineno,cmd);
X		(void) exit(1);
X	    }
X	    *defp = Sstrdup(arg);
X	}
X
X	if ( htype >= 0 ) {
X	    if ( Num_hdrs >= MAXHDRS ) {
X		fprintf(stderr,"%s(%d): too many header specifications\n",
X		    fname,lineno);
X		(void) exit(1);
X	    }
X	    Hdr_defs[Num_hdrs].type = htype;
X	    for ( s = arg ; *s != '\0' && !isspace(*s) ; ++s ) ;
X	    if ( *s == '\0' ) {
X		Hdr_defs[Num_hdrs].value = NULL;
X	    } else {
X		for ( *s++ = '\0' ; isspace(*s) ; ++s ) ;
X		Hdr_defs[Num_hdrs].value = Sstrdup(s);
X	    }
X	    Hdr_defs[Num_hdrs].name = Sstrdup(arg);
X	    ++Num_hdrs;
X	}
X
X    }
X
X    if ( Dig_name == NULL ) {
X	fprintf(stderr,"%s: parameter 'DIG_NAME' unspecified\n", fname);
X	(void) exit(1);
X    }
X    if ( Tmp_name == NULL ) {
X	fprintf(stderr,"%s: parameter 'TMP_NAME' unspecified\n", fname);
X	(void) exit(1);
X    }
X    if ( Addrchk_cmd == NULL ) {
X	fprintf(stderr,"%s: parameter 'ADDRCHK_CMD' unspecified\n", fname);
X	(void) exit(1);
X    }
X    if ( Pat_title == NULL ) {
X	fprintf(stderr,"%s: parameter 'PAT_TITLE' unspecified\n", fname);
X	(void) exit(1);
X    }
X    if ( Pat_hdrsep == NULL ) {
X	fprintf(stderr,"%s: parameter 'PAT_HDRSEP' unspecified\n", fname);
X	(void) exit(1);
X    }
X    if ( Pat_mssgsep == NULL ) {
X	fprintf(stderr,"%s: parameter 'PAT_MSSGSEP' unspecified\n", fname);
X	(void) exit(1);
X    }
X    if ( Pat_digend == NULL ) {
X	fprintf(stderr,"%s: parameter 'PAT_DIGEND' unspecified\n", fname);
X	(void) exit(1);
X    }
X
X}
X
END_OF_FILE_loaddefs.c
    size="`wc -c < loaddefs.c`"
    if test 3732 -ne "$size" ; then
	echo "loaddefs.c: extraction error - got $size chars"
    fi
fi
if test -f block.c -a "$1" != "-c" ; then
    echo "block.c: file exists - will not be overwritten"
else
    echo "x - block.c (file 6 of 12, 4263 chars)"
    sed -e 's/^X//' << 'END_OF_FILE_block.c' > block.c
X/* @(#) block.c 1.1 89/08/25 20:47:39
X *
X * Module:	block.c - text block manipulation routines
X * Package:	brkdig - digest processor
X *
X * Fri Aug 25 20:46:32 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
X *	Cleanup and beta release.
X * Sun Jul 16 03:13:45 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
X *	Original composition.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <string.h>
X#include "brkdig.h"
X
XSCCSID("@(#) block.c 1.1 89/08/25 20:47:39")
X
Xstatic struct text_block *block_clean();
X
X
X/*
X * block_load() - load the contents of a file into a block.
X *	The block will be initialized prior to loading.  Any information
X *	currently in the block prior to the load will be lost.
X */
Xint block_load(b,fname)
Xstruct text_block *b;
Xchar *fname;
X{
X    FILE *fp;
X    char *s;
X    static char buf[1024];
X
X    if ( fname == NULL )
X	fp = stdin;
X    else if ( (fp=fopen(fname,"r")) == NULL )
X	return -1;
X
X    b->nlines = b->max = 0;
X    b->line = NULL;
X
X    while ( fgets(buf,sizeof(buf),fp) != NULL ) {
X
X	/*
X	 * Expand block if more space is needed.
X	 */
X	if ( b->nlines >= b->max ) {
X	    b->max = ( b->max == 0 ? 128 : b->max << 1 ) ;
X	    b->line = 
X		(char **)Srealloc( (char *)b->line, b->max*sizeof(char *) );
X	}
X
X	/*
X	 * Trim trailing space.
X	 */
X	for ( s = buf+strlen(buf)-1 ; s >= buf && isspace(*s) ; --s ) ;
X	*(s+1) = '\0';
X
X	/*
X	 * Append line to block.
X	 */
X	b->line[b->nlines++] = Sstrdup(buf);
X
X    }
X
X    if ( fname != NULL )
X	(void) fclose(fp);
X    (void) block_clean(b);
X    return 0;
X}
X
X
X/*
X * block_extract() - move a group of lines from one block to another
X *	Lines are numbered "0" through "nlines-1".  The inclusive range
X *	of lines is deleted from the "b_old" block.  The "b_new" block is
X *	initialized, and the deleted lines are placed there.  If there was
X *	any information in the "b_new" block prior to extraction then it
X *	will be lost.  If the "b_new" block is NULL, then this procedure
X *	just deletes the range of lines from the "b_old" block.
X */
Xint block_extract(b_new,b_old,nstart,nend)
Xstruct text_block *b_new;
Xstruct text_block *b_old;
Xint nstart;
Xint nend;
X{
X    int nlines, i;
X
X    if ( nstart < 0 || nend >= b_old->nlines || nstart > nend )
X	return -1;
X
X    nlines = nend-nstart+1;
X
X    if ( b_new != NULL ) {
X	b_new->max = b_new->nlines = nlines;
X	b_new->line = (char **) Smalloc( b_new->max * sizeof(char *) );
X	for ( i = 0 ; i < nlines ; ++i )
X	    b_new->line[i] = b_old->line[nstart+i];
X	(void) block_clean(b_new);
X    }
X
X    for ( i = nend+1 ; i < b_old->nlines ; ++i )
X	b_old->line[i-nlines] = b_old->line[i];
X    b_old->nlines -= nlines;
X    (void) block_clean(b_old);
X
X    return 0;
X}
X
X
X/*
X * block_find() - find the first instance of a pattern in a block.
X *	The "pattern" is a regular expression.  The arguments allow up
X *	to five sections of the line to be extracted (c.f. regexp(3)).
X *	The index (between "0" and "nlines-1") of the first line in the
X *	block which matches the pattern is returned.  A "-1" is returned
X *	upon search failure or error.
X */
X/*VARARGS*/
Xint block_find(b,pattern,a0,a1,a2,a3,a4)
Xstruct text_block *b;
Xchar *pattern, *a0, *a1, *a2, *a3, *a4;
X{
X    char *re;
X    int i;
X    extern char *regcmp(), *regex();
X    extern void free();
X
X    if ( (re=regcmp(pattern,(char *)0)) == NULL ) {
X	fputs("malloc: out of space\n",stderr);
X	(void) exit(2);
X    }
X
X    for ( i = 0 ; i < b->nlines ; ++i ) {
X	if ( regex(re,b->line[i],a0,a1,a2,a3,a4,(char *)0) != NULL )
X	    break;
X    }
X
X    (void) free(re);
X    return ( i < b->nlines ? i : -1 );
X}
X
X
X/*
X * block_clean() - trim leading and trailing blank lines from a block.
X *	The "nlines" count is updated to reflect any changes.
X */
Xstatic struct text_block *block_clean(b)
Xstruct text_block *b;
X{
X    int i, j;
X    char *s;
X
X    /*
X     * Trim leading blank lines.
X     */
X    for ( i = 0 ; i < b->nlines ; ++i ) {
X	for ( s = b->line[i] ; isspace(*s) ; ++s ) ;
X	if ( *s != '\0' )
X	    break;
X    }
X    if ( i > 0 ) {
X	for ( j = i ; j < b->nlines ; ++j )
X	    b->line[j-i] = b->line[j];
X	b->nlines -= i;
X    }
X
X    /*
X     * Trim trailing blank lines.
X     */
X    for ( i = b->nlines-1 ; i >= 0 ; --i ) {
X	for ( s = b->line[i] ; isspace(*s) ; ++s ) ;
X	if ( *s != '\0' )
X	    break;
X    }
X    b->nlines = i+1;
X
X    return b;
X}
X
X
END_OF_FILE_block.c
    size="`wc -c < block.c`"
    if test 4263 -ne "$size" ; then
	echo "block.c: extraction error - got $size chars"
    fi
fi
if test -f header.c -a "$1" != "-c" ; then
    echo "header.c: file exists - will not be overwritten"
else
    echo "x - header.c (file 7 of 12, 6640 chars)"
    sed -e 's/^X//' << 'END_OF_FILE_header.c' > header.c
X/* @(#) header.c 1.1 89/08/25 20:47:41
X *
X * Module:	header.c - news article header generation
X * Package:	brkdig - digest processor
X *
X * Fri Aug 25 20:46:32 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
X *	Cleanup and beta release.
X * Sun Jul 16 03:13:45 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
X *	Original composition.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <string.h>
X#include "brkdig.h"
X
XSCCSID("@(#) header.c 1.1 89/08/25 20:47:41")
X
X
Xstatic char *find_header();
Xstatic char *append_header();
X
X
Xstatic void hdr_error(mssgno,hdr_name,problem,old_value,new_value)
Xint mssgno;
Xchar *hdr_name;
Xchar *problem;
Xchar *old_value;
Xchar *new_value;
X{
X    fprintf(stderr,"  message %d - \"%s\" header problem - %s\n",
X	mssgno, hdr_name, problem);
X    if ( old_value != NULL )
X	fprintf(stderr,"  ..removing \"%s: %s\"\n", hdr_name, old_value);
X    if ( new_value != NULL )
X	fprintf(stderr,"  ....adding \"%s: %s\"\n", hdr_name, new_value);
X}
X
X
Xvoid output_header(fp,b,volno,issno,mssgno,nummssgs)
XFILE *fp;
Xstruct text_block *b;
Xint volno, issno, mssgno, nummssgs;
X{
X    int hdrno, i;
X    long time_now, time_post;
X    char *appendhdr, *name, *value, *new_value;
X    static char fmtbuf[BUFLEN];
X    extern long time(), getdate();
X    extern void free();
X
X    (void) time(&time_now);
X    appendhdr = NULL;
X
X    /*
X     * Go through all of the header items.
X     */
X    for ( hdrno = 0 ; hdrno < Num_hdrs ; ++hdrno ) {
X
X	name = Hdr_defs[hdrno].name;
X
X	/*
X	 * Process the header.
X	 */
X	switch ( Hdr_defs[hdrno].type ) {
X
X	case HDR_REQ:
X	    if ( (value=find_header(b,name,mssgno)) == NULL ) {
X		value = Hdr_defs[hdrno].value;
X		hdr_error(mssgno, name, "required header not in message",
X		    (char *)NULL, value);
X	    }
X	    break;
X
X	case HDR_ADD:
X	    (void) find_header(b,name,mssgno);
X	    value = Hdr_defs[hdrno].value;
X	    break;
X
X	case HDR_FMT:
X	    (void) find_header(b,name,mssgno);
X	    (void) sprintf(fmtbuf,
X		Hdr_defs[hdrno].value, volno, issno, mssgno, nummssgs);
X	    value = fmtbuf;
X	    break;
X
X	case HDR_OPT:
X	    value = find_header(b,name,mssgno);
X	    break;
X
X	case HDR_IGN:
X	    (void) find_header(b,name,mssgno);
X	    value = NULL;
X	    break;
X
X	case HDR_FROM:
X
X	    /*
X	     * Make sure the user gave us an address.
X	     */
X	    if ( (value=find_header(b,name,mssgno)) == NULL ) {
X		value = Hdr_defs[hdrno].value;
X		hdr_error(mssgno, name, "required header not in message",
X		    (char *)NULL, value);
X		appendhdr = append_header(appendhdr,
X		    "message missing 'From' field - bogus address added",
X		    (char *)NULL, (char *)NULL
X		);
X		break;
X	    }
X
X	    /*
X	     * If there is a '@' in it, then assume it is OK.
X	     */
X	    if ( strchr(value,'@') != NULL )
X		break;
X
X	    /*
X	     * Try to munge bang-paths into an address.  If that doesn't
X	     * work then use a bogus default.
X	     */
X	    if ( (new_value=fix_bang_from(value)) == NULL )
X		new_value = Hdr_defs[hdrno].value;
X	    hdr_error(mssgno, name, "address is not valid USENET syntax",
X		value, new_value);
X	    appendhdr = append_header(appendhdr,
X		"original 'From' address is not valid USENET syntax",
X		"X-Originally-From", value
X	    );
X	    value = new_value;
X	    break;
X
X
X	case HDR_DATE:
X
X	    /*
X	     * If the user didn't specify a date, that's OK.
X	     */
X	    if ( (value=find_header(b,name,mssgno)) == NULL )
X		break;
X
X	    /*
X	     * Verify that the syntax is correct.
X	     */
X	    if ( (time_post=getdate(value,(char *)NULL)) <= 0 ) {
X		hdr_error(mssgno, name, "date is not valid USENET syntax",
X		    value, (char *)NULL);
X		appendhdr = append_header(appendhdr,
X		    "original 'Date' value is not valid USENET syntax",
X		    "X-Original-Date", value
X		);
X		value = NULL;
X		break;
X	    }
X
X	    /*
X	     * Verify that it isn't too old - otherwise inews might reject it.
X	     */
X	    if ( Max_age > 0 && time_now-time_post > Max_age ) {
X		hdr_error(mssgno, name, "date is too old for posting",
X		    value, (char *)NULL);
X		appendhdr = append_header(appendhdr,
X		    "original 'Date' value is too old for posting",
X		    "X-Original-Date", value
X		);
X		value = NULL;
X		break;
X	    }
X
X	    /*
X	     * The date looks good.
X	     */
X	    break;
X
X	default:
X	    hdr_error(mssgno, name, "INTERNAL ERROR - bad header code",
X		(char *)NULL, (char *)NULL);
X	    (void) exit(1);
X
X	}
X
X	if ( value != NULL )
X	    fprintf(fp, "%s: %s\n", name, value);
X
X    }
X
X    for ( i = 0 ; i < b->nlines ; ++i ) {
X	if ( b->line[i] != NULL ) {
X	    name = b->line[i];
X	    for ( value = name ; *value != ':' && *value != '\0' ; ++value ) ;
X	    if ( *value != '\0' )
X		for ( *value++ = '\0' ; isspace(*value) ; ++value ) ;
X	    hdr_error(mssgno, name, "unknown or unsupported header",
X		value, (char *)NULL);
X	}
X    }
X
X    if ( appendhdr != NULL ) {
X	fputs(appendhdr,fp);
X	(void) free(appendhdr);
X    }
X
X}
X
X
Xstatic char *find_header(b,name,mssgno)
Xstruct text_block *b;
Xchar *name;
Xint mssgno;
X{
X    int count, len, i;
X    char *value, *s;
X    static char valbuf[BUFLEN];
X    extern void free();
X    extern char *strcpy();
X
X    len = strlen(name);
X    value = NULL;
X    count = 0;
X
X    for ( i = 0 ; i < b->nlines ; ++i ) {
X
X	/*
X	 * See if this is a header line we want.
X	 */
X	if ( b->line[i] == NULL )
X	    continue;
X	if ( strncmp(b->line[i],name,len) != 0 )
X	    continue;
X	if ( b->line[i][len] != ':' || !isspace(b->line[i][len+1]) )
X	    continue;
X
X	/* 
X	 * Locate and save off the value.
X	 */
X	for ( s = b->line[i]+len+1 ; isspace(*s) ; ++s ) ;
X	if ( *s != '\0' ) {
X	    if ( ++count > 1 ) {
X		hdr_error(mssgno, name, "duplicate header value",
X		    s, (char *)NULL);
X	    } else {
X		value = strcpy(valbuf,s);
X	    }
X	}
X
X	/*
X	 * Erase and free up the header.
X	 */
X	(void) free(b->line[i]);
X	b->line[i] = NULL;
X
X    }
X
X    return value;
X}
X
X
Xstatic char *append_header(h,problem,add_hdr,add_value)
Xchar *h, *problem, *add_hdr, *add_value;
X{
X    unsigned len;
X    char *newh;
X    static char warning_mssg[] = "X-GATEWAY-WARNING: ";
X    extern char *malloc(), *realloc();
X
X    len =
X	sizeof(warning_mssg)-1
X	    + strlen(problem)
X	    + sizeof("\n")-1
X	+ strlen(add_hdr)
X	    + sizeof(": ")-1
X	    + strlen(add_value)
X	    + sizeof("\n")-1
X	;
X
X    newh = ( h == NULL ? malloc(len+1) : realloc(h,strlen(h)+len+1) );
X    if ( newh == NULL ) {
X	fputs("malloc: out of space\n",stderr);
X	(void) exit(1);
X    }
X    if ( h == NULL )
X	*newh = '\0';
X
X    (void) strcat(newh,warning_mssg);
X    (void) strcat(newh,problem);
X    (void) strcat(newh,"\n");
X    if ( add_hdr != NULL && add_value != NULL ) {
X	(void) strcat(newh,add_hdr);
X	(void) strcat(newh,": ");
X	(void) strcat(newh,add_value);
X	(void) strcat(newh,"\n");
X    }
X
X    return newh;
X}
X
END_OF_FILE_header.c
    size="`wc -c < header.c`"
    if test 6640 -ne "$size" ; then
	echo "header.c: extraction error - got $size chars"
    fi
fi
if test -f cleanhdr.c -a "$1" != "-c" ; then
    echo "cleanhdr.c: file exists - will not be overwritten"
else
    echo "x - cleanhdr.c (file 8 of 12, 1686 chars)"
    sed -e 's/^X//' << 'END_OF_FILE_cleanhdr.c' > cleanhdr.c
X/* @(#) cleanhdr.c 1.1 89/09/21 20:26:07
X *
X * Module:	cleanhdr.c - header block cleanups
X * Package:	brkdig - digest processor
X *
X * Thu Sep 21 20:00:31 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
X *	Original composition.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <string.h>
X#include "brkdig.h"
X
XSCCSID("@(#) cleanhdr.c 1.1 89/09/21 20:26:07")
X
X/*
X * clean_header() - perform various header cleanups.
X *	This procedure attempts to work around various limitations and
X *	glitches which occur to headers when passed through mail.
X */
Xvoid clean_header(h,mssgno)
Xstruct text_block *h;
Xint mssgno;
X{
X    char *s;
X    int i, j;
X
X    for ( i = 0 ; i < h->nlines ; ++i ) {
X
X	/*
X	 * Assume a line starting with whitespace is a continuation line.
X	 */
X	if ( i > 0 && isspace(*h->line[i]) ) {
X	    for ( s = h->line[i] ; isspace(*s) ; ++s ) ;
X	    *--s = ' ';
X	    j = strlen(h->line[i-1]) + strlen(s) + 1;
X	    h->line[i-1] = Srealloc( h->line[i-1], j );
X	    (void) strcat(h->line[i-1],s);
X	    (void) free(h->line[i]);
X	    for ( j = i+1 ; j < h->nlines ; ++j )
X		h->line[j-1] = h->line[j];
X	    --h->nlines;
X	    --i;
X	    continue;
X	}
X
X	/*
X	 * Convert ">From" to "From".
X	 */
X	if ( strncmp( h->line[i], ">From:", sizeof(">From:")-1 ) == 0 ) {
X	    for ( s = h->line[i] ; *s != '\0' ; ++s )
X		*s = *(s+1);
X	}
X
X	/*
X	 * Verify that this looks like a header.
X	 */
X	for ( s = h->line[i] ; *s != '\0' && *s != ':' && !isspace(*s) ; ++s ) ;
X	if ( *s != ':' ) {
X	    fprintf(stderr,"  message %d - illegal header removed \"%s\"\n",
X		mssgno, h->line[i]);
X	    for ( j = i+1 ; j < h->nlines ; ++j )
X		h->line[j-1] = h->line[j];
X	    --h->nlines;
X	    --i;
X	    continue;
X	}
X
X    }
X
X}
END_OF_FILE_cleanhdr.c
    size="`wc -c < cleanhdr.c`"
    if test 1686 -ne "$size" ; then
	echo "cleanhdr.c: extraction error - got $size chars"
    fi
fi
if test -f fixfrom.c -a "$1" != "-c" ; then
    echo "fixfrom.c: file exists - will not be overwritten"
else
    echo "x - fixfrom.c (file 9 of 12, 3936 chars)"
    sed -e 's/^X//' << 'END_OF_FILE_fixfrom.c' > fixfrom.c
X/* @(#) fixfrom.c 1.1 89/08/25 20:47:40
X *
X * Module:	fixfrom.c - fix bang-path style from lines
X * Package:	brkdig - digest processor
X *
X * Fri Aug 25 20:46:32 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
X *	Cleanup and beta release.
X * Sun Jul 16 03:13:45 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
X *	Original composition.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <string.h>
X#include "brkdig.h"
X
XSCCSID("@(#) fixfrom.c 1.1 89/08/25 20:47:40")
X
Xstatic int checkaddr();
X
X/*
X * fix_bang_from() - convert "From" field to an RFC822 address
X *
X * The "from" argument is a "from" field which contains a bang-path rather
X * than a valid address.  The path is converted to a legal (and hopefully
X * valid) address.  If the field doesn't contain a bang path or the conversion
X * fails, then NULL is returned.
X *
X * Two attempts are performed at translation.  In the first attempt, we
X * look for a fully qualified domain name, and build an address from there.
X * If several such qualified names exist, then the rightmost is taken.  For
X * example, we would convert "cs.utech.edu!bigvax!moscvax.krem.org!ihnp4!fred"
X * to "inhp4!fred@moscvax.krem.org".
X *
X * In the second attempt, we try to build addresses until we find one our
X * router program understands.  For example, given "utech!bigvax!ihnp4!fred",
X * we would try (in order) "fred@ihnp4.UUCP", "ihnp4!fred@bigvax.UUCP", and
X * "bigvax!ihnp4!fred@utech.UUCP".
X *
X * If neither of these approaches work we give up and return NULL.
X */
Xchar *fix_bang_from(from)
Xchar *from;
X{
X    static char pathbuf[BUFLEN], addrbuf[BUFLEN];
X    char *pathelem[64], *s1, *s2;
X    int nelem, n, i;
X
X    (void) strcpy(pathbuf,from);
X
X    /*
X     * Extract anything enclosed in '<' angle brackets '>'
X     */
X    if ( (s1=strchr(pathbuf,'<')) != NULL && (s2=strchr(s1,'>')) != NULL ) {
X	*s2 = '\0';
X	(void) strcpy(pathbuf,s1+1);
X    }
X
X    /*
X     * Throw out anything enclosed in '(' parens ')'
X     */
X    if ( (s1=strchr(pathbuf,'(')) != NULL && (s2=strchr(s1,')')) != NULL )
X	(void) strcpy(s1,s2+1);
X
X    /*
X     * Trim leading space.
X     */
X    for ( s1 = pathbuf ; isspace(*s1) ; ++s1 ) ;
X    if ( s1 != pathbuf )
X	(void) strcpy(pathbuf,s1);
X
X    /*
X     * Terminate the line after the first word.
X     */
X    for ( s1 = pathbuf ; *s1 != '\0' && !isspace(*s1) ; ++s1 ) ;
X    *s1 = '\0';
X
X    /*
X     * We are only dealing with bang paths here.
X     */
X    if ( strchr(pathbuf,'!') == NULL )
X	return NULL;
X
X    /*
X     * Break the path at the '!' bangs.
X     */
X   for (
X	s1 = pathbuf, nelem = 0 ;
X	(pathelem[nelem]=strtok(s1,"!")) != NULL ;
X	s1 = NULL, ++nelem
X    ) ;
X
X    /*
X     * Search for an element which is a fully qualified domain name.
X     */
X    for ( n = nelem-2 ; n >= 0 ; --n ) {
X	if ( strchr(pathelem[n],'.') == NULL )
X	    continue;
X	addrbuf[0] = '\0';
X	for ( i = n+1 ; i < nelem ; ++i ) {
X	    if ( i > n+1 )
X		(void) strcat(addrbuf,"!");
X	    (void) strcat(addrbuf,pathelem[i]);
X	}
X	(void) strcat(addrbuf,"@");
X	(void) strcat(addrbuf,pathelem[n]);
X	return addrbuf;
X    }
X
X    /*
X     * Search for an element which we can build an address from.
X     */
X    for ( n = nelem-2 ; n >= 0 ; --n ) {
X	addrbuf[0] = '\0';
X	for ( i = n+1 ; i < nelem ; ++i ) {
X	    if ( i > n+1 )
X		(void) strcat(addrbuf,"!");
X	    (void) strcat(addrbuf,pathelem[i]);
X	}
X	(void) strcat(addrbuf,"@");
X	(void) strcat(addrbuf,pathelem[n]);
X	(void) strcat(addrbuf,".UUCP");
X	if ( checkaddr(addrbuf) )
X	    return addrbuf;
X    }
X
X    return NULL;
X}
X
X
X
Xstatic int checkaddr(addr)
Xchar *addr;
X{
X    static char cmdbuf[BUFLEN];
X    int status, pid;
X
X    (void) sprintf(cmdbuf,Addrchk_cmd,addr);
X    switch ( (pid=fork()) ) {
X    case -1:			/* fork error */
X	perror("fork");
X	(void) exit(1);
X    case 0:			/* child */
X	(void) execl( "/bin/sh", "sh", "-c", cmdbuf, (char *)NULL );
X	perror("exec");
X	(void) exit(1);
X    default:
X	while ( wait(&status) != pid ) ;
X	return ( status == 0 );
X    }
X}
X
END_OF_FILE_fixfrom.c
    size="`wc -c < fixfrom.c`"
    if test 3936 -ne "$size" ; then
	echo "fixfrom.c: extraction error - got $size chars"
    fi
fi
if test -f getdate.y -a "$1" != "-c" ; then
    echo "getdate.y: file exists - will not be overwritten"
else
    echo "x - getdate.y (file 10 of 12, 13345 chars)"
    sed -e 's/^X//' << 'END_OF_FILE_getdate.y' > getdate.y
X%token ID MONTH DAY MERIDIAN SNUMBER UNUMBER UNIT MUNIT SUNIT ZONE DAYZONE AGO
X%{
X	/* 	Originally from: Steven M. Bellovin (unc!smb)	*/ 
X	/*	Dept. of Computer Science			*/
X	/*	University of North Carolina at Chapel Hill	*/
X	/*	@(#)getdate.y	2.19	1/17/89	*/
X
X/*
X * Fri Mar 24 22:57:24 1989 - Chip Rosenthal <chip@vector.UUCP>
X *	Added new "tspec" to handle HH:MM:SS.mm type times.
X *	Added new "dtspec" to handle DD-MMM-YYYY type dates.
X *	Yep...it ain't RFC822...but what does VMS care?
X */
X
X/*****
X#include "defs.h"
X******/
X#include <sys/types.h>
X#ifdef USG
Xstruct timeb
X{
X	time_t	time;
X	unsigned short millitm;
X	short	timezone;
X	short	dstflag;
X};
X#else
X#include <sys/timeb.h>
X#endif
X#include <ctype.h>
X
X#if defined(BSD4_2)
X#include <sys/time.h>
X#else /* sane */
X#include <time.h>
X#endif /* sane */
X
X#define	NULL	0
X#define daysec (24L*60L*60L)
X	static int timeflag, zoneflag, dateflag, dayflag, relflag;
X	static time_t relsec, relmonth;
X	static int hh, mm, ss, merid, daylight;
X	static int dayord, dayreq;
X	static int month, day, year;
X	static int ourzone;
X#define AM 1
X#define PM 2
X#define DAYLIGHT 1
X#define STANDARD 2
X#define MAYBE    3
X%}
X
X%%
Xtimedate: 		/* empty */
X	| timedate item;
X
Xitem:	tspec =
X		{timeflag++;}
X	| zone =
X		{zoneflag++;}
X	| dtspec =
X		{dateflag++;}
X	| dyspec =
X		{dayflag++;}
X	| rspec =
X		{relflag++;}
X	| nspec;
X
Xnspec:	UNUMBER =
X		{if (timeflag && dateflag && !relflag) year = $1;
X		else {timeflag++;hh = $1/100;mm = $1%100;ss = 0;merid = 24;}};
X
Xtspec:	UNUMBER MERIDIAN =
X		{hh = $1; mm = 0; ss = 0; merid = $2;}
X	| UNUMBER ':' UNUMBER =
X		{hh = $1; mm = $3; merid = 24;}
X	| UNUMBER ':' UNUMBER MERIDIAN =
X		{hh = $1; mm = $3; merid = $4;}
X	| UNUMBER ':' UNUMBER SNUMBER =
X		{hh = $1; mm = $3; merid = 24;
X		daylight = STANDARD; ourzone = -($4%100 + 60*($4/100));}
X	| UNUMBER ':' UNUMBER ':' UNUMBER =
X		{hh = $1; mm = $3; ss = $5; merid = 24;}
X	| UNUMBER ':' UNUMBER ':' UNUMBER '.' UNUMBER =
X		{hh = $1; mm = $3; ss = $5; merid = 24; /* added 3/24/89 CR */ }
X	| UNUMBER ':' UNUMBER ':' UNUMBER MERIDIAN =
X		{hh = $1; mm = $3; ss = $5; merid = $6;}
X	| UNUMBER ':' UNUMBER ':' UNUMBER SNUMBER =
X		{hh = $1; mm = $3; ss = $5; merid = 24;
X		daylight = STANDARD; ourzone = -($6%100 + 60*($6/100));};
X
Xzone:	ZONE =
X		{ourzone = $1; daylight = STANDARD;}
X	| DAYZONE =
X		{ourzone = $1; daylight = DAYLIGHT;};
X
Xdyspec:	DAY =
X		{dayord = 1; dayreq = $1;}
X	| DAY ',' =
X		{dayord = 1; dayreq = $1;}
X	| UNUMBER DAY =
X		{dayord = $1; dayreq = $2;};
X
Xdtspec:	UNUMBER '/' UNUMBER =
X		{month = $1; day = $3;}
X	| UNUMBER '/' UNUMBER '/' UNUMBER =
X		{month = $1; day = $3; year = $5;}
X	| UNUMBER MONTH SNUMBER =
X		{month = $2; day = $1; year = -$3; /* added 3/24/89 CR */ }
X	| MONTH UNUMBER =
X		{month = $1; day = $2;}
X	| MONTH UNUMBER ',' UNUMBER =
X		{month = $1; day = $2; year = $4;}
X	| UNUMBER MONTH =
X		{month = $2; day = $1;}
X	| UNUMBER MONTH UNUMBER =
X		{month = $2; day = $1; year = $3;};
X
X
Xrspec:	SNUMBER UNIT =
X		{relsec +=  60L * $1 * $2;}
X	| SNUMBER MUNIT =
X		{relmonth += $1 * $2;}
X	| SNUMBER SUNIT =
X		{relsec += $1;}
X	| UNIT =
X		{relsec +=  60L * $1;}
X	| MUNIT =
X		{relmonth += $1;}
X	| SUNIT =
X		{relsec++;}
X	| rspec AGO =
X		{relsec = -relsec; relmonth = -relmonth;};
X%%
X
Xstatic int mdays[12] =
X	{31, 0, 31,  30, 31, 30,  31, 31, 30,  31, 30, 31};
X#define epoch 1970
X
Xextern struct tm *localtime();
X
Xtime_t
Xdateconv(mm, dd, yy, h, m, s, mer, zone, dayflag)
Xint mm, dd, yy, h, m, s, mer, zone, dayflag;
X{
X	time_t tod, jdate;
X	register int i;
X	time_t timeconv();
X
X	if (yy < 0) yy = -yy;
X	if (yy < 100) yy += 1900;
X	mdays[1] = 28 + (yy%4 == 0 && (yy%100 != 0 || yy%400 == 0));
X	if (yy < epoch || yy > 1999 || mm < 1 || mm > 12 ||
X		dd < 1 || dd > mdays[--mm]) return (-1);
X	jdate = dd-1;
X        for (i=0; i<mm; i++) jdate += mdays[i];
X	for (i = epoch; i < yy; i++) jdate += 365 + (i%4 == 0);
X	jdate *= daysec;
X	jdate += zone * 60L;
X	if ((tod = timeconv(h, m, s, mer)) < 0) return (-1);
X	jdate += tod;
X	if (dayflag==DAYLIGHT || (dayflag==MAYBE&&localtime(&jdate)->tm_isdst))
X		jdate += -1*60*60;
X	return (jdate);
X}
X
Xtime_t
Xdayconv(ord, day, now)
Xint ord, day; time_t now;
X{
X	register struct tm *loctime;
X	time_t tod;
X	time_t daylcorr();
X
X	tod = now;
X	loctime = localtime(&tod);
X	tod += daysec * ((day - loctime->tm_wday + 7) % 7);
X	tod += 7*daysec*(ord<=0?ord:ord-1);
X	return daylcorr(tod, now);
X}
X
Xtime_t
Xtimeconv(hh, mm, ss, mer)
Xregister int hh, mm, ss, mer;
X{
X	if (mm < 0 || mm > 59 || ss < 0 || ss > 59) return (-1);
X	switch (mer) {
X		case AM: if (hh < 1 || hh > 12) return(-1);
X			 return (60L * ((hh%12)*60L + mm)+ss);
X		case PM: if (hh < 1 || hh > 12) return(-1);
X			 return (60L * ((hh%12 +12)*60L + mm)+ss);
X		case 24: if (hh < 0 || hh > 23) return (-1);
X			 return (60L * (hh*60L + mm)+ss);
X		default: return (-1);
X	}
X}
Xtime_t
Xmonthadd(sdate, relmonth)
Xtime_t sdate, relmonth;
X{
X	struct tm *ltime;
X	time_t dateconv();
X	time_t daylcorr();
X	int mm, yy;
X
X	if (relmonth == 0) return 0;
X	ltime = localtime(&sdate);
X	mm = 12*ltime->tm_year + ltime->tm_mon + relmonth;
X	yy = mm/12;
X	mm = mm%12 + 1;
X	return daylcorr(dateconv(mm, ltime->tm_mday, yy, ltime->tm_hour,
X		ltime->tm_min, ltime->tm_sec, 24, ourzone, MAYBE), sdate);
X}
X
Xtime_t
Xdaylcorr(future, now)
Xtime_t future, now;
X{
X	int fdayl, nowdayl;
X
X	nowdayl = (localtime(&now)->tm_hour+1) % 24;
X	fdayl = (localtime(&future)->tm_hour+1) % 24;
X	return (future-now) + 60L*60L*(nowdayl-fdayl);
X}
X
Xstatic char *lptr;
X
Xyylex()
X{
X	extern int yylval;
X	int sign = 0;
X	register char c;
X	register char *p;
X	char idbuf[20];
X	int pcnt;
X
X	for (;;) {
X		while (isspace(*lptr))
X			lptr++;
X
X		if (isdigit(c = *lptr) || c == '-' || c == '+') {
X			if (c== '-' || c == '+') {
X				if (c=='-') sign = -1;
X				else sign = 1;
X				if (!isdigit(*++lptr)) {
X					/* yylval = sign; return (UNUMBER); */
X					return yylex();	/* skip the '-' sign */
X				}
X			}
X			yylval = 0;
X			while (isdigit(c = *lptr++))
X				yylval = 10*yylval + c - '0';
X			lptr--;
X			if (sign < 0)
X				yylval = -yylval;
X			if (sign != 0)
X				return SNUMBER;
X			 else
X				return UNUMBER;
X
X		} else if (isalpha(c)) {
X			p = idbuf;
X			while (isalpha(c = *lptr++) || c=='.')
X				if (p < &idbuf[sizeof(idbuf)-1]) *p++ = c;
X			*p = '\0';
X			lptr--;
X			return (lookup(idbuf));
X		}
X
X		else if (c == '(') {
X			pcnt = 0;
X			do {
X				c = *lptr++;
X				if (c == '\0') return(c);
X				else if (c == '(') pcnt++;
X				else if (c == ')') pcnt--;
X			} while (pcnt > 0);
X		}
X
X		else return (*lptr++);
X	}
X}
X
Xstruct table {
X	char *name;
X	int type, value;
X};
X
Xstruct table mdtab[] = {
X	{"january", MONTH, 1},
X	{"february", MONTH, 2},
X	{"march", MONTH, 3},
X	{"april", MONTH, 4},
X	{"may", MONTH, 5},
X	{"june", MONTH, 6},
X	{"july", MONTH, 7},
X	{"august", MONTH, 8},
X	{"september", MONTH, 9},
X	{"sept", MONTH, 9},
X	{"october", MONTH, 10},
X	{"november", MONTH, 11},
X	{"december", MONTH, 12},
X
X	{"sunday", DAY, 0},
X	{"monday", DAY, 1},
X	{"tuesday", DAY, 2},
X	{"tues", DAY, 2},
X	{"wednesday", DAY, 3},
X	{"wednes", DAY, 3},
X	{"thursday", DAY, 4},
X	{"thur", DAY, 4},
X	{"thurs", DAY, 4},
X	{"friday", DAY, 5},
X	{"saturday", DAY, 6},
X	{0, 0, 0}};
X
X#define HRS *60
X#define HALFHR 30
Xstruct table mztab[] = {
X	{"a.m.", MERIDIAN, AM},
X	{"am", MERIDIAN, AM},
X	{"p.m.", MERIDIAN, PM},
X	{"pm", MERIDIAN, PM},
X	{"nst", ZONE, 3 HRS + HALFHR},		/* Newfoundland */
X	{"n.s.t.", ZONE, 3 HRS + HALFHR},
X	{"ast", ZONE, 4 HRS},		/* Atlantic */
X	{"a.s.t.", ZONE, 4 HRS},
X	{"adt", DAYZONE, 4 HRS},
X	{"a.d.t.", DAYZONE, 4 HRS},
X	{"est", ZONE, 5 HRS},		/* Eastern */
X	{"e.s.t.", ZONE, 5 HRS},
X	{"edt", DAYZONE, 5 HRS},
X	{"e.d.t.", DAYZONE, 5 HRS},
X	{"cst", ZONE, 6 HRS},		/* Central */
X	{"c.s.t.", ZONE, 6 HRS},
X	{"cdt", DAYZONE, 6 HRS},
X	{"c.d.t.", DAYZONE, 6 HRS},
X	{"mst", ZONE, 7 HRS},		/* Mountain */
X	{"m.s.t.", ZONE, 7 HRS},
X	{"mdt", DAYZONE, 7 HRS},
X	{"m.d.t.", DAYZONE, 7 HRS},
X	{"pst", ZONE, 8 HRS},		/* Pacific */
X	{"p.s.t.", ZONE, 8 HRS},
X	{"pdt", DAYZONE, 8 HRS},
X	{"p.d.t.", DAYZONE, 8 HRS},
X	{"yst", ZONE, 9 HRS},		/* Yukon */
X	{"y.s.t.", ZONE, 9 HRS},
X	{"ydt", DAYZONE, 9 HRS},
X	{"y.d.t.", DAYZONE, 9 HRS},
X	{"hst", ZONE, 10 HRS},		/* Hawaii */
X	{"h.s.t.", ZONE, 10 HRS},
X	{"hdt", DAYZONE, 10 HRS},
X	{"h.d.t.", DAYZONE, 10 HRS},
X
X	{"gmt", ZONE, 0 HRS},
X	{"g.m.t.", ZONE, 0 HRS},
X	{"bst", DAYZONE, 0 HRS},		/* British Summer Time */
X	{"b.s.t.", DAYZONE, 0 HRS},
X	{"eet", ZONE, 0 HRS},		/* European Eastern Time */
X	{"e.e.t.", ZONE, 0 HRS},
X	{"eest", DAYZONE, 0 HRS},	/* European Eastern Summer Time */
X	{"e.e.s.t.", DAYZONE, 0 HRS},
X	{"met", ZONE, -1 HRS},		/* Middle European Time */
X	{"m.e.t.", ZONE, -1 HRS},
X	{"mest", DAYZONE, -1 HRS},	/* Middle European Summer Time */
X	{"m.e.s.t.", DAYZONE, -1 HRS},
X	{"wet", ZONE, -2 HRS },		/* Western European Time */
X	{"w.e.t.", ZONE, -2 HRS },
X	{"west", DAYZONE, -2 HRS},	/* Western European Summer Time */
X	{"w.e.s.t.", DAYZONE, -2 HRS},
X
X	{"jst", ZONE, -9 HRS},		/* Japan Standard Time */
X	{"j.s.t.", ZONE, -9 HRS},	/* Japan Standard Time */
X					/* No daylight savings time */
X
X	{"aest", ZONE, -10 HRS},	/* Australian Eastern Time */
X	{"a.e.s.t.", ZONE, -10 HRS},
X	{"aesst", DAYZONE, -10 HRS},	/* Australian Eastern Summer Time */
X	{"a.e.s.s.t.", DAYZONE, -10 HRS},
X	{"acst", ZONE, -(9 HRS + HALFHR)},	/* Australian Central Time */
X	{"a.c.s.t.", ZONE, -(9 HRS + HALFHR)},
X	{"acsst", DAYZONE, -(9 HRS + HALFHR)},	/* Australian Central Summer */
X	{"a.c.s.s.t.", DAYZONE, -(9 HRS + HALFHR)},
X	{"awst", ZONE, -8 HRS},		/* Australian Western Time */
X	{"a.w.s.t.", ZONE, -8 HRS},	/* (no daylight time there, I'm told */
X	{0, 0, 0}};
X
Xstruct table unittb[] = {
X	{"year", MUNIT, 12},
X	{"month", MUNIT, 1},
X	{"fortnight", UNIT, 14*24*60},
X	{"week", UNIT, 7*24*60},
X	{"day", UNIT, 1*24*60},
X	{"hour", UNIT, 60},
X	{"minute", UNIT, 1},
X	{"min", UNIT, 1},
X	{"second", SUNIT, 1},
X	{"sec", SUNIT, 1},
X	{0, 0, 0}};
X
Xstruct table othertb[] = {
X	{"tomorrow", UNIT, 1*24*60},
X	{"yesterday", UNIT, -1*24*60},
X	{"today", UNIT, 0},
X	{"now", UNIT, 0},
X	{"last", UNUMBER, -1},
X	{"this", UNIT, 0},
X	{"next", UNUMBER, 2},
X	{"first", UNUMBER, 1},
X	/* {"second", UNUMBER, 2}, */
X	{"third", UNUMBER, 3},
X	{"fourth", UNUMBER, 4},
X	{"fifth", UNUMBER, 5},
X	{"sixth", UNUMBER, 6},
X	{"seventh", UNUMBER, 7},
X	{"eighth", UNUMBER, 8},
X	{"ninth", UNUMBER, 9},
X	{"tenth", UNUMBER, 10},
X	{"eleventh", UNUMBER, 11},
X	{"twelfth", UNUMBER, 12},
X	{"ago", AGO, 1},
X	{0, 0, 0}};
X
Xstruct table milzone[] = {
X	{"a", ZONE, 1 HRS},
X	{"b", ZONE, 2 HRS},
X	{"c", ZONE, 3 HRS},
X	{"d", ZONE, 4 HRS},
X	{"e", ZONE, 5 HRS},
X	{"f", ZONE, 6 HRS},
X	{"g", ZONE, 7 HRS},
X	{"h", ZONE, 8 HRS},
X	{"i", ZONE, 9 HRS},
X	{"k", ZONE, 10 HRS},
X	{"l", ZONE, 11 HRS},
X	{"m", ZONE, 12 HRS},
X	{"n", ZONE, -1 HRS},
X	{"o", ZONE, -2 HRS},
X	{"p", ZONE, -3 HRS},
X	{"q", ZONE, -4 HRS},
X	{"r", ZONE, -5 HRS},
X	{"s", ZONE, -6 HRS},
X	{"t", ZONE, -7 HRS},
X	{"u", ZONE, -8 HRS},
X	{"v", ZONE, -9 HRS},
X	{"w", ZONE, -10 HRS},
X	{"x", ZONE, -11 HRS},
X	{"y", ZONE, -12 HRS},
X	{"z", ZONE, 0 HRS},
X	{0, 0, 0}};
X
Xlookup(id)
Xchar *id;
X{
X#define gotit (yylval=i->value,  i->type)
X
X	char idvar[128];
X	register char *j, *k;
X	register struct table *i;
X	int abbrev;
X
X	(void) strcpy(idvar, id);
X	j = idvar;
X	k = id - 1;
X	while (*++k)
X		*j++ = isupper(*k) ? tolower(*k) : *k;
X	*j = '\0';
X
X	if (strlen(idvar) == 3)
X		abbrev = 1;
X	else
X		if (strlen(idvar) == 4 && idvar[3] == '.') {
X			abbrev = 1;
X			idvar[3] = '\0';
X		}
X	else
X		abbrev = 0;
X
X	for (i = mdtab; i->name; i++) {
X		k = idvar;
X		for (j = i->name; *j++ == *k++;) {
X			if (abbrev && j == i->name+3)
X				return gotit;
X			if (j[-1] == 0)
X				return gotit;
X		}
X	}
X
X	for (i = mztab; i->name; i++)
X		if (strcmp(i->name, idvar) == 0)
X			return gotit;
X
X	for (i=mztab; i->name; i++)
X		if (strcmp(i->name, idvar) == 0)
X			return gotit;
X
X	for (i=unittb; i->name; i++)
X		if (strcmp(i->name, idvar) == 0)
X			return gotit;
X
X	if (idvar[strlen(idvar)-1] == 's')
X		idvar[strlen(idvar)-1] = '\0';
X
X	for (i=unittb; i->name; i++)
X		if (strcmp(i->name, idvar) == 0)
X			return gotit;
X
X	for (i = othertb; i->name; i++)
X		if (strcmp(i->name, idvar) == 0)
X			return gotit;
X
X	if (strlen(idvar) == 1 && isalpha(*idvar)) {
X		for (i = milzone; i->name; i++)
X			if (strcmp(i->name, idvar) == 0)
X				return gotit;
X	}
X
X	return ID;
X}
X
Xtime_t
Xgetdate(p, now)
Xchar *p;
Xstruct timeb *now;
X{
X#define mcheck(f)	if (f>1) err++
X	time_t monthadd();
X	int err;
X	struct tm *lt;
X	struct timeb ftz;
X
X	time_t sdate, tod;
X
X	lptr = p;
X	if (now == ((struct timeb *) NULL)) {
X		now = &ftz;
X		ftime(&ftz);
X	}
X	lt = localtime(&now->time);
X	year = lt->tm_year;
X	month = lt->tm_mon+1;
X	day = lt->tm_mday;
X	relsec = 0; relmonth = 0;
X	timeflag=zoneflag=dateflag=dayflag=relflag=0;
X	ourzone = now->timezone;
X	daylight = MAYBE;
X	hh = mm = ss = 0;
X	merid = 24;
X
X	if (err = yyparse()) return (-1);
X
X	mcheck(timeflag);
X	mcheck(zoneflag);
X	mcheck(dateflag);
X	mcheck(dayflag);
X
X	if (err) return (-1);
X	if (dateflag || timeflag || dayflag) {
X		sdate = dateconv(month,day,year,hh,mm,ss,merid,ourzone,daylight);
X		if (sdate < 0) return -1;
X	}
X	else {
X		sdate = now->time;
X		if (relflag == 0)
X			sdate -= (lt->tm_sec + lt->tm_min*60 +
X				lt->tm_hour*(60L*60L));
X	}
X
X	sdate += relsec;
X	sdate += monthadd(sdate, relmonth);
X
X	if (dayflag && !dateflag) {
X		tod = dayconv(dayord, dayreq, sdate);
X		sdate += tod;
X	}
X
X	/*
X	** Have to do *something* with a legitimate -1 so it's distinguishable
X	** from the error return value.  (Alternately could set errno on error.)
X	*/
X	return (sdate == -1) ? 0 : sdate;
X}
X
Xyyerror(s) char *s;
X{}
END_OF_FILE_getdate.y
    size="`wc -c < getdate.y`"
    if test 13345 -ne "$size" ; then
	echo "getdate.y: extraction error - got $size chars"
    fi
fi
if test -f Makefile -a "$1" != "-c" ; then
    echo "Makefile: file exists - will not be overwritten"
else
    echo "x - Makefile (file 11 of 12, 1697 chars)"
    sed -e 's/^X//' << 'END_OF_FILE_Makefile' > Makefile
X
X# %Z% %M% %I% %E% %U%
X# Makefile for "brkdig" (generated by /local/bin/makemake version 1.00.08)
X# Created by news@vector on Thu Sep 21 20:18:05 CDT 1989
X
XSHELL = /bin/sh
XCC = cc
XDEFS = 
XCOPTS = -O
XLOPTS = 
XLIBS = -lx
XDEBUG = -g -DDEBUG
XLINTFLAGS = -DLINT
X
XTARG = brkdig
XOTHERS = getdate.c
X
XSRCS = brkdig.c loaddefs.c block.c cleanhdr.c header.c fixfrom.c getdate.c
X
XOBJS = brkdig.o loaddefs.o block.o cleanhdr.o header.o fixfrom.o getdate.o
X
X# Any edits below this line will be lost if "makemake" is rerun!
X# Commands may be inserted after the '#%custom' line at the end of this file.
X
XCFLAGS = $(COPTS) $(DEFS) # $(DEBUG)
XLFLAGS = $(LOPTS) # $(DEBUG)
X
Xall:		$(TARG) $(OTHERS)
Xinstall:	all		; inst Install
Xclean:				; rm -f $(TARG) $(OBJS) a.out core $(TARG).lint
Xclobber:	clean		; inst -u Install
Xlint:		$(TARG).lint
X
X$(TARG):		$(OBJS)
X		$(CC) $(LFLAGS) -o $@ $(OBJS) $(LIBS)
X
X$(TARG).lint:	$(TARG)
X		lint $(LINTFLAGS) $(DEFS) $(SRCS) $(LIBS) > $@
X
Xbrkdig.o: brkdig.c brkdig.h
Xloaddefs.o: brkdig.h loaddefs.c
Xblock.o: block.c brkdig.h
Xcleanhdr.o: brkdig.h cleanhdr.c
Xheader.o: brkdig.h header.c
Xfixfrom.o: brkdig.h fixfrom.c
Xgetdate.o: getdate.c getdate.y
X
Xmake:		;
X		/local/bin/makemake -i -v1.00.08 -aMakefile \
X		    -DSHELL='$(SHELL)' -DCC='$(CC)' -DDEFS='$(DEFS)' \
X		    -DCOPTS='$(COPTS)' -DLOPTS='$(LOPTS)' -DLIBS='$(LIBS)' \
X		    -DDEBUG='$(DEBUG)' -DLINTFLAGS='$(LINTFLAGS)' \
X		    -DOTHERS='$(OTHERS)' $(TARG) $(SRCS)
X
X#%custom - commands below this line will be maintained if 'makemake' is rerun
X
XARLIST =								\
X	README Defs-example brkdig.h brkdig.c				\
X	loaddefs.c block.c header.c cleanhdr.c fixfrom.c		\
X	getdate.y Makefile testaddr
X
Xshar:	; shar $(ARLIST) > brkdig.shar
X
END_OF_FILE_Makefile
    size="`wc -c < Makefile`"
    if test 1697 -ne "$size" ; then
	echo "Makefile: extraction error - got $size chars"
    fi
fi
if test -f testaddr -a "$1" != "-c" ; then
    echo "testaddr: file exists - will not be overwritten"
else
    echo "x - testaddr (file 12 of 12, 744 chars)"
    sed -e 's/^X//' << 'END_OF_FILE_testaddr' > testaddr
X:
X# %Z% %M% %I% %E% %U%
X#
X# testaddr - Determine if an address is reachable.
X#
X# Mon Dec  4 08:46:36 1989 - Chip Rosenthal <chip@chinacat.Lonestar.ORG>
X#	Original composition.
X#
X# This script uses smail2.5 to determine if an address is known.  It is
X# required with "brkdig" because smail always returns a zero exit status
X# with the "-A" option.  This script is intended for use in the "ADDR_CHECK"
X# parameter in the "brkdig" definitions file.
X
XUSAGE="usage: $0 addr"
XSMAIL=/bin/smail
X
Xif [ $# -ne 1 ] ; then
X    echo "$USAGE" 1>&2
X    exit 1
Xfi
X
Xaddr="$1"					# addr to check
Xpath=`$SMAIL -A $addr 2>&1`			# let smail route it
Xtest "$path" != "" -a "$path" != "$addr"	# examine smail path result
Xexit $?						# indicate whether addr worked
X
END_OF_FILE_testaddr
    size="`wc -c < testaddr`"
    if test 744 -ne "$size" ; then
	echo "testaddr: extraction error - got $size chars"
    fi
fi
echo "done - 12 files extracted"
exit 0