[alt.sources] News/mail gatewaying software, ALPHA RELEASE, Part02/03

rsalz@bbn.com (Rich Salz) (02/21/89)

This code gateways between newsgroups and mailing lists.  It definitely
has bugs, use at your own risk.  Please send bug reports back to me.
For more details, see the file "README" in the first shar.

Please don't redistribute this code; wait for the "real" version in
comp.sources.unix in a month or two.

Hope to hear from you,
	/rich $alz

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 3)."
# Contents:  hdr.c news2mail.c regex.3 rfc822.c
# Wrapped by rsalz@fig.bbn.com on Mon Feb 20 18:36:42 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'hdr.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'hdr.c'\"
else
echo shar: Extracting \"'hdr.c'\" \(10645 characters\)
sed "s/^X//" >'hdr.c' <<'END_OF_FILE'
X/*
X**  Header-cracking and address re-writing routines, with sincere
X**  apologies to Upas and Sendmail.
X*/
X#include "gate.h"
X#ifdef	HAVE_NETDB
X#include <netdb.h>
X#endif	/* HAVE_NETDB */
X#ifdef	RCSID
Xstatic char RCS[] =
X	"$Header$";
X#endif	/* RCSID */
X
X
X#ifdef	TEST
X#define dprintf(string, buff)	(void)printf((string), (buff))
X#else
X#define dprintf(string, buff)	/* NULL */
X#endif	/* TEST */
X
X
X/*
X**  List of domains that we recognize.  CSNET is special -- leave it first!
X*/
XSTATIC char	*Domains[] = {
X    "RELAY.CS.NET",	".MAILNET",	".BITNET",	".CSNET",
X    ".UUCP",		".DEC",		".CDN",		".OZ",
X    ".EDU",		".COM",		".GOV",		".MIL",
X    ".ORG",		".NET",		".UK",		".AU",
X    NULL
X};
X
X
X/*
X**  Local user?
X*/
XSTATIC int
XLocal(p)
X    register char	*p;
X{
X    for ( ; *p; p++)
X	if (NETCHR(*p))
X	    break;
X    return(*p == '\0');
X}
X
X
X/*
X**  Case-insensitive strncmp.
X*/
XSTATIC char *
XFound(dp, p)
X    register char	*dp;
X    register char	*p;
X{
X    register char	*q;
X    register char	*r;
X
X    for ( ; *p; p++)
X	if (CHREQ(*p, *dp)) {
X	    for (q = p, r = dp; *r && CHREQ(*q, *r); q++, r++)
X		;
X	    if (*r == '\0')
X		return(p);
X	}
X
X    return(NULL);
X}
X
X
X/*
X**  Find all domain names and make them uppercase:
X**	joe%site.edu@relay.cs.net --> joe%site.EDU@RELAY.CS.NET
X*/
XSTATIC void
XCasify(p)
X    register char	 *p;
X{
X    register char	 *q;
X    register char	 *r;
X    register char	**dp;
X
X    for (dp = Domains; *dp; dp++)
X	for (q = p; q = Found(*dp, q); )
X	    for (r = *dp; *r; *q++ = *r++)
X		;
X}
X
X
X/*
X**  Handle route-addresses:
X**	@cruft:joe@site --> joe@site
X*/
XSTATIC void
XRouteAddr(p)
X    register char	*p;
X{
X    register char	*q;
X    register char	*r;
X
X    for (r = p; *p == '@'; p = q)
X	if ((q = IDX(p, ':')) && IDX(q, '@'))
X	    *q++ = '\0';
X	else
X	    break;
X    if (p > r)
X	/* Avoid overlaping strcpy() call. */
X	while (*r++ = *p++)
X	    ;
X}
X
X/*
X**  Does the pattern exist in the character range between p and end?
X*/
XSTATIC int
XBetween(p, pat, q)
X    register char	*p;
X    register char	*pat;
X    register char	*q;
X{
X    register char	*r;
X
X    for (r = pat + strlen(pat); p < q && r >= pat && *--q == *--r; )
X	;
X    return(*r == '\0');
X}
X
X
X/*
X**  Handle the '%' syntax:
X**	joe%site.EDU@gateway.DOMAIN -> joe@site.EDU
X*/
XSTATIC void
XPercent(p)
X    register char	 *p;
X{
X    register char	 *q;
X    register char	 *r;
X    register char	**dp;
X
X    while ((r = IDX(p, '%')) && (q = IDX(r, '@'))) {
X	for (dp = &Domains[1]; *dp; dp++)
X	    if (Between(r, *dp, q)) {
X		*RDX(p, '@') = '\0';
X		*RDX(p, '%') = '@';
X		break;
X	    }
X	if (*dp == NULL)
X	    break;
X    }
X}
X
X
X/*
X**  Handle CSNET, which is slowly going domainist:
X**	joe%site@RELAY.CS.NET --> joe@site.CSNET
X**	joe%site.EDU@RELAY.CS.NET --> joe@site.EDU
X*/
XSTATIC void
XCsnet(p)
X    register char	*p;
X{
X    register char	*q;
X    register char	*r;
X
X    if ((q = RDX(p, '@'))
X     && strcmp(q, "@RELAY.CS.NET") == 0
X     && (r = RDX(p, '%'))) {
X	*RDX(p, '@') = '\0';
X	*r = '@';
X	if (IDX(r, '.') == NULL)
X	    Strcat(p, ".CSNET");
X    }
X}
X
X
X/*
X**  Handle hybrid "!" and "@" addresses:
X**	a!site!joe@site --> joe@site
X**	a!site!joe --> joe@site.UUCP
X**	a!site.EDU!joe --> joe@site.EDU
X*/
XSTATIC void
XHybrid(p)
X    register char	*p;
X{
X    register char	*q;
X    register char	*user;
X    char		 buff[SM_SIZE];
X
X    if (user = RDX(p, '!')) {
X	*user++ = '\0';
X	if (q = IDX(user, '@'))
X	    *q = '\0';
X	q = (q = RDX(p, '!')) ? q + 1 : p;
X	Sprintf(buff, "%s@%s", user, q);
X	if (IDX(q, '.') == NULL)
X	    Strcat(buff, ".UUCP");
X	Strcpy(p, buff);
X    }
X}
X
X
X/*
X**  Handle special cases for DEC and Australia:
X**	user@site.OZ --> user@site.OZ.AU
X**	user@dec-site.UUCP --> user@site.dec.com
X*/
XSTATIC void
XEndpart(p)
X    register char	*p;
X{
X    register char	*r;
X    register char	*q;
X
X    if ((r = IDX(p, '@')) && (q = IDX(r, '.'))
X     && strncmp(++r, "dec-", 4) == 0 && strncmp(q, ".UUCP", 5) == 0) {
X	for (; r[4] != '.'; r++)
X	    *r = r[4];
X	Strcpy(r, ".DEC.COM");
X    }
X    else if (r = RDX(p, '.'))
X	if (r[1] == 'D' && r[2] == 'E' && r[3] == 'C' && r[4] == '\0')
X	    Strcpy(&r[4], ".COM");
X	else if (r[1] == 'O' && r[2] == 'Z' && r[3] == '\0')
X	    Strcpy(&r[3], ".AU");
X}
X
X
X/*
X**  General address canonicalizer.
X*/
XSTATIC char *
XFixAddress(p)
X    register char	*p;
X{
X    static char		 buff[1024];
X    char		 host[128];
X
X    if (Local(p)) {
X	if (gethostname(host, sizeof host) < 0) {
X	    Fprintf(stderr, "%s:  Can't get my hostname, %s.\n",
X		    Pname, Estring());
X	    exit(EX_TEMPFAIL);
X	}
X	Sprintf(buff, "%s@%s", p, host);
X    }
X    else {
X	Strcpy(buff, p);
X	Casify(buff);
X	dprintf("   Casify returns %s\n", buff);
X	RouteAddr(buff);
X	dprintf("RouteAddr returns %s\n", buff);
X	Percent(buff);
X	dprintf("  Percent returns %s\n", buff);
X	Csnet(buff);
X	dprintf("    Csnet returns %s\n", buff);
X	Hybrid(buff);
X	dprintf("   Hybrid returns %s\n", buff);
X	Endpart(buff);
X	dprintf("  Endpart returns %s\n", buff);
X    }
X    return(buff);
X}
X
X
X
X/*
X**  This subroutine is a concession to the realities of the Internet
X**  and the USENET. Much as the idea is distasteful and likely
X**  to get me in trouble, I have to hack message-ids into a format
X**  that the USENET won't choke on.  Pray that if we're doing multiple
X**  insertion point gatewaying that ALL the gateways mung exactly the
X**  same things.
X**
X**  (Death to HERMES! Death to UNIX/MM-11! Death to EAN!)
X*/
XSTATIC int
XFixMessageID(s)
X    register char	*s;
X{
X    register int	 atdot;
X    register int	 closed;
X
X    /* Quickie tests -- why waste time? */
X    if (*s != '<')
X	return(FALSE);
X
X    for (atdot = FALSE, closed = FALSE; *++s; )
X	switch (*s) {
X	default:
X	    if (!isascii(*s) || iscntrl(*s) || isspace(*s))
X		return(FALSE);
X	    break;
X	case '<':
X	    /* Already got one. */
X	    return(FALSE);
X	case '>':
X	    /* I hope no one is stupid enough to quote this... */
X	    closed = TRUE;
X	    s[1] = '\0';
X	    break;
X	case '.':
X	case '@':
X	    /* We should check for a domain spec, not just either/or. */
X	    atdot = TRUE;
X	    break;
X	case '\t':
X	case ' ':
X	case '/':
X	    /* Avoid various problem characters. */
X	    *s = '.';
X	    break;
X	}
X
X    return(atdot && closed);
X}
X
X
X/*
X**  Fix up the contents of In-Reply-To: fields and References: fields.
X*/
XSTATIC void
XFixReferences(hp)
X    register HBUF		*hp;
X{
X    register char		*cp;
X    register char		*ep;
X    register char		*p;
X    register char		*max;
X    char			 scratch[LG_SIZE];
X
X    cp = hp->followid;
X    max = cp + strlen(cp);
X    for (p = scratch; cp = IDX(cp, '<'); ) {
X	if ((ep = IDX(cp, '>')) == NULL
X	 || ((ep - cp) + 1) > sizeof scratch - (p - scratch + 2))
X	    /* Unterminated ID, or no more room. */
X	    break;
X
X	if (FixMessageID(cp)) {
X	    if (p > scratch) {
X		*p++ = ' ';
X		*p++ = '\0';
X	    }
X	    p += APPEND(p, cp);
X	}
X	cp = ep + 2;
X	if (cp >= max)
X	    break;
X    }
X    Strcpy(hp->followid, scratch);
X}
X
X
X
X/*
X**  Convert string to upper case.
X*/
XSTATIC char *
XMakeUpper(s)
X    register char	*s;
X{
X    register char	*p;
X
X    if (s)
X	for (p = s; *p; p++)
X	    if (islower(*p))
X		*p = toupper(*p);
X    return(s);
X}
X
X
X/*
X**  Count the number of '@' in the string.
X*/
XSTATIC int
XAtCount(s)
X    register char	*s;
X{
X    register int	 n;
X
X    for (n = 0; *s; s++)
X	if (*s == '@')
X	    n++;
X    return(n);
X}
X
X
X/*
X**  Canonicalize the "From:" line into the form
X**	From: local-part@domain (full-name)
X** RFC822 doesn't require the comment to be at the end of the string
X** like that.
X*/
XSTATIC void
XFixFrom(hp)
X    register HBUF		*hp;
X{
X    register char		*p;
X#ifdef	HAVE_NETDB
X    register struct hostent	*host;
X#endif	/* HAVE_NETDB */
X    char			 address[LG_SIZE];
X    char			 fullname[LG_SIZE];
X    char			 scratch[sizeof address];
X
X    /* We should handle "Full-Name:" too, but it doesn't get read by the
X     * news header reader. */
X    (void)CrackFrom(address, fullname, hp->from);
X    Strcpy(address, FixAddress(address));
X
X    if (AtCount(address) == 1) {
X	p = IDX(address, '@');
X	*p++ = '\0';
X
X#ifdef	HAVE_NETDB
X	/* If we can find the host's official name, use that, instead of
X	 * what they gave us, because some people don't know how to
X	 * write sendmail.cf files right...  We should also check for MX
X	 * records. */
X	if (host = gethostbyname(MakeLower(strcpy(scratch, p))))
X	    p = MakeUpper(host->h_name);
X	else if (IDX(scratch, '.') == NULL
X	      && (host = gethostbyname(strcat(scratch, ".arpa"))))
X	    /* If it doesn't have a dot, they must be using an alias.
X	     * Add .ARPA and try again... */
X	    p = MakeUpper(host->h_name);
X#endif	/* HAVE_NETDB */
X
X	Sprintf(scratch, "%s@%s", address, p);
X	Strncpy(address, scratch, sizeof address);
X	address[sizeof address - 1] = '\0';
X
X#ifdef	GATEWAY
X	Sprintf(scratch, "%s!%s!%s", GATEWAY, p, address);
X#else
X	Sprintf(scratch, "%s!%s", p, address);
X#endif	/* GATEWAY */
X    }
X    else {
X#ifdef	GATEWAY
X	Sprintf(scratch, "%s!%s", GATEWAY, address);
X#else
X	Strcpy(scratch, address);
X#endif	/* GATEWAY */
X    }
X    Strncpy(hp->path, scratch, sizeof hp->path);
X    hp->path[sizeof hp->path - 1] = '\0';
X
X    if (fullname[0]) {
X	p = address + strlen(address);
X	*p++ = ' ';
X	*p++ = '(';
X	p += APPEND(p, fullname);
X	*p++ = ')';
X	*p++ = '\0';
X    }
X
X    /* Stick the canonicalized From: back in. */
X    Strcpy(hp->from, address);
X}
X
X
X#define ERROR "\
XMessage-ID syntax error.\n\
X*** Please refer to page 23, paragraph 4.6.1. and Appendix D\n\
X*** of NIC RFC #822 for the correct syntax, and fix your mailer."
X
X/*
X** Check an RFC822 header for validity and hack it to RFC850 spec.
X** returns NULL for everything OK, or a character pointer to an
X** error message.
X*/
Xchar *
XHackHeader(hp, SubjReqd)
X    register HBUF		*hp;
X    int				 SubjReqd;
X{
X#ifdef	SUBMIT
X    /* MMDF doesn't always have a Message-ID. */
X    if (hp->ident[0] && !FixMessageID(hp->ident))
X	return(ERROR);
X#else
X    /* Sendmail always has a Message-ID */
X    if (hp->ident[0] == '\0')
X	return("Message-ID header missing");
X    if (!FixMessageID(hp->ident))
X	return(ERROR);
X#endif	/* SUBMIT */
X
X    /* Newsgroups */
X    if (hp->nbuf[0] == '\0')
X	return("Newsgroups header missing");
X
X    /* Subject */
X    if (hp->title[0] == '\0') {
X	if (SubjReqd)
X	    return("Subject header missing");
X	Strcpy(hp->title, "(none)");
X    }
X
X    /* From */
X    if (hp->from[0] == '\0')
X	return("From header missing");
X    FixFrom(hp);
X
X    /* References and In-Reply-To */
X    if (hp->followid[0]) 
X	FixReferences(hp);
X
X    return(NULL);
X}
X
X
X#ifdef	TEST
Xmain()
X{
X    char	buff[256];
X    int		i;
X
X    if (i = isatty(0))
X	(void)printf("Enter addresses:\n");
X    for ( ; ; ) {
X	if (i)
X	    (void)printf(">  ");
X	if (gets(buff) == NULL || buff[0] == '\0')
X	    break;
X	(void)printf("\t%s -> %s\n\n", buff, FixAddress(buff));
X    }
X
X    exit(0);
X}
X#endif	/* TEST */
END_OF_FILE
if test 10645 -ne `wc -c <'hdr.c'`; then
    echo shar: \"'hdr.c'\" unpacked with wrong size!
