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