xref: /plan9/sys/src/libhtml/lex.c (revision 4ac975e2e38b792d24bc60de7fce5e6173f046ea)
19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <draw.h>
49a747e4fSDavid du Colombier #include <ctype.h>
59a747e4fSDavid du Colombier #include <html.h>
69a747e4fSDavid du Colombier #include "impl.h"
79a747e4fSDavid du Colombier 
89a747e4fSDavid du Colombier typedef struct TokenSource TokenSource;
99a747e4fSDavid du Colombier struct TokenSource
109a747e4fSDavid du Colombier {
119a747e4fSDavid du Colombier 	int			i;		// index of next byte to use
129a747e4fSDavid du Colombier 	uchar*		data;		// all the data
139a747e4fSDavid du Colombier 	int			edata;	// data[0:edata] is valid
149a747e4fSDavid du Colombier 	int			chset;	// one of US_Ascii, etc.
159a747e4fSDavid du Colombier 	int			mtype;	// TextHtml or TextPlain
169a747e4fSDavid du Colombier };
179a747e4fSDavid du Colombier 
189a747e4fSDavid du Colombier enum {
199a747e4fSDavid du Colombier 	EOF = -2,
209a747e4fSDavid du Colombier 	EOB = -1
219a747e4fSDavid du Colombier };
229a747e4fSDavid du Colombier 
239a747e4fSDavid du Colombier #define ISNAMCHAR(c)	((c)<256 && (isalpha(c) || isdigit(c) || (c) == '-' || (c) == '.'))
249a747e4fSDavid du Colombier 
259a747e4fSDavid du Colombier #define SMALLBUFSIZE 240
269a747e4fSDavid du Colombier #define BIGBUFSIZE 2000
279a747e4fSDavid du Colombier 
289a747e4fSDavid du Colombier // HTML 4.0 tag names.
299a747e4fSDavid du Colombier // Keep sorted, and in correspondence with enum in iparse.h.
309a747e4fSDavid du Colombier Rune* tagnames[] = {
319a747e4fSDavid du Colombier 	L" ",
329a747e4fSDavid du Colombier 	L"!",
339a747e4fSDavid du Colombier 	L"a",
349a747e4fSDavid du Colombier 	L"abbr",
359a747e4fSDavid du Colombier 	L"acronym",
369a747e4fSDavid du Colombier 	L"address",
379a747e4fSDavid du Colombier 	L"applet",
389a747e4fSDavid du Colombier 	L"area",
399a747e4fSDavid du Colombier 	L"b",
409a747e4fSDavid du Colombier 	L"base",
419a747e4fSDavid du Colombier 	L"basefont",
429a747e4fSDavid du Colombier 	L"bdo",
439a747e4fSDavid du Colombier 	L"big",
449a747e4fSDavid du Colombier 	L"blink",
459a747e4fSDavid du Colombier 	L"blockquote",
469a747e4fSDavid du Colombier 	L"body",
479a747e4fSDavid du Colombier 	L"bq",
489a747e4fSDavid du Colombier 	L"br",
499a747e4fSDavid du Colombier 	L"button",
509a747e4fSDavid du Colombier 	L"caption",
519a747e4fSDavid du Colombier 	L"center",
529a747e4fSDavid du Colombier 	L"cite",
539a747e4fSDavid du Colombier 	L"code",
549a747e4fSDavid du Colombier 	L"col",
559a747e4fSDavid du Colombier 	L"colgroup",
569a747e4fSDavid du Colombier 	L"dd",
579a747e4fSDavid du Colombier 	L"del",
589a747e4fSDavid du Colombier 	L"dfn",
599a747e4fSDavid du Colombier 	L"dir",
609a747e4fSDavid du Colombier 	L"div",
619a747e4fSDavid du Colombier 	L"dl",
629a747e4fSDavid du Colombier 	L"dt",
639a747e4fSDavid du Colombier 	L"em",
649a747e4fSDavid du Colombier 	L"fieldset",
659a747e4fSDavid du Colombier 	L"font",
669a747e4fSDavid du Colombier 	L"form",
679a747e4fSDavid du Colombier 	L"frame",
689a747e4fSDavid du Colombier 	L"frameset",
699a747e4fSDavid du Colombier 	L"h1",
709a747e4fSDavid du Colombier 	L"h2",
719a747e4fSDavid du Colombier 	L"h3",
729a747e4fSDavid du Colombier 	L"h4",
739a747e4fSDavid du Colombier 	L"h5",
749a747e4fSDavid du Colombier 	L"h6",
759a747e4fSDavid du Colombier 	L"head",
769a747e4fSDavid du Colombier 	L"hr",
779a747e4fSDavid du Colombier 	L"html",
789a747e4fSDavid du Colombier 	L"i",
799a747e4fSDavid du Colombier 	L"iframe",
809a747e4fSDavid du Colombier 	L"img",
819a747e4fSDavid du Colombier 	L"input",
829a747e4fSDavid du Colombier 	L"ins",
839a747e4fSDavid du Colombier 	L"isindex",
849a747e4fSDavid du Colombier 	L"kbd",
859a747e4fSDavid du Colombier 	L"label",
869a747e4fSDavid du Colombier 	L"legend",
879a747e4fSDavid du Colombier 	L"li",
889a747e4fSDavid du Colombier 	L"link",
899a747e4fSDavid du Colombier 	L"map",
909a747e4fSDavid du Colombier 	L"menu",
919a747e4fSDavid du Colombier 	L"meta",
929a747e4fSDavid du Colombier 	L"nobr",
939a747e4fSDavid du Colombier 	L"noframes",
949a747e4fSDavid du Colombier 	L"noscript",
959a747e4fSDavid du Colombier 	L"object",
969a747e4fSDavid du Colombier 	L"ol",
979a747e4fSDavid du Colombier 	L"optgroup",
989a747e4fSDavid du Colombier 	L"option",
999a747e4fSDavid du Colombier 	L"p",
1009a747e4fSDavid du Colombier 	L"param",
1019a747e4fSDavid du Colombier 	L"pre",
1029a747e4fSDavid du Colombier 	L"q",
1039a747e4fSDavid du Colombier 	L"s",
1049a747e4fSDavid du Colombier 	L"samp",
1059a747e4fSDavid du Colombier 	L"script",
1069a747e4fSDavid du Colombier 	L"select",
1079a747e4fSDavid du Colombier 	L"small",
1089a747e4fSDavid du Colombier 	L"span",
1099a747e4fSDavid du Colombier 	L"strike",
1109a747e4fSDavid du Colombier 	L"strong",
1119a747e4fSDavid du Colombier 	L"style",
1129a747e4fSDavid du Colombier 	L"sub",
1139a747e4fSDavid du Colombier 	L"sup",
1149a747e4fSDavid du Colombier 	L"table",
1159a747e4fSDavid du Colombier 	L"tbody",
1169a747e4fSDavid du Colombier 	L"td",
1179a747e4fSDavid du Colombier 	L"textarea",
1189a747e4fSDavid du Colombier 	L"tfoot",
1199a747e4fSDavid du Colombier 	L"th",
1209a747e4fSDavid du Colombier 	L"thead",
1219a747e4fSDavid du Colombier 	L"title",
1229a747e4fSDavid du Colombier 	L"tr",
1239a747e4fSDavid du Colombier 	L"tt",
1249a747e4fSDavid du Colombier 	L"u",
1259a747e4fSDavid du Colombier 	L"ul",
1269a747e4fSDavid du Colombier 	L"var"
1279a747e4fSDavid du Colombier };
1289a747e4fSDavid du Colombier 
1299a747e4fSDavid du Colombier // HTML 4.0 attribute names.
1309a747e4fSDavid du Colombier // Keep sorted, and in correspondence with enum in i.h.
1319a747e4fSDavid du Colombier Rune* attrnames[] = {
1329a747e4fSDavid du Colombier 	L"abbr",
1339a747e4fSDavid du Colombier 	L"accept-charset",
1349a747e4fSDavid du Colombier 	L"access-key",
1359a747e4fSDavid du Colombier 	L"action",
1369a747e4fSDavid du Colombier 	L"align",
1379a747e4fSDavid du Colombier 	L"alink",
1389a747e4fSDavid du Colombier 	L"alt",
1399a747e4fSDavid du Colombier 	L"archive",
1409a747e4fSDavid du Colombier 	L"axis",
1419a747e4fSDavid du Colombier 	L"background",
1429a747e4fSDavid du Colombier 	L"bgcolor",
1439a747e4fSDavid du Colombier 	L"border",
1449a747e4fSDavid du Colombier 	L"cellpadding",
1459a747e4fSDavid du Colombier 	L"cellspacing",
1469a747e4fSDavid du Colombier 	L"char",
1479a747e4fSDavid du Colombier 	L"charoff",
1489a747e4fSDavid du Colombier 	L"charset",
1499a747e4fSDavid du Colombier 	L"checked",
1509a747e4fSDavid du Colombier 	L"cite",
1519a747e4fSDavid du Colombier 	L"class",
1529a747e4fSDavid du Colombier 	L"classid",
1539a747e4fSDavid du Colombier 	L"clear",
1549a747e4fSDavid du Colombier 	L"code",
1559a747e4fSDavid du Colombier 	L"codebase",
1569a747e4fSDavid du Colombier 	L"codetype",
1579a747e4fSDavid du Colombier 	L"color",
1589a747e4fSDavid du Colombier 	L"cols",
1599a747e4fSDavid du Colombier 	L"colspan",
1609a747e4fSDavid du Colombier 	L"compact",
1619a747e4fSDavid du Colombier 	L"content",
1629a747e4fSDavid du Colombier 	L"coords",
1639a747e4fSDavid du Colombier 	L"data",
1649a747e4fSDavid du Colombier 	L"datetime",
1659a747e4fSDavid du Colombier 	L"declare",
1669a747e4fSDavid du Colombier 	L"defer",
1679a747e4fSDavid du Colombier 	L"dir",
1689a747e4fSDavid du Colombier 	L"disabled",
1699a747e4fSDavid du Colombier 	L"enctype",
1709a747e4fSDavid du Colombier 	L"face",
1719a747e4fSDavid du Colombier 	L"for",
1729a747e4fSDavid du Colombier 	L"frame",
1739a747e4fSDavid du Colombier 	L"frameborder",
1749a747e4fSDavid du Colombier 	L"headers",
1759a747e4fSDavid du Colombier 	L"height",
1769a747e4fSDavid du Colombier 	L"href",
1779a747e4fSDavid du Colombier 	L"hreflang",
1789a747e4fSDavid du Colombier 	L"hspace",
1799a747e4fSDavid du Colombier 	L"http-equiv",
1809a747e4fSDavid du Colombier 	L"id",
1819a747e4fSDavid du Colombier 	L"ismap",
1829a747e4fSDavid du Colombier 	L"label",
1839a747e4fSDavid du Colombier 	L"lang",
1849a747e4fSDavid du Colombier 	L"link",
1859a747e4fSDavid du Colombier 	L"longdesc",
1869a747e4fSDavid du Colombier 	L"marginheight",
1879a747e4fSDavid du Colombier 	L"marginwidth",
1889a747e4fSDavid du Colombier 	L"maxlength",
1899a747e4fSDavid du Colombier 	L"media",
1909a747e4fSDavid du Colombier 	L"method",
1919a747e4fSDavid du Colombier 	L"multiple",
1929a747e4fSDavid du Colombier 	L"name",
1939a747e4fSDavid du Colombier 	L"nohref",
1949a747e4fSDavid du Colombier 	L"noresize",
1959a747e4fSDavid du Colombier 	L"noshade",
1969a747e4fSDavid du Colombier 	L"nowrap",
1979a747e4fSDavid du Colombier 	L"object",
1989a747e4fSDavid du Colombier 	L"onblur",
1999a747e4fSDavid du Colombier 	L"onchange",
2009a747e4fSDavid du Colombier 	L"onclick",
2019a747e4fSDavid du Colombier 	L"ondblclick",
2029a747e4fSDavid du Colombier 	L"onfocus",
2039a747e4fSDavid du Colombier 	L"onkeypress",
2049a747e4fSDavid du Colombier 	L"onkeyup",
2059a747e4fSDavid du Colombier 	L"onload",
2069a747e4fSDavid du Colombier 	L"onmousedown",
2079a747e4fSDavid du Colombier 	L"onmousemove",
2089a747e4fSDavid du Colombier 	L"onmouseout",
2099a747e4fSDavid du Colombier 	L"onmouseover",
2109a747e4fSDavid du Colombier 	L"onmouseup",
2119a747e4fSDavid du Colombier 	L"onreset",
2129a747e4fSDavid du Colombier 	L"onselect",
2139a747e4fSDavid du Colombier 	L"onsubmit",
2149a747e4fSDavid du Colombier 	L"onunload",
2159a747e4fSDavid du Colombier 	L"profile",
2169a747e4fSDavid du Colombier 	L"prompt",
2179a747e4fSDavid du Colombier 	L"readonly",
2189a747e4fSDavid du Colombier 	L"rel",
2199a747e4fSDavid du Colombier 	L"rev",
2209a747e4fSDavid du Colombier 	L"rows",
2219a747e4fSDavid du Colombier 	L"rowspan",
2229a747e4fSDavid du Colombier 	L"rules",
2239a747e4fSDavid du Colombier 	L"scheme",
2249a747e4fSDavid du Colombier 	L"scope",
2259a747e4fSDavid du Colombier 	L"scrolling",
2269a747e4fSDavid du Colombier 	L"selected",
2279a747e4fSDavid du Colombier 	L"shape",
2289a747e4fSDavid du Colombier 	L"size",
2299a747e4fSDavid du Colombier 	L"span",
2309a747e4fSDavid du Colombier 	L"src",
2319a747e4fSDavid du Colombier 	L"standby",
2329a747e4fSDavid du Colombier 	L"start",
2339a747e4fSDavid du Colombier 	L"style",
2349a747e4fSDavid du Colombier 	L"summary",
2359a747e4fSDavid du Colombier 	L"tabindex",
2369a747e4fSDavid du Colombier 	L"target",
2379a747e4fSDavid du Colombier 	L"text",
2389a747e4fSDavid du Colombier 	L"title",
2399a747e4fSDavid du Colombier 	L"type",
2409a747e4fSDavid du Colombier 	L"usemap",
2419a747e4fSDavid du Colombier 	L"valign",
2429a747e4fSDavid du Colombier 	L"value",
2439a747e4fSDavid du Colombier 	L"valuetype",
2449a747e4fSDavid du Colombier 	L"version",
2459a747e4fSDavid du Colombier 	L"vlink",
2469a747e4fSDavid du Colombier 	L"vspace",
2479a747e4fSDavid du Colombier 	L"width"
2489a747e4fSDavid du Colombier };
2499a747e4fSDavid du Colombier 
2509a747e4fSDavid du Colombier 
2519a747e4fSDavid du Colombier // Character entity to unicode character number map.
2529a747e4fSDavid du Colombier // Keep sorted by name.
2539027b8f7SDavid du Colombier StringInt	chartab[]= {
2549a747e4fSDavid du Colombier 	{L"AElig", 198},
2559a747e4fSDavid du Colombier 	{L"Aacute", 193},
2569a747e4fSDavid du Colombier 	{L"Acirc", 194},
2579a747e4fSDavid du Colombier 	{L"Agrave", 192},
258*4ac975e2SDavid du Colombier 	{L"Alpha", 913},
2599a747e4fSDavid du Colombier 	{L"Aring", 197},
2609a747e4fSDavid du Colombier 	{L"Atilde", 195},
2619a747e4fSDavid du Colombier 	{L"Auml", 196},
262*4ac975e2SDavid du Colombier 	{L"Beta", 914},
2639a747e4fSDavid du Colombier 	{L"Ccedil", 199},
264*4ac975e2SDavid du Colombier 	{L"Chi", 935},
265*4ac975e2SDavid du Colombier 	{L"Dagger", 8225},
266*4ac975e2SDavid du Colombier 	{L"Delta", 916},
2679a747e4fSDavid du Colombier 	{L"ETH", 208},
2689a747e4fSDavid du Colombier 	{L"Eacute", 201},
2699a747e4fSDavid du Colombier 	{L"Ecirc", 202},
2709a747e4fSDavid du Colombier 	{L"Egrave", 200},
271*4ac975e2SDavid du Colombier 	{L"Epsilon", 917},
272*4ac975e2SDavid du Colombier 	{L"Eta", 919},
2739a747e4fSDavid du Colombier 	{L"Euml", 203},
274*4ac975e2SDavid du Colombier 	{L"Gamma", 915},
2759a747e4fSDavid du Colombier 	{L"Iacute", 205},
2769a747e4fSDavid du Colombier 	{L"Icirc", 206},
2779a747e4fSDavid du Colombier 	{L"Igrave", 204},
278*4ac975e2SDavid du Colombier 	{L"Iota", 921},
2799a747e4fSDavid du Colombier 	{L"Iuml", 207},
280*4ac975e2SDavid du Colombier 	{L"Kappa", 922},
281*4ac975e2SDavid du Colombier 	{L"Lambda", 923},
282*4ac975e2SDavid du Colombier 	{L"Mu", 924},
2839a747e4fSDavid du Colombier 	{L"Ntilde", 209},
284*4ac975e2SDavid du Colombier 	{L"Nu", 925},
285*4ac975e2SDavid du Colombier 	{L"OElig", 338},
2869a747e4fSDavid du Colombier 	{L"Oacute", 211},
2879a747e4fSDavid du Colombier 	{L"Ocirc", 212},
2889a747e4fSDavid du Colombier 	{L"Ograve", 210},
289*4ac975e2SDavid du Colombier 	{L"Omega", 937},
290*4ac975e2SDavid du Colombier 	{L"Omicron", 927},
2919a747e4fSDavid du Colombier 	{L"Oslash", 216},
2929a747e4fSDavid du Colombier 	{L"Otilde", 213},
2939a747e4fSDavid du Colombier 	{L"Ouml", 214},
294*4ac975e2SDavid du Colombier 	{L"Phi", 934},
295*4ac975e2SDavid du Colombier 	{L"Pi", 928},
296*4ac975e2SDavid du Colombier 	{L"Prime", 8243},
297*4ac975e2SDavid du Colombier 	{L"Psi", 936},
298*4ac975e2SDavid du Colombier 	{L"Rho", 929},
299*4ac975e2SDavid du Colombier 	{L"Scaron", 352},
300*4ac975e2SDavid du Colombier 	{L"Sigma", 931},
3019a747e4fSDavid du Colombier 	{L"THORN", 222},
302*4ac975e2SDavid du Colombier 	{L"Tau", 932},
303*4ac975e2SDavid du Colombier 	{L"Theta", 920},
3049a747e4fSDavid du Colombier 	{L"Uacute", 218},
3059a747e4fSDavid du Colombier 	{L"Ucirc", 219},
3069a747e4fSDavid du Colombier 	{L"Ugrave", 217},
307*4ac975e2SDavid du Colombier 	{L"Upsilon", 933},
3089a747e4fSDavid du Colombier 	{L"Uuml", 220},
309*4ac975e2SDavid du Colombier 	{L"Xi", 926},
3109a747e4fSDavid du Colombier 	{L"Yacute", 221},
311*4ac975e2SDavid du Colombier 	{L"Yuml", 376},
312*4ac975e2SDavid du Colombier 	{L"Zeta", 918},
3139a747e4fSDavid du Colombier 	{L"aacute", 225},
3149a747e4fSDavid du Colombier 	{L"acirc", 226},
3159a747e4fSDavid du Colombier 	{L"acute", 180},
3169a747e4fSDavid du Colombier 	{L"aelig", 230},
3179a747e4fSDavid du Colombier 	{L"agrave", 224},
318*4ac975e2SDavid du Colombier 	{L"alefsym", 8501},
3199a747e4fSDavid du Colombier 	{L"alpha", 945},
3209a747e4fSDavid du Colombier 	{L"amp", 38},
321*4ac975e2SDavid du Colombier 	{L"and", 8743},
322*4ac975e2SDavid du Colombier 	{L"ang", 8736},
3239a747e4fSDavid du Colombier 	{L"aring", 229},
324*4ac975e2SDavid du Colombier 	{L"asymp", 8776},
3259a747e4fSDavid du Colombier 	{L"atilde", 227},
3269a747e4fSDavid du Colombier 	{L"auml", 228},
327*4ac975e2SDavid du Colombier 	{L"bdquo", 8222},
3289a747e4fSDavid du Colombier 	{L"beta", 946},
3299a747e4fSDavid du Colombier 	{L"brvbar", 166},
330*4ac975e2SDavid du Colombier 	{L"bull", 8226},
331*4ac975e2SDavid du Colombier 	{L"cap", 8745},
3329a747e4fSDavid du Colombier 	{L"ccedil", 231},
3339a747e4fSDavid du Colombier 	{L"cdots", 8943},
3349a747e4fSDavid du Colombier 	{L"cedil", 184},
3359a747e4fSDavid du Colombier 	{L"cent", 162},
3369a747e4fSDavid du Colombier 	{L"chi", 967},
337*4ac975e2SDavid du Colombier 	{L"circ", 710},
338*4ac975e2SDavid du Colombier 	{L"clubs", 9827},
339*4ac975e2SDavid du Colombier 	{L"cong", 8773},
3409a747e4fSDavid du Colombier 	{L"copy", 169},
341*4ac975e2SDavid du Colombier 	{L"crarr", 8629},
342*4ac975e2SDavid du Colombier 	{L"cup", 8746},
3439a747e4fSDavid du Colombier 	{L"curren", 164},
344*4ac975e2SDavid du Colombier 	{L"dArr", 8659},
345*4ac975e2SDavid du Colombier 	{L"dagger", 8224},
346*4ac975e2SDavid du Colombier 	{L"darr", 8595},
3479a747e4fSDavid du Colombier 	{L"ddots", 8945},
3489a747e4fSDavid du Colombier 	{L"deg", 176},
3499a747e4fSDavid du Colombier 	{L"delta", 948},
350*4ac975e2SDavid du Colombier 	{L"diams", 9830},
3519a747e4fSDavid du Colombier 	{L"divide", 247},
3529a747e4fSDavid du Colombier 	{L"eacute", 233},
3539a747e4fSDavid du Colombier 	{L"ecirc", 234},
3549a747e4fSDavid du Colombier 	{L"egrave", 232},
3559027b8f7SDavid du Colombier 	{L"emdash", 8212},	/* non-standard but commonly used */
356*4ac975e2SDavid du Colombier 	{L"empty", 8709},
3579a747e4fSDavid du Colombier 	{L"emsp", 8195},
3589027b8f7SDavid du Colombier 	{L"endash", 8211},	/* non-standard but commonly used */
3599a747e4fSDavid du Colombier 	{L"ensp", 8194},
3609a747e4fSDavid du Colombier 	{L"epsilon", 949},
361*4ac975e2SDavid du Colombier 	{L"equiv", 8801},
3629a747e4fSDavid du Colombier 	{L"eta", 951},
3639a747e4fSDavid du Colombier 	{L"eth", 240},
3649a747e4fSDavid du Colombier 	{L"euml", 235},
365*4ac975e2SDavid du Colombier 	{L"euro", 8364},
366*4ac975e2SDavid du Colombier 	{L"exist", 8707},
367*4ac975e2SDavid du Colombier 	{L"fnof", 402},
368*4ac975e2SDavid du Colombier 	{L"forall", 8704},
3699a747e4fSDavid du Colombier 	{L"frac12", 189},
3709a747e4fSDavid du Colombier 	{L"frac14", 188},
3719a747e4fSDavid du Colombier 	{L"frac34", 190},
372*4ac975e2SDavid du Colombier 	{L"frasl", 8260},
3739a747e4fSDavid du Colombier 	{L"gamma", 947},
374*4ac975e2SDavid du Colombier 	{L"ge", 8805},
3759a747e4fSDavid du Colombier 	{L"gt", 62},
376*4ac975e2SDavid du Colombier 	{L"hArr", 8660},
377*4ac975e2SDavid du Colombier 	{L"harr", 8596},
378*4ac975e2SDavid du Colombier 	{L"hearts", 9829},
379*4ac975e2SDavid du Colombier 	{L"hellip", 8230},
3809a747e4fSDavid du Colombier 	{L"iacute", 237},
3819a747e4fSDavid du Colombier 	{L"icirc", 238},
3829a747e4fSDavid du Colombier 	{L"iexcl", 161},
3839a747e4fSDavid du Colombier 	{L"igrave", 236},
384*4ac975e2SDavid du Colombier 	{L"image", 8465},
385*4ac975e2SDavid du Colombier 	{L"infin", 8734},
386*4ac975e2SDavid du Colombier 	{L"int", 8747},
3879a747e4fSDavid du Colombier 	{L"iota", 953},
3889a747e4fSDavid du Colombier 	{L"iquest", 191},
389*4ac975e2SDavid du Colombier 	{L"isin", 8712},
3909a747e4fSDavid du Colombier 	{L"iuml", 239},
3919a747e4fSDavid du Colombier 	{L"kappa", 954},
392*4ac975e2SDavid du Colombier 	{L"lArr", 8656},
3939a747e4fSDavid du Colombier 	{L"lambda", 955},
394*4ac975e2SDavid du Colombier 	{L"lang", 9001},
3959a747e4fSDavid du Colombier 	{L"laquo", 171},
396*4ac975e2SDavid du Colombier 	{L"larr", 8592},
397*4ac975e2SDavid du Colombier 	{L"lceil", 8968},
3989a747e4fSDavid du Colombier 	{L"ldots", 8230},
399*4ac975e2SDavid du Colombier 	{L"ldquo", 8220},
400*4ac975e2SDavid du Colombier 	{L"le", 8804},
401*4ac975e2SDavid du Colombier 	{L"lfloor", 8970},
402*4ac975e2SDavid du Colombier 	{L"lowast", 8727},
403*4ac975e2SDavid du Colombier 	{L"loz", 9674},
404*4ac975e2SDavid du Colombier 	{L"lrm", 8206},
405*4ac975e2SDavid du Colombier 	{L"lsaquo", 8249},
406*4ac975e2SDavid du Colombier 	{L"lsquo", 8216},
4079a747e4fSDavid du Colombier 	{L"lt", 60},
4089a747e4fSDavid du Colombier 	{L"macr", 175},
4099027b8f7SDavid du Colombier 	{L"mdash", 8212},
4109a747e4fSDavid du Colombier 	{L"micro", 181},
4119a747e4fSDavid du Colombier 	{L"middot", 183},
412*4ac975e2SDavid du Colombier 	{L"minus", 8722},
4139a747e4fSDavid du Colombier 	{L"mu", 956},
414*4ac975e2SDavid du Colombier 	{L"nabla", 8711},
4159a747e4fSDavid du Colombier 	{L"nbsp", 160},
4169027b8f7SDavid du Colombier 	{L"ndash", 8211},
417*4ac975e2SDavid du Colombier 	{L"ne", 8800},
418*4ac975e2SDavid du Colombier 	{L"ni", 8715},
4199a747e4fSDavid du Colombier 	{L"not", 172},
420*4ac975e2SDavid du Colombier 	{L"notin", 8713},
421*4ac975e2SDavid du Colombier 	{L"nsub", 8836},
4229a747e4fSDavid du Colombier 	{L"ntilde", 241},
4239a747e4fSDavid du Colombier 	{L"nu", 957},
4249a747e4fSDavid du Colombier 	{L"oacute", 243},
4259a747e4fSDavid du Colombier 	{L"ocirc", 244},
426*4ac975e2SDavid du Colombier 	{L"oelig", 339},
4279a747e4fSDavid du Colombier 	{L"ograve", 242},
428*4ac975e2SDavid du Colombier 	{L"oline", 8254},
4299a747e4fSDavid du Colombier 	{L"omega", 969},
4309a747e4fSDavid du Colombier 	{L"omicron", 959},
431*4ac975e2SDavid du Colombier 	{L"oplus", 8853},
432*4ac975e2SDavid du Colombier 	{L"or", 8744},
4339a747e4fSDavid du Colombier 	{L"ordf", 170},
4349a747e4fSDavid du Colombier 	{L"ordm", 186},
4359a747e4fSDavid du Colombier 	{L"oslash", 248},
4369a747e4fSDavid du Colombier 	{L"otilde", 245},
437*4ac975e2SDavid du Colombier 	{L"otimes", 8855},
4389a747e4fSDavid du Colombier 	{L"ouml", 246},
4399a747e4fSDavid du Colombier 	{L"para", 182},
440*4ac975e2SDavid du Colombier 	{L"part", 8706},
441*4ac975e2SDavid du Colombier 	{L"permil", 8240},
442*4ac975e2SDavid du Colombier 	{L"perp", 8869},
4439a747e4fSDavid du Colombier 	{L"phi", 966},
4449a747e4fSDavid du Colombier 	{L"pi", 960},
445*4ac975e2SDavid du Colombier 	{L"piv", 982},
4469a747e4fSDavid du Colombier 	{L"plusmn", 177},
4479a747e4fSDavid du Colombier 	{L"pound", 163},
448*4ac975e2SDavid du Colombier 	{L"prime", 8242},
449*4ac975e2SDavid du Colombier 	{L"prod", 8719},
450*4ac975e2SDavid du Colombier 	{L"prop", 8733},
4519a747e4fSDavid du Colombier 	{L"psi", 968},
4529a747e4fSDavid du Colombier 	{L"quad", 8193},
4539a747e4fSDavid du Colombier 	{L"quot", 34},
454*4ac975e2SDavid du Colombier 	{L"rArr", 8658},
455*4ac975e2SDavid du Colombier 	{L"radic", 8730},
456*4ac975e2SDavid du Colombier 	{L"rang", 9002},
4579a747e4fSDavid du Colombier 	{L"raquo", 187},
458*4ac975e2SDavid du Colombier 	{L"rarr", 8594},
459*4ac975e2SDavid du Colombier 	{L"rceil", 8969},
460*4ac975e2SDavid du Colombier 	{L"rdquo", 8221},
461*4ac975e2SDavid du Colombier 	{L"real", 8476},
4629a747e4fSDavid du Colombier 	{L"reg", 174},
463*4ac975e2SDavid du Colombier 	{L"rfloor", 8971},
4649a747e4fSDavid du Colombier 	{L"rho", 961},
465*4ac975e2SDavid du Colombier 	{L"rlm", 8207},
466*4ac975e2SDavid du Colombier 	{L"rsaquo", 8250},
467*4ac975e2SDavid du Colombier 	{L"rsquo", 8217},
468*4ac975e2SDavid du Colombier 	{L"sbquo", 8218},
469*4ac975e2SDavid du Colombier 	{L"scaron", 353},
470*4ac975e2SDavid du Colombier 	{L"sdot", 8901},
4719a747e4fSDavid du Colombier 	{L"sect", 167},
4729a747e4fSDavid du Colombier 	{L"shy", 173},
4739a747e4fSDavid du Colombier 	{L"sigma", 963},
474*4ac975e2SDavid du Colombier 	{L"sigmaf", 962},
475*4ac975e2SDavid du Colombier 	{L"sim", 8764},
4769a747e4fSDavid du Colombier 	{L"sp", 8194},
477*4ac975e2SDavid du Colombier 	{L"spades", 9824},
478*4ac975e2SDavid du Colombier 	{L"sub", 8834},
479*4ac975e2SDavid du Colombier 	{L"sube", 8838},
480*4ac975e2SDavid du Colombier 	{L"sum", 8721},
481*4ac975e2SDavid du Colombier 	{L"sup", 8835},
4829a747e4fSDavid du Colombier 	{L"sup1", 185},
4839a747e4fSDavid du Colombier 	{L"sup2", 178},
4849a747e4fSDavid du Colombier 	{L"sup3", 179},
485*4ac975e2SDavid du Colombier 	{L"supe", 8839},
4869a747e4fSDavid du Colombier 	{L"szlig", 223},
4879a747e4fSDavid du Colombier 	{L"tau", 964},
488*4ac975e2SDavid du Colombier 	{L"there4", 8756},
4899a747e4fSDavid du Colombier 	{L"theta", 952},
490*4ac975e2SDavid du Colombier 	{L"thetasym", 977},
4919a747e4fSDavid du Colombier 	{L"thinsp", 8201},
4929a747e4fSDavid du Colombier 	{L"thorn", 254},
493*4ac975e2SDavid du Colombier 	{L"tilde", 732},
4949a747e4fSDavid du Colombier 	{L"times", 215},
4959a747e4fSDavid du Colombier 	{L"trade", 8482},
496*4ac975e2SDavid du Colombier 	{L"uArr", 8657},
4979a747e4fSDavid du Colombier 	{L"uacute", 250},
498*4ac975e2SDavid du Colombier 	{L"uarr", 8593},
4999a747e4fSDavid du Colombier 	{L"ucirc", 251},
5009a747e4fSDavid du Colombier 	{L"ugrave", 249},
5019a747e4fSDavid du Colombier 	{L"uml", 168},
502*4ac975e2SDavid du Colombier 	{L"upsih", 978},
5039a747e4fSDavid du Colombier 	{L"upsilon", 965},
5049a747e4fSDavid du Colombier 	{L"uuml", 252},
5059a747e4fSDavid du Colombier 	{L"varepsilon", 8712},
5069a747e4fSDavid du Colombier 	{L"varphi", 981},
5079a747e4fSDavid du Colombier 	{L"varpi", 982},
5089a747e4fSDavid du Colombier 	{L"varrho", 1009},
5099a747e4fSDavid du Colombier 	{L"vdots", 8942},
5109a747e4fSDavid du Colombier 	{L"vsigma", 962},
5119a747e4fSDavid du Colombier 	{L"vtheta", 977},
512*4ac975e2SDavid du Colombier 	{L"weierp", 8472},
5139a747e4fSDavid du Colombier 	{L"xi", 958},
5149a747e4fSDavid du Colombier 	{L"yacute", 253},
5159a747e4fSDavid du Colombier 	{L"yen", 165},
5169a747e4fSDavid du Colombier 	{L"yuml", 255},
517*4ac975e2SDavid du Colombier 	{L"zeta", 950},
518*4ac975e2SDavid du Colombier 	{L"zwj", 8205},
519*4ac975e2SDavid du Colombier 	{L"zwnj", 8204}
5209a747e4fSDavid du Colombier };
5219a747e4fSDavid du Colombier #define NCHARTAB (sizeof(chartab)/sizeof(chartab[0]))
5229a747e4fSDavid du Colombier 
5239a747e4fSDavid du Colombier // Characters Winstart..Winend are those that Windows
5249a747e4fSDavid du Colombier // uses interpolated into the Latin1 set.
5259a747e4fSDavid du Colombier // They aren't supposed to appear in HTML, but they do....
5269a747e4fSDavid du Colombier enum {
5279a747e4fSDavid du Colombier 	Winstart = 127,
5289a747e4fSDavid du Colombier 	Winend = 159
5299a747e4fSDavid du Colombier };
5309a747e4fSDavid du Colombier 
5319a747e4fSDavid du Colombier static int	winchars[]= { 8226,	// 8226 is a bullet
5329a747e4fSDavid du Colombier 	8226, 8226, 8218, 402, 8222, 8230, 8224, 8225,
5339a747e4fSDavid du Colombier 	710, 8240, 352, 8249, 338, 8226, 8226, 8226,
5349a747e4fSDavid du Colombier 	8226, 8216, 8217, 8220, 8221, 8226, 8211, 8212,
5359a747e4fSDavid du Colombier 	732, 8482, 353, 8250, 339, 8226, 8226, 376};
5369a747e4fSDavid du Colombier 
5379a747e4fSDavid du Colombier static StringInt*	tagtable;		// initialized from tagnames
5389a747e4fSDavid du Colombier static StringInt*	attrtable;		// initialized from attrnames
5399a747e4fSDavid du Colombier 
5409a747e4fSDavid du Colombier static void		lexinit();
5419a747e4fSDavid du Colombier static int		getplaindata(TokenSource* ts, Token* a, int* pai);
5429a747e4fSDavid du Colombier static int		getdata(TokenSource* ts, int firstc, int starti, Token* a, int* pai);
5439a747e4fSDavid du Colombier static int		getscriptdata(TokenSource* ts, int firstc, int starti, Token* a, int* pai);
5449a747e4fSDavid du Colombier static int		gettag(TokenSource* ts, int starti, Token* a, int* pai);
5459a747e4fSDavid du Colombier static Rune*	buftostr(Rune* s, Rune* buf, int j);
5469a747e4fSDavid du Colombier static int		comment(TokenSource* ts);
5479a747e4fSDavid du Colombier static int		findstr(TokenSource* ts, Rune* s);
5489a747e4fSDavid du Colombier static int		ampersand(TokenSource* ts);
5499a747e4fSDavid du Colombier static int		lowerc(int c);
5509a747e4fSDavid du Colombier static int		getchar(TokenSource* ts);
5519a747e4fSDavid du Colombier static void		ungetchar(TokenSource* ts, int c);
5529a747e4fSDavid du Colombier static void		backup(TokenSource* ts, int savei);
5539a747e4fSDavid du Colombier static void		freeinsidetoken(Token* t);
5549a747e4fSDavid du Colombier static void		freeattrs(Attr* ahead);
5559a747e4fSDavid du Colombier static Attr*	newattr(int attid, Rune* value, Attr* link);
5569a747e4fSDavid du Colombier static int		Tconv(Fmt* f);
5579a747e4fSDavid du Colombier 
5589a747e4fSDavid du Colombier int	dbglex = 0;
5599a747e4fSDavid du Colombier static int lexinited = 0;
5609a747e4fSDavid du Colombier 
5619a747e4fSDavid du Colombier static void
5629a747e4fSDavid du Colombier lexinit(void)
5639a747e4fSDavid du Colombier {
5649a747e4fSDavid du Colombier 	tagtable = _makestrinttab(tagnames, Numtags);
5659a747e4fSDavid du Colombier 	attrtable = _makestrinttab(attrnames, Numattrs);
5669a747e4fSDavid du Colombier 	fmtinstall('T', Tconv);
5679a747e4fSDavid du Colombier 	lexinited = 1;
5689a747e4fSDavid du Colombier }
5699a747e4fSDavid du Colombier 
5709a747e4fSDavid du Colombier static TokenSource*
5719a747e4fSDavid du Colombier newtokensource(uchar* data, int edata, int chset, int mtype)
5729a747e4fSDavid du Colombier {
5739a747e4fSDavid du Colombier 	TokenSource*	ans;
5749a747e4fSDavid du Colombier 
5759a747e4fSDavid du Colombier 	assert(chset == US_Ascii || chset == ISO_8859_1 ||
5769a747e4fSDavid du Colombier 			chset == UTF_8 || chset == Unicode);
5779a747e4fSDavid du Colombier 	ans = (TokenSource*)emalloc(sizeof(TokenSource));
5789a747e4fSDavid du Colombier 	ans->i = 0;
5799a747e4fSDavid du Colombier 	ans->data = data;
5809a747e4fSDavid du Colombier 	ans->edata = edata;
5819a747e4fSDavid du Colombier 	ans->chset = chset;
5829a747e4fSDavid du Colombier 	ans->mtype = mtype;
5839a747e4fSDavid du Colombier 	return ans;
5849a747e4fSDavid du Colombier }
5859a747e4fSDavid du Colombier 
5869a747e4fSDavid du Colombier enum {
5879a747e4fSDavid du Colombier 	ToksChunk = 500
5889a747e4fSDavid du Colombier };
5899a747e4fSDavid du Colombier 
5909a747e4fSDavid du Colombier // Call this to get the tokens.
5919a747e4fSDavid du Colombier //  The number of returned tokens is returned in *plen.
5929a747e4fSDavid du Colombier Token*
5939a747e4fSDavid du Colombier _gettoks(uchar* data, int datalen, int chset, int mtype, int* plen)
5949a747e4fSDavid du Colombier {
5959a747e4fSDavid du Colombier 	TokenSource*	ts;
5969a747e4fSDavid du Colombier 	Token*		a;
5979a747e4fSDavid du Colombier 	int	alen;
5989a747e4fSDavid du Colombier 	int	ai;
5999a747e4fSDavid du Colombier 	int	starti;
6009a747e4fSDavid du Colombier 	int	c;
6019a747e4fSDavid du Colombier 	int	tag;
6029a747e4fSDavid du Colombier 
6039a747e4fSDavid du Colombier 	if(!lexinited)
6049a747e4fSDavid du Colombier 		lexinit();
6059a747e4fSDavid du Colombier 	ts = newtokensource(data, datalen, chset, mtype);
6069a747e4fSDavid du Colombier 	alen = ToksChunk;
6079a747e4fSDavid du Colombier 	a = (Token*)emalloc(alen * sizeof(Token));
6089a747e4fSDavid du Colombier 	ai = 0;
6099a747e4fSDavid du Colombier 	if(dbglex)
6109a747e4fSDavid du Colombier 		fprint(2, "_gettoks starts, ts.i=%d, ts.edata=%d\n", ts->i, ts->edata);
6119a747e4fSDavid du Colombier 	if(ts->mtype == TextHtml) {
6129a747e4fSDavid du Colombier 		for(;;) {
6139a747e4fSDavid du Colombier 			if(ai == alen) {
6149a747e4fSDavid du Colombier 				a = (Token*)erealloc(a, (alen+ToksChunk)*sizeof(Token));
6159a747e4fSDavid du Colombier 				alen += ToksChunk;
6169a747e4fSDavid du Colombier 			}
6179a747e4fSDavid du Colombier 			starti = ts->i;
6189a747e4fSDavid du Colombier 			c = getchar(ts);
6199a747e4fSDavid du Colombier 			if(c < 0)
6209a747e4fSDavid du Colombier 				break;
6219a747e4fSDavid du Colombier 			if(c == '<') {
6229a747e4fSDavid du Colombier 				tag = gettag(ts, starti, a, &ai);
6239a747e4fSDavid du Colombier 				if(tag == Tscript) {
6249a747e4fSDavid du Colombier 					// special rules for getting Data after....
6259a747e4fSDavid du Colombier 					starti = ts->i;
6269a747e4fSDavid du Colombier 					c = getchar(ts);
6279a747e4fSDavid du Colombier 					tag = getscriptdata(ts, c, starti, a, &ai);
6289a747e4fSDavid du Colombier 				}
6299a747e4fSDavid du Colombier 			}
6309a747e4fSDavid du Colombier 			else
6319a747e4fSDavid du Colombier 				tag = getdata(ts, c, starti, a, &ai);
6329a747e4fSDavid du Colombier 			if(tag == -1)
6339a747e4fSDavid du Colombier 				break;
6349a747e4fSDavid du Colombier 			else if(dbglex > 1 && tag != Comment)
6359a747e4fSDavid du Colombier 				fprint(2, "lex: got token %T\n", &a[ai-1]);
6369a747e4fSDavid du Colombier 		}
6379a747e4fSDavid du Colombier 	}
6389a747e4fSDavid du Colombier 	else {
6399a747e4fSDavid du Colombier 		// plain text (non-html) tokens
6409a747e4fSDavid du Colombier 		for(;;) {
6419a747e4fSDavid du Colombier 			if(ai == alen) {
6429a747e4fSDavid du Colombier 				a = (Token*)erealloc(a, (alen+ToksChunk)*sizeof(Token));
6439a747e4fSDavid du Colombier 				alen += ToksChunk;
6449a747e4fSDavid du Colombier 			}
6459a747e4fSDavid du Colombier 			tag = getplaindata(ts, a, &ai);
6469a747e4fSDavid du Colombier 			if(tag == -1)
6479a747e4fSDavid du Colombier 				break;
6489a747e4fSDavid du Colombier 			if(dbglex > 1)
6499a747e4fSDavid du Colombier 				fprint(2, "lex: got token %T\n", &a[ai]);
6509a747e4fSDavid du Colombier 		}
6519a747e4fSDavid du Colombier 	}
6529a747e4fSDavid du Colombier 	if(dbglex)
6539a747e4fSDavid du Colombier 		fprint(2, "lex: returning %d tokens\n", ai);
6549a747e4fSDavid du Colombier 	*plen = ai;
6559a747e4fSDavid du Colombier 	if(ai == 0)
6569a747e4fSDavid du Colombier 		return nil;
6579a747e4fSDavid du Colombier 	return a;
6589a747e4fSDavid du Colombier }
6599a747e4fSDavid du Colombier 
6609a747e4fSDavid du Colombier // For case where source isn't HTML.
6619a747e4fSDavid du Colombier // Just make data tokens, one per line (or partial line,
6629a747e4fSDavid du Colombier // at end of buffer), ignoring non-whitespace control
6639a747e4fSDavid du Colombier // characters and dumping \r's.
6649a747e4fSDavid du Colombier // If find non-empty token, fill in a[*pai], bump *pai, and return Data.
6659a747e4fSDavid du Colombier // Otherwise return -1;
6669a747e4fSDavid du Colombier static int
6679a747e4fSDavid du Colombier getplaindata(TokenSource* ts, Token* a, int* pai)
6689a747e4fSDavid du Colombier {
6699a747e4fSDavid du Colombier 	Rune*	s;
6709a747e4fSDavid du Colombier 	int	j;
6719a747e4fSDavid du Colombier 	int	starti;
6729a747e4fSDavid du Colombier 	int	c;
6739a747e4fSDavid du Colombier 	Token*	tok;
6749a747e4fSDavid du Colombier 	Rune	buf[BIGBUFSIZE];
6759a747e4fSDavid du Colombier 
6769a747e4fSDavid du Colombier 	s = nil;
6779a747e4fSDavid du Colombier 	j = 0;
6789a747e4fSDavid du Colombier 	starti = ts->i;
6799a747e4fSDavid du Colombier 	for(c = getchar(ts); c >= 0; c = getchar(ts)) {
6809a747e4fSDavid du Colombier 		if(c < ' ') {
6819a747e4fSDavid du Colombier 			if(isspace(c)) {
6829a747e4fSDavid du Colombier 				if(c == '\r') {
6839a747e4fSDavid du Colombier 					// ignore it unless no following '\n',
6849a747e4fSDavid du Colombier 					// in which case treat it like '\n'
6859a747e4fSDavid du Colombier 					c = getchar(ts);
6869a747e4fSDavid du Colombier 					if(c != '\n') {
6879a747e4fSDavid du Colombier 						if(c >= 0)
6889a747e4fSDavid du Colombier 							ungetchar(ts, c);
6899a747e4fSDavid du Colombier 						c = '\n';
6909a747e4fSDavid du Colombier 					}
6919a747e4fSDavid du Colombier 				}
6929a747e4fSDavid du Colombier 			}
6939a747e4fSDavid du Colombier 			else
6949a747e4fSDavid du Colombier 				c = 0;
6959a747e4fSDavid du Colombier 		}
6969a747e4fSDavid du Colombier 		if(c != 0) {
6979a747e4fSDavid du Colombier 			buf[j++] = c;
6989a747e4fSDavid du Colombier 			if(j == sizeof(buf)-1) {
6999a747e4fSDavid du Colombier 				s = buftostr(s, buf, j);
7009a747e4fSDavid du Colombier 				j = 0;
7019a747e4fSDavid du Colombier 			}
7029a747e4fSDavid du Colombier 		}
7039a747e4fSDavid du Colombier 		if(c == '\n')
7049a747e4fSDavid du Colombier 			break;
7059a747e4fSDavid du Colombier 	}
7069a747e4fSDavid du Colombier 	s = buftostr(s, buf, j);
7079a747e4fSDavid du Colombier 	if(s == nil)
7089a747e4fSDavid du Colombier 		return -1;
7099a747e4fSDavid du Colombier 	tok = &a[(*pai)++];
7109a747e4fSDavid du Colombier 	tok->tag = Data;
7119a747e4fSDavid du Colombier 	tok->text = s;
7129a747e4fSDavid du Colombier 	tok->attr = nil;
7139a747e4fSDavid du Colombier 	tok->starti = starti;
7149a747e4fSDavid du Colombier 	return Data;
7159a747e4fSDavid du Colombier }
7169a747e4fSDavid du Colombier 
7179a747e4fSDavid du Colombier // Return concatenation of s and buf[0:j]
7189a747e4fSDavid du Colombier static Rune*
7199a747e4fSDavid du Colombier buftostr(Rune* s, Rune* buf, int j)
7209a747e4fSDavid du Colombier {
7219a747e4fSDavid du Colombier 	buf[j] = 0;
7229a747e4fSDavid du Colombier 	if(s == nil)
7239a747e4fSDavid du Colombier 		s = _Strndup(buf, j);
7249a747e4fSDavid du Colombier 	else
7259a747e4fSDavid du Colombier 		s = _Strdup2(s, buf);
7269a747e4fSDavid du Colombier 	return s;
7279a747e4fSDavid du Colombier }
7289a747e4fSDavid du Colombier 
7299a747e4fSDavid du Colombier // Gather data up to next start-of-tag or end-of-buffer.
7309a747e4fSDavid du Colombier // Translate entity references (&amp;).
7319a747e4fSDavid du Colombier // Ignore non-whitespace control characters and get rid of \r's.
7329a747e4fSDavid du Colombier // If find non-empty token, fill in a[*pai], bump *pai, and return Data.
7339a747e4fSDavid du Colombier // Otherwise return -1;
7349a747e4fSDavid du Colombier static int
7359a747e4fSDavid du Colombier getdata(TokenSource* ts, int firstc, int starti, Token* a, int* pai)
7369a747e4fSDavid du Colombier {
7379a747e4fSDavid du Colombier 	Rune*	s;
7389a747e4fSDavid du Colombier 	int	j;
7399a747e4fSDavid du Colombier 	int	c;
7409a747e4fSDavid du Colombier 	Token*	tok;
7419a747e4fSDavid du Colombier 	Rune	buf[BIGBUFSIZE];
7429a747e4fSDavid du Colombier 
7439a747e4fSDavid du Colombier 	s = nil;
7449a747e4fSDavid du Colombier 	j = 0;
7459a747e4fSDavid du Colombier 	c = firstc;
7469a747e4fSDavid du Colombier 	while(c >= 0) {
7479a747e4fSDavid du Colombier 		if(c == '&') {
7489a747e4fSDavid du Colombier 			c = ampersand(ts);
7499a747e4fSDavid du Colombier 			if(c < 0)
7509a747e4fSDavid du Colombier 				break;
7519a747e4fSDavid du Colombier 		}
7529a747e4fSDavid du Colombier 		else if(c < ' ') {
7539a747e4fSDavid du Colombier 			if(isspace(c)) {
7549a747e4fSDavid du Colombier 				if(c == '\r') {
7559a747e4fSDavid du Colombier 					// ignore it unless no following '\n',
7569a747e4fSDavid du Colombier 					// in which case treat it like '\n'
7579a747e4fSDavid du Colombier 					c = getchar(ts);
7589a747e4fSDavid du Colombier 					if(c != '\n') {
7599a747e4fSDavid du Colombier 						if(c >= 0)
7609a747e4fSDavid du Colombier 							ungetchar(ts, c);
7619a747e4fSDavid du Colombier 						c = '\n';
7629a747e4fSDavid du Colombier 					}
7639a747e4fSDavid du Colombier 				}
7649a747e4fSDavid du Colombier 			}
7659a747e4fSDavid du Colombier 			else {
7669a747e4fSDavid du Colombier 				if(warn)
7679a747e4fSDavid du Colombier 					fprint(2, "warning: non-whitespace control character %d ignored\n", c);
7689a747e4fSDavid du Colombier 				c = 0;
7699a747e4fSDavid du Colombier 			}
7709a747e4fSDavid du Colombier 		}
7719a747e4fSDavid du Colombier 		else if(c == '<') {
7729a747e4fSDavid du Colombier 			ungetchar(ts, c);
7739a747e4fSDavid du Colombier 			break;
7749a747e4fSDavid du Colombier 		}
7759a747e4fSDavid du Colombier 		if(c != 0) {
7769a747e4fSDavid du Colombier 			buf[j++] = c;
7779a747e4fSDavid du Colombier 			if(j == BIGBUFSIZE-1) {
7789a747e4fSDavid du Colombier 				s = buftostr(s, buf, j);
7799a747e4fSDavid du Colombier 				j = 0;
7809a747e4fSDavid du Colombier 			}
7819a747e4fSDavid du Colombier 		}
7829a747e4fSDavid du Colombier 		c = getchar(ts);
7839a747e4fSDavid du Colombier 	}
7849a747e4fSDavid du Colombier 	s = buftostr(s, buf, j);
7859a747e4fSDavid du Colombier 	if(s == nil)
7869a747e4fSDavid du Colombier 		return -1;
7879a747e4fSDavid du Colombier 	tok = &a[(*pai)++];
7889a747e4fSDavid du Colombier 	tok->tag = Data;
7899a747e4fSDavid du Colombier 	tok->text = s;
7909a747e4fSDavid du Colombier 	tok->attr = nil;
7919a747e4fSDavid du Colombier 	tok->starti = starti;
7929a747e4fSDavid du Colombier 	return Data;
7939a747e4fSDavid du Colombier }
7949a747e4fSDavid du Colombier 
7959a747e4fSDavid du Colombier // The rules for lexing scripts are different (ugh).
7969a747e4fSDavid du Colombier // Gather up everything until see a </SCRIPT>.
7979a747e4fSDavid du Colombier static int
7989a747e4fSDavid du Colombier getscriptdata(TokenSource* ts, int firstc, int starti, Token* a, int* pai)
7999a747e4fSDavid du Colombier {
8009a747e4fSDavid du Colombier 	Rune*	s;
8019a747e4fSDavid du Colombier 	int	j;
8029a747e4fSDavid du Colombier 	int	tstarti;
8039a747e4fSDavid du Colombier 	int	savei;
8049a747e4fSDavid du Colombier 	int	c;
8059a747e4fSDavid du Colombier 	int	tag;
8069a747e4fSDavid du Colombier 	int	done;
8079a747e4fSDavid du Colombier 	Token*	tok;
8089a747e4fSDavid du Colombier 	Rune	buf[BIGBUFSIZE];
8099a747e4fSDavid du Colombier 
8109a747e4fSDavid du Colombier 	s = nil;
8119a747e4fSDavid du Colombier 	j = 0;
8129a747e4fSDavid du Colombier 	tstarti = starti;
8139a747e4fSDavid du Colombier 	c = firstc;
8149a747e4fSDavid du Colombier 	done = 0;
8159a747e4fSDavid du Colombier 	while(c >= 0) {
8169a747e4fSDavid du Colombier 		if(c == '<') {
8179a747e4fSDavid du Colombier 			// other browsers ignore stuff to end of line after <!
8189a747e4fSDavid du Colombier 			savei = ts->i;
8199a747e4fSDavid du Colombier 			c = getchar(ts);
8209a747e4fSDavid du Colombier 			if(c == '!') {
8219a747e4fSDavid du Colombier 				while(c >= 0 && c != '\n' && c != '\r')
8229a747e4fSDavid du Colombier 					c = getchar(ts);
8239a747e4fSDavid du Colombier 				if(c == '\r')
8249a747e4fSDavid du Colombier 					c = getchar(ts);
8259a747e4fSDavid du Colombier 				if(c == '\n')
8269a747e4fSDavid du Colombier 					c = getchar(ts);
8279a747e4fSDavid du Colombier 			}
8289a747e4fSDavid du Colombier 			else if(c >= 0) {
8299a747e4fSDavid du Colombier 				backup(ts, savei);
8309a747e4fSDavid du Colombier 				tag = gettag(ts, tstarti, a, pai);
8319a747e4fSDavid du Colombier 				if(tag == -1)
8329a747e4fSDavid du Colombier 					break;
8339a747e4fSDavid du Colombier 				if(tag != Comment)
8349a747e4fSDavid du Colombier 					(*pai)--;
8359a747e4fSDavid du Colombier 				backup(ts, tstarti);
8369a747e4fSDavid du Colombier 				if(tag == Tscript + RBRA) {
8379a747e4fSDavid du Colombier 					done = 1;
8389a747e4fSDavid du Colombier 					break;
8399a747e4fSDavid du Colombier 				}
8409a747e4fSDavid du Colombier 				// here tag was not </SCRIPT>, so take as regular data
8419a747e4fSDavid du Colombier 				c = getchar(ts);
8429a747e4fSDavid du Colombier 			}
8439a747e4fSDavid du Colombier 		}
8449a747e4fSDavid du Colombier 		if(c < 0)
8459a747e4fSDavid du Colombier 			break;
8469a747e4fSDavid du Colombier 		if(c != 0) {
8479a747e4fSDavid du Colombier 			buf[j++] = c;
8489a747e4fSDavid du Colombier 			if(j == BIGBUFSIZE-1) {
8499a747e4fSDavid du Colombier 				s = buftostr(s, buf, j);
8509a747e4fSDavid du Colombier 				j = 0;
8519a747e4fSDavid du Colombier 			}
8529a747e4fSDavid du Colombier 		}
8539a747e4fSDavid du Colombier 		tstarti = ts->i;
8549a747e4fSDavid du Colombier 		c = getchar(ts);
8559a747e4fSDavid du Colombier 	}
8569a747e4fSDavid du Colombier 	if(done || ts->i == ts->edata) {
8579a747e4fSDavid du Colombier 		s = buftostr(s, buf, j);
8589a747e4fSDavid du Colombier 		tok = &a[(*pai)++];
8599a747e4fSDavid du Colombier 		tok->tag = Data;
8609a747e4fSDavid du Colombier 		tok->text = s;
8619a747e4fSDavid du Colombier 		tok->attr = nil;
8629a747e4fSDavid du Colombier 		tok->starti = starti;
8639a747e4fSDavid du Colombier 		return Data;
8649a747e4fSDavid du Colombier 	}
8659a747e4fSDavid du Colombier 	backup(ts, starti);
8669a747e4fSDavid du Colombier 	return -1;
8679a747e4fSDavid du Colombier }
8689a747e4fSDavid du Colombier 
8699a747e4fSDavid du Colombier // We've just seen a '<'.  Gather up stuff to closing '>' (if buffer
8709a747e4fSDavid du Colombier // ends before then, return -1).
8719a747e4fSDavid du Colombier // If it's a tag, look up the name, gather the attributes, and return
8729a747e4fSDavid du Colombier // the appropriate token.
8739a747e4fSDavid du Colombier // Else it's either just plain data or some kind of ignorable stuff:
8749a747e4fSDavid du Colombier // return Data or Comment as appropriate.
8759a747e4fSDavid du Colombier // If it's not a Comment, put it in a[*pai] and bump *pai.
8769a747e4fSDavid du Colombier static int
8779a747e4fSDavid du Colombier gettag(TokenSource* ts, int starti, Token* a, int* pai)
8789a747e4fSDavid du Colombier {
8799a747e4fSDavid du Colombier 	int	rbra;
8809a747e4fSDavid du Colombier 	int	ans;
8819a747e4fSDavid du Colombier 	Attr*	al;
8829a747e4fSDavid du Colombier 	int	nexti;
8839a747e4fSDavid du Colombier 	int	c;
8849a747e4fSDavid du Colombier 	int	ti;
8859a747e4fSDavid du Colombier 	int	afnd;
8869a747e4fSDavid du Colombier 	int	attid;
8879a747e4fSDavid du Colombier 	int	quote;
8889a747e4fSDavid du Colombier 	Rune*	val;
8899a747e4fSDavid du Colombier 	int	nv;
8909a747e4fSDavid du Colombier 	int	i;
8919a747e4fSDavid du Colombier 	int	tag;
8929a747e4fSDavid du Colombier 	Token*	tok;
8939a747e4fSDavid du Colombier 	Rune	buf[BIGBUFSIZE];
8949a747e4fSDavid du Colombier 
8959a747e4fSDavid du Colombier 	rbra = 0;
8969a747e4fSDavid du Colombier 	nexti = ts->i;
8979a747e4fSDavid du Colombier 	tok = &a[*pai];
8989a747e4fSDavid du Colombier 	tok->tag = Notfound;
8999a747e4fSDavid du Colombier 	tok->text = nil;
9009a747e4fSDavid du Colombier 	tok->attr = nil;
9019a747e4fSDavid du Colombier 	tok->starti = starti;
9029a747e4fSDavid du Colombier 	c = getchar(ts);
9039a747e4fSDavid du Colombier 	if(c == '/') {
9049a747e4fSDavid du Colombier 		rbra = RBRA;
9059a747e4fSDavid du Colombier 		c = getchar(ts);
9069a747e4fSDavid du Colombier 	}
9079a747e4fSDavid du Colombier 	if(c < 0)
9089a747e4fSDavid du Colombier 		goto eob_done;
9099a747e4fSDavid du Colombier 	if(c >= 256 || !isalpha(c)) {
9109a747e4fSDavid du Colombier 		// not a tag
9119a747e4fSDavid du Colombier 		if(c == '!') {
9129a747e4fSDavid du Colombier 			ans = comment(ts);
9139a747e4fSDavid du Colombier 			if(ans != -1)
9149a747e4fSDavid du Colombier 				return ans;
9159a747e4fSDavid du Colombier 			goto eob_done;
9169a747e4fSDavid du Colombier 		}
9179a747e4fSDavid du Colombier 		else {
9189a747e4fSDavid du Colombier 			backup(ts, nexti);
9199a747e4fSDavid du Colombier 			tok->tag = Data;
9209a747e4fSDavid du Colombier 			tok->text = _Strdup(L"<");
9219a747e4fSDavid du Colombier 			(*pai)++;
9229a747e4fSDavid du Colombier 			return Data;
9239a747e4fSDavid du Colombier 		}
9249a747e4fSDavid du Colombier 	}
9259a747e4fSDavid du Colombier 	// c starts a tagname
9269a747e4fSDavid du Colombier 	buf[0] = c;
9279a747e4fSDavid du Colombier 	i = 1;
9289a747e4fSDavid du Colombier 	while(1) {
9299a747e4fSDavid du Colombier 		c = getchar(ts);
9309a747e4fSDavid du Colombier 		if(c < 0)
9319a747e4fSDavid du Colombier 			goto eob_done;
9329a747e4fSDavid du Colombier 		if(!ISNAMCHAR(c))
9339a747e4fSDavid du Colombier 			break;
9349a747e4fSDavid du Colombier 		// if name is bigger than buf it won't be found anyway...
9359a747e4fSDavid du Colombier 		if(i < BIGBUFSIZE)
9369a747e4fSDavid du Colombier 			buf[i++] = c;
9379a747e4fSDavid du Colombier 	}
9389a747e4fSDavid du Colombier 	if(_lookup(tagtable, Numtags, buf, i, &tag))
9399a747e4fSDavid du Colombier 		tok->tag = tag + rbra;
9409a747e4fSDavid du Colombier 	else
9419a747e4fSDavid du Colombier 		tok->text = _Strndup(buf, i);	// for warning print, in build
9429a747e4fSDavid du Colombier 
9439a747e4fSDavid du Colombier 	// attribute gathering loop
9449a747e4fSDavid du Colombier 	al = nil;
9459a747e4fSDavid du Colombier 	while(1) {
9469a747e4fSDavid du Colombier 		// look for "ws name" or "ws name ws = ws val"  (ws=whitespace)
9479a747e4fSDavid du Colombier 		// skip whitespace
9489a747e4fSDavid du Colombier attrloop_continue:
9499a747e4fSDavid du Colombier 		while(c < 256 && isspace(c)) {
9509a747e4fSDavid du Colombier 			c = getchar(ts);
9519a747e4fSDavid du Colombier 			if(c < 0)
9529a747e4fSDavid du Colombier 				goto eob_done;
9539a747e4fSDavid du Colombier 		}
9549a747e4fSDavid du Colombier 		if(c == '>')
9559a747e4fSDavid du Colombier 			goto attrloop_done;
9569a747e4fSDavid du Colombier 		if(c == '<') {
9579a747e4fSDavid du Colombier 			if(warn)
9589a747e4fSDavid du Colombier 				fprint(2, "warning: unclosed tag\n");
9599a747e4fSDavid du Colombier 			ungetchar(ts, c);
9609a747e4fSDavid du Colombier 			goto attrloop_done;
9619a747e4fSDavid du Colombier 		}
9629a747e4fSDavid du Colombier 		if(c >= 256 || !isalpha(c)) {
9639a747e4fSDavid du Colombier 			if(warn)
9649a747e4fSDavid du Colombier 				fprint(2, "warning: expected attribute name\n");
9659a747e4fSDavid du Colombier 			// skipt to next attribute name
9669a747e4fSDavid du Colombier 			while(1) {
9679a747e4fSDavid du Colombier 				c = getchar(ts);
9689a747e4fSDavid du Colombier 				if(c < 0)
9699a747e4fSDavid du Colombier 					goto eob_done;
9709a747e4fSDavid du Colombier 				if(c < 256 && isalpha(c))
9719a747e4fSDavid du Colombier 					goto attrloop_continue;
9729a747e4fSDavid du Colombier 				if(c == '<') {
9739a747e4fSDavid du Colombier 					if(warn)
9749a747e4fSDavid du Colombier 						fprint(2, "warning: unclosed tag\n");
9759a747e4fSDavid du Colombier 					ungetchar(ts, 60);
9769a747e4fSDavid du Colombier 					goto attrloop_done;
9779a747e4fSDavid du Colombier 				}
9789a747e4fSDavid du Colombier 				if(c == '>')
9799a747e4fSDavid du Colombier 					goto attrloop_done;
9809a747e4fSDavid du Colombier 			}
9819a747e4fSDavid du Colombier 		}
9829a747e4fSDavid du Colombier 		// gather attribute name
9839a747e4fSDavid du Colombier 		buf[0] = c;
9849a747e4fSDavid du Colombier 		i = 1;
9859a747e4fSDavid du Colombier 		while(1) {
9869a747e4fSDavid du Colombier 			c = getchar(ts);
9879a747e4fSDavid du Colombier 			if(c < 0)
9889a747e4fSDavid du Colombier 				goto eob_done;
9899a747e4fSDavid du Colombier 			if(!ISNAMCHAR(c))
9909a747e4fSDavid du Colombier 				break;
9919a747e4fSDavid du Colombier 			if(i < BIGBUFSIZE-1)
9929a747e4fSDavid du Colombier 				buf[i++] = c;
9939a747e4fSDavid du Colombier 		}
9949a747e4fSDavid du Colombier 		afnd = _lookup(attrtable, Numattrs, buf, i, &attid);
9959a747e4fSDavid du Colombier 		if(warn && !afnd) {
9969a747e4fSDavid du Colombier 			buf[i] = 0;
9979a747e4fSDavid du Colombier 			fprint(2, "warning: unknown attribute name %S\n", buf);
9989a747e4fSDavid du Colombier 		}
9999a747e4fSDavid du Colombier 		// skip whitespace
10009a747e4fSDavid du Colombier 		while(c < 256 && isspace(c)) {
10019a747e4fSDavid du Colombier 			c = getchar(ts);
10029a747e4fSDavid du Colombier 			if(c < 0)
10039a747e4fSDavid du Colombier 				goto eob_done;
10049a747e4fSDavid du Colombier 		}
10059a747e4fSDavid du Colombier 		if(c != '=') {
10069a747e4fSDavid du Colombier 			if(afnd)
10079a747e4fSDavid du Colombier 				al = newattr(attid, nil, al);
10089a747e4fSDavid du Colombier 			goto attrloop_continue;
10099a747e4fSDavid du Colombier 		}
10109a747e4fSDavid du Colombier 		//# c is '=' here;  skip whitespace
10119a747e4fSDavid du Colombier 		while(1) {
10129a747e4fSDavid du Colombier 			c = getchar(ts);
10139a747e4fSDavid du Colombier 			if(c < 0)
10149a747e4fSDavid du Colombier 				goto eob_done;
10159a747e4fSDavid du Colombier 			if(c >= 256 || !isspace(c))
10169a747e4fSDavid du Colombier 				break;
10179a747e4fSDavid du Colombier 		}
10189a747e4fSDavid du Colombier 		quote = 0;
10199a747e4fSDavid du Colombier 		if(c == '\'' || c == '"') {
10209a747e4fSDavid du Colombier 			quote = c;
10219a747e4fSDavid du Colombier 			c = getchar(ts);
10229a747e4fSDavid du Colombier 			if(c < 0)
10239a747e4fSDavid du Colombier 				goto eob_done;
10249a747e4fSDavid du Colombier 		}
10259a747e4fSDavid du Colombier 		val = nil;
10269a747e4fSDavid du Colombier 		nv = 0;
10279a747e4fSDavid du Colombier 		while(1) {
10289a747e4fSDavid du Colombier valloop_continue:
10299a747e4fSDavid du Colombier 			if(c < 0)
10309a747e4fSDavid du Colombier 				goto eob_done;
10319a747e4fSDavid du Colombier 			if(c == '>') {
10329a747e4fSDavid du Colombier 				if(quote) {
10339a747e4fSDavid du Colombier 					// c might be part of string (though not good style)
10349a747e4fSDavid du Colombier 					// but if line ends before close quote, assume
10359a747e4fSDavid du Colombier 					// there was an unmatched quote
10369a747e4fSDavid du Colombier 					ti = ts->i;
10379a747e4fSDavid du Colombier 					while(1) {
10389a747e4fSDavid du Colombier 						c = getchar(ts);
10399a747e4fSDavid du Colombier 						if(c < 0)
10409a747e4fSDavid du Colombier 							goto eob_done;
10419a747e4fSDavid du Colombier 						if(c == quote) {
10429a747e4fSDavid du Colombier 							backup(ts, ti);
10439a747e4fSDavid du Colombier 							buf[nv++] = '>';
10449a747e4fSDavid du Colombier 							if(nv == BIGBUFSIZE-1) {
10459a747e4fSDavid du Colombier 								val = buftostr(val, buf, nv);
10469a747e4fSDavid du Colombier 								nv = 0;
10479a747e4fSDavid du Colombier 							}
10489a747e4fSDavid du Colombier 							c = getchar(ts);
10499a747e4fSDavid du Colombier 							goto valloop_continue;
10509a747e4fSDavid du Colombier 						}
10519a747e4fSDavid du Colombier 						if(c == '\n') {
10529a747e4fSDavid du Colombier 							if(warn)
10539a747e4fSDavid du Colombier 								fprint(2, "warning: apparent unmatched quote\n");
10549a747e4fSDavid du Colombier 							backup(ts, ti);
10559a747e4fSDavid du Colombier 							c = '>';
10569a747e4fSDavid du Colombier 							goto valloop_done;
10579a747e4fSDavid du Colombier 						}
10589a747e4fSDavid du Colombier 					}
10599a747e4fSDavid du Colombier 				}
10609a747e4fSDavid du Colombier 				else
10619a747e4fSDavid du Colombier 					goto valloop_done;
10629a747e4fSDavid du Colombier 			}
10639a747e4fSDavid du Colombier 			if(quote) {
10649a747e4fSDavid du Colombier 				if(c == quote) {
10659a747e4fSDavid du Colombier 					c = getchar(ts);
10669a747e4fSDavid du Colombier 					if(c < 0)
10679a747e4fSDavid du Colombier 						goto eob_done;
10689a747e4fSDavid du Colombier 					goto valloop_done;
10699a747e4fSDavid du Colombier 				}
10709a747e4fSDavid du Colombier 				if(c == '\r') {
10719a747e4fSDavid du Colombier 					c = getchar(ts);
10729a747e4fSDavid du Colombier 					goto valloop_continue;
10739a747e4fSDavid du Colombier 				}
10749a747e4fSDavid du Colombier 				if(c == '\t' || c == '\n')
10759a747e4fSDavid du Colombier 					c = ' ';
10769a747e4fSDavid du Colombier 			}
10779a747e4fSDavid du Colombier 			else {
10789a747e4fSDavid du Colombier 				if(c < 256 && isspace(c))
10799a747e4fSDavid du Colombier 					goto valloop_done;
10809a747e4fSDavid du Colombier 			}
10819a747e4fSDavid du Colombier 			if(c == '&') {
10829a747e4fSDavid du Colombier 				c = ampersand(ts);
10839a747e4fSDavid du Colombier 				if(c == -1)
10849a747e4fSDavid du Colombier 					goto eob_done;
10859a747e4fSDavid du Colombier 			}
10869a747e4fSDavid du Colombier 			buf[nv++] = c;
10879a747e4fSDavid du Colombier 			if(nv == BIGBUFSIZE-1) {
10889a747e4fSDavid du Colombier 				val = buftostr(val, buf, nv);
10899a747e4fSDavid du Colombier 				nv = 0;
10909a747e4fSDavid du Colombier 			}
10919a747e4fSDavid du Colombier 			c = getchar(ts);
10929a747e4fSDavid du Colombier 		}
10939a747e4fSDavid du Colombier valloop_done:
10949a747e4fSDavid du Colombier 		if(afnd) {
10959a747e4fSDavid du Colombier 			val = buftostr(val, buf, nv);
10969a747e4fSDavid du Colombier 			al = newattr(attid, val, al);
10979a747e4fSDavid du Colombier 		}
10989a747e4fSDavid du Colombier 	}
10999a747e4fSDavid du Colombier 
11009a747e4fSDavid du Colombier attrloop_done:
11019a747e4fSDavid du Colombier 	tok->attr = al;
11029a747e4fSDavid du Colombier 	(*pai)++;
11039a747e4fSDavid du Colombier 	return tok->tag;
11049a747e4fSDavid du Colombier 
11059a747e4fSDavid du Colombier eob_done:
11069a747e4fSDavid du Colombier 	if(warn)
11079a747e4fSDavid du Colombier 		fprint(2, "warning: incomplete tag at end of page\n");
11089a747e4fSDavid du Colombier 	backup(ts, nexti);
11099a747e4fSDavid du Colombier 	tok->tag = Data;
11109a747e4fSDavid du Colombier 	tok->text = _Strdup(L"<");
11119a747e4fSDavid du Colombier 	return Data;
11129a747e4fSDavid du Colombier }
11139a747e4fSDavid du Colombier 
11149a747e4fSDavid du Colombier // We've just read a '<!' at position starti,
11159a747e4fSDavid du Colombier // so this may be a comment or other ignored section, or it may
11169a747e4fSDavid du Colombier // be just a literal string if there is no close before end of file
11179a747e4fSDavid du Colombier // (other browsers do that).
11189a747e4fSDavid du Colombier // The accepted practice seems to be (note: contrary to SGML spec!):
11199a747e4fSDavid du Colombier // If see <!--, look for --> to close, or if none, > to close.
11209a747e4fSDavid du Colombier // If see <!(not --), look for > to close.
11219a747e4fSDavid du Colombier // If no close before end of file, leave original characters in as literal data.
11229a747e4fSDavid du Colombier //
11239a747e4fSDavid du Colombier // If we see ignorable stuff, return Comment.
11249a747e4fSDavid du Colombier // Else return nil (caller should back up and try again when more data arrives,
11259a747e4fSDavid du Colombier // unless at end of file, in which case caller should just make '<' a data token).
11269a747e4fSDavid du Colombier static int
11279a747e4fSDavid du Colombier comment(TokenSource* ts)
11289a747e4fSDavid du Colombier {
11299a747e4fSDavid du Colombier 	int	nexti;
11309a747e4fSDavid du Colombier 	int	havecomment;
11319a747e4fSDavid du Colombier 	int	c;
11329a747e4fSDavid du Colombier 
11339a747e4fSDavid du Colombier 	nexti = ts->i;
11349a747e4fSDavid du Colombier 	havecomment = 0;
11359a747e4fSDavid du Colombier 	c = getchar(ts);
11369a747e4fSDavid du Colombier 	if(c == '-') {
11379a747e4fSDavid du Colombier 		c = getchar(ts);
11389a747e4fSDavid du Colombier 		if(c == '-') {
11399a747e4fSDavid du Colombier 			if(findstr(ts, L"-->"))
11409a747e4fSDavid du Colombier 				havecomment = 1;
11419a747e4fSDavid du Colombier 			else
11429a747e4fSDavid du Colombier 				backup(ts, nexti);
11439a747e4fSDavid du Colombier 		}
11449a747e4fSDavid du Colombier 	}
11459a747e4fSDavid du Colombier 	if(!havecomment) {
11469a747e4fSDavid du Colombier 		if(c == '>')
11479a747e4fSDavid du Colombier 			havecomment = 1;
11489a747e4fSDavid du Colombier 		else if(c >= 0) {
11499a747e4fSDavid du Colombier 			if(findstr(ts, L">"))
11509a747e4fSDavid du Colombier 				havecomment = 1;
11519a747e4fSDavid du Colombier 		}
11529a747e4fSDavid du Colombier 	}
11539a747e4fSDavid du Colombier 	if(havecomment)
11549a747e4fSDavid du Colombier 		return Comment;
11559a747e4fSDavid du Colombier 	return -1;
11569a747e4fSDavid du Colombier }
11579a747e4fSDavid du Colombier 
11589a747e4fSDavid du Colombier // Look for string s in token source.
11599a747e4fSDavid du Colombier // If found, return 1, with buffer at next char after s,
11609a747e4fSDavid du Colombier // else return 0 (caller should back up).
11619a747e4fSDavid du Colombier static int
11629a747e4fSDavid du Colombier findstr(TokenSource* ts, Rune* s)
11639a747e4fSDavid du Colombier {
11649a747e4fSDavid du Colombier 	int	c0;
11659a747e4fSDavid du Colombier 	int	n;
11669a747e4fSDavid du Colombier 	int	nexti;
11679a747e4fSDavid du Colombier 	int	i;
11689a747e4fSDavid du Colombier 	int	c;
11699a747e4fSDavid du Colombier 
11709a747e4fSDavid du Colombier 	c0 = s[0];
11719a747e4fSDavid du Colombier 	n = runestrlen(s);
11729a747e4fSDavid du Colombier 	while(1) {
11739a747e4fSDavid du Colombier 		c = getchar(ts);
11749a747e4fSDavid du Colombier 		if(c < 0)
11759a747e4fSDavid du Colombier 			break;
11769a747e4fSDavid du Colombier 		if(c == c0) {
11779a747e4fSDavid du Colombier 			if(n == 1)
11789a747e4fSDavid du Colombier 				return 1;
11799a747e4fSDavid du Colombier 			nexti = ts->i;
11809a747e4fSDavid du Colombier 			for(i = 1; i < n; i++) {
11819a747e4fSDavid du Colombier 				c = getchar(ts);
11829a747e4fSDavid du Colombier 				if(c < 0)
11839a747e4fSDavid du Colombier 					goto mainloop_done;
11849a747e4fSDavid du Colombier 				if(c != s[i])
11859a747e4fSDavid du Colombier 					break;
11869a747e4fSDavid du Colombier 			}
11879a747e4fSDavid du Colombier 			if(i == n)
11889a747e4fSDavid du Colombier 				return 1;
11899a747e4fSDavid du Colombier 			backup(ts, nexti);
11909a747e4fSDavid du Colombier 		}
11919a747e4fSDavid du Colombier 	}
11929a747e4fSDavid du Colombier mainloop_done:
11939a747e4fSDavid du Colombier 	return 0;
11949a747e4fSDavid du Colombier }
11959a747e4fSDavid du Colombier 
11969a747e4fSDavid du Colombier // We've just read an '&'; look for an entity reference
11979a747e4fSDavid du Colombier // name, and if found, return translated char.
11989a747e4fSDavid du Colombier // if there is a complete entity name but it isn't known,
11999a747e4fSDavid du Colombier // try prefixes (gets around some buggy HTML out there),
12009a747e4fSDavid du Colombier // and if that fails, back up to just past the '&' and return '&'.
12019a747e4fSDavid du Colombier // If the entity can't be completed in the current buffer, back up
12029a747e4fSDavid du Colombier // to the '&' and return -1.
12039a747e4fSDavid du Colombier static int
12049a747e4fSDavid du Colombier ampersand(TokenSource* ts)
12059a747e4fSDavid du Colombier {
12069a747e4fSDavid du Colombier 	int	savei;
12079a747e4fSDavid du Colombier 	int	c;
12089a747e4fSDavid du Colombier 	int	fnd;
12099a747e4fSDavid du Colombier 	int	ans;
12109a747e4fSDavid du Colombier 	int	v;
12119a747e4fSDavid du Colombier 	int	i;
12129a747e4fSDavid du Colombier 	int	k;
12139a747e4fSDavid du Colombier 	Rune	buf[SMALLBUFSIZE];
12149a747e4fSDavid du Colombier 
12159a747e4fSDavid du Colombier 	savei = ts->i;
12169a747e4fSDavid du Colombier 	c = getchar(ts);
12179a747e4fSDavid du Colombier 	fnd = 0;
12189a747e4fSDavid du Colombier 	ans = -1;
12199a747e4fSDavid du Colombier 	if(c == '#') {
12209a747e4fSDavid du Colombier 		c = getchar(ts);
12219a747e4fSDavid du Colombier 		v = 0;
12229a747e4fSDavid du Colombier 		while(c >= 0) {
12239a747e4fSDavid du Colombier 			if(!(c < 256 && isdigit(c)))
12249a747e4fSDavid du Colombier 				break;
12259a747e4fSDavid du Colombier 			v = v*10 + c - 48;
12269a747e4fSDavid du Colombier 			c = getchar(ts);
12279a747e4fSDavid du Colombier 		}
12289a747e4fSDavid du Colombier 		if(c >= 0) {
12299a747e4fSDavid du Colombier 			if(!(c == ';' || c == '\n' || c == '\r'))
12309a747e4fSDavid du Colombier 				ungetchar(ts, c);
12319a747e4fSDavid du Colombier 			c = v;
12329a747e4fSDavid du Colombier 			if(c == 160)
12339a747e4fSDavid du Colombier 				c = 160;
12349a747e4fSDavid du Colombier 			if(c >= Winstart && c <= Winend) {
12359a747e4fSDavid du Colombier 				c = winchars[c - Winstart];
12369a747e4fSDavid du Colombier 			}
12379a747e4fSDavid du Colombier 			ans = c;
12389a747e4fSDavid du Colombier 			fnd = 1;
12399a747e4fSDavid du Colombier 		}
12409a747e4fSDavid du Colombier 	}
12419a747e4fSDavid du Colombier 	else if(c < 256 && isalpha(c)) {
12429a747e4fSDavid du Colombier 		buf[0] = c;
12439a747e4fSDavid du Colombier 		k = 1;
12449a747e4fSDavid du Colombier 		while(1) {
12459a747e4fSDavid du Colombier 			c = getchar(ts);
12469a747e4fSDavid du Colombier 			if(c < 0)
12479a747e4fSDavid du Colombier 				break;
12489a747e4fSDavid du Colombier 			if(ISNAMCHAR(c)) {
12499a747e4fSDavid du Colombier 				if(k < SMALLBUFSIZE-1)
12509a747e4fSDavid du Colombier 					buf[k++] = c;
12519a747e4fSDavid du Colombier 			}
12529a747e4fSDavid du Colombier 			else {
12539a747e4fSDavid du Colombier 				if(!(c == ';' || c == '\n' || c == '\r'))
12549a747e4fSDavid du Colombier 					ungetchar(ts, c);
12559a747e4fSDavid du Colombier 				break;
12569a747e4fSDavid du Colombier 			}
12579a747e4fSDavid du Colombier 		}
12589a747e4fSDavid du Colombier 		if(c >= 0) {
12599a747e4fSDavid du Colombier 			fnd = _lookup(chartab, NCHARTAB, buf, k, &ans);
12609a747e4fSDavid du Colombier 			if(!fnd) {
12619a747e4fSDavid du Colombier 				// Try prefixes of s
12629a747e4fSDavid du Colombier 				if(c == ';' || c == '\n' || c == '\r')
12639a747e4fSDavid du Colombier 					ungetchar(ts, c);
12649a747e4fSDavid du Colombier 				i = k;
12659a747e4fSDavid du Colombier 				while(--k > 0) {
12669a747e4fSDavid du Colombier 					fnd = _lookup(chartab, NCHARTAB, buf, k, &ans);
12679a747e4fSDavid du Colombier 					if(fnd) {
12689a747e4fSDavid du Colombier 						while(i > k) {
12699a747e4fSDavid du Colombier 							i--;
12709a747e4fSDavid du Colombier 							ungetchar(ts, buf[i]);
12719a747e4fSDavid du Colombier 						}
12729a747e4fSDavid du Colombier 						break;
12739a747e4fSDavid du Colombier 					}
12749a747e4fSDavid du Colombier 				}
12759a747e4fSDavid du Colombier 			}
12769a747e4fSDavid du Colombier 		}
12779a747e4fSDavid du Colombier 	}
12789a747e4fSDavid du Colombier 	if(!fnd) {
12799a747e4fSDavid du Colombier 		backup(ts, savei);
12809a747e4fSDavid du Colombier 		ans = '&';
12819a747e4fSDavid du Colombier 	}
12829a747e4fSDavid du Colombier 	return ans;
12839a747e4fSDavid du Colombier }
12849a747e4fSDavid du Colombier 
12859a747e4fSDavid du Colombier // Get next char, obeying ts.chset.
12869a747e4fSDavid du Colombier // Returns -1 if no complete character left before current end of data.
12879a747e4fSDavid du Colombier static int
12889a747e4fSDavid du Colombier getchar(TokenSource* ts)
12899a747e4fSDavid du Colombier {
12909a747e4fSDavid du Colombier 	uchar*	buf;
12919a747e4fSDavid du Colombier 	int	c;
12929a747e4fSDavid du Colombier 	int	n;
12939a747e4fSDavid du Colombier 	int	ok;
12949a747e4fSDavid du Colombier 	Rune	r;
12959a747e4fSDavid du Colombier 
12969a747e4fSDavid du Colombier 	if(ts->i >= ts->edata)
12979a747e4fSDavid du Colombier 		return -1;
12989a747e4fSDavid du Colombier 	buf = ts->data;
12999a747e4fSDavid du Colombier 	c = buf[ts->i];
13009a747e4fSDavid du Colombier 	switch(ts->chset) {
13019a747e4fSDavid du Colombier 	case ISO_8859_1:
13029a747e4fSDavid du Colombier 		if(c >= Winstart && c <= Winend)
13039a747e4fSDavid du Colombier 			c = winchars[c - Winstart];
13049a747e4fSDavid du Colombier 		ts->i++;
13059a747e4fSDavid du Colombier 		break;
13069a747e4fSDavid du Colombier 	case US_Ascii:
13079a747e4fSDavid du Colombier 		if(c > 127) {
13089a747e4fSDavid du Colombier 			if(warn)
13099a747e4fSDavid du Colombier 				fprint(2, "non-ascii char (%x) when US-ASCII specified\n", c);
13109a747e4fSDavid du Colombier 		}
13119a747e4fSDavid du Colombier 		ts->i++;
13129a747e4fSDavid du Colombier 		break;
13139a747e4fSDavid du Colombier 	case UTF_8:
13149a747e4fSDavid du Colombier 		ok = fullrune((char*)(buf+ts->i), ts->edata-ts->i);
13159a747e4fSDavid du Colombier 		n = chartorune(&r, (char*)(buf+ts->i));
13169a747e4fSDavid du Colombier 		if(ok) {
13179a747e4fSDavid du Colombier 			if(warn && c == 0x80)
13189a747e4fSDavid du Colombier 				fprint(2, "warning: invalid utf-8 sequence (starts with %x)\n", ts->data[ts->i]);
13199a747e4fSDavid du Colombier 			ts->i += n;
13209a747e4fSDavid du Colombier 			c = r;
13219a747e4fSDavid du Colombier 		}
13229a747e4fSDavid du Colombier 		else {
13239a747e4fSDavid du Colombier 			// not enough bytes in buf to complete utf-8 char
13249a747e4fSDavid du Colombier 			ts->i = ts->edata;	// mark "all used"
13259a747e4fSDavid du Colombier 			c = -1;
13269a747e4fSDavid du Colombier 		}
13279a747e4fSDavid du Colombier 		break;
13289a747e4fSDavid du Colombier 	case Unicode:
13299a747e4fSDavid du Colombier 		if(ts->i < ts->edata - 1) {
13309a747e4fSDavid du Colombier 			//standards say most-significant byte first
13319a747e4fSDavid du Colombier 			c = (c << 8)|(buf[ts->i + 1]);
13329a747e4fSDavid du Colombier 			ts->i += 2;
13339a747e4fSDavid du Colombier 		}
13349a747e4fSDavid du Colombier 		else {
13359a747e4fSDavid du Colombier 			ts->i = ts->edata;	// mark "all used"
13369a747e4fSDavid du Colombier 			c = -1;
13379a747e4fSDavid du Colombier 		}
13389a747e4fSDavid du Colombier 		break;
13399a747e4fSDavid du Colombier 	}
13409a747e4fSDavid du Colombier 	return c;
13419a747e4fSDavid du Colombier }
13429a747e4fSDavid du Colombier 
13439a747e4fSDavid du Colombier // Assuming c was the last character returned by getchar, set
13449a747e4fSDavid du Colombier // things up so that next getchar will get that same character
13459a747e4fSDavid du Colombier // followed by the current 'next character', etc.
13469a747e4fSDavid du Colombier static void
13479a747e4fSDavid du Colombier ungetchar(TokenSource* ts, int c)
13489a747e4fSDavid du Colombier {
13499a747e4fSDavid du Colombier 	int	n;
13509a747e4fSDavid du Colombier 	Rune	r;
13519a747e4fSDavid du Colombier 	char	a[UTFmax];
13529a747e4fSDavid du Colombier 
13539a747e4fSDavid du Colombier 	n = 1;
13549a747e4fSDavid du Colombier 	switch(ts->chset) {
13559a747e4fSDavid du Colombier 	case UTF_8:
13569a747e4fSDavid du Colombier 		if(c >= 128) {
13579a747e4fSDavid du Colombier 			r = c;
13589a747e4fSDavid du Colombier 			n = runetochar(a, &r);
13599a747e4fSDavid du Colombier 		}
13609a747e4fSDavid du Colombier 		break;
13619a747e4fSDavid du Colombier 	case Unicode:
13629a747e4fSDavid du Colombier 		n = 2;
13639a747e4fSDavid du Colombier 		break;
13649a747e4fSDavid du Colombier 	}
13659a747e4fSDavid du Colombier 	ts->i -= n;
13669a747e4fSDavid du Colombier }
13679a747e4fSDavid du Colombier 
13689a747e4fSDavid du Colombier // Restore ts so that it is at the state where the index was savei.
13699a747e4fSDavid du Colombier static void
13709a747e4fSDavid du Colombier backup(TokenSource* ts, int savei)
13719a747e4fSDavid du Colombier {
13729a747e4fSDavid du Colombier 	if(dbglex)
13739a747e4fSDavid du Colombier 		fprint(2, "lex: backup; i=%d, savei=%d\n", ts->i, savei);
13749a747e4fSDavid du Colombier 	ts->i = savei;
13759a747e4fSDavid du Colombier }
13769a747e4fSDavid du Colombier 
13779a747e4fSDavid du Colombier 
13789a747e4fSDavid du Colombier // Look for value associated with attribute attid in token t.
13799a747e4fSDavid du Colombier // If there is one, return 1 and put the value in *pans,
13809a747e4fSDavid du Colombier // else return 0.
13819a747e4fSDavid du Colombier // If xfer is true, transfer ownership of the string to the caller
13829a747e4fSDavid du Colombier // (nil it out here); otherwise, caller must duplicate the answer
13839a747e4fSDavid du Colombier // if it needs to save it.
13849a747e4fSDavid du Colombier // OK to have pans==0, in which case this is just looking
13859a747e4fSDavid du Colombier // to see if token is present.
13869a747e4fSDavid du Colombier int
13879a747e4fSDavid du Colombier _tokaval(Token* t, int attid, Rune** pans, int xfer)
13889a747e4fSDavid du Colombier {
13899a747e4fSDavid du Colombier 	Attr*	attr;
13909a747e4fSDavid du Colombier 
13919a747e4fSDavid du Colombier 	attr = t->attr;
13929a747e4fSDavid du Colombier 	while(attr != nil) {
13939a747e4fSDavid du Colombier 		if(attr->attid == attid) {
13949a747e4fSDavid du Colombier 			if(pans != nil)
13959a747e4fSDavid du Colombier 				*pans = attr->value;
13969a747e4fSDavid du Colombier 			if(xfer)
13979a747e4fSDavid du Colombier 				attr->value = nil;
13989a747e4fSDavid du Colombier 			return 1;
13999a747e4fSDavid du Colombier 		}
14009a747e4fSDavid du Colombier 		attr = attr->next;
14019a747e4fSDavid du Colombier 	}
14029a747e4fSDavid du Colombier 	if(pans != nil)
14039a747e4fSDavid du Colombier 		*pans = nil;
14049a747e4fSDavid du Colombier 	return 0;
14059a747e4fSDavid du Colombier }
14069a747e4fSDavid du Colombier 
14079a747e4fSDavid du Colombier static int
14089a747e4fSDavid du Colombier Tconv(Fmt *f)
14099a747e4fSDavid du Colombier {
14109a747e4fSDavid du Colombier 	Token*	t;
14119a747e4fSDavid du Colombier 	int	i;
14129a747e4fSDavid du Colombier 	int	tag;
14139a747e4fSDavid du Colombier 	char*	srbra;
14149a747e4fSDavid du Colombier 	Rune*	aname;
14159a747e4fSDavid du Colombier 	Rune*	tname;
14169a747e4fSDavid du Colombier 	Attr*	a;
14179a747e4fSDavid du Colombier 	char	buf[BIGBUFSIZE];
14189a747e4fSDavid du Colombier 
14199a747e4fSDavid du Colombier 	t = va_arg(f->args, Token*);
14209a747e4fSDavid du Colombier 	if(t == nil)
14219a747e4fSDavid du Colombier 		sprint(buf, "<null>");
14229a747e4fSDavid du Colombier 	else {
14239a747e4fSDavid du Colombier 		i = 0;
14249a747e4fSDavid du Colombier 		if(dbglex > 1)
14259a747e4fSDavid du Colombier 			i = snprint(buf, sizeof(buf), "[%d]", t->starti);
14269a747e4fSDavid du Colombier 		tag = t->tag;
14279a747e4fSDavid du Colombier 		if(tag == Data) {
14289a747e4fSDavid du Colombier 			i += snprint(buf+i, sizeof(buf)-i-1, "'%S'", t->text);
14299a747e4fSDavid du Colombier 		}
14309a747e4fSDavid du Colombier 		else {
14319a747e4fSDavid du Colombier 			srbra = "";
14329a747e4fSDavid du Colombier 			if(tag >= RBRA) {
14339a747e4fSDavid du Colombier 				tag -= RBRA;
14349a747e4fSDavid du Colombier 				srbra = "/";
14359a747e4fSDavid du Colombier 			}
14369a747e4fSDavid du Colombier 			tname = tagnames[tag];
14379a747e4fSDavid du Colombier 			if(tag == Notfound)
14389a747e4fSDavid du Colombier 				tname = L"?";
14399a747e4fSDavid du Colombier 			i += snprint(buf+i, sizeof(buf)-i-1, "<%s%S", srbra, tname);
14409a747e4fSDavid du Colombier 			for(a = t->attr; a != nil; a = a->next) {
14419a747e4fSDavid du Colombier 				aname = attrnames[a->attid];
14429a747e4fSDavid du Colombier 				i += snprint(buf+i, sizeof(buf)-i-1, " %S", aname);
14439a747e4fSDavid du Colombier 				if(a->value != nil)
14449a747e4fSDavid du Colombier 					i += snprint(buf+i, sizeof(buf)-i-1, "=%S", a->value);
14459a747e4fSDavid du Colombier 			}
14469a747e4fSDavid du Colombier 			i += snprint(buf+i, sizeof(buf)-i-1, ">");
14479a747e4fSDavid du Colombier 		}
14489a747e4fSDavid du Colombier 		buf[i] = 0;
14499a747e4fSDavid du Colombier 	}
14509a747e4fSDavid du Colombier 	return fmtstrcpy(f, buf);
14519a747e4fSDavid du Colombier }
14529a747e4fSDavid du Colombier 
14539a747e4fSDavid du Colombier // Attrs own their constituent strings, but build may eventually
14549a747e4fSDavid du Colombier // transfer some values to its items and nil them out in the Attr.
14559a747e4fSDavid du Colombier static Attr*
14569a747e4fSDavid du Colombier newattr(int attid, Rune* value, Attr* link)
14579a747e4fSDavid du Colombier {
14589a747e4fSDavid du Colombier 	Attr* ans;
14599a747e4fSDavid du Colombier 
14609a747e4fSDavid du Colombier 	ans = (Attr*)emalloc(sizeof(Attr));
14619a747e4fSDavid du Colombier 	ans->attid = attid;
14629a747e4fSDavid du Colombier 	ans->value = value;
14639a747e4fSDavid du Colombier 	ans->next = link;
14649a747e4fSDavid du Colombier 	return ans;
14659a747e4fSDavid du Colombier }
14669a747e4fSDavid du Colombier 
14679a747e4fSDavid du Colombier // Free list of Attrs linked through next field
14689a747e4fSDavid du Colombier static void
14699a747e4fSDavid du Colombier freeattrs(Attr* ahead)
14709a747e4fSDavid du Colombier {
14719a747e4fSDavid du Colombier 	Attr* a;
14729a747e4fSDavid du Colombier 	Attr* nexta;
14739a747e4fSDavid du Colombier 
14749a747e4fSDavid du Colombier 	a = ahead;
14759a747e4fSDavid du Colombier 	while(a != nil) {
14769a747e4fSDavid du Colombier 		nexta = a->next;
14779a747e4fSDavid du Colombier 		free(a->value);
14789a747e4fSDavid du Colombier 		free(a);
14799a747e4fSDavid du Colombier 		a = nexta;
14809a747e4fSDavid du Colombier 	}
14819a747e4fSDavid du Colombier }
14829a747e4fSDavid du Colombier 
14839a747e4fSDavid du Colombier // Free array of Tokens.
14849a747e4fSDavid du Colombier // Allocated space might have room for more than n tokens,
14859a747e4fSDavid du Colombier // but only n of them are initialized.
14869a747e4fSDavid du Colombier // If caller has transferred ownership of constitutent strings
14879a747e4fSDavid du Colombier // or attributes, it must have nil'd out the pointers in the Tokens.
14889a747e4fSDavid du Colombier void
14899a747e4fSDavid du Colombier _freetokens(Token* tarray, int n)
14909a747e4fSDavid du Colombier {
14919a747e4fSDavid du Colombier 	int i;
14929a747e4fSDavid du Colombier 	Token* t;
14939a747e4fSDavid du Colombier 
14949a747e4fSDavid du Colombier 	if(tarray == nil)
14959a747e4fSDavid du Colombier 		return;
14969a747e4fSDavid du Colombier 	for(i = 0; i < n; i++) {
14979a747e4fSDavid du Colombier 		t = &tarray[i];
14989a747e4fSDavid du Colombier 		free(t->text);
14999a747e4fSDavid du Colombier 		freeattrs(t->attr);
15009a747e4fSDavid du Colombier 	}
15019a747e4fSDavid du Colombier 	free(tarray);
15029a747e4fSDavid du Colombier }
1503