[comp.sources.misc] RFC822 parser in Yacc

david@ms.uky.edu (David Herron -- Resident E-mail Hack) (10/29/87)

Brandon,

Enclosed is something I've received about 30 requests for.  Rich felt
it wasn't "clean" enough just yet to go through sources.unix -- so here
it is.  The intention is that someone take it and insert it into some
program or some such and have it go through comp.sources.unix that
way.

Note that while it isn't truly mine, that the heredity is fully
documented and nobody is making restricting claims on the source.
In fact, I think everybody involved would URGE this to be incorporated
into something larger.  I personally would like to see a fancier 
version of this be sent through sources.unix just to ensure that
a more "pure" form of the grammar be in the archives, aside from
this being used in some larger program.


	-- David

#! /bin/sh
: This is a shell archive, meaning:
: 1. Remove everything above the '#! /bin/sh' line.
: 2. Save the resulting text in a file.
: 3. Execute the file with /bin/sh '(not csh)' to create the files:
:	'README'
:	'README.old'
:	'NOTES'
:	'Makefile'
:	'addr.c'
:	'addr.h'
:	'atest.c'
:	'rfc822.y'
: This archive created: 'Tue Oct 27 17:50:49 1987
'
: By:	'David Herron -- Resident E-mail Hack ()'
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(1347 characters)'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
sed 's/^X//'  >'README' <<'SHAR_EOF'
XThese files make up a YACC grammar for parsing RFC822 addresses which
XI received a long time ago from
X
X	From: Daniel Karrenberg <dfk@unido.uucp>
X
XThey kind of hung around a long time gathering mold until someone posted
Xa request for a LEX parser for RFC822.  Well, I mentioned this thing
Xand the responses just took off.  Rich Salz contributed a few changes.
X(mainly, it seems, improving portability).  For my part, I've gone
Xover the grammar with a fine-tooth comb to check if it matches the spec.
XThe results of that are in NOTES.
X
X	Date: Tue, 13 Oct 87 14:22:11 edt
X	From: Rich Salz <rsalz@PINEAPPLE.BBN.COM>
X	To: david%ms.uky.edu@HARVARD.HARVARD.EDU
X	Subject: rfc822 parser
X
X	Here's my changes, basically, a rewrite.  Do what you will -- I suppose my
X	name should go in there, in case I put in any bugs...
X
XOne thing this stuff doesn't do is parse a whole message. (or even
Xjust the headers).  It only parses addresses.  It should be simple
Xenough to wrap a header-parser around this address-parser, probably
Xin the same grammar.
X
XI personally am slightly displeased that it will group all the comment
Xtext into one thing.  That's not a very nice thing to do, and shouldn't
Xreally be necessary ...  But it's sat here long enough without my doing
Xanything with it ... :-)
X
X
X
X	-- David Herron <david@ms.uky.edu>
X	   Tue Oct 27 17:49:58 EST 1987
SHAR_EOF
if test 1347 -ne "`wc -c < 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 1347 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'README.old'" '(3100 characters)'
if test -f 'README.old'
then
	echo shar: will not over-write existing file "'README.old'"