fi
# end of 'hdr.c'
fi
if test -f 'news2mail.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'news2mail.c'\"
else
echo shar: Extracting \"'news2mail.c'\" \(10061 characters\)
sed "s/^X//" >'news2mail.c' <<'END_OF_FILE'
X/*
X**  NEWS2MAIL
X**  Read a news article on standard input, and send it to the mailing
X**  list as directed by the command-line arguments.  It does some
X**  parsing and converting of news headers into mail headers.
X**
X**  This program wants to lie to sendmail, so it should be setuid to
X**  one of the "trusted" users as listed in your sendmail.cf file.
X**
X*/
X#include "gate.h"
X#include <sys/stat.h>
X#ifdef	RCSID
Xstatic char RCS[] =
X	"$Header$";
X#endif	/* RCSID */
X
X
X/* Offsets into our argument list. */
X#define LISTNAME	1
X#define CONTACTADDR	2
X#define REQUESTNAME	3
X#define HOST		4
X#define ARTICLE		5
X
X/* Flags for special header lines. */
X#define HDR_NORM	0
X#define HDR_SUBJ	1
X#define HDR_CTRL	2
X#define HDR_REFS	3
X#define HDR_PATH	4
X#define HDR_FROM	5
X
X
X/* Header-cracking datatype. */
Xtypedef struct {
X    int		 Flag;
X    char	 Value[SM_SIZE];
X    int		 Length;
X    char	*Tag;
X} HEADER;
X
X
X/* The headers we care about. */
XSTATIC HEADER	 Table[] = {
X    {	HDR_NORM,	"",	  5,	"Date:"		},
X    {	HDR_PATH,	"",	  5,	"Path:"		},
X    {	HDR_FROM,	"",	  5,	"From:"		},
X    {	HDR_SUBJ,	"",	  8,	"Subject:"	},
X    {	HDR_CTRL,	"",	  8,	"Control:"	},
X    {	HDR_NORM,	"",	  9,	"Reply-To:"	},
X    {	HDR_NORM,	"",	 11,	"Message-ID:"	},
X    {	HDR_REFS,	"",	 11,	"References:"	},
X    {	HDR_NORM,	"",	 13,	"Organization:"	}
X};
X
X/* Program name. */
Xchar	*Pname;
X
X
X/*
X**  Figure out the return address, by doing some optimization on the
X**  path.  If we find an Internet host or a UUCP neighbor, remove all
X**  hosts before that one from the path.
X*/
XSTATIC char *
XEditPath(path, Host)
X    register char	*path;
X    char		*Host;
X{
X    static char		**Hinet;
X    static char		**Huucp;
X    static char		**Lsys;
X    static char		  buff[SM_SIZE];
X    register char	 *p;
X    register char	 *q;
X    register char	**V;
X    register int	  ac;
X    register int	  i;
X    register int	  uucppal;
X    register int	  inetpal;
X    FILE		 *F;
X    FILE		 *P;
X    struct stat		  Sb1;
X    struct stat		  Sb2;
X    char		**av;
X    char		 *Inetsite;
X
X    if ((ac = Split(path, &av, '!')) == 2) {
X	/* Simple case:  "site!user" */
X	Strcpy(buff, av[1]);
X	SplitFree(&av);
X	return(buff);
X    }
X
X    /* Initialize.  This is silly for now, but eventually we might want
X     * to be able to handle a batched feed. */
X#ifdef	UUNAME
X    if (Lsys == NULL) {
X	p = UUNAME;
X	/* If someone diddled L.sys, rebuild uuname output. */
X	if (stat(L_SYS, &Sb1) < 0 || stat(p, &Sb2) < 0
X	  || Sb1.st_mtime >= Sb2.st_mtime)
X	    if ((F = fopen(p, "w"))  == NULL)
X		Fprintf(stderr, "%s:  Can't create %s, %s.\n",
X			Pname, p, Estring());
X	    else {
X		if ((P = popen("exec uuname", "r")) == NULL)
X		    Fprintf(stderr, "%s:  popen failed, %s.\n",
X			    Pname, Estring());
X		else {
X		    while (fgets(buff, sizeof buff, P))
X			Fputs(buff, F);
X		    if (pclose(P))
X			Fprintf(stderr, "%s:  pclose failed, %s.\n",
X				Pname, Estring());
X		}
X		if (fclose(F) == EOF)
X		    Fprintf(stderr, "%s:  Error closing %s, %s.\n",
X			    Pname, p, Estring());
X	    }
X
X	/* Slurp up names of UUCP hosts we talk to. */
X	Lsys = ReadFile(p);
X
X#ifdef	UUCP_INET
X	/* Slurp up the UUCP->Internet name mappings. */
X	for (Huucp = ReadFile(UUCP_INET), i = 1; Huucp[i]; i++)
X	    ;
X	for (Hinet = NEW(char*, i), i = 0; p = Huucp[i]; i++) {
X	    while (*p && !WHITE(*p))
X		p++;
X	    if (*p)
X		for (*p++ = '\0'; *p && WHITE(*p); p++)
X		    ;
X	    Hinet[i] = p;
X	}
X#endif	/* UUCP_INET */
X    }
X#endif	/* UUNAME */
X
X    /* Scan the path, noting if we find a UUCP or Internet neighbor. */
X    for (uucppal = 1, inetpal = 0, i = 0; i < ac; i++) {
X	for (V = Lsys; *V; V++)
X	    if (EQ(av[i], *V))
X		uucppal = i;
X	for (V = Huucp; *V; V++)
X	    if (EQ(av[i], *V)) {
X		inetpal = i;
X		Inetsite = Hinet[V - Huucp];
X	    }
X    }
X
X    if (inetpal < uucppal) {
X	/* No Internet site found, turn a!b!c into a!b!c@this-host */
X	for (p = buff + APPEND(buff, av[uucppal]); ++uucppal < ac; ) {
X	    *p++ = '!';
X	    p += APPEND(p, av[uucppal]);
X	}
X	*p++ = '@';
X	Strcpy(p, Host);
X    }
X    else if (inetpal == ac - 1)
X	/* Turn a!b!inet!user into user@inet.domain.name */
X	Sprintf(buff, "%s@%s", av[ac], Inetsite);
X    else {
X	/* Turn a!inet!b!user into b!user@inet.domain.name */
X	for (p = buff + APPEND(buff, av[++inetpal]); ++inetpal < ac; ) {
X	    *p++ = '!';
X	    p += APPEND(p, av[inetpal]);
X	}
X	*p++ = '@';
X	Strcpy(p, Inetsite);
X    }
X
X    /* Convert all but the last "@" to "%" (fie on decwrl and psuecl!). */
X    if (p = IDX(buff, '@'))
X	for ( ; q = IDX(p + 1, '@'); p = q)
X	    *p = '%';
X
X    SplitFree(&av);
X    return(buff);
X}
X
X
X/*
X**  Hack up the references, taking only the last three.
X*/
XSTATIC char *
XTrimReferences(refs)
X    char		 *refs;
X{
X    static char		  buff[SM_SIZE];
X    register char	 *p;
X    register int	  i;
X    register int	  ac;
X    char		**av;
X
X    if (ac = Split(refs, &av, '\0')) {
X	/* Tricky.  If there are five references, we want subscripts 2,3,4. */
X	i = ac < 3 ? 0 : ac - 3;
X	for (p = buff + APPEND(buff, av[i]); ++i < ac; ) {
X	    *p++ = ',';
X	    *p++ = ' ';
X	    p += APPEND(p, av[i]);
X	}
X	SplitFree(&av);
X    }
X    else
X	buff[0] = '\0';
X    return(buff);
X}
X
X
Xmain(ac, av)
X    int			 ac;
X    register char	*av[];
X{
X    static char		 tmp[sizeof TEMPFILE];
X    register FILE	*F;
X    register HEADER	*hp;
X    register char	*p;
X    char		*sv[10];
X    char		 buff[BUFSIZ];
X    char		 Host[SM_SIZE];
X    char		 Fullname[SM_SIZE];
X
X    /* Arrange for logging. */
X    if (freopen(ERR_LOG, "a", stderr) == NULL)
X	/* Sigh; error in error handler.... */
X	(void)freopen("/dev/console", "w", stderr);
X
X    /* Check and parse JCL. */
X    Pname = (Pname = RDX(av[0], '/')) ? Pname + 1 : av[0];
X    if (ac != 5 && ac != 6) {
X	Fprintf(stderr, "%s:  Bad calling sequence.\n", Pname);
X	exit(EX_USAGE);
X    }
X    if (ac == 6 && freopen(av[ARTICLE], "r", stdin) == NULL) {
X	Fprintf(stderr, "%s:  Can't open %s, %s.\n",
X		Pname, av[ARTICLE], Estring());
X	exit(EX_NOINPUT);
X    }
X
X    /* Who are we? */
X    if (gethostname(Host, sizeof Host) < 0) {
X	Fprintf(stderr, "%s:  Can't get hostname, %s.\n", Pname, Estring());
X	exit(EX_TEMPFAIL);
X    }
X
X    /* Read headers, storing the ones we want. */
X    while (fgets(buff, sizeof buff, stdin)) {
X	if (p = IDX(buff, '\n'))
X	    *p = '\0';
X	else
X	    Fprintf(stderr, "%s:  Header line too long for me:\n\t%s\n",
X		    Pname, buff);
X	if (p == buff)
X	    /* Blank line means end of headers. */
X	    break;
X
X	for (hp = Table; hp < ENDOF(Table); hp++)
X	    if (EQn(hp->Tag, buff, hp->Length)) {
X		/* Skip whitespace. */
X		for (p = &buff[hp->Length]; *p && WHITE(*p); p++)
X		    ;
X		switch (hp->Flag) {
X		case HDR_SUBJ:
X		    if (!EQn(p, "cmsg", 4)) {
X			Strcpy(hp->Value, p);
X			break;
X		    }
X		    /* FALLTHROUGH */
X		case HDR_CTRL:
X		    /* Eat rest of message. */
X		    while (fgets(buff, sizeof buff, stdin))
X			;
X		    exit(EX_OK);
X		    /* NOTREACHED */
X		case HDR_NORM:
X		    Strcpy(hp->Value, p);
X		    break;
X		case HDR_FROM:
X		    /* Turn "joe@site.uucp (My Name)" into "(My Name)" */
X		    if (p = IDX(p, ' '))
X			Strcpy(Fullname, ++p);
X		    else
X			Fullname[0] = '\0';
X		    break;
X		case HDR_REFS:
X		    Strcpy(hp->Value, TrimReferences(p));
X		    break;
X		case HDR_PATH:
X		    Strcpy(hp->Value, EditPath(p, Host));
X		    break;
X		}
X	}
X    }
X
X    /* Set up temp output. */
X    if ((F = fopen(mktemp(strcpy(tmp, TEMPFILE)), "w")) == NULL) {
X	Fprintf(stderr, "%s:  Can't create %s, %s.\n", tmp, Estring());
X	exit(EX_CANTCREAT);
X    }
X
X    /* Print out a sanitized header for mail. */
X    Fprintf(F, "Received: from USENET by %s with netnews\n", Host);
X    Fprintf(F, "\tfor %s@%s (%s@%s);\n",
X	    av[CONTACTADDR], av[HOST], av[LISTNAME], av[HOST]);
X    Fprintf(F, "\tcontact usenet@%s if you have questions.\n", Host);
X    Fprintf(F, "To: %s@%s\n", av[LISTNAME], av[HOST]);
X    for (hp = Table; hp < ENDOF(Table); hp++)
X	if (hp->Flag == HDR_PATH) {
X	    Fprintf(F, "From: %s %s\n", hp->Value, Fullname);
X	    Fprintf(F, "Sender: %s@%s\n", av[REQUESTNAME], av[HOST]);
X	}
X	else if (hp->Value[0])
X	    Fprintf(F, "%s %s\n", hp->Tag, hp->Value);
X    Fprintf(F, "\n");
X
X    /* Dump the body of the message. */
X    while (fgets(buff, sizeof buff, stdin))
X	Fputs(buff, F);
X    if (fclose(F) == EOF)
X	Fprintf(stderr, "%s:  Error closing %s, %s.\n",
X		Pname, tmp, Estring());
X
X    /* Set I/O correctly.  Stderr is going to the log, stdin should be the
X     * message we created.  Easiest thing is to unlink an open file. */
X    if (freopen(tmp, "r", stdin) == NULL) {
X	Fprintf(stderr, "%s:  Can't open %s for reading, %s.\n",
X		Pname, tmp, Estring());
X	exit(EX_OSERR);
X    }
X    if (unlink(tmp) < 0)
X	Fprintf(stderr, "%s:  Can't unlink %s, %s.\n",
X		Pname, tmp, Estring());
X
X#ifdef	SENDMAIL
X    /* Build of the SENDMAIL argument vector. */
X    sv[0] = SENDMAIL;
X    /* I forget what this does. */
X    sv[1] = "-i";
X    /* Queued delivery. */
X    sv[2] = "-odq";
X    /* Set the "From:" address. */
X    sv[3] = "-f";
X    Sprintf(sv[4] = buff, "%s@%s", av[REQUESTNAME], av[HOST]);
X    /* Set the recipient. */
X    Sprintf(sv[5] = Host, "%s@%s", av[CONTACTADDR], av[HOST]);
X    sv[6] = NULL;
X#endif	/* SENDMAIL */
X
X#ifdef	SUBMIT
X    /* Build of the SUBMIT argument vector. */
X    sv[0] = SUBMIT;
X    /* Deliver to mailbox (m), deliver all mail now (ln), trust me (t), send
X     * no warnings (z), return to "Sender:" (s), get recipients from the
X     * "To:" address (xto*). */
X    sv[1] = "-mlntzsxto*";
X    /* Set the recipient. */
X    Sprintf(sv[2] = buff, "%s@%s", av[CONTACTADDR], av[HOST]);
X    sv[3] = NULL;
X#endif	/* SUBMIT */
X
X    /* Try to setuid, if desired.  Could "factor out" the execv from the
X     * #ifdef, but I hate the way the resultant "dangling else" looks. */
X#ifdef	TRUSTED
X    if (setuid(TRUSTED) < 0)
X	Fprintf(stderr, "%s:  Can't setuid to %d, %s.\n",
X		Pname, TRUSTED, Estring());
X    else
X	(void)execv(sv[0], sv);
X#else
X    (void)execv(sv[0], sv);
X#endif	/* TRUSTED */
X
X    /* Something failed; dump the message and quit */
X    Fprintf(stderr, "%s:  Can't execv %s, %s.\n", Pname, sv[0], Estring());
X    while (fgets(buff, sizeof buff, stdin))
X	Fputs(buff, stdout);
X    exit(EX_OSERR);
X}
END_OF_FILE
if test 10061 -ne `wc -c <'news2mail.c'`; then
    echo shar: \"'news2mail.c'\" unpacked with wrong size!
