%{ /* * 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 word domain_ref sub_domain local_part phrase %type domain route_list route %type 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 #include #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); } /* * : [ "" ] unexpected [ ] */ 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; }