else
sed 's/^X//'  >'README.old' <<'SHAR_EOF'
X[ This is the original information I received along with these
X  files ... David ]
X
X
XReceived: from  by .ukma.UUCP id a028381; 1 Apr 86 10:23 EST
XReceived: From UKMA.BITNET By UKMA.BITNET ; 01 Apr 86 15:22:19 GMT
XMessage-Id: <8604010817.AA27744@unido.uucp>
XReceived: by unido.uucp; Tue, 1 Apr 86 10:17:13 -0200
XTo: david@UKMA
XSubject: Re: BSMTP & sendmail
XIn-Reply-To: Your message of Thu, 27 Mar 86 11:46:58 EST.
X             <8603271918.AA28476@unido.uucp>
XDate: 01 Apr 86 10:17:09 N (Tue)
XFrom: Daniel Karrenberg <dfk@unido.uucp>
X
X
X  > I assume you've tried to use simple and found it disgusting?
X
XI tried to fully understand it, but .......
X
X  > I used simple and friends with sendmail for quite awhile and could never
X  > get it to work well.  I wasn't using it for sending BSMTP tho, but I
X  > assume that it was just a matter of saying the right incantations into
X  > the configuration.  (Currently I'm using it with mmdf -- it works a
X  > little better with mmdf).
X
XSending BSMTP is not the problem. Receiving is the dificult part.
X
X  > I've been considering doing up a set of programs for sending and
X  > recieving BSMTP but not using simple.  But I've got too many other
X  > pressing matters.
X
XVery similar to my situation !
X
X  > I would like to have the RFC-822 grammer you mentioned (please?).
X
XSure, see below. Please feel invited to send comments and suggestions.
XIf you go ahead with a BSMTP server please let me know. If there are
Xcode conversion problems I could send it in tarmail format.
X
X
XRegards
X
X-Daniel  <dfk@unido.uucp> , <dfk@unido.bitnet>
X
X
X
X--------------  Original README
X
X
X                                       Tue Apr  1 09:52:46 MET DST 1986
X
XThis is an RFC822 addressparser. The grammer and scanner are based on work
Xdone by Bill Nesheim of Cornell:
X
X  > -------
X  > Date:     Thu, 13 Feb 86 16:31:51 EST
X  > From:     <bill%bullwinkle@crnlcs.bitnet>
X  > To:       dfk@unido.uucp
X  > Subject:  Re:  damail.c
X  > -------
X  >
X  > I've spooled a tar file of damail to you via bitnet.
X  >                 Bill
X  >
X  > (ps: go ahead and give it to whoever wants it.  It's not great, but it
X  > works well enough.)
X
XI have generalized it to accept -hopefully- full RFC822 syntax and store
Xthe pieces in a reasonable datastructure for processing. The rationale for
Xthis is not to do any string shoving in the mailer proper. I have had to
Xunderstand too much Mail software that munged address strings in too many
Xplaces.
X
XHowever I haven't written a program using this parser yet because I have
Xlittle time for this 'hobby'.
X
XOne thing it doesn't handle very well are ()-comments. Since they are
Xessentially white space, they are skipped by the scanner. If there is
Xmore than one of this kind in any given address, they will be concatenated
Xin the adderss structure. This makes it impossible to reconstruct a
Xcommented address string from the parsed version exactly.
X
XThere is a program to test the parser: tparse.c
X
XIt has been run on VAX, NS32016 and 68000 CPUs.
X
XPleas feel invited to send comments, suggestions and fixes!
X
X
X- Daniel  <dfk@unido.bitnet>, <dfk@unido.uucp>
SHAR_EOF
if test 3100 -ne "`wc -c < 'README.old'`"
then
	echo shar: error transmitting "'README.old'" '(should have been 3100 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'NOTES'" '(893 characters)'
if test -f 'NOTES'
then
	echo shar: will not over-write existing file "'NOTES'"