fi
# end of 'news2mail.c'
fi
if test -f 'regex.3' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'regex.3'\"
else
echo shar: Extracting \"'regex.3'\" \(7372 characters\)
sed "s/^X//" >'regex.3' <<'END_OF_FILE'
X.TH REGEX 3 LOCAL
X\" $Header: regex.3,v 1.3 88/08/10 12:24:15 rsalz Exp $
X.SH NAME
Xre_comp, re_exec, re_subs, re_modw, re_fail \- regular expression handling
X.SH SYNOPSIS
X.nf
X.B "char *re_comp(pat)"
X.B "char *pat;"
X.sp
X.B "re_exec(str)"
X.B "char *str;"
X.sp
X.B "re_subs(src, dst)"
X.B "char *src;"
X.B "char *dst;"
X.sp
X.B "void re_fail(msg, op)"
X.B "char *msg;"
X.B "char op;"
X.sp
X.B "void re_modw(str)"
X.B "char *str;"
X.fi
X.SH DESCRIPTION
XThese functions implement
X.IR ed (1)\-style
Xpartial regular expressions and supporting facilities.
X.PP
X.I Re_comp
Xcompiles a pattern string into an internal form (a deterministic finite\-state
Xautomaton) to be executed by
X.I re_exec
Xfor pattern matching.
X.I Re_comp
Xreturns zero if the pattern is compiled successfully, otherwise it returns an
Xerror message string.
XIf
X.I re_comp
Xis called with a null pointer or a pointer to an empty string, it returns
Xwithout changing the currently compiled regular expression.
X.PP
X.I Re_comp
Xsupports the same limited set of
X.I "regular expressions"
Xfound in
X.I ed
Xand Berkeley
X.IR regex (3)
Xroutines:
X.in +1i
X.de Ti
X.sp
X.ti -1i
X.ta 0.5i +0.5i +0.5i
X..
X.Ti
X[1]	\fIchar\fR	Matches itself, unless it is a special
Xcharacter (meta\-character): \fB. \e [ ] * + ^ $\fR
X.Ti
X[2]	\fB.\fR	Matches \fIany\fR character.
X.Ti
X[3]	\fB\e\fR	Matches the character following it, except
Xwhen followed by a digit, \fB(\fR, \fB)\fR, \fB<\fR or \fB>\fR
X(see [7], [8] and [9]).
XIt is used as an escape character for all other meta\-characters, and itself.
XWhen used in a set ([4]), it is treated as an ordinary character.
X.Ti
X[4]	\fB[\fIset\fB]\fR	Matches one of the characters in the set.
XIf the first character in the set is \fB^\fR, it matches a character \fInot\fP
Xin the set.
Xthe shorthand
X.IR S \- E
Xspecifies the set of characters
X.I S
Xthrough
X.IR E ,
Xinclusive.
XThe special characters \fB]\fR and \fB\-\fR have no special meaning if they
Xappear as the first characters in the set.
X.nf
X.ta \w'[a\-zA\-Z0\-9]    'u
XExamples	Match
X[a\-z]		any lowercase alpha
X[^]\-]		any char except ] and \-
X[^A\-Z]		any char except uppercase alpha
X[a\-zA\-Z0\-9]	any alphanumeric
X.fi
X.Ti
X[5]	\fB*\fR	Any regular expression form [1] to [4], followed by the
Xclosure character (*) matches zero or more matches of that form.
X.Ti
X[6]	\fB+\fR	Same as [5], except it matches one or more.
X.Ti
X[7]	\e\|( \e)	A regular expression in the form [1] to [10], enclosed
Xas \e\|(\fIform\fR\e) matches what form matches.
XThe enclosure creates a set of tags, used for [8] and for pattern
Xsubstitution in
X.IR re_subs .
XThe tagged forms are numbered starting from one.
X.Ti
X[8]	\ed	A \e followed by a digit matches whatever a previously tagged
Xregular expression ([7]) matched.
X.Ti
X[9]	\fB\e<\fR	Matches the beginning of a \fIword\fR; that is,
Xan empty string followed by a letter, digit, or _ and not preceded by a
Xletter, digit, or _ .
X.Ti
X	\fB\e>\fR	Matches the end of a \fIword\fR; that is, an empty
Xstring preceded by a letter, digit, or _ , and not followed by a letter,
Xdigit, or _ .
X.Ti
X[10]		A composite regular expression \fIxy\fR where \fIx\fR and
X\fIy\fR are in the form of [1] to [10] matches the longest match of \fIx\fR
Xfollowed by a match for \fIy\fR.
X.Ti
X[11]	\fB^ $\fR	A regular expression starting with a \fB^\fR character
Xand/or ending with a \fB$\fR character, restricts the pattern matching to the
Xbeginning of the line, and/or the end of line (anchors).
XElsewhere in the pattern, \fB^\fR and \fB$\fR are treated as ordinary
Xcharacters.
X.in -1i
X.PP
X.I Re_exec
Xexecutes the internal form produced by
X.I re_comp
Xand searches the argument string for the regular expression described
Xby the internal form.
X.I Re_exec
Xreturns 1 if the last regular expression pattern is matched within the string,
X0 if no match is found.
XIn case of an internal error (corrupted internal form),
X.I re_exec
Xcalls the user\-supplied
X.I re_fail
Xand returns 0.
X.PP
XThe strings passed to both
X.I re_comp
Xand
X.I re_exec
Xmay have trailing or embedded newline characters, but must be properly
Xterminated with a NUL.
X.PP
X.I Re_subs
Xdoes
X.IR ed \-style
Xpattern substitution, after a successful match is found by
X.I re_exec.
XThe source string parameter to
X.I re_subs
Xis copied to the destination string with the following interpretation:
X.sp
X.in +1i
X.Ti
X[1]	&	Substitute the entire matched string in the destination.
X.Ti
X[2]	\e\fId\fR	Substitute the substring matched by a tagged subpattern
Xnumbered \fId\fR, where \fId\fR is between 1 and 9, inclusive.
X.Ti
X[3]	\e\fIc\fR	Treat the next character literally, unless the
Xcharacter is a digit ([2]).
X.in -1i
X.PP
XIf the copy operation with the substitutions is successful,
X.I re_subs
Xreturns 1.
XIf the source string is corrupted, or the last call to
X.I re_exec
Xfails, it returns 0.
X.PP
X.I Re_modw
Xis used to
Xadd new characters into an internal table to
Xchange the re_exec's understanding of what
Xa \fIword\fR should look like, when matching with \fB\e<\fR and \fB\e>\fR
Xconstructs. If the string parameter is 0 or null string,
Xthe table is reset back to the default, which contains \fBA\-Z a\-z 0\-9 _\fR .
X.PP
X.I Re_fail
Xis a user\-supplied routine to handle internal errors.
X.I Re_exec
Xcalls
X.I re_fail
Xwith an error message string, and the opcode character that caused the error.
XThe default
X.I re_fail
Xroutine simply prints the message and the opcode character to the standard
Xerror and calls
X.IR exit (2).
X.SH EXAMPLES
XFor additional details, refer to the sources.
X.PP
X.RS
X.nf
X.ta \w'\e\|(foo\e)[1\-3]\e1    'u
Xfoo*.*	fo foo fooo foobar fobar foxx ...
X
Xfo[ob]a[rz]	fobar fooar fobaz fooaz
X
Xfoo\e\e+	foo\e foo\e\e foo\e\e\e  ...
X
X\e\|(foo\e)[1\-3]\e1	foo1foo foo2foo foo3foo
X(This is the same as \fIfoo[1\-3]foo\fP, but it takes less internal space.)
X
X\e\|(fo.*\e)\-\e1	foo\-foo fo\-fo fob\-fob foobar\-foobar ...
X.fi
X.RE
X.SH DIAGNOSTICS
X.I Re_comp
Xreturns one of the following strings if an error occurs:
X.RS
X.nf
X.I "No previous regular expression"
X.I "Empty closure"
X.I "Illegal closure"
X.I "Cyclical reference"
X.I "Undetermined reference"
X.I "Unmatched \e\|("
X.I "Missing ]"
X.I "Null pattern inside \e\|(\e)"
X.I "Null pattern inside \e<\e>"
X.I "Too many \e\|(\e) pairs"
X.I "Unmatched \e)"
X.fi
X.RE
X.SH REFERENCES
X.nf
X.IR "Software tools" ", Kernighan & Plauger."
X.IR "Software tools in Pascal" ", Kernighan & Plauger."
X.IR "Grep sources [rsx\-11 C dist]" ", David Conroy."
X.IR "Ed \- text editor" ", Unix Programmer's Manual."
X.IR "Advanced editing on Unix" ", B. W. Kernighan."
X.IR "RegExp sources" ", Henry Spencer."
X.fi
X.SH "HISTORY AND NOTES"
XThese routines are
X.IR Public Domain ,
Xyou can get them in source.
XThey are derived from various implementations found in the
X.I "Software Tools"
Xbooks, and David Conroy's
X.IR grep .
XThey are NOT derived from licensed/restricted software.
XFor more interesting/academic/complicated implementations, see Henry Spencer's
X.I regexp
Xroutines, or the
X.I "GNU Emacs"
Xpattern
Xmatching module.
X.PP
X.I Re_comp
Xand
X.I re_exec
Xgenerally perform at least as well as their licensed counterparts.
XIn a very few instances, they are about 10% to 15% slower.
X.SH AUTHOR
XOzan S. Yigit <yunexus!oz>.
X.br
XThis manual page was edited from the original by Rich $alz <rsalz@bbn.com>.
X.SH BUGS
XThe internal buffer for the compiled pattern is not checked for overflow;
Xthe size is currently 1024 bytes.
X.br
XThere are no doubt other bugs, too.
X.SH "SEE ALSO"
Xed(1), egrep(1), fgrep(1), grep(1)
END_OF_FILE
if test 7372 -ne `wc -c <'regex.3'`; then
    echo shar: \"'regex.3'\" unpacked with wrong size!
