xref: /plan9-contrib/sys/src/cmd/upas/smtp/rfc822.y (revision 426fe5994a5cc658001a4bec2c6d67ca434c4ae9)
13e12c5d1SDavid du Colombier %{
23e12c5d1SDavid du Colombier #include "common.h"
33e12c5d1SDavid du Colombier #include "smtp.h"
43e12c5d1SDavid du Colombier #include <ctype.h>
53e12c5d1SDavid du Colombier 
6*426fe599SDavid du Colombier #define YYMAXDEPTH	500		/* was default 150 */
7*426fe599SDavid du Colombier 
83e12c5d1SDavid du Colombier char	*yylp;		/* next character to be lex'd */
9332d2f58SDavid du Colombier int	yydone;		/* tell yylex to give up */
10219b2ee8SDavid du Colombier char	*yybuffer;	/* first parsed character */
119a747e4fSDavid du Colombier char	*yyend;		/* end of buffer to be parsed */
123e12c5d1SDavid du Colombier Node	*root;
133e12c5d1SDavid du Colombier Field	*firstfield;
143e12c5d1SDavid du Colombier Field	*lastfield;
153e12c5d1SDavid du Colombier Node	*usender;
163e12c5d1SDavid du Colombier Node	*usys;
173e12c5d1SDavid du Colombier Node	*udate;
18332d2f58SDavid du Colombier char	*startfield, *endfield;
193e12c5d1SDavid du Colombier int	originator;
20219b2ee8SDavid du Colombier int	destination;
213e12c5d1SDavid du Colombier int	date;
227dd7cddfSDavid du Colombier int	received;
239a747e4fSDavid du Colombier int	messageid;
243e12c5d1SDavid du Colombier %}
253e12c5d1SDavid du Colombier 
263e12c5d1SDavid du Colombier %term WORD
273e12c5d1SDavid du Colombier %term DATE
283e12c5d1SDavid du Colombier %term RESENT_DATE
293e12c5d1SDavid du Colombier %term RETURN_PATH
303e12c5d1SDavid du Colombier %term FROM
313e12c5d1SDavid du Colombier %term SENDER
323e12c5d1SDavid du Colombier %term REPLY_TO
333e12c5d1SDavid du Colombier %term RESENT_FROM
343e12c5d1SDavid du Colombier %term RESENT_SENDER
353e12c5d1SDavid du Colombier %term RESENT_REPLY_TO
36219b2ee8SDavid du Colombier %term SUBJECT
373e12c5d1SDavid du Colombier %term TO
383e12c5d1SDavid du Colombier %term CC
393e12c5d1SDavid du Colombier %term BCC
403e12c5d1SDavid du Colombier %term RESENT_TO
413e12c5d1SDavid du Colombier %term RESENT_CC
423e12c5d1SDavid du Colombier %term RESENT_BCC
433e12c5d1SDavid du Colombier %term REMOTE
447dd7cddfSDavid du Colombier %term PRECEDENCE
45219b2ee8SDavid du Colombier %term MIMEVERSION
46219b2ee8SDavid du Colombier %term CONTENTTYPE
47219b2ee8SDavid du Colombier %term MESSAGEID
48219b2ee8SDavid du Colombier %term RECEIVED
49219b2ee8SDavid du Colombier %term MAILER
507dd7cddfSDavid du Colombier %term BADTOKEN
513e12c5d1SDavid du Colombier %start msg
523e12c5d1SDavid du Colombier %%
533e12c5d1SDavid du Colombier 
543e12c5d1SDavid du Colombier msg		: fields
553e12c5d1SDavid du Colombier 		| unixfrom '\n' fields
563e12c5d1SDavid du Colombier 		;
57332d2f58SDavid du Colombier fields		: '\n'
58332d2f58SDavid du Colombier 			{ yydone = 1; }
59332d2f58SDavid du Colombier 		| field '\n'
603e12c5d1SDavid du Colombier 		| field '\n' fields
613e12c5d1SDavid du Colombier 		;
623e12c5d1SDavid du Colombier field		: dates
633e12c5d1SDavid du Colombier 			{ date = 1; }
643e12c5d1SDavid du Colombier 		| originator
653e12c5d1SDavid du Colombier 			{ originator = 1; }
663e12c5d1SDavid du Colombier 		| destination
67219b2ee8SDavid du Colombier 			{ destination = 1; }
68219b2ee8SDavid du Colombier 		| subject
693e12c5d1SDavid du Colombier 		| optional
70219b2ee8SDavid du Colombier 		| ignored
717dd7cddfSDavid du Colombier 		| received
727dd7cddfSDavid du Colombier 		| precedence
73332d2f58SDavid du Colombier 		| error '\n' field
743e12c5d1SDavid du Colombier 		;
753e12c5d1SDavid du Colombier unixfrom	: FROM route_addr unix_date_time REMOTE FROM word
763e12c5d1SDavid du Colombier 			{ freenode($1); freenode($4); freenode($5);
773e12c5d1SDavid du Colombier 			  usender = $2; udate = $3; usys = $6;
783e12c5d1SDavid du Colombier 			}
793e12c5d1SDavid du Colombier 		;
803e12c5d1SDavid du Colombier originator	: REPLY_TO ':' address_list
813e12c5d1SDavid du Colombier 			{ newfield(link3($1, $2, $3), 1); }
823e12c5d1SDavid du Colombier 		| RETURN_PATH ':' route_addr
833e12c5d1SDavid du Colombier 			{ newfield(link3($1, $2, $3), 1); }
843e12c5d1SDavid du Colombier 		| FROM ':' mailbox_list
853e12c5d1SDavid du Colombier 			{ newfield(link3($1, $2, $3), 1); }
863e12c5d1SDavid du Colombier 		| SENDER ':' mailbox
873e12c5d1SDavid du Colombier 			{ newfield(link3($1, $2, $3), 1); }
883e12c5d1SDavid du Colombier 		| RESENT_REPLY_TO ':' address_list
893e12c5d1SDavid du Colombier 			{ newfield(link3($1, $2, $3), 1); }
907dd7cddfSDavid du Colombier 		| RESENT_SENDER ':' mailbox
917dd7cddfSDavid du Colombier 			{ newfield(link3($1, $2, $3), 1); }
927dd7cddfSDavid du Colombier 		| RESENT_FROM ':' mailbox
937dd7cddfSDavid du Colombier 			{ newfield(link3($1, $2, $3), 1); }
943e12c5d1SDavid du Colombier 		;
953e12c5d1SDavid du Colombier dates 		: DATE ':' date_time
963e12c5d1SDavid du Colombier 			{ newfield(link3($1, $2, $3), 0); }
973e12c5d1SDavid du Colombier 		| RESENT_DATE ':' date_time
983e12c5d1SDavid du Colombier 			{ newfield(link3($1, $2, $3), 0); }
993e12c5d1SDavid du Colombier 		;
1007dd7cddfSDavid du Colombier destination	: TO ':'
1017dd7cddfSDavid du Colombier 			{ newfield(link2($1, $2), 0); }
1027dd7cddfSDavid du Colombier 		| TO ':' address_list
1033e12c5d1SDavid du Colombier 			{ newfield(link3($1, $2, $3), 0); }
1047dd7cddfSDavid du Colombier 		| RESENT_TO ':'
1057dd7cddfSDavid du Colombier 			{ newfield(link2($1, $2), 0); }
1063e12c5d1SDavid du Colombier 		| RESENT_TO ':' address_list
1073e12c5d1SDavid du Colombier 			{ newfield(link3($1, $2, $3), 0); }
1087dd7cddfSDavid du Colombier 		| CC ':'
1097dd7cddfSDavid du Colombier 			{ newfield(link2($1, $2), 0); }
1103e12c5d1SDavid du Colombier 		| CC ':' address_list
1113e12c5d1SDavid du Colombier 			{ newfield(link3($1, $2, $3), 0); }
1127dd7cddfSDavid du Colombier 		| RESENT_CC ':'
1137dd7cddfSDavid du Colombier 			{ newfield(link2($1, $2), 0); }
1143e12c5d1SDavid du Colombier 		| RESENT_CC ':' address_list
1153e12c5d1SDavid du Colombier 			{ newfield(link3($1, $2, $3), 0); }
1163e12c5d1SDavid du Colombier 		| BCC ':'
1173e12c5d1SDavid du Colombier 			{ newfield(link2($1, $2), 0); }
1183e12c5d1SDavid du Colombier 		| BCC ':' address_list
1193e12c5d1SDavid du Colombier 			{ newfield(link3($1, $2, $3), 0); }
1203e12c5d1SDavid du Colombier 		| RESENT_BCC ':'
1213e12c5d1SDavid du Colombier 			{ newfield(link2($1, $2), 0); }
1223e12c5d1SDavid du Colombier 		| RESENT_BCC ':' address_list
1233e12c5d1SDavid du Colombier 			{ newfield(link3($1, $2, $3), 0); }
1243e12c5d1SDavid du Colombier 		;
125219b2ee8SDavid du Colombier subject		: SUBJECT ':' things
126219b2ee8SDavid du Colombier 			{ newfield(link3($1, $2, $3), 0); }
1277dd7cddfSDavid du Colombier 		| SUBJECT ':'
1287dd7cddfSDavid du Colombier 			{ newfield(link2($1, $2), 0); }
1297dd7cddfSDavid du Colombier 		;
1307dd7cddfSDavid du Colombier received	: RECEIVED ':' things
1317dd7cddfSDavid du Colombier 			{ newfield(link3($1, $2, $3), 0); received++; }
1327dd7cddfSDavid du Colombier 		| RECEIVED ':'
1337dd7cddfSDavid du Colombier 			{ newfield(link2($1, $2), 0); received++; }
1347dd7cddfSDavid du Colombier 		;
1357dd7cddfSDavid du Colombier precedence	: PRECEDENCE ':' things
1367dd7cddfSDavid du Colombier 			{ newfield(link3($1, $2, $3), 0); }
1377dd7cddfSDavid du Colombier 		| PRECEDENCE ':'
1387dd7cddfSDavid du Colombier 			{ newfield(link2($1, $2), 0); }
139219b2ee8SDavid du Colombier 		;
140219b2ee8SDavid du Colombier ignored		: ignoredhdr ':' things
141219b2ee8SDavid du Colombier 			{ newfield(link3($1, $2, $3), 0); }
142219b2ee8SDavid du Colombier 		| ignoredhdr ':'
143219b2ee8SDavid du Colombier 			{ newfield(link2($1, $2), 0); }
144219b2ee8SDavid du Colombier 		;
1459a747e4fSDavid du Colombier ignoredhdr	: MIMEVERSION | CONTENTTYPE | MESSAGEID { messageid = 1; } | MAILER
146219b2ee8SDavid du Colombier 		;
1477dd7cddfSDavid du Colombier optional	: fieldwords ':' things
1487dd7cddfSDavid du Colombier 			{ /* hack to allow same lex for field names and the rest */
1497dd7cddfSDavid du Colombier 			 if(badfieldname($1)){
1507dd7cddfSDavid du Colombier 				freenode($1);
1517dd7cddfSDavid du Colombier 				freenode($2);
1527dd7cddfSDavid du Colombier 				freenode($3);
1537dd7cddfSDavid du Colombier 				return 1;
1547dd7cddfSDavid du Colombier 			 }
1557dd7cddfSDavid du Colombier 			 newfield(link3($1, $2, $3), 0);
1567dd7cddfSDavid du Colombier 			}
1577dd7cddfSDavid du Colombier 		| fieldwords ':'
1587dd7cddfSDavid du Colombier 			{ /* hack to allow same lex for field names and the rest */
1597dd7cddfSDavid du Colombier 			 if(badfieldname($1)){
1607dd7cddfSDavid du Colombier 				freenode($1);
1617dd7cddfSDavid du Colombier 				freenode($2);
1627dd7cddfSDavid du Colombier 				return 1;
1637dd7cddfSDavid du Colombier 			 }
1647dd7cddfSDavid du Colombier 			 newfield(link2($1, $2), 0);
1657dd7cddfSDavid du Colombier 			}
1663e12c5d1SDavid du Colombier 		;
1673e12c5d1SDavid du Colombier address_list	: address
1683e12c5d1SDavid du Colombier 		| address_list ',' address
1693e12c5d1SDavid du Colombier 			{ $$ = link3($1, $2, $3); }
1703e12c5d1SDavid du Colombier 		;
1713e12c5d1SDavid du Colombier address		: mailbox
1723e12c5d1SDavid du Colombier 		| group
1733e12c5d1SDavid du Colombier 		;
1747dd7cddfSDavid du Colombier group		: phrase ':' address_list ';'
1757dd7cddfSDavid du Colombier 			{ $$ = link2($1, link3($2, $3, $4)); }
1767dd7cddfSDavid du Colombier 		| phrase ':' ';'
1777dd7cddfSDavid du Colombier 			{ $$ = link3($1, $2, $3); }
1783e12c5d1SDavid du Colombier 		;
1793e12c5d1SDavid du Colombier mailbox_list	: mailbox
1803e12c5d1SDavid du Colombier 		| mailbox_list ',' mailbox
1813e12c5d1SDavid du Colombier 			{ $$ = link3($1, $2, $3); }
1823e12c5d1SDavid du Colombier 		;
1833e12c5d1SDavid du Colombier mailbox		: route_addr
1843e12c5d1SDavid du Colombier 		| phrase brak_addr
1853e12c5d1SDavid du Colombier 			{ $$ = link2($1, $2); }
1867dd7cddfSDavid du Colombier 		| brak_addr
1873e12c5d1SDavid du Colombier 		;
1883e12c5d1SDavid du Colombier brak_addr	: '<' route_addr '>'
1893e12c5d1SDavid du Colombier 			{ $$ = link3($1, $2, $3); }
1903e12c5d1SDavid du Colombier 		| '<' '>'
1919a747e4fSDavid du Colombier 			{ $$ = nobody($2); freenode($1); }
1923e12c5d1SDavid du Colombier 		;
193219b2ee8SDavid du Colombier route_addr	: route ':' at_addr
1947dd7cddfSDavid du Colombier 			{ $$ = address(concat($1, concat($2, $3))); }
1953e12c5d1SDavid du Colombier 		| addr_spec
1963e12c5d1SDavid du Colombier 		;
1973e12c5d1SDavid du Colombier route		: '@' domain
1987dd7cddfSDavid du Colombier 			{ $$ = concat($1, $2); }
1993e12c5d1SDavid du Colombier 		| route ',' '@' domain
2007dd7cddfSDavid du Colombier 			{ $$ = concat($1, concat($2, concat($3, $4))); }
2013e12c5d1SDavid du Colombier 		;
2023e12c5d1SDavid du Colombier addr_spec	: local_part
2033e12c5d1SDavid du Colombier 			{ $$ = address($1); }
204219b2ee8SDavid du Colombier 		| at_addr
205219b2ee8SDavid du Colombier 		;
206219b2ee8SDavid du Colombier at_addr		: local_part '@' domain
2077dd7cddfSDavid du Colombier 			{ $$ = address(concat($1, concat($2, $3)));}
2087dd7cddfSDavid du Colombier 		| at_addr '@' domain
2097dd7cddfSDavid du Colombier 			{ $$ = address(concat($1, concat($2, $3)));}
2103e12c5d1SDavid du Colombier 		;
2113e12c5d1SDavid du Colombier local_part	: word
2123e12c5d1SDavid du Colombier 		;
2133e12c5d1SDavid du Colombier domain		: word
2143e12c5d1SDavid du Colombier 		;
2153e12c5d1SDavid du Colombier phrase		: word
2163e12c5d1SDavid du Colombier 		| phrase word
2173e12c5d1SDavid du Colombier 			{ $$ = link2($1, $2); }
2183e12c5d1SDavid du Colombier 		;
2193e12c5d1SDavid du Colombier things		: thing
2203e12c5d1SDavid du Colombier 		| things thing
2213e12c5d1SDavid du Colombier 			{ $$ = link2($1, $2); }
2223e12c5d1SDavid du Colombier 		;
2233e12c5d1SDavid du Colombier thing		: word | '<' | '>' | '@' | ':' | ';' | ','
2243e12c5d1SDavid du Colombier 		;
2253e12c5d1SDavid du Colombier date_time	: things
2263e12c5d1SDavid du Colombier 		;
2273e12c5d1SDavid du Colombier unix_date_time	: word word word unix_time word word
2283e12c5d1SDavid du Colombier 			{ $$ = link3($1, $3, link3($2, $6, link2($4, $5))); }
2293e12c5d1SDavid du Colombier 		;
2303e12c5d1SDavid du Colombier unix_time	: word
2313e12c5d1SDavid du Colombier 		| unix_time ':' word
2323e12c5d1SDavid du Colombier 			{ $$ = link3($1, $2, $3); }
2333e12c5d1SDavid du Colombier 		;
2343e12c5d1SDavid du Colombier word		: WORD | DATE | RESENT_DATE | RETURN_PATH | FROM | SENDER
2353e12c5d1SDavid du Colombier 		| REPLY_TO | RESENT_FROM | RESENT_SENDER | RESENT_REPLY_TO
236219b2ee8SDavid du Colombier 		| TO | CC | BCC | RESENT_TO | RESENT_CC | RESENT_BCC | REMOTE | SUBJECT
2377dd7cddfSDavid du Colombier 		| PRECEDENCE | MIMEVERSION | CONTENTTYPE | MESSAGEID | RECEIVED | MAILER
2383e12c5d1SDavid du Colombier 		;
2397dd7cddfSDavid du Colombier fieldwords	: fieldword
2407dd7cddfSDavid du Colombier 		| WORD
2417dd7cddfSDavid du Colombier 		| fieldwords fieldword
2427dd7cddfSDavid du Colombier 			{ $$ = link2($1, $2); }
2437dd7cddfSDavid du Colombier 		| fieldwords word
2447dd7cddfSDavid du Colombier 			{ $$ = link2($1, $2); }
2457dd7cddfSDavid du Colombier 		;
2467dd7cddfSDavid du Colombier fieldword	: '<' | '>' | '@' | ';' | ','
2473e12c5d1SDavid du Colombier 		;
2483e12c5d1SDavid du Colombier %%
2493e12c5d1SDavid du Colombier 
2503e12c5d1SDavid du Colombier /*
2513e12c5d1SDavid du Colombier  *  Initialize the parsing.  Done once for each header field.
2523e12c5d1SDavid du Colombier  */
2533e12c5d1SDavid du Colombier void
2549a747e4fSDavid du Colombier yyinit(char *p, int len)
2553e12c5d1SDavid du Colombier {
256219b2ee8SDavid du Colombier 	yybuffer = p;
257bd389b36SDavid du Colombier 	yylp = p;
2589a747e4fSDavid du Colombier 	yyend = p + len;
2593e12c5d1SDavid du Colombier 	firstfield = lastfield = 0;
2607dd7cddfSDavid du Colombier 	received = 0;
2613e12c5d1SDavid du Colombier }
2623e12c5d1SDavid du Colombier 
2633e12c5d1SDavid du Colombier /*
2643e12c5d1SDavid du Colombier  *  keywords identifying header fields we care about
2653e12c5d1SDavid du Colombier  */
2663e12c5d1SDavid du Colombier typedef struct Keyword	Keyword;
2673e12c5d1SDavid du Colombier struct Keyword {
2683e12c5d1SDavid du Colombier 	char	*rep;
2693e12c5d1SDavid du Colombier 	int	val;
2703e12c5d1SDavid du Colombier };
2713e12c5d1SDavid du Colombier 
2727dd7cddfSDavid du Colombier /* field names that we need to recognize */
2733e12c5d1SDavid du Colombier Keyword key[] = {
2743e12c5d1SDavid du Colombier 	{ "date", DATE },
2753e12c5d1SDavid du Colombier 	{ "resent-date", RESENT_DATE },
2763e12c5d1SDavid du Colombier 	{ "return_path", RETURN_PATH },
2773e12c5d1SDavid du Colombier 	{ "from", FROM },
2783e12c5d1SDavid du Colombier 	{ "sender", SENDER },
2793e12c5d1SDavid du Colombier 	{ "reply-to", REPLY_TO },
2803e12c5d1SDavid du Colombier 	{ "resent-from", RESENT_FROM },
2813e12c5d1SDavid du Colombier 	{ "resent-sender", RESENT_SENDER },
2823e12c5d1SDavid du Colombier 	{ "resent-reply-to", RESENT_REPLY_TO },
2833e12c5d1SDavid du Colombier 	{ "to", TO },
2843e12c5d1SDavid du Colombier 	{ "cc", CC },
2853e12c5d1SDavid du Colombier 	{ "bcc", BCC },
2863e12c5d1SDavid du Colombier 	{ "resent-to", RESENT_TO },
2873e12c5d1SDavid du Colombier 	{ "resent-cc", RESENT_CC },
2883e12c5d1SDavid du Colombier 	{ "resent-bcc", RESENT_BCC },
2893e12c5d1SDavid du Colombier 	{ "remote", REMOTE },
290219b2ee8SDavid du Colombier 	{ "subject", SUBJECT },
2917dd7cddfSDavid du Colombier 	{ "precedence", PRECEDENCE },
292219b2ee8SDavid du Colombier 	{ "mime-version", MIMEVERSION },
293219b2ee8SDavid du Colombier 	{ "content-type", CONTENTTYPE },
294219b2ee8SDavid du Colombier 	{ "message-id", MESSAGEID },
295219b2ee8SDavid du Colombier 	{ "received", RECEIVED },
296219b2ee8SDavid du Colombier 	{ "mailer", MAILER },
2973e12c5d1SDavid du Colombier 	{ "who-the-hell-cares", WORD }
2983e12c5d1SDavid du Colombier };
2993e12c5d1SDavid du Colombier 
3003e12c5d1SDavid du Colombier /*
3013e12c5d1SDavid du Colombier  *  Lexical analysis for an rfc822 header field.  Continuation lines
3023e12c5d1SDavid du Colombier  *  are handled in yywhite() when skipping over white space.
3033e12c5d1SDavid du Colombier  *
3043e12c5d1SDavid du Colombier  */
yylex(void)3053e12c5d1SDavid du Colombier yylex(void)
3063e12c5d1SDavid du Colombier {
3073e12c5d1SDavid du Colombier 	String *t;
3083e12c5d1SDavid du Colombier 	int quoting;
3097dd7cddfSDavid du Colombier 	int escaping;
3103e12c5d1SDavid du Colombier 	char *start;
3113e12c5d1SDavid du Colombier 	Keyword *kp;
312219b2ee8SDavid du Colombier 	int c, d;
3133e12c5d1SDavid du Colombier 
3143e12c5d1SDavid du Colombier /*	print("lexing\n"); /**/
3159a747e4fSDavid du Colombier 	if(yylp >= yyend)
3163e12c5d1SDavid du Colombier 		return 0;
317332d2f58SDavid du Colombier 	if(yydone)
318332d2f58SDavid du Colombier 		return 0;
3193e12c5d1SDavid du Colombier 
3207dd7cddfSDavid du Colombier 	quoting = escaping = 0;
3213e12c5d1SDavid du Colombier 	start = yylp;
3223e12c5d1SDavid du Colombier 	yylval = malloc(sizeof(Node));
3233e12c5d1SDavid du Colombier 	yylval->white = yylval->s = 0;
3243e12c5d1SDavid du Colombier 	yylval->next = 0;
3253e12c5d1SDavid du Colombier 	yylval->addr = 0;
326332d2f58SDavid du Colombier 	yylval->start = yylp;
3279a747e4fSDavid du Colombier 	for(t = 0; yylp < yyend; yylp++){
3287dd7cddfSDavid du Colombier 		c = *yylp & 0xff;
3299a747e4fSDavid du Colombier 
3309a747e4fSDavid du Colombier 		/* dump nulls, they can't be in header */
3319a747e4fSDavid du Colombier 		if(c == 0)
3329a747e4fSDavid du Colombier 			continue;
3339a747e4fSDavid du Colombier 
3347dd7cddfSDavid du Colombier 		if(escaping) {
3357dd7cddfSDavid du Colombier 			escaping = 0;
3367dd7cddfSDavid du Colombier 		} else if(quoting) {
3373e12c5d1SDavid du Colombier 			switch(c){
3383e12c5d1SDavid du Colombier 			case '\\':
3397dd7cddfSDavid du Colombier 				escaping = 1;
3403e12c5d1SDavid du Colombier 				break;
341219b2ee8SDavid du Colombier 			case '\n':
3427dd7cddfSDavid du Colombier 				d = (*(yylp+1))&0xff;
343219b2ee8SDavid du Colombier 				if(d != ' ' && d != '\t'){
344219b2ee8SDavid du Colombier 					quoting = 0;
345219b2ee8SDavid du Colombier 					yylp--;
3467dd7cddfSDavid du Colombier 					continue;
347219b2ee8SDavid du Colombier 				}
348219b2ee8SDavid du Colombier 				break;
3493e12c5d1SDavid du Colombier 			case '"':
3503e12c5d1SDavid du Colombier 				quoting = 0;
3513e12c5d1SDavid du Colombier 				break;
3523e12c5d1SDavid du Colombier 			}
3533e12c5d1SDavid du Colombier 		} else {
3543e12c5d1SDavid du Colombier 			switch(c){
3553e12c5d1SDavid du Colombier 			case '\\':
3567dd7cddfSDavid du Colombier 				escaping = 1;
3573e12c5d1SDavid du Colombier 				break;
3583e12c5d1SDavid du Colombier 			case '(':
3593e12c5d1SDavid du Colombier 			case ' ':
3603e12c5d1SDavid du Colombier 			case '\t':
3613e12c5d1SDavid du Colombier 			case '\r':
3623e12c5d1SDavid du Colombier 				goto out;
3633e12c5d1SDavid du Colombier 			case '\n':
364219b2ee8SDavid du Colombier 				if(yylp == start){
365219b2ee8SDavid du Colombier 					yylp++;
366219b2ee8SDavid du Colombier /*					print("lex(c %c)\n", c); /**/
367219b2ee8SDavid du Colombier 					yylval->end = yylp;
368219b2ee8SDavid du Colombier 					return yylval->c = c;
369219b2ee8SDavid du Colombier 				}
370219b2ee8SDavid du Colombier 				goto out;
3713e12c5d1SDavid du Colombier 			case '@':
3723e12c5d1SDavid du Colombier 			case '>':
3733e12c5d1SDavid du Colombier 			case '<':
3743e12c5d1SDavid du Colombier 			case ':':
3753e12c5d1SDavid du Colombier 			case ',':
3763e12c5d1SDavid du Colombier 			case ';':
3773e12c5d1SDavid du Colombier 				if(yylp == start){
3783e12c5d1SDavid du Colombier 					yylp++;
3793e12c5d1SDavid du Colombier 					yylval->white = yywhite();
3803e12c5d1SDavid du Colombier /*					print("lex(c %c)\n", c); /**/
3813e12c5d1SDavid du Colombier 					yylval->end = yylp;
3823e12c5d1SDavid du Colombier 					return yylval->c = c;
3833e12c5d1SDavid du Colombier 				}
3843e12c5d1SDavid du Colombier 				goto out;
3853e12c5d1SDavid du Colombier 			case '"':
3863e12c5d1SDavid du Colombier 				quoting = 1;
3873e12c5d1SDavid du Colombier 				break;
3883e12c5d1SDavid du Colombier 			default:
3893e12c5d1SDavid du Colombier 				break;
3903e12c5d1SDavid du Colombier 			}
3913e12c5d1SDavid du Colombier 		}
3923e12c5d1SDavid du Colombier 		if(t == 0)
3933e12c5d1SDavid du Colombier 			t = s_new();
3943e12c5d1SDavid du Colombier 		s_putc(t, c);
3953e12c5d1SDavid du Colombier 	}
3963e12c5d1SDavid du Colombier out:
3973e12c5d1SDavid du Colombier 	yylval->white = yywhite();
398bd389b36SDavid du Colombier 	if(t) {
399bd389b36SDavid du Colombier 		s_terminate(t);
400bd389b36SDavid du Colombier 	} else				/* message begins with white-space! */
401bd389b36SDavid du Colombier 		return yylval->c = '\n';
4023e12c5d1SDavid du Colombier 	yylval->s = t;
4033e12c5d1SDavid du Colombier 	for(kp = key; kp->val != WORD; kp++)
4043e12c5d1SDavid du Colombier 		if(cistrcmp(s_to_c(t), kp->rep)==0)
4053e12c5d1SDavid du Colombier 			break;
4063e12c5d1SDavid du Colombier /*	print("lex(%d) %s\n", kp->val-WORD, s_to_c(t)); /**/
4073e12c5d1SDavid du Colombier 	yylval->end = yylp;
4083e12c5d1SDavid du Colombier 	return yylval->c = kp->val;
4093e12c5d1SDavid du Colombier }
4103e12c5d1SDavid du Colombier 
4113e12c5d1SDavid du Colombier void
yyerror(char * x)4123e12c5d1SDavid du Colombier yyerror(char *x)
4133e12c5d1SDavid du Colombier {
414bd389b36SDavid du Colombier 	USED(x);
415bd389b36SDavid du Colombier 
4163e12c5d1SDavid du Colombier 	/*fprint(2, "parse err: %s\n", x);/**/
4173e12c5d1SDavid du Colombier }
4183e12c5d1SDavid du Colombier 
4193e12c5d1SDavid du Colombier /*
4203e12c5d1SDavid du Colombier  *  parse white space and comments
4213e12c5d1SDavid du Colombier  */
4223e12c5d1SDavid du Colombier String *
yywhite(void)4233e12c5d1SDavid du Colombier yywhite(void)
4243e12c5d1SDavid du Colombier {
4253e12c5d1SDavid du Colombier 	String *w;
4263e12c5d1SDavid du Colombier 	int clevel;
4273e12c5d1SDavid du Colombier 	int c;
4287dd7cddfSDavid du Colombier 	int escaping;
4293e12c5d1SDavid du Colombier 
4307dd7cddfSDavid du Colombier 	escaping = clevel = 0;
4319a747e4fSDavid du Colombier 	for(w = 0; yylp < yyend; yylp++){
4327dd7cddfSDavid du Colombier 		c = *yylp & 0xff;
4339a747e4fSDavid du Colombier 
4349a747e4fSDavid du Colombier 		/* dump nulls, they can't be in header */
4359a747e4fSDavid du Colombier 		if(c == 0)
4369a747e4fSDavid du Colombier 			continue;
4379a747e4fSDavid du Colombier 
4387dd7cddfSDavid du Colombier 		if(escaping){
4397dd7cddfSDavid du Colombier 			escaping = 0;
4407dd7cddfSDavid du Colombier 		} else if(clevel) {
4413e12c5d1SDavid du Colombier 			switch(c){
4423e12c5d1SDavid du Colombier 			case '\n':
4433e12c5d1SDavid du Colombier 				/*
4443e12c5d1SDavid du Colombier 				 *  look for multiline fields
4453e12c5d1SDavid du Colombier 				 */
4463e12c5d1SDavid du Colombier 				if(*(yylp+1)==' ' || *(yylp+1)=='\t')
4473e12c5d1SDavid du Colombier 					break;
4483e12c5d1SDavid du Colombier 				else
4493e12c5d1SDavid du Colombier 					goto out;
4503e12c5d1SDavid du Colombier 			case '\\':
4517dd7cddfSDavid du Colombier 				escaping = 1;
4523e12c5d1SDavid du Colombier 				break;
4533e12c5d1SDavid du Colombier 			case '(':
4543e12c5d1SDavid du Colombier 				clevel++;
4553e12c5d1SDavid du Colombier 				break;
4563e12c5d1SDavid du Colombier 			case ')':
4573e12c5d1SDavid du Colombier 				clevel--;
4583e12c5d1SDavid du Colombier 				break;
4593e12c5d1SDavid du Colombier 			}
4603e12c5d1SDavid du Colombier 		} else {
4613e12c5d1SDavid du Colombier 			switch(c){
4623e12c5d1SDavid du Colombier 			case '\\':
4637dd7cddfSDavid du Colombier 				escaping = 1;
4643e12c5d1SDavid du Colombier 				break;
4653e12c5d1SDavid du Colombier 			case '(':
4663e12c5d1SDavid du Colombier 				clevel++;
4673e12c5d1SDavid du Colombier 				break;
4683e12c5d1SDavid du Colombier 			case ' ':
4693e12c5d1SDavid du Colombier 			case '\t':
4703e12c5d1SDavid du Colombier 			case '\r':
4713e12c5d1SDavid du Colombier 				break;
4723e12c5d1SDavid du Colombier 			case '\n':
4733e12c5d1SDavid du Colombier 				/*
4743e12c5d1SDavid du Colombier 				 *  look for multiline fields
4753e12c5d1SDavid du Colombier 				 */
4763e12c5d1SDavid du Colombier 				if(*(yylp+1)==' ' || *(yylp+1)=='\t')
4773e12c5d1SDavid du Colombier 					break;
4783e12c5d1SDavid du Colombier 				else
4793e12c5d1SDavid du Colombier 					goto out;
4803e12c5d1SDavid du Colombier 			default:
4813e12c5d1SDavid du Colombier 				goto out;
4823e12c5d1SDavid du Colombier 			}
4833e12c5d1SDavid du Colombier 		}
4843e12c5d1SDavid du Colombier 		if(w == 0)
4853e12c5d1SDavid du Colombier 			w = s_new();
4863e12c5d1SDavid du Colombier 		s_putc(w, c);
4873e12c5d1SDavid du Colombier 	}
4883e12c5d1SDavid du Colombier out:
4893e12c5d1SDavid du Colombier 	if(w)
4903e12c5d1SDavid du Colombier 		s_terminate(w);
4913e12c5d1SDavid du Colombier 	return w;
4923e12c5d1SDavid du Colombier }
4933e12c5d1SDavid du Colombier 
4943e12c5d1SDavid du Colombier /*
4953e12c5d1SDavid du Colombier  *  link two parsed entries together
4963e12c5d1SDavid du Colombier  */
4973e12c5d1SDavid du Colombier Node*
link2(Node * p1,Node * p2)4983e12c5d1SDavid du Colombier link2(Node *p1, Node *p2)
4993e12c5d1SDavid du Colombier {
5003e12c5d1SDavid du Colombier 	Node *p;
5013e12c5d1SDavid du Colombier 
5023e12c5d1SDavid du Colombier 	for(p = p1; p->next; p = p->next)
5033e12c5d1SDavid du Colombier 		;
5043e12c5d1SDavid du Colombier 	p->next = p2;
5053e12c5d1SDavid du Colombier 	return p1;
5063e12c5d1SDavid du Colombier }
5073e12c5d1SDavid du Colombier 
5083e12c5d1SDavid du Colombier /*
5093e12c5d1SDavid du Colombier  *  link three parsed entries together
5103e12c5d1SDavid du Colombier  */
5113e12c5d1SDavid du Colombier Node*
link3(Node * p1,Node * p2,Node * p3)5123e12c5d1SDavid du Colombier link3(Node *p1, Node *p2, Node *p3)
5133e12c5d1SDavid du Colombier {
5143e12c5d1SDavid du Colombier 	Node *p;
5153e12c5d1SDavid du Colombier 
5163e12c5d1SDavid du Colombier 	for(p = p2; p->next; p = p->next)
5173e12c5d1SDavid du Colombier 		;
5183e12c5d1SDavid du Colombier 	p->next = p3;
5193e12c5d1SDavid du Colombier 
5203e12c5d1SDavid du Colombier 	for(p = p1; p->next; p = p->next)
5213e12c5d1SDavid du Colombier 		;
5223e12c5d1SDavid du Colombier 	p->next = p2;
5233e12c5d1SDavid du Colombier 
5243e12c5d1SDavid du Colombier 	return p1;
5253e12c5d1SDavid du Colombier }
5263e12c5d1SDavid du Colombier 
5273e12c5d1SDavid du Colombier /*
528219b2ee8SDavid du Colombier  *  make a:b, move all white space after both
529219b2ee8SDavid du Colombier  */
530219b2ee8SDavid du Colombier Node*
colon(Node * p1,Node * p2)531219b2ee8SDavid du Colombier colon(Node *p1, Node *p2)
532219b2ee8SDavid du Colombier {
533219b2ee8SDavid du Colombier 	if(p1->white){
534219b2ee8SDavid du Colombier 		if(p2->white)
535219b2ee8SDavid du Colombier 			s_append(p1->white, s_to_c(p2->white));
5367dd7cddfSDavid du Colombier 	} else {
537219b2ee8SDavid du Colombier 		p1->white = p2->white;
5387dd7cddfSDavid du Colombier 		p2->white = 0;
5397dd7cddfSDavid du Colombier 	}
540219b2ee8SDavid du Colombier 
541219b2ee8SDavid du Colombier 	s_append(p1->s, ":");
542219b2ee8SDavid du Colombier 	if(p2->s)
543219b2ee8SDavid du Colombier 		s_append(p1->s, s_to_c(p2->s));
544219b2ee8SDavid du Colombier 
5457dd7cddfSDavid du Colombier 	if(p1->end < p2->end)
5467dd7cddfSDavid du Colombier 		p1->end = p2->end;
547219b2ee8SDavid du Colombier 	freenode(p2);
548219b2ee8SDavid du Colombier 	return p1;
549219b2ee8SDavid du Colombier }
550219b2ee8SDavid du Colombier 
551219b2ee8SDavid du Colombier /*
5527dd7cddfSDavid du Colombier  *  concatenate two fields, move all white space after both
5533e12c5d1SDavid du Colombier  */
5543e12c5d1SDavid du Colombier Node*
concat(Node * p1,Node * p2)5557dd7cddfSDavid du Colombier concat(Node *p1, Node *p2)
5563e12c5d1SDavid du Colombier {
5577dd7cddfSDavid du Colombier 	char buf[2];
5587dd7cddfSDavid du Colombier 
5593e12c5d1SDavid du Colombier 	if(p1->white){
5603e12c5d1SDavid du Colombier 		if(p2->white)
5613e12c5d1SDavid du Colombier 			s_append(p1->white, s_to_c(p2->white));
5627dd7cddfSDavid du Colombier 	} else {
5633e12c5d1SDavid du Colombier 		p1->white = p2->white;
5647dd7cddfSDavid du Colombier 		p2->white = 0;
5657dd7cddfSDavid du Colombier 	}
5663e12c5d1SDavid du Colombier 
5677dd7cddfSDavid du Colombier 	if(p1->s == nil){
5687dd7cddfSDavid du Colombier 		buf[0] = p1->c;
5697dd7cddfSDavid du Colombier 		buf[1] = 0;
5707dd7cddfSDavid du Colombier 		p1->s = s_new();
5717dd7cddfSDavid du Colombier 		s_append(p1->s, buf);
5727dd7cddfSDavid du Colombier 	}
5737dd7cddfSDavid du Colombier 
5743e12c5d1SDavid du Colombier 	if(p2->s)
5753e12c5d1SDavid du Colombier 		s_append(p1->s, s_to_c(p2->s));
5767dd7cddfSDavid du Colombier 	else {
5777dd7cddfSDavid du Colombier 		buf[0] = p2->c;
5787dd7cddfSDavid du Colombier 		buf[1] = 0;
5797dd7cddfSDavid du Colombier 		s_append(p1->s, buf);
5807dd7cddfSDavid du Colombier 	}
5813e12c5d1SDavid du Colombier 
5827dd7cddfSDavid du Colombier 	if(p1->end < p2->end)
5837dd7cddfSDavid du Colombier 		p1->end = p2->end;
5843e12c5d1SDavid du Colombier 	freenode(p2);
5853e12c5d1SDavid du Colombier 	return p1;
5863e12c5d1SDavid du Colombier }
5873e12c5d1SDavid du Colombier 
5883e12c5d1SDavid du Colombier /*
5897dd7cddfSDavid du Colombier  *  look for disallowed chars in the field name
5907dd7cddfSDavid du Colombier  */
5917dd7cddfSDavid du Colombier int
badfieldname(Node * p)5927dd7cddfSDavid du Colombier badfieldname(Node *p)
5937dd7cddfSDavid du Colombier {
5947dd7cddfSDavid du Colombier 	for(; p; p = p->next){
5957dd7cddfSDavid du Colombier 		/* field name can't contain white space */
5967dd7cddfSDavid du Colombier 		if(p->white && p->next)
5977dd7cddfSDavid du Colombier 			return 1;
5987dd7cddfSDavid du Colombier 	}
5997dd7cddfSDavid du Colombier 	return 0;
6007dd7cddfSDavid du Colombier }
6017dd7cddfSDavid du Colombier 
6027dd7cddfSDavid du Colombier /*
6033e12c5d1SDavid du Colombier  *  mark as an address
6043e12c5d1SDavid du Colombier  */
6053e12c5d1SDavid du Colombier Node *
address(Node * p)6063e12c5d1SDavid du Colombier address(Node *p)
6073e12c5d1SDavid du Colombier {
6083e12c5d1SDavid du Colombier 	p->addr = 1;
6093e12c5d1SDavid du Colombier 	return p;
6103e12c5d1SDavid du Colombier }
6113e12c5d1SDavid du Colombier 
6123e12c5d1SDavid du Colombier /*
6133e12c5d1SDavid du Colombier  *  case independent string compare
6143e12c5d1SDavid du Colombier  */
6153e12c5d1SDavid du Colombier int
cistrcmp(char * s1,char * s2)6163e12c5d1SDavid du Colombier cistrcmp(char *s1, char *s2)
6173e12c5d1SDavid du Colombier {
6183e12c5d1SDavid du Colombier 	int c1, c2;
6193e12c5d1SDavid du Colombier 
6203e12c5d1SDavid du Colombier 	for(; *s1; s1++, s2++){
6213e12c5d1SDavid du Colombier 		c1 = isupper(*s1) ? tolower(*s1) : *s1;
6223e12c5d1SDavid du Colombier 		c2 = isupper(*s2) ? tolower(*s2) : *s2;
6233e12c5d1SDavid du Colombier 		if (c1 != c2)
6243e12c5d1SDavid du Colombier 			return -1;
6253e12c5d1SDavid du Colombier 	}
6263e12c5d1SDavid du Colombier 	return *s2;
6273e12c5d1SDavid du Colombier }
6283e12c5d1SDavid du Colombier 
6293e12c5d1SDavid du Colombier /*
6303e12c5d1SDavid du Colombier  *  free a node
6313e12c5d1SDavid du Colombier  */
6323e12c5d1SDavid du Colombier void
freenode(Node * p)6333e12c5d1SDavid du Colombier freenode(Node *p)
6343e12c5d1SDavid du Colombier {
6353e12c5d1SDavid du Colombier 	Node *tp;
6363e12c5d1SDavid du Colombier 
6373e12c5d1SDavid du Colombier 	while(p){
6383e12c5d1SDavid du Colombier 		tp = p->next;
6393e12c5d1SDavid du Colombier 		if(p->s)
6403e12c5d1SDavid du Colombier 			s_free(p->s);
6413e12c5d1SDavid du Colombier 		if(p->white)
6423e12c5d1SDavid du Colombier 			s_free(p->white);
6433e12c5d1SDavid du Colombier 		free(p);
6443e12c5d1SDavid du Colombier 		p = tp;
6453e12c5d1SDavid du Colombier 	}
6463e12c5d1SDavid du Colombier }
6473e12c5d1SDavid du Colombier 
6483e12c5d1SDavid du Colombier 
6493e12c5d1SDavid du Colombier /*
6503e12c5d1SDavid du Colombier  *  an anonymous user
6513e12c5d1SDavid du Colombier  */
6523e12c5d1SDavid du Colombier Node*
nobody(Node * p)6539a747e4fSDavid du Colombier nobody(Node *p)
6543e12c5d1SDavid du Colombier {
6553e12c5d1SDavid du Colombier 	if(p->s)
6563e12c5d1SDavid du Colombier 		s_free(p->s);
6573e12c5d1SDavid du Colombier 	p->s = s_copy("pOsTmAsTeR");
6583e12c5d1SDavid du Colombier 	p->addr = 1;
6593e12c5d1SDavid du Colombier 	return p;
6603e12c5d1SDavid du Colombier }
6613e12c5d1SDavid du Colombier 
6623e12c5d1SDavid du Colombier /*
663332d2f58SDavid du Colombier  *  add anything that was dropped because of a parse error
664332d2f58SDavid du Colombier  */
665332d2f58SDavid du Colombier void
missing(Node * p)666332d2f58SDavid du Colombier missing(Node *p)
667332d2f58SDavid du Colombier {
668332d2f58SDavid du Colombier 	Node *np;
669332d2f58SDavid du Colombier 	char *start, *end;
670332d2f58SDavid du Colombier 	Field *f;
671332d2f58SDavid du Colombier 	String *s;
672332d2f58SDavid du Colombier 
673332d2f58SDavid du Colombier 	start = yybuffer;
674332d2f58SDavid du Colombier 	if(lastfield != nil){
675332d2f58SDavid du Colombier 		for(np = lastfield->node; np; np = np->next)
676332d2f58SDavid du Colombier 			start = np->end+1;
677332d2f58SDavid du Colombier 	}
678332d2f58SDavid du Colombier 
679332d2f58SDavid du Colombier 	end = p->start-1;
680332d2f58SDavid du Colombier 
681332d2f58SDavid du Colombier 	if(end <= start)
682332d2f58SDavid du Colombier 		return;
683332d2f58SDavid du Colombier 
684332d2f58SDavid du Colombier 	if(strncmp(start, "From ", 5) == 0)
685332d2f58SDavid du Colombier 		return;
686332d2f58SDavid du Colombier 
687332d2f58SDavid du Colombier 	np = malloc(sizeof(Node));
688332d2f58SDavid du Colombier 	np->start = start;
689332d2f58SDavid du Colombier 	np->end = end;
690332d2f58SDavid du Colombier 	np->white = nil;
691332d2f58SDavid du Colombier 	s = s_copy("BadHeader: ");
692332d2f58SDavid du Colombier 	np->s = s_nappend(s, start, end-start);
693332d2f58SDavid du Colombier 	np->next = nil;
694332d2f58SDavid du Colombier 
695332d2f58SDavid du Colombier 	f = malloc(sizeof(Field));
696332d2f58SDavid du Colombier 	f->next = 0;
697332d2f58SDavid du Colombier 	f->node = np;
698332d2f58SDavid du Colombier 	f->source = 0;
699332d2f58SDavid du Colombier 	if(firstfield)
700332d2f58SDavid du Colombier 		lastfield->next = f;
701332d2f58SDavid du Colombier 	else
702332d2f58SDavid du Colombier 		firstfield = f;
703332d2f58SDavid du Colombier 	lastfield = f;
704332d2f58SDavid du Colombier }
705332d2f58SDavid du Colombier 
706332d2f58SDavid du Colombier /*
7073e12c5d1SDavid du Colombier  *  create a new field
7083e12c5d1SDavid du Colombier  */
7093e12c5d1SDavid du Colombier void
newfield(Node * p,int source)7103e12c5d1SDavid du Colombier newfield(Node *p, int source)
7113e12c5d1SDavid du Colombier {
7123e12c5d1SDavid du Colombier 	Field *f;
7133e12c5d1SDavid du Colombier 
714332d2f58SDavid du Colombier 	missing(p);
715332d2f58SDavid du Colombier 
7163e12c5d1SDavid du Colombier 	f = malloc(sizeof(Field));
7173e12c5d1SDavid du Colombier 	f->next = 0;
7183e12c5d1SDavid du Colombier 	f->node = p;
7193e12c5d1SDavid du Colombier 	f->source = source;
7203e12c5d1SDavid du Colombier 	if(firstfield)
7213e12c5d1SDavid du Colombier 		lastfield->next = f;
7223e12c5d1SDavid du Colombier 	else
7233e12c5d1SDavid du Colombier 		firstfield = f;
7243e12c5d1SDavid du Colombier 	lastfield = f;
725332d2f58SDavid du Colombier 	endfield = startfield;
726332d2f58SDavid du Colombier 	startfield = yylp;
7273e12c5d1SDavid du Colombier }
7283e12c5d1SDavid du Colombier 
7293e12c5d1SDavid du Colombier /*
7303e12c5d1SDavid du Colombier  *  fee a list of fields
7313e12c5d1SDavid du Colombier  */
7323e12c5d1SDavid du Colombier void
freefield(Field * f)7333e12c5d1SDavid du Colombier freefield(Field *f)
7343e12c5d1SDavid du Colombier {
7353e12c5d1SDavid du Colombier 	Field *tf;
7363e12c5d1SDavid du Colombier 
7373e12c5d1SDavid du Colombier 	while(f){
7383e12c5d1SDavid du Colombier 		tf = f->next;
7393e12c5d1SDavid du Colombier 		freenode(f->node);
7403e12c5d1SDavid du Colombier 		free(f);
7413e12c5d1SDavid du Colombier 		f = tf;
7423e12c5d1SDavid du Colombier 	}
7433e12c5d1SDavid du Colombier }
7443e12c5d1SDavid du Colombier 
7453e12c5d1SDavid du Colombier /*
7463e12c5d1SDavid du Colombier  *  add some white space to a node
7473e12c5d1SDavid du Colombier  */
7483e12c5d1SDavid du Colombier Node*
whiten(Node * p)7493e12c5d1SDavid du Colombier whiten(Node *p)
7503e12c5d1SDavid du Colombier {
7513e12c5d1SDavid du Colombier 	Node *tp;
7523e12c5d1SDavid du Colombier 
7533e12c5d1SDavid du Colombier 	for(tp = p; tp->next; tp = tp->next)
7543e12c5d1SDavid du Colombier 		;
7553e12c5d1SDavid du Colombier 	if(tp->white == 0)
7563e12c5d1SDavid du Colombier 		tp->white = s_copy(" ");
7573e12c5d1SDavid du Colombier 	return p;
7583e12c5d1SDavid du Colombier }
759219b2ee8SDavid du Colombier 
760219b2ee8SDavid du Colombier void
yycleanup(void)761219b2ee8SDavid du Colombier yycleanup(void)
762219b2ee8SDavid du Colombier {
763219b2ee8SDavid du Colombier 	Field *f, *fnext;
764219b2ee8SDavid du Colombier 	Node *np, *next;
765219b2ee8SDavid du Colombier 
766219b2ee8SDavid du Colombier 	for(f = firstfield; f; f = fnext){
767219b2ee8SDavid du Colombier 		for(np = f->node; np; np = next){
768219b2ee8SDavid du Colombier 			if(np->s)
769219b2ee8SDavid du Colombier 				s_free(np->s);
770219b2ee8SDavid du Colombier 			if(np->white)
771219b2ee8SDavid du Colombier 				s_free(np->white);
772219b2ee8SDavid du Colombier 			next = np->next;
773219b2ee8SDavid du Colombier 			free(np);
774219b2ee8SDavid du Colombier 		}
775219b2ee8SDavid du Colombier 		fnext = f->next;
776219b2ee8SDavid du Colombier 		free(f);
777219b2ee8SDavid du Colombier 	}
778219b2ee8SDavid du Colombier 	firstfield = lastfield = 0;
779219b2ee8SDavid du Colombier }
780