%{
/*
* RFC 822 address parsing routines.
*/
#ifndef lint
static char sccsid[] = "%W%";
#endif lint
#include "mace.h"
#include "headers.h"
#include "flex.h"
#include "addr.h"
#define panic(s) abort(s)
#define nomem() abort("nomem")
static flex f = { NULLSTR, NULLSTR, NULLSTR, };
/* $Log: address.y,v $
* Revision 1.5 88/12/29 00:44:14 john
* Moved to MIPS
*
* Revision 1.5 88/12/29 00:44:14 john
* Move eatcomment() into address.y where it belongs, and fix it to
* handle \-quoting of parens inside comments.
*
* Revision 1.4 88/12/28 22:13:40 john
* Fix \ quoting within quoted-strings and domain-literals (it was
* missing). Also fix missing cases for ']' as an atom.
*
* Revision 1.3 88/12/28 00:14:03 john
* Fix lexical analysis bug, parentheses were not being correctly
* recognized as atoms.
*
* Revision 1.2 88/10/30 07:46:22 john
* Improve compliance of grammar to RFC822:
* - insist on a domain at the end of a source-route
* Change makeaddress() to do the right thing with a
* source-routed address (i.e., make it an actual textual
* representation of the source-routed address).
* Fix bug in putdom.
*
* Revision 1.1 88/10/26 09:54:04 john
* Initial revision
*
*/
static char RCSid[] =
"$Header: address.y,v 1.5 88/12/29 00:44:14 john Exp $";
static char *errstr;
static char *comstr;
static char *cp;
static char *savecp;
static char *saveline;
static flex errbuf = { NULLSTR, NULLSTR, NULLSTR };
static flex combuf = { NULLSTR, NULLSTR, NULLSTR };
static int iseol;
static char *text;
Addr *adrlist;
Addr *errlist;
%}
%union {
char yChar;
char *yString;
Dom *yDom;
Addr *yAddr;
}
%token EOL ATOM LIT_DOMAIN QUOTED_STRING
%type <yString> word domain_ref sub_domain local_part phrase
%type <yDom> domain route_list route
%type <yAddr> addr_spec non_local_addr_spec route_addr mailbox mbox_list group address
%start addr_list
%%
addr_list: addr_lel
| addr_list addr_lel
;
addr_lel: address EOL {
$1->comment = comstr;
$1->error = errstr;
comstr = NULL;
errstr = NULL;
appAddr(&adrlist, $1);
}
| address ',' {
$1->comment = comstr;
$1->error = errstr;
comstr = NULL;
errstr = NULL;
appAddr(&adrlist, $1);
}
| error {
register Addr *ap;
ap = newAddr();
if (savecp > saveline) {
flex_str(&errbuf, " after \"");
flex_nstr(&errbuf, saveline, savecp - saveline);
flex_char(&errbuf, '"');
if (cp && *cp) {
flex_str(&errbuf, ", before \"");
flex_str(&errbuf, cp);
flex_char(&errbuf, '"');
}
}
else if (cp && *cp) {
flex_str(&errbuf, " before \"");
flex_str(&errbuf, cp);
flex_char(&errbuf, '"');
}
if (errbuf.f_ptr != errbuf.f_str) {
flex_char(&errbuf, '\0');
errstr = newstring2(errstr, errbuf.f_str);
flex_end(&errbuf);
}
ap->error = errstr;
errstr = NULL;
comstr = NULL;
appAddr(&errlist, ap);
}
;
address: mailbox {
$$ = $1;
}
| group {
$$ = $1;
}
;
group : phrase ':' mbox_list ';' {
register Addr *a;
for (a = $3; a; a = a->next)
a->group = $1;
$$ = $3;
}
;
mbox_list: mailbox {
$$ = $1;
}
| mbox_list ',' mailbox {
$3->comment = comstr;
$3->error = errstr;
comstr = NULL;
errstr = NULL;
appAddr(&($1), $3);
$$ = $1;
}
;
mailbox: addr_spec {
$$ = $1;
}
| route_addr {
$$ = $1;
}
| phrase route_addr {
$2->name = $1;
$$ = $2;
}
;
phrase : word {
$$ = $1;
}
| phrase word {
$$ = newstring3($1, " ", $2);
free($1);
free($2);
}
;
route_addr: '<' addr_spec '>' {
$$ = $2;
}
| '<' route non_local_addr_spec '>' {
prepDom(&($3->route), $2);
$$ = $3;
}
;
route : route_list ':' {
$$ = $1;
}
;
route_list: '@' domain {
$$ = $2;
}
| route_list ',' '@' domain {
appDom(&($1), $4);
$$ = $1;
}
;
addr_spec: non_local_addr_spec
| local_part {
register Addr *ap;
$$ = ap = newAddr();
ap->localp = $1;
ap->destdom = NULL;
}
;
non_local_addr_spec: local_part '@' domain {
register Addr *ap;
$$ = ap = newAddr();
ap->localp = $1;
ap->destdom = $3;
ap->route = $3;
}
;
local_part: word {
$$ = $1;
}
| local_part '.' word {
$$ = newstring3($1, ".", $3);
free($1);
free($3);
}
| local_part '%' word {
$$ = newstring3($1, "%", $3);
free($1);
free($3);
}
;
domain : sub_domain {
register Dom *dp;
dp = newDom();
dp->sub[0] = $1;
dp->top = dp->sub;
$$ = dp;
}
| domain '.' sub_domain {
($1->top)++;
*($1->top) = $3;
$$ = $1;
}
;
sub_domain: domain_ref {
$$ = $1;
}
| LIT_DOMAIN {
$$ = yylval.yString;
}
;
domain_ref: ATOM {
$$ = yyval.yString;
}
;
word : ATOM {
$$ = yylval.yString;
}
| QUOTED_STRING {
$$ = yylval.yString;
}
;
%%
#include <stdio.h>
#include <ctype.h>
#define ERROR -2
static char *
newstring3(a, b, c)
char *a;
char *b;
char *c;
{
char *p;
char *q;
int i;
i = strlen(a) + strlen(b) + strlen(c) + 1;
if ((p = salloc((MALLOCT)i)) == NULL)
nomem();
q = p + strlen(strcpy(p, a));
q += strlen(strcpy(q, b));
Strcpy(q, c);
return(p);
}
static char *
newstring2(a, b)
char *a;
char *b;
{
char *p;
int i;
if (a == (char *)0)
a = "";
i = strlen(a) + strlen(b) + 1;
if ((p = salloc((MALLOCT)i)) == NULL)
nomem();
Strcpy(p, a);
Strcat(p, b);
return(p);
}
/*
* <mess>: [ "<what>" ] unexpected [ <tail> ]
*/
static void
unexpected(mess, what, tail)
char *mess;
char *what;
char *tail;
{
flex_str(&errbuf, mess);
flex_str(&errbuf, ": ");
if (what != NULLSTR)
{
flex_char(&errbuf, '"');
flex_str(&errbuf, what);
flex_str(&errbuf, "\" ");
}
flex_str(&errbuf, "unexpected");
if (tail != NULLSTR)
{
flex_char(&errbuf, ' ');
flex_str(&errbuf, tail);
}
flex_char(&errbuf, '\0');
}
static void
yyerror(s)
char *s;
{
static char c[2] = { '\0', '\0' };
switch(yychar) {
default:
c[0] = yylval.yChar;
unexpected(s, c, NULLSTR);
errstr = newstring2(errstr, errbuf.f_str);
break;
case LIT_DOMAIN:
case QUOTED_STRING:
case ATOM:
unexpected(s, yylval.yString, NULLSTR);
errstr = newstring2(errstr, errbuf.f_str);
break;
case EOL:
case 0: /* EOF */
unexpected(s, NULLSTR, "end-of-header");
errstr = newstring2(errstr, errbuf.f_str);
break;
}
flex_end(&errbuf);
}
parseit(line)
char *line;
{
saveline = cp = text = line;
adrlist = NULL;
errlist = NULL;
if (combuf.f_str == NULLSTR)
flex_init(&combuf);
flex_end(&combuf);
if (errbuf.f_str == NULLSTR)
flex_init(&errbuf);
flex_end(&errbuf);
(void)yyparse();
}
char *
eatcomment(s)
register char *s;
{
register int parencount;
parencount = 0;
for (;;) {
if (*s == '\\') { /* quoted-pair */
s++;
if (*s == '\0')
return ((char *)0);
s++;
if (*s == '\0')
return ((char *)0);
continue;
}
if (*s == '(')
parencount++;
else if (*s == ')')
parencount--;
if (parencount == 0)
return (++s);
else if (parencount < 0)
panic("eatcomment botch");
if (*++s == '\0')
return ((char *)0);
}
}
yylex()
{
register char *p;
savecp = cp;
while (isascii(*cp) && (isspace(*cp) || (*cp == '('))) {
if (*cp == '(') {
p = eatcomment(cp);
if (p == (char *)0)
return (EOF);
flex_nstr(&combuf, cp + 1, (p - 2) - cp);
flex_char(&combuf, '\0');
if (comstr == NULL) {
if ((comstr = salloc((MALLOCT)(strlen(combuf.f_str) + 1))) == NULL)
nomem();
Strcpy(comstr, combuf.f_str);
}
else
comstr = newstring3(comstr, ", ", combuf.f_str);
flex_end(&combuf);
cp = p;
}
else
cp++;
}
if (!isascii(*cp))
return(ERROR);
switch (*cp) {
case '\0':
if (iseol) {
iseol = 0;
return(EOF);
}
iseol = 1;
return(EOL);
case ',':
case ':':
case ';':
case '.':
case '@':
case '%':
case '<':
case '>':
case '(':
case ')':
case ']':
yylval.yChar = *cp;
return(*cp++);
case '[': /* LIT_DOMAIN */
for (p = cp + 1; *p && *p != ']'; ) {
if (*p == '\\') {
p++;
if (*p == '\0')
return(EOF);
}
p++;
}
if (*p == '\0')
return(EOF);
if ((yylval.yString = salloc((MALLOCT)(p - cp + 2))) == NULL)
nomem();
Strncpy(yylval.yString, cp, p - cp + 1);
yylval.yString[p - cp + 1] = '\0';
cp = ++p;
return(LIT_DOMAIN);
case '"': /* QUOTED_STRING */
for (p = cp + 1; *p && *p != '"'; ) {
if (*p == '\\') {
p++;
if (*p == '\0')
return(EOF);
}
p++;
}
if (*p == '\0')
return(EOF);
if ((yylval.yString = salloc((MALLOCT)(p - cp))) == NULL)
nomem();
Strncpy(yylval.yString, cp + 1, p - cp);
yylval.yString[p - cp - 1] = '\0';
cp = ++p;
return(QUOTED_STRING);
}
for (p = cp; ; p++)
switch (*p) {
case ',':
case ':':
case ';':
case '.':
case '@':
case '%':
case '<':
case '>':
case '(':
case ')':
case '[':
case ']':
case '"':
case '\0':
goto out;
default:
if (isspace(*p))
goto out;
}
out:
if ((yylval.yString = salloc((MALLOCT)(p - cp + 1))) == NULL)
nomem();
Strncpy(yylval.yString, cp, p - cp);
yylval.yString[p - cp] = '\0';
cp = p;
return(ATOM);
}
/*
** Create and initialize a new address.
*/
Addr *
newAddr()
{
register Addr *ap;
if ((ap = (Addr *)salloc((MALLOCT)sizeof *ap)) == NULL)
nomem();
memset((char *)ap, '\0', sizeof *ap);
return(ap);
}
/*
** Append addresslist "addr" to addresslist "head".
*/
appAddr(head, addr)
Addr **head;
Addr *addr;
{
register Addr *ap;
register char *p;
register int i;
if (*head) {
for (ap = *head; ap->next; ap = ap->next)
;
ap->next = addr;
}
else
*head = addr;
if (head == &adrlist)
{
while (isspace(*text))
text++;
if (*(p = cp) != '\0')
{
p--;
if (isspace(p[-1]))
{
p--;
while (isspace(*p))
p--;
p++;
}
}
if ((addr->text = salloc((MALLOCT)(p - text + 1))) == NULLSTR)
nomem();
strncpy(addr->text, text, i = p - text);
addr->text[i] = '\0';
text = cp;
}
else
addr->text = NULLSTR;
}
/*
** Create and initialize a new domain.
*/
Dom *
newDom()
{
register Dom *dp;
if ((dp = (Dom *)salloc((MALLOCT)sizeof *dp)) == NULL)
nomem();
memset((char *)dp, '\0', sizeof *dp);
dp->top = dp->sub;
return(dp);
}
/*
** Append domainlist "dom" to domainlist "head".
*/
appDom(head, dom)
Dom **head;
Dom *dom;
{
register Dom *dp;
if (*head) {
for (dp = *head; dp->next; dp = dp->next)
;
dp->next = dom;
}
else
*head = dom;
}
/*
** Prepend domainlist "dom" before domainlist "head".
*/
prepDom(head, dom)
Dom **head;
Dom *dom;
{
register Dom *dp;
for (dp = dom; dp->next; dp = dp->next)
;
dp->next = *head;
*head = dom;
}
static void
putdom(d)
register Dom *d;
{
register char **p;
for (p = d->sub; p != d->top; p++)
{
flex_str(&f, *p);
flex_char(&f, RFC_SPEC_DOT);
}
flex_str(&f, *p);
}
/*
* Take an Addr struct and return a textual representation that
* a delivery agent can use to deliver the message.
*/
char *
make_address(a)
register Addr *a;
{
register Dom *d;
register char *p;
if (f.f_str == NULLSTR)
flex_init(&f);
d = a->route;
while (d != (Dom *)0 && d->next != (Dom *)0)
{
/* source route */
flex_char(&f, RFC_SPEC_AT);
putdom(d);
flex_char(&f, d->next->next ? RFC_SPEC_COMMA : RFC_SPEC_COLON);
d = d->next;
}
flex_str(&f, a->localp);
if (d != (Dom *)0)
{
flex_char(&f, RFC_SPEC_AT);
putdom(d);
}
flex_char(&f, '\0');
p = newstr(f.f_str);
flex_end(&f);
return p;
}
/*
* Parse a textual address list and return it as a list of Addr structs.
*
* Returns NULLSTR for ok; otherwise an error string.
*/
char *
parse_address(ap, s)
Addr **ap;
char *s;
{
register Addr *a;
parseit(s);
if (errlist != (Addr *)0)
{
for (a = errlist; a != (Addr *)0; a = a->next)
{
if (a->error != NULLSTR)
return a->error;
}
abort("make_address");
/* NOTREACHED */
}
for (a = adrlist; a != (Addr *)0; a = a->next)
{
if (a->localp == (char *)0 || a->localp[0] == '\0')
return "null local-part in address";
}
*ap = adrlist;
return NULLSTR;
}
|