fi
# end of 'regex.3'
fi
if test -f 'rfc822.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rfc822.c'\"
else
echo shar: Extracting \"'rfc822.c'\" \(13702 characters\)
sed "s/^X//" >'rfc822.c' <<'END_OF_FILE'
X/*
X**  Routines to read and write mail and news headers.
X*/
X#include "gate.h"
X#include <time.h>
X#ifdef	RCSID
Xstatic char RCS[] =
X	"$Header$";
X#endif	/* RCSID */
X
X#ifdef	HAVE_TIMEB
X#include <sys/timeb.h>
X#else
Xstruct timeb {
X    time_t		time;
X    unsigned short	millitm;
X    short		timezone;
X    short		dstflag;
X};
X#endif	/* HAVE_TIMEB */
X
Xextern time_t		 time();
Xextern char		*asctime();
Xextern struct tm	*gmtime();
X
X#define HDR_OTHER		-1
X#define HDR_END			FALSE
X#define HDR_APPROVED		 1
X#define HDR_CONTROL		 2
X#define HDR_DATE		 3
X#define HDR_DISTRIBUTION	 4
X#define HDR_EXPIRE		 5
X#define HDR_FOLLOWTO		 6
X#define HDR_FROM 		 7
X#define HDR_KEYWORDS		 8
X#define HDR_MESSAGEID		 9
X#define HDR_NEWSGROUP 		10
X#define HDR_ORGANIZATION	11
X#define HDR_REFERENCES		12
X#define HDR_REPLYTO		13
X#define HDR_SENDER		14
X#define HDR_SUMMARY		15
X#define HDR_TITLE 		16
X
X
X/*
X**  The list of headers we recognize; all others are stripped.
X*/
Xtypedef struct {
X    char	*Name;
X    int		 Type;
X} HTYPE;
X
XSTATIC HTYPE	 HeaderList[] = {
X    {	"Approved:",		HDR_APPROVED		},
X    {	"Control:",		HDR_CONTROL		},
X    {	"Date:",		HDR_DATE		},
X    {	"Posted:",		HDR_DATE		},
X    {	"Distribution:",	HDR_DISTRIBUTION	},
X    {	"Expires:",		HDR_EXPIRE		},
X    {	"Followup-To:",		HDR_FOLLOWTO		},
X    {	"From:",		HDR_FROM		},
X    {	"Keywords:",		HDR_KEYWORDS		},
X    {	"Message-ID:",		HDR_MESSAGEID		},
X    {	"Newsgroups:",		HDR_NEWSGROUP		},
X    {	"Organization:",	HDR_ORGANIZATION	},
X    {	"In-Reply-To:",		HDR_REFERENCES		},
X    {	"References:",		HDR_REFERENCES		},
X    {	"Reply-To:",		HDR_REPLYTO		},
X    {	"Sender:",		HDR_SENDER		},
X    {	"Summary:",		HDR_SUMMARY		},
X    {	"Subject:",		HDR_TITLE		},
X    {	"Title:",		HDR_TITLE		},
X    { NULL }
X};
X
X
X
Xtypedef struct _LIST {
X    struct _LIST	*Next;
X    char		*Item;
X    unsigned int	 Length;
X} LLIST;
X
X#define	align(x)	((x) + ((x) % sizeof (long)))
X
X/*
X**  Recursively free a linked list.
X*/
XSTATIC void
XLLISTfree(lp)
X    register LLIST	*lp;
X{
X    if (lp->Next) {
X	LLISTfree(lp->Next);
X	free(lp->Item);
X    }
X}
X
X
X/*
X** allocate a new element in a linked list, along with enough space
X** at the end of the item for the next list element header.
X*/
XSTATIC LLIST *
XLLISTnew(lp, s, len)
X    register LLIST	*lp;
X    char		*s;
X    int			 len;
X{
X    if (s == NULL || lp == NULL)
X	return(NULL);
X
X    lp->Length = len;
X    len = align(len);
X    lp->Item = NEW(char, len + sizeof (LLIST));
X    bcopy(s, lp->Item, len);
X    lp->Next = (LLIST *)&lp->Item[len];
X
X    /* set up next list entry */
X    lp = lp->Next;
X    lp->Next = NULL;
X    lp->Item = NULL;
X    lp->Length = 0;
X    return(lp);
X}
X
X
X
X/*
X**  Getline is like fgets, but deals with continuation lines.
X**  It also ensures that even if a line that is too long is
X**  received, the remainder of the line is thrown away
X**  instead of treated like a second line.
X*/
XSTATIC char *
XGetline(buf, len, fp)
X    char *buf;
X    int len;
X    FILE *fp;
X{
X    register int c;
X    register int n;
X    register char *cp;
X
X    for (n = 0, cp = buf; n < len && (c = getc(fp)) != EOF && c != '\n'; )
X	if (!iscntrl(c) || c == '\b' || c == '\t') {
X	    *cp++ = c;
X	    n++;
X	}
X    if (c == EOF && cp == buf)
X	return(NULL);
X    *cp = '\0';
X
X    if (c != '\n')
X	/* Line too long - part read didn't fit into a newline */
X	while ((c = getc(fp)) != '\n' && c != EOF)
X	    ;
X    else if (cp == buf) {
X	/* Don't look for continuation of blank lines */
X	*cp++ = '\n';
X	*cp = '\0';
X	return(buf);
X    }
X
X    while ((c = getc(fp)) == ' ' || c == '\t') {
X	/* Continuation line. */
X	if ((n += 2) < len) {
X	    *cp++ = '\n';
X	    *cp++ = c;
X	}
X	while ((c = getc(fp)) != '\n' && c != EOF)
X	    if ((!iscntrl(c) || c == '\b' || c == '\t') && n++ < len)
X		*cp++ = c;
X    }
X    if (n >= len - 1)
X	cp = buf + len - 2;
X    *cp++ = '\n';
X    *cp = '\0';
X    if (c != EOF)
X	/* push back first char of next header */
X	(void)ungetc(c, fp);
X    return(buf);
X}
X
X
XSTATIC int
Xprefix(full, pref)
X    register char	*full;
X    register char	*pref;
X{
X    register char	 fc;
X    register char	 pc;
X
X    while (pc = *pref++) {
X	fc = *full++;
X	if (isupper(fc))
X	    fc = tolower(fc);
X	if (isupper(pc))
X	    pc = tolower(pc);
X	if (fc != pc)
X	    return(FALSE);
X    }
X    return(TRUE);
X}
X
X
XSTATIC int
XHeaderType(p)
X    register char	*p;
X{
X    static int		 save = HDR_END;
X    register HTYPE	*hp;
X    char		*colon;
X    char		*space;
X
X    /* some consistency checks (i.e. is this really a header line?) */
X    if (p == NULL || !isascii(*p) || *p == '\n')
X	return(save = HDR_END);
X
X    if (WHITE(*p))
X	/* Continuation line. */
X	return(save);
X
X    if ((colon = IDX(p, ':')) == NULL
X     || ((space = IDX(p, ' ')) && space < colon))
X	return(save = HDR_END);
X
X    for (hp = HeaderList; hp < ENDOF(HeaderList); hp++)
X	if (prefix(p, hp->Name))
X	    return(save = hp->Type);
X    return(save = HDR_OTHER);
X}
X
X
X/*
X**  Get the contents of the field of the header line, appending it, with a
X**  space delimeter if it's a continuation line.  If there is already
X**  something in the header storage, skip this header line and the
X**  continuations.
X*/
XSTATIC void
Xgetfield(src, dest, size)
X    register char	*src;
X    register char	*dest;
X    register int	 size;
X{
X    static int		 skip = FALSE;
X    register char	*p;
X
X    if (src == NULL || dest == NULL)
X	return;
X
X    if (WHITE(*src)) {
X	/* Continuation line.  If skipping or no room, ignore. */
X	if (skip || (size -= strlen(dest)) <= 0)
X	    return;
X	/* Munch all but one whitespace, append it to header. */
X	while (*src && WHITE(*src))
X	    src++;
X	*--src = ' ';
X	(void)strncat(dest, src, size - 1);
X    }
X    else {
X	skip = FALSE;
X	if (*dest) {
X	    /* Already got a value, so mark this as one to skip. */
X	    skip = TRUE;
X	    return;
X	}
X	if ((src = IDX(src, ':')) == NULL)
X	    /* Can't happen! */
X	    return;
X	/* Skip colon, eat whitespace. */
X	for (src++; *src && WHITE(*src); )
X	    src++;
X	(void)strncpy(dest, src, size - 1);
X    }
X
X    /* Munch trailing whitespace. */
X    for (p = dest + strlen(dest); --p >= dest && (*p == '\n' || WHITE(*p)); )
X	;
X    p[1] = '\0';
X}
X
X
XSTATIC time_t
Xcgtdate(datestr)
X    char		*datestr;
X{
X    static time_t	 lasttime;
X    static char		 save[SM_SIZE];
X    char		 junk[40];
X    char		 month[40];
X    char		 day[30];
X    char		 tod[60];
X    char		 year[50];
X    char		 buf[SM_SIZE];
X    extern time_t	 getdate();
X
X    if (save[0] && EQ(datestr, save))
X	return(lasttime);
X    lasttime = getdate(datestr, (struct timeb *)NULL);
X    if (lasttime < 0 &&
X      sscanf(datestr, "%s %s %s %s %s", junk, month, day, tod, year) == 5) {
X	(void)sprintf(buf, "%s %s, %s %s", month, day, year, tod);
X	lasttime = getdate(buf, (struct timeb *)NULL);
X    }
X    (void)strncpy(save, datestr, sizeof save);
X    return(lasttime);
X}
X
X
X/*
X**  Print the date in ARPA format, not ctime(3) format.
X*/
XSTATIC void
XDoDate(fp, ud)
X    FILE		*fp;
X    register char	*ud;
X{
X    register char	*p;
X    register char	*q;
X    register int	 i;
X    char		 buff[SM_SIZE];
X
X    q = buff;
X
X#ifdef	notdef
X    /* until every site installs the fix to getdate.y, the day
X     * of the week can cause time warps */
X    /* "Mon, " */
X    p = &ud[0]; *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ','; *q++ = ' ';
X#endif	/* notdef */
X
X    /* "16" */
X    p = &ud[8];
X    if (*p == ' ')
X	p++;
X    else
X	*q++ = *p++;
X    *q++ = *p++; *q++ = ' ';
X
X    /* "Sep " */
X    p = &ud[4]; *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ' ';
X
X    /* "79 " */
X    p = &ud[22]; *q++ = *p++; *q++ = *p++; *q++ = ' ';
X
X    /* "01:03:52" */
X    for (p = &ud[11], i = 8; i > 0; i--)
X	*q++ = *p++;
X
X    /* " GMT" */
X    *q++ = ' '; *q++ = 'G'; *q++ = 'M'; *q++ = 'T'; *q = '\0';
X
X    Fprintf(fp, "Date: %s\n", buff);
X}
X
X
X/*
X**  Strip leading and trailing spaces; returns pointer to first non-space.
X*/
XSTATIC char *
XStripSpaces(s)
X    register char *s;
X{
X    register char *cp;
X
X    if (s == NULL || *s == '\0')
X	return(s);
X
X    /* Skip leading spaces. */
X    while (*s && isspace(*s))
X	s++;
X
X    /* zap trailing spaces */
X    for (cp = &s[strlen(s) - 1]; cp > s && isspace(*cp); )
X	cp--;
X    cp[1] = '\0';
X    return(s);
X}
X
X
X/*
X**  Crack an RFC822 from header field into address and fullname.
X*/
Xint
XCrackFrom(addr, name, p)
X    char		*addr;
X    char		*name;
X    char		*p;
X{
X    register LLIST	*cp;
X    register char	*ap;
X    register char	*np;
X    register int	 flag;
X    register int	 commfound;
X    register int	 comment;
X    register int	 address;
X    register int	 addrfound;
X    LLIST		 comm;
X    char		 commbuf[LG_SIZE];
X    char		 addrbuf[LG_SIZE];
X
X    *name = '\0';	/* just make sure */
X    *addr = '\0';
X
X    if (p == NULL)
X	return(FALSE);
X
X    while (*p && isspace(*p))	/* eat leading white space */
X	p++;
X
X    cp = &comm;
X    ap = addrbuf;
X    commfound = 0;
X    comment = 0;
X    addrfound = 0;
X    address = 0;
X    for (flag = 0; *p; p++) {
X	switch (*p) {
X	case '"':
X	    if (flag) {
X		--flag;
X		goto EndComment;
X	    }
X	    flag++;
X	    /* FALLTHROUGH */
X	case '(':
X	    if (comment == 0) {
X		np = commbuf;
X		*np = '\0';
X	    }
X	    comment++;
X	    break;
X	case ')':
X    EndComment:
X	    if (comment > 0 && --comment == 0) {
X		*np++ = *p++;		/* Skip the ')' */
X		*np++ = '\0';
X		if ((cp = LLISTnew(cp, commbuf, strlen(commbuf) + 1)) == NULL) {
X		    if (commfound)
X			LLISTfree(&comm);
X		    return(FALSE);
X		}
X		commfound++;
X		np = NULL;
X		continue;
X	    }
X	    break;
X	case '<':
X	    if (address) {
X		if (commfound)
X		    LLISTfree(&comm);
X		return(FALSE);	/* AWK! Abort! */
X	    }
X	    if (!comment) {
X		address++;
X		*ap = '\0';
X		ap = addr;
X	    }
X	    break;
X	case '>':
X	    if (!comment && address) {
X		address--;
X		addrfound++;
X		*ap = '\0';
X		ap = &addrbuf[strlen(addrbuf)];
X		p++;	/* skip the `>' */
X	    }
X	    break;
X	}
X
X	if (comment)
X	    *np++ = *p;
X	else if (address) {
X	    if (*p != '<')
X		*ap++ = *p;
X	}
X	else
X		*ap++ = *p;
X	if (*p == '\0')
X	    break;
X    }
X
X    *ap++ = '\0';
X
X    if (addrfound) {
X	Strcpy(name, StripSpaces(addrbuf));
X	Strcpy(addr, strcpy(commbuf, StripSpaces(addr)));
X    }
X    else {
X	(void)strcpy(addr, StripSpaces(addrbuf));
X	*name = '\0';
X    }
X    /* Just to be sure that we got the full name, we'll take all of
X     * the comments. */
X    if (commfound) {
X	for (flag = *name ? TRUE : FALSE, cp = &comm; cp->Item; cp = cp->Next) {
X	    if (flag)
X		Strcat(name, ", ");
X	    flag = TRUE;
X	    if (cp->Item[cp->Length - 2] == ')')
X		    cp->Item[cp->Length - 2] = '\0';
X	    Strcat(name, StripSpaces(&cp->Item[1]));
X	}
X	LLISTfree(&comm);
X    }
X    return(TRUE);
X}
X
X
X/*
X**  Write out an RFC822 header, paying no attention to line limits.
X**  Ideally, we should do continuations in here...
X*/
Xint
Xrfc822write(hp, fp)
X    register HBUF	*hp;
X    register FILE	*fp;
X{
X    time_t		 t;
X
X    if (hp->path[0])
X	Fprintf(fp, "Path: %s\n", hp->path);
X    if (hp->from[0])
X	Fprintf(fp, "From: %s\n", hp->from);
X    if (hp->nbuf[0])
X	Fprintf(fp, "Newsgroups: %s\n", hp->nbuf);
X    if (hp->title[0])
X	Fprintf(fp, "Subject: %s\n", hp->title);
X    if (hp->ident[0])
X	Fprintf(fp, "Message-ID: %s\n", hp->ident);
X    /* Get current time. This will be used resolve the timezone. */
X    t = hp->subdate[0] ? cgtdate(hp->subdate) : time((time_t *)NULL);
X    DoDate(fp, asctime(gmtime(&t)));
X    if (hp->expdate[0])
X	Fprintf(fp, "Expires: %s\n", hp->expdate);
X    if (hp->followid[0])
X	Fprintf(fp, "References: %s\n", hp->followid);
X    if (hp->ctlmsg[0])
X	Fprintf(fp, "Control: %s\n", hp->ctlmsg);
X    if (hp->sender[0])
X	Fprintf(fp, "Sender: %s\n", hp->sender);
X    if (hp->replyto[0])
X	Fprintf(fp, "Reply-To: %s\n", hp->replyto);
X    if (hp->followto[0])
X	Fprintf(fp, "Followup-To: %s\n", hp->followto);
X    if (hp->distribution[0])
X	Fprintf(fp, "Distribution: %s\n", hp->distribution);
X    if (hp->organization[0])
X	Fprintf(fp, "Organization: %s\n", hp->organization);
X    if (hp->keywords[0])
X	Fprintf(fp, "Keywords: %s\n", hp->keywords);
X    if (hp->summary[0])
X	Fprintf(fp, "Summary: %s\n", hp->summary);
X    if (hp->approved[0])
X	Fprintf(fp, "Approved: %s\n", hp->approved);
X    Fprintf(fp, "\n");
X    return(!ferror(fp));
X}
X
X
Xrfc822read(hp, fp, bfr, bfrsize)
X    register HBUF *hp;
X    register FILE *fp;
X    char *bfr;
X    int bfrsize;
X{
X    register int i;
X    long curpos;
X
X    i = HeaderType(bfr);
X    do {
X	curpos = ftell(fp);
X	switch (i) {
X	case HDR_APPROVED:
X	    getfield(bfr, hp->approved, sizeof hp->approved);
X	    break;
X	case HDR_CONTROL:
X	    getfield(bfr, hp->ctlmsg, sizeof hp->ctlmsg);
X	    break;
X	case HDR_DATE:
X	    getfield(bfr, hp->subdate, sizeof hp->subdate);
X	    break;
X	case HDR_DISTRIBUTION:
X	    getfield(bfr, hp->distribution, sizeof hp->distribution);
X	    break;
X	case HDR_EXPIRE:
X	    getfield(bfr, hp->expdate, sizeof hp->expdate);
X	    break;
X	case HDR_FOLLOWTO:
X	    getfield(bfr, hp->followto, sizeof hp->followto);
X	    break;
X	case HDR_FROM:
X	    getfield(bfr, hp->from, sizeof hp->from);
X	    break;
X	case HDR_KEYWORDS:
X	    getfield(bfr, hp->keywords, sizeof hp->keywords);
X	    break;
X	case HDR_MESSAGEID:
X	    getfield(bfr, hp->ident, sizeof hp->ident);
X	    break;
X	case HDR_NEWSGROUP:
X	    getfield(bfr, hp->nbuf, sizeof hp->nbuf);
X	    break;
X	case HDR_ORGANIZATION:
X	    getfield(bfr, hp->organization, sizeof hp->organization);
X	    break;
X	case HDR_REFERENCES:
X	    getfield(bfr, hp->followid, sizeof hp->followid);
X	    break;
X	case HDR_REPLYTO:
X	    getfield(bfr, hp->replyto, sizeof hp->replyto);
X	    break;
X	case HDR_SENDER:
X	    getfield(bfr, hp->sender, sizeof hp->sender);
X	    break;
X	case HDR_SUMMARY:
X	    getfield(bfr, hp->summary, sizeof hp->summary);
X	    break;
X	case HDR_TITLE:
X	    getfield(bfr, hp->title, sizeof hp->title);
X	    break;
X	}
X    } while ((i = HeaderType(Getline(bfr, bfrsize, fp))) != HDR_END);
X
X    if (*bfr != '\n')
X	(void)fseek(fp, curpos, 0);
X}
END_OF_FILE
if test 13702 -ne `wc -c <'rfc822.c'`; then
    echo shar: \"'rfc822.c'\" unpacked with wrong size!
fi
# end of 'rfc822.c'
fi
echo shar: End of archive 2 \(of 3\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 archives.
    echo "Have fun; bug reports to rsalz@bbn.com"
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.