else
sed 's/^X//'  >'NOTES' <<'SHAR_EOF'
XObservations while going over this grammar.
X
X1. An embedded '\n' will muck things up.  In yylex(), an embedded '\n'
X   will bring you through the default: of the second switch(), and
X   since isspace('\n') fails we'll continue picking up characters
X   for the current ATOM.  But an eol is supposed to end the current 
X   token.
X2. An address like "a@b.[1.2.3.4].c" is allowed, and looking in the
X   actual spec, it's supposed to be allowed.  This is moderately
X   strange ...
X3. A not-very major no-no ... In this grammar, a "mailbox" can be
X   an "addr_spec", a "route_addr", or a "phrase route_addr".  Strict
X   interpretation of the spec says that it can't be just a "route_addr".
X4. At first glance, the code for addr_lel would allow '\n' to
X   seperate addresses, which is plain wrong.  Fortunately, EOL really
X   just means the end of the buffer ...
X
X
X-- David Herron <david@ms.uky.edu>
SHAR_EOF
if test 893 -ne "`wc -c < 'NOTES'`"
then
	echo shar: error transmitting "'NOTES'" '(should have been 893 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'Makefile'" '(291 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
sed 's/^X//'  >'Makefile' <<'SHAR_EOF'
X##
XCFLAGS	= -O
XOBJS	= addr.o rfc822.o
XSRCS	= addr.c rfc822.c atest.c
X
Xall:		atest
X
Xatest:		$(OBJS) atest.c
X	$(CC) -o atest $(CFLAGS) atest.c $(OBJS)
X
Xrfc822.o:	rfc822.c
X$(OBJS):	addr.h
X
Xclean:
X	rm -f a.out foo core tags lint rfc822.c atest *.o y.*
X
Xlint:		all
X	exec lint -hba $(SRCS) >lint 
SHAR_EOF
if test 291 -ne "`wc -c < 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 291 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'addr.c'" '(1489 characters)'
if test -f 'addr.c'
then
	echo shar: will not over-write existing file "'addr.c'"
else
sed 's/^X//'  >'addr.c' <<'SHAR_EOF'
X#include <stdio.h>
X#include "addr.h"
X
X
X
X#ifdef	NEED_BZERO
X/*
X**  I forget what bzero is called for SystemV, so...
X*/
Xbzero(p, i)
X    register char	*p;
X    register int	 i;
X{
X    while (--i >= 0)
X	*p++ = '\0';
X}
X#endif	/* NEED_BZERO */
X
X
X/*
X**  Create and initialize a new address.
X*/
XAddr *
XnewAddr()
X{
X    register Addr	*ap;
X
X    if ((ap = (Addr *)malloc((MALLOCT)sizeof *ap)) == NULL) {
X	perror("Addr alloc");
X	exit(1);
X    }
X    bzero((char *)ap, sizeof *ap);
X    return(ap);
X}
X
X
X/*
X**  Append addresslist "addr" to addresslist "head".
X*/
XappAddr(head, addr)
X    Addr		**head;
X    Addr		 *addr;
X{
X    register Addr	*ap;
X
X    if (*head) {
X	for (ap = *head; ap->next; ap = ap->next)
X	    ;
X	ap->next = addr;
X    }
X    else
X	*head = addr;
X}
X
X
X
X/*
X**  Create and initialize a new domain.
X*/
XDom *
XnewDom()
X{
X    register Dom	*dp;
X
X    if ((dp = (Dom *)malloc((MALLOCT)sizeof *dp)) == NULL) {
X	perror("Dom alloc");
X	exit(1);
X    }
X    bzero((char *)dp, sizeof *dp);
X    dp->top = dp->sub;
X    return(dp);
X}
X
X
X/*
X**  Append domainlist "dom" to domainlist "head".
X*/
XappDom(head, dom)
X    Dom			**head;
X    Dom			*dom;
X{
X    register Dom	*dp;
X
X    if (*head) {
X	for (dp = *head; dp->next; dp = dp->next)
X	    ;
X	dp->next = dom;
X    }
X    else
X    *head = dom;
X}
X
X
X/*
X**  Prepend domainlist "dom" before domainlist "head".
X*/
XprepDom(head, dom)
X    Dom			**head;
X    Dom			 *dom;
X{
X    register Dom	 *dp;
X
X    for (dp = dom; dp->next; dp = dp->next)
X	;
X    dp->next = *head;
X    *head = dom;
X}
SHAR_EOF
if test 1489 -ne "`wc -c < 'addr.c'`"
then
	echo shar: error transmitting "'addr.c'" '(should have been 1489 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'addr.h'" '(1198 characters)'
if test -f 'addr.h'
then
	echo shar: will not over-write existing file "'addr.h'"
else
sed 's/^X//'  >'addr.h' <<'SHAR_EOF'
X/*
X** Mail address data structures
X*/
X
X#undef	NEED_BZERO
X#define NSUBDOM		20      	/* # of subdomain names in domain */
X
Xtypedef unsigned int MALLOCT;		/* Parameter to malloc		*/
X
X/*
X**  An address.
X*/
Xtypedef struct _addr {
X    struct _addr	*next;		/* next address in list		*/
X    struct _dom		*route;		/* route icl. destination domain */
X    struct _dom		*destdom;	/* destination domain		*/
X    char		*localp;	/* RFC local part		*/
X    char		*name;		/* Comment name			*/
X    char		*group;		/* Group (List) phrase		*/
X    char		*comment;	/* () comment phrase		*/
X    char		*error;		/* error text if not NULL	*/
X} Addr;
X
X
X/*
X**  A domain.
X*/
Xtypedef struct _dom {
X    struct _dom		*next;		/* next domain (f.i. in route)	*/
X    char		*sub[NSUBDOM];	/* subdomain strins		*/
X    char		**top;		/* toplevel domain		*/
X} Dom;
X
X
Xextern Addr	*newAddr();		/* Create a new address		*/
Xextern Dom	*newDom();		/* Create a new domain		*/
Xextern Addr	*adrlist;
Xextern Addr	*errlist;
Xextern char	*malloc();
Xextern char	*strcpy();
Xextern char	*strncpy();
Xextern char	*strcat();
X
X/* SHUT UP! */
X#define Strcpy		(void)strcpy
X#define Strncpy		(void)strncpy
X#define Strcat		(void)strcat
X#define Sprintf		(void)sprintf
SHAR_EOF
if test 1198 -ne "`wc -c < 'addr.h'`"
then
	echo shar: error transmitting "'addr.h'" '(should have been 1198 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'atest.c'" '(1121 characters)'
if test -f 'atest.c'
then
	echo shar: will not over-write existing file "'atest.c'"
else
sed 's/^X//'  >'atest.c' <<'SHAR_EOF'
X/*
X**  Checkout program for rfc822-address parser
X**  Takes address-lines from stdin up to EOF or empty line.
X**  Writes results to stdout, errors to stderr.
X*/
X#include <stdio.h>
X#include "addr.h"
X
X
Xstatic void
Xprtroute(dp)
X    register Dom	*dp;
X{
X    register char	**p;
X
X    for (; dp; dp = dp->next) {
X	printf("\t");
X	for (p = dp->sub; p != dp->top; p++)
X	    printf("%s.", *p);
X	printf("%s\n", *p);
X    }
X}
X
X
Xmain()
X{
X    register Addr	*ap;
X    register int	 i;
X    char		 line[100];
X
X    while (gets(line) && *line) {
X	parseit(line);
X	for (i = 0; i < 2; i++)
X	    for (ap = i ? adrlist : errlist; ap; ap = ap->next) {
X		if (ap->error)
X		    printf("error:\t%s\n", ap->error);
X		if (ap->name)
X		    printf("name:\t%s\n", ap->name);
X		if (ap->comment)
X		    printf("comment:\t%s\n", ap->comment);
X		if (ap->group)
X		    printf("group:\t%s\n", ap->group);
X		if (ap->localp)
X		    printf("localp:\t%s\n", ap->localp);
X		if (ap->destdom) {
X		    printf("destdom:\n");
X		    prtroute(ap->destdom);
X		}
X		if (ap->route) {
X		    printf("route:\n");
X		    prtroute(ap->route);
X		}
X		printf("\n");
X	    }
X    }
X    exit(0);
X}
SHAR_EOF
if test 1121 -ne "`wc -c < 'atest.c'`"
then
	echo shar: error transmitting "'atest.c'" '(should have been 1121 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'rfc822.y'" '(6772 characters)'
if test -f 'rfc822.y'
then
	echo shar: will not over-write existing file "'rfc822.y'"
else
sed 's/^X//'  >'rfc822.y' <<'SHAR_EOF'
X%{
X#include "addr.h"
X
Xstatic char	*errstr;
Xstatic char	*comstr;
Xstatic char	*cp;
Xstatic char	 errbuf[256];
Xstatic char	 combuf[256];
Xstatic int	 iseol;
X
XAddr		*adrlist;
XAddr		*errlist;
X%}
X
X%union {
X   char	 yChar;
X   char	*yString;
X   Dom	*yDom;
X   Addr	*yAddr;
X}
X
X%token	EOL ATOM LIT_DOMAIN QUOTED_STRING
X
X%type	<yString>	word domain_ref sub_domain local_part phrase
X%type	<yDom>		domain route_list route
X%type	<yAddr>		addr_spec route_addr mailbox mbox_list group address
X
X%start	addr_list
X
X%%
Xaddr_list:	addr_lel
X       | addr_list addr_lel
X       ;
X
Xaddr_lel: address EOL {
X	    $1->comment = comstr;
X	    $1->error = errstr;
X	    comstr = NULL;
X	    errstr = NULL;
X	    appAddr(&adrlist, $1);
X	}
X	| address ',' {
X	    $1->comment = comstr;
X	    $1->error = errstr;
X	    comstr = NULL;
X	    errstr = NULL;
X	    appAddr(&adrlist, $1);
X	}
X	| error {
X	    register Addr	*ap;
X
X	    ap = newAddr();
X	    Sprintf(errbuf, "after \"%s\", before \"%s\"\n", $<yString>1, cp);
X	    errstr = newstring2(errstr, errbuf);
X	    ap->error = errstr;
X	    errstr = NULL;
X	    comstr = NULL;
X	    appAddr(&errlist, ap);
X	}
X	;
X
Xaddress: mailbox {
X	    $$ = $1;
X	}
X	| group {
X	    $$ = $1;
X	}
X	;
X
Xgroup	: phrase ':' mbox_list ';' {
X	    register Addr	*a;
X
X	    for (a = $3; a; a = a->next)
X		a->group = $1;
X	    $$ = $3;
X	}
X	;
X
Xmbox_list: mailbox {
X	    $$ = $1;
X	}
X	| mbox_list ',' mailbox {
X	    $3->comment = comstr;
X	    $3->error = errstr;
X	    comstr = NULL;
X	    errstr = NULL;
X	    appAddr(&($1), $3);
X	    $$ = $1;
X	}
X	;
X
Xmailbox: addr_spec {
X	    $$ = $1;
X	}
X	| route_addr {
X	    $$ = $1;
X	}
X	| phrase route_addr {
X	    $2->name = $1;
X	    $$ = $2;
X	}
X	;
X
Xphrase	: word {
X	    $$ = $1;
X	}
X	| phrase word {
X	    $$ = newstring3($1, " ", $2);
X	    free($1);
X	    free($2);
X       }
X       ;
X
Xroute_addr: '<' addr_spec '>' {
X	   $$ = $2;
X       }
X       | '<' route addr_spec '>' {
X	   prepDom(&($3->route), $2);
X	   $$ = $3;
X       }
X       ;
X
Xroute	: route_list ':' {
X	    $$ = $1;
X	}
X	;
X
Xroute_list: '@' domain {
X	    $$ = $2;
X	}
X	| route_list ',' '@' domain {
X	    appDom(&($1), $4);
X	    $$ = $1;
X	}
X	;
X
Xaddr_spec: local_part '@' domain {
X	    register Addr	*ap;
X
X	    $$ = ap = newAddr();
X	    ap->localp = $1;
X	    ap->destdom = $3;
X	    ap->route = $3;
X	}
X	| local_part {
X	    register Addr	*ap;
X
X	    $$ = ap = newAddr();
X	    ap->localp = $1;
X	    ap->destdom = NULL;
X	}
X	;
X
X
Xlocal_part: word {
X		$$ = $1;
X	    }
X	    | local_part '.' word {
X		$$ = newstring3($1, ".", $3);
X		free($1);
X		free($3);
X	    }
X	    | local_part '%' word {
X		$$ = newstring3($1, "%", $3);
X		free($1);
X		free($3);
X	    }
X	    ;
X
Xdomain	: sub_domain {
X	    register Dom	*dp;
X
X	    dp = newDom();
X	    dp->sub[0] = $1;
X	    dp->top = dp->sub;
X	    $$ = dp;
X	}
X	| domain '.' sub_domain {
X	    ($1->top)++;
X	    *($1->top) = $3;
X	    $$ = $1;
X	}
X	;
X
Xsub_domain: domain_ref {
X	    $$ = $1;
X	}
X	| LIT_DOMAIN {
X	    $$ = yylval.yString;
X	}
X	;
X
Xdomain_ref: ATOM {
X	    $$ = yyval.yString;
X	}
X	;
X
Xword	: ATOM {
X	    $$ = yylval.yString;
X	}
X	| QUOTED_STRING {
X	    $$ = yylval.yString;
X	}
X	;
X
X%%
X
X#include <stdio.h>
X#include <ctype.h>
X
X#define ERROR	-2
X
X
Xstatic char *
Xnewstring3(a, b, c)
X    char	*a;
X    char	*b;
X    char	*c;
X{
X    char	*p;
X    char	*q;
X    int		 i;
X
X    i = strlen(a) + strlen(b) + strlen(c) + 1;
X    if ((p = malloc((MALLOCT)i)) == NULL) {
X	perror("newstring3 (malloc)");
X	exit(1);
X    }
X    q = p + strlen(strcpy(p, a));
X    q += strlen(strcpy(q, b));
X    Strcpy(q, c);
X    return(q);
X}
X
X
Xstatic char *
Xnewstring2(a, b)
X    char		*a;
X    char		*b;
X{
X    char		*p;
X    int			 i;
X
X    i = strlen(a) + strlen(b) + 1;
X    if ((p = malloc((MALLOCT)i)) == NULL) {
X	perror("newstring2 (malloc)");
X	exit(1);
X    }
X    Strcpy(p, a);
X    Strcat(p, b);
X    return(p);
X}
X
X
Xstatic void
Xyyerror(s)
X    char	*s;
X{
X   switch(yychar) {
X       default:
X	   Sprintf(errbuf, "%s: \"%c\" unexpected\n", s, yylval.yChar);
X	   errstr = newstring2(errstr, errbuf);
X	   break;
X       case LIT_DOMAIN:
X       case QUOTED_STRING:
X       case ATOM:
X	   Sprintf(errbuf, "%s: \"%s\" unexpected\n", s, yylval.yString);
X	   errstr = newstring2(errstr, errbuf);
X	   break;
X       case 0: /* EOF */
X	   Sprintf(errbuf, "%s: unexpected EOF\n", s);
X	   errstr = newstring2(errstr, errbuf);
X	   break;
X   }
X
X}
X
X
Xparseit(line)
X    char	*line;
X{
X    cp = line;
X    adrlist = NULL;
X    errlist = NULL;
X    (void)yyparse();
X}
X
X
Xyylex()
X{
X    register char	*p;
X    register int	 paren_count;
X
X    while (isascii(*cp) && (isspace(*cp) || (*cp == '('))) {
X	if (*cp == '(') {
X	    for (paren_count = 1, p = cp + 1; paren_count; p++) {
X		if (*p == '\0')
X		    return(EOF);
X		if (*p == '(')
X		    paren_count++;
X		else if( *p == ')')
X		    paren_count--;
X	    }
X	    Strncpy(combuf, cp + 1, (p - 2) - cp);
X	    if (comstr == NULL) {
X		if ((comstr = malloc((MALLOCT)(strlen(combuf) + 1))) == NULL) {
X		    perror("malloc failure");
X		    exit(1);
X		}
X		Strcpy(comstr, combuf);
X	    }
X	    else
X		comstr = newstring3(comstr, ", ", combuf);
X	    cp = p;
X	}
X	else
X	    cp++;
X    }
X
X    if (!isascii(*cp))
X	return(ERROR);
X
X    switch (*cp) {
X	case '\0':
X	    if (iseol) {
X		iseol = 0;
X		return(EOF);
X	    }
X	    iseol = 1;
X	    return(EOL);
X	case ',':
X	case ':':
X	case ';':
X	case '.':
X	case '@':
X	case '%':
X	case '<':
X	case '>':
X	    yylval.yChar = *cp;
X	    return(*cp++);
X	case '[':       /* LIT_DOMAIN */
X	    for (p = cp; *++p && *p != ']'; )
X		;
X	    if (*p == '\0')
X		return(EOF);
X	    if ((yylval.yString = malloc((MALLOCT)(p - cp + 2))) == NULL) {
X		fprintf(stderr,"not enough memory\n");
X		return(ERROR);
X	    }
X	    Strncpy(yylval.yString, cp, p - cp + 1);
X	    yylval.yString[p - cp + 2] = '\0';
X	    cp = ++p;
X	    return(LIT_DOMAIN);
X	case '"':       /* QUOTED_STRING */
X	    for (p = cp; *++p && *p != '"'; )
X		;
X	    if (*p == '\0')
X		return(EOF);
X	    if ((yylval.yString = malloc((MALLOCT)(p - cp + 2))) == NULL) {
X		fprintf(stderr,"not enough memory\n");
X		return(ERROR);
X	    }
X	    Strncpy(yylval.yString, cp, p - cp + 1);
X	    yylval.yString[p - cp + 2] = '\0';
X	    cp = ++p;
X	    return(QUOTED_STRING);
X    }
X    for (p = cp; ; p++)
X	switch (*p) {
X	    case 'a':
X	    case 'A':
X		if ((p[1] == 't' || p[1] == 'T') && isspace(p[2])) {
X		    yylval.yChar = '@';
X		    cp = p + 2;
X		    return('@');
X		}
X		break;
X	    case ',':
X	    case ':':
X	    case ';' :
X	    case '.':
X	    case '@':
X	    case '%':
X	    case '<':
X	    case '>':
X	    case '(':
X	    case ')':
X	    case '[':
X	    case '"':
X	    case '\0':
X		goto out;
X	    default:
X		if (isspace(*p))
X		    goto out;
X	}
Xout:
X    if ((yylval.yString = malloc((MALLOCT)(p - cp + 1))) == NULL) {
X	fprintf(stderr,"not enough memory\n");
X	return(ERROR);
X    }
X    Strncpy(yylval.yString, cp, p - cp);
X    yylval.yString[p - cp] = '\0';
X    cp = p;
X    return(ATOM);
X}
SHAR_EOF
if test 6772 -ne "`wc -c < 'rfc822.y'`"
then
	echo shar: error transmitting "'rfc822.y'" '(should have been 6772 characters)'
fi
fi # end of overwriting check
:	End of shell archive
exit 0
-- 
<---- David Herron,  Local E-Mail Hack,  david@ms.uky.edu, david@ms.uky.csnet
<----                    {rutgers,uunet,cbosgd}!ukma!david, david@UKMA.BITNET
<---- I thought that time was this neat invention that kept everything
<---- from happening at once.  Why doesn't this work in practice?