xref: /plan9/sys/src/libhtml/lex.c (revision edc15dd6d93c9a1b27df91ec433ef75c20e7345d)
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.
130314a20f0SDavid du Colombier // Keep sorted, and in correspondence with enum in impl.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},
2584ac975e2SDavid du Colombier 	{L"Alpha", 913},
2599a747e4fSDavid du Colombier 	{L"Aring", 197},
2609a747e4fSDavid du Colombier 	{L"Atilde", 195},
2619a747e4fSDavid du Colombier 	{L"Auml", 196},
2624ac975e2SDavid du Colombier 	{L"Beta", 914},
2639a747e4fSDavid du Colombier 	{L"Ccedil", 199},
2644ac975e2SDavid du Colombier 	{L"Chi", 935},
2654ac975e2SDavid du Colombier 	{L"Dagger", 8225},
2664ac975e2SDavid 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},
2714ac975e2SDavid du Colombier 	{L"Epsilon", 917},
2724ac975e2SDavid du Colombier 	{L"Eta", 919},
2739a747e4fSDavid du Colombier 	{L"Euml", 203},
2744ac975e2SDavid du Colombier 	{L"Gamma", 915},
2759a747e4fSDavid du Colombier 	{L"Iacute", 205},
2769a747e4fSDavid du Colombier 	{L"Icirc", 206},
2779a747e4fSDavid du Colombier 	{L"Igrave", 204},
2784ac975e2SDavid du Colombier 	{L"Iota", 921},
2799a747e4fSDavid du Colombier 	{L"Iuml", 207},
2804ac975e2SDavid du Colombier 	{L"Kappa", 922},
2814ac975e2SDavid du Colombier 	{L"Lambda", 923},
2824ac975e2SDavid du Colombier 	{L"Mu", 924},
2839a747e4fSDavid du Colombier 	{L"Ntilde", 209},
2844ac975e2SDavid du Colombier 	{L"Nu", 925},
2854ac975e2SDavid du Colombier 	{L"OElig", 338},
2869a747e4fSDavid du Colombier 	{L"Oacute", 211},
2879a747e4fSDavid du Colombier 	{L"Ocirc", 212},
2889a747e4fSDavid du Colombier 	{L"Ograve", 210},
2894ac975e2SDavid du Colombier 	{L"Omega", 937},
2904ac975e2SDavid du Colombier 	{L"Omicron", 927},
2919a747e4fSDavid du Colombier 	{L"Oslash", 216},
2929a747e4fSDavid du Colombier 	{L"Otilde", 213},
2939a747e4fSDavid du Colombier 	{L"Ouml", 214},
2944ac975e2SDavid du Colombier 	{L"Phi", 934},
2954ac975e2SDavid du Colombier 	{L"Pi", 928},
2964ac975e2SDavid du Colombier 	{L"Prime", 8243},
2974ac975e2SDavid du Colombier 	{L"Psi", 936},
2984ac975e2SDavid du Colombier 	{L"Rho", 929},
2994ac975e2SDavid du Colombier 	{L"Scaron", 352},
3004ac975e2SDavid du Colombier 	{L"Sigma", 931},
3019a747e4fSDavid du Colombier 	{L"THORN", 222},
3024ac975e2SDavid du Colombier 	{L"Tau", 932},
3034ac975e2SDavid du Colombier 	{L"Theta", 920},
3049a747e4fSDavid du Colombier 	{L"Uacute", 218},
3059a747e4fSDavid du Colombier 	{L"Ucirc", 219},
3069a747e4fSDavid du Colombier 	{L"Ugrave", 217},
3074ac975e2SDavid du Colombier 	{L"Upsilon", 933},
3089a747e4fSDavid du Colombier 	{L"Uuml", 220},
3094ac975e2SDavid du Colombier 	{L"Xi", 926},
3109a747e4fSDavid du Colombier 	{L"Yacute", 221},
3114ac975e2SDavid du Colombier 	{L"Yuml", 376},
3124ac975e2SDavid 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},
3184ac975e2SDavid du Colombier 	{L"alefsym", 8501},
3199a747e4fSDavid du Colombier 	{L"alpha", 945},
3209a747e4fSDavid du Colombier 	{L"amp", 38},
3214ac975e2SDavid du Colombier 	{L"and", 8743},
3224ac975e2SDavid du Colombier 	{L"ang", 8736},
3239a747e4fSDavid du Colombier 	{L"aring", 229},
3244ac975e2SDavid du Colombier 	{L"asymp", 8776},
3259a747e4fSDavid du Colombier 	{L"atilde", 227},
3269a747e4fSDavid du Colombier 	{L"auml", 228},
3274ac975e2SDavid du Colombier 	{L"bdquo", 8222},
3289a747e4fSDavid du Colombier 	{L"beta", 946},
3299a747e4fSDavid du Colombier 	{L"brvbar", 166},
3304ac975e2SDavid du Colombier 	{L"bull", 8226},
3314ac975e2SDavid 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},
3374ac975e2SDavid du Colombier 	{L"circ", 710},
3384ac975e2SDavid du Colombier 	{L"clubs", 9827},
3394ac975e2SDavid du Colombier 	{L"cong", 8773},
3409a747e4fSDavid du Colombier 	{L"copy", 169},
3414ac975e2SDavid du Colombier 	{L"crarr", 8629},
3424ac975e2SDavid du Colombier 	{L"cup", 8746},
3439a747e4fSDavid du Colombier 	{L"curren", 164},
3444ac975e2SDavid du Colombier 	{L"dArr", 8659},
3454ac975e2SDavid du Colombier 	{L"dagger", 8224},
3464ac975e2SDavid du Colombier 	{L"darr", 8595},
3479a747e4fSDavid du Colombier 	{L"ddots", 8945},
3489a747e4fSDavid du Colombier 	{L"deg", 176},
3499a747e4fSDavid du Colombier 	{L"delta", 948},
3504ac975e2SDavid 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 */
3564ac975e2SDavid 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},
3614ac975e2SDavid du Colombier 	{L"equiv", 8801},
3629a747e4fSDavid du Colombier 	{L"eta", 951},
3639a747e4fSDavid du Colombier 	{L"eth", 240},
3649a747e4fSDavid du Colombier 	{L"euml", 235},
3654ac975e2SDavid du Colombier 	{L"euro", 8364},
3664ac975e2SDavid du Colombier 	{L"exist", 8707},
3674ac975e2SDavid du Colombier 	{L"fnof", 402},
3684ac975e2SDavid du Colombier 	{L"forall", 8704},
3699a747e4fSDavid du Colombier 	{L"frac12", 189},
3709a747e4fSDavid du Colombier 	{L"frac14", 188},
3719a747e4fSDavid du Colombier 	{L"frac34", 190},
3724ac975e2SDavid du Colombier 	{L"frasl", 8260},
3739a747e4fSDavid du Colombier 	{L"gamma", 947},
3744ac975e2SDavid du Colombier 	{L"ge", 8805},
3759a747e4fSDavid du Colombier 	{L"gt", 62},
3764ac975e2SDavid du Colombier 	{L"hArr", 8660},
3774ac975e2SDavid du Colombier 	{L"harr", 8596},
3784ac975e2SDavid du Colombier 	{L"hearts", 9829},
3794ac975e2SDavid 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},
3844ac975e2SDavid du Colombier 	{L"image", 8465},
3854ac975e2SDavid du Colombier 	{L"infin", 8734},
3864ac975e2SDavid du Colombier 	{L"int", 8747},
3879a747e4fSDavid du Colombier 	{L"iota", 953},
3889a747e4fSDavid du Colombier 	{L"iquest", 191},
3894ac975e2SDavid du Colombier 	{L"isin", 8712},
3909a747e4fSDavid du Colombier 	{L"iuml", 239},
3919a747e4fSDavid du Colombier 	{L"kappa", 954},
3924ac975e2SDavid du Colombier 	{L"lArr", 8656},
3939a747e4fSDavid du Colombier 	{L"lambda", 955},
3944ac975e2SDavid du Colombier 	{L"lang", 9001},
3959a747e4fSDavid du Colombier 	{L"laquo", 171},
3964ac975e2SDavid du Colombier 	{L"larr", 8592},
3974ac975e2SDavid du Colombier 	{L"lceil", 8968},
3989a747e4fSDavid du Colombier 	{L"ldots", 8230},
3994ac975e2SDavid du Colombier 	{L"ldquo", 8220},
4004ac975e2SDavid du Colombier 	{L"le", 8804},
4014ac975e2SDavid du Colombier 	{L"lfloor", 8970},
4024ac975e2SDavid du Colombier 	{L"lowast", 8727},
4034ac975e2SDavid du Colombier 	{L"loz", 9674},
4044ac975e2SDavid du Colombier 	{L"lrm", 8206},
4054ac975e2SDavid du Colombier 	{L"lsaquo", 8249},
4064ac975e2SDavid 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},
4124ac975e2SDavid du Colombier 	{L"minus", 8722},
4139a747e4fSDavid du Colombier 	{L"mu", 956},
4144ac975e2SDavid du Colombier 	{L"nabla", 8711},
4159a747e4fSDavid du Colombier 	{L"nbsp", 160},
4169027b8f7SDavid du Colombier 	{L"ndash", 8211},
4174ac975e2SDavid du Colombier 	{L"ne", 8800},
4184ac975e2SDavid du Colombier 	{L"ni", 8715},
4199a747e4fSDavid du Colombier 	{L"not", 172},
4204ac975e2SDavid du Colombier 	{L"notin", 8713},
4214ac975e2SDavid 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},
4264ac975e2SDavid du Colombier 	{L"oelig", 339},
4279a747e4fSDavid du Colombier 	{L"ograve", 242},
4284ac975e2SDavid du Colombier 	{L"oline", 8254},
4299a747e4fSDavid du Colombier 	{L"omega", 969},
4309a747e4fSDavid du Colombier 	{L"omicron", 959},
4314ac975e2SDavid du Colombier 	{L"oplus", 8853},
4324ac975e2SDavid 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},
4374ac975e2SDavid du Colombier 	{L"otimes", 8855},
4389a747e4fSDavid du Colombier 	{L"ouml", 246},
4399a747e4fSDavid du Colombier 	{L"para", 182},
4404ac975e2SDavid du Colombier 	{L"part", 8706},
4414ac975e2SDavid du Colombier 	{L"permil", 8240},
4424ac975e2SDavid du Colombier 	{L"perp", 8869},
4439a747e4fSDavid du Colombier 	{L"phi", 966},
4449a747e4fSDavid du Colombier 	{L"pi", 960},
4454ac975e2SDavid du Colombier 	{L"piv", 982},
4469a747e4fSDavid du Colombier 	{L"plusmn", 177},
4479a747e4fSDavid du Colombier 	{L"pound", 163},
4484ac975e2SDavid du Colombier 	{L"prime", 8242},
4494ac975e2SDavid du Colombier 	{L"prod", 8719},
4504ac975e2SDavid du Colombier 	{L"prop", 8733},
4519a747e4fSDavid du Colombier 	{L"psi", 968},
4529a747e4fSDavid du Colombier 	{L"quad", 8193},
4539a747e4fSDavid du Colombier 	{L"quot", 34},
4544ac975e2SDavid du Colombier 	{L"rArr", 8658},
4554ac975e2SDavid du Colombier 	{L"radic", 8730},
4564ac975e2SDavid du Colombier 	{L"rang", 9002},
4579a747e4fSDavid du Colombier 	{L"raquo", 187},
4584ac975e2SDavid du Colombier 	{L"rarr", 8594},
4594ac975e2SDavid du Colombier 	{L"rceil", 8969},
4604ac975e2SDavid du Colombier 	{L"rdquo", 8221},
4614ac975e2SDavid du Colombier 	{L"real", 8476},
4629a747e4fSDavid du Colombier 	{L"reg", 174},
4634ac975e2SDavid du Colombier 	{L"rfloor", 8971},
4649a747e4fSDavid du Colombier 	{L"rho", 961},
4654ac975e2SDavid du Colombier 	{L"rlm", 8207},
4664ac975e2SDavid du Colombier 	{L"rsaquo", 8250},
4674ac975e2SDavid du Colombier 	{L"rsquo", 8217},
4684ac975e2SDavid du Colombier 	{L"sbquo", 8218},
4694ac975e2SDavid du Colombier 	{L"scaron", 353},
4704ac975e2SDavid du Colombier 	{L"sdot", 8901},
4719a747e4fSDavid du Colombier 	{L"sect", 167},
4729a747e4fSDavid du Colombier 	{L"shy", 173},
4739a747e4fSDavid du Colombier 	{L"sigma", 963},
4744ac975e2SDavid du Colombier 	{L"sigmaf", 962},
4754ac975e2SDavid du Colombier 	{L"sim", 8764},
4769a747e4fSDavid du Colombier 	{L"sp", 8194},
4774ac975e2SDavid du Colombier 	{L"spades", 9824},
4784ac975e2SDavid du Colombier 	{L"sub", 8834},
4794ac975e2SDavid du Colombier 	{L"sube", 8838},
4804ac975e2SDavid du Colombier 	{L"sum", 8721},
4814ac975e2SDavid du Colombier 	{L"sup", 8835},
4829a747e4fSDavid du Colombier 	{L"sup1", 185},
4839a747e4fSDavid du Colombier 	{L"sup2", 178},
4849a747e4fSDavid du Colombier 	{L"sup3", 179},
4854ac975e2SDavid du Colombier 	{L"supe", 8839},
4869a747e4fSDavid du Colombier 	{L"szlig", 223},
4879a747e4fSDavid du Colombier 	{L"tau", 964},
4884ac975e2SDavid du Colombier 	{L"there4", 8756},
4899a747e4fSDavid du Colombier 	{L"theta", 952},
4904ac975e2SDavid du Colombier 	{L"thetasym", 977},
4919a747e4fSDavid du Colombier 	{L"thinsp", 8201},
4929a747e4fSDavid du Colombier 	{L"thorn", 254},
4934ac975e2SDavid du Colombier 	{L"tilde", 732},
4949a747e4fSDavid du Colombier 	{L"times", 215},
4959a747e4fSDavid du Colombier 	{L"trade", 8482},
4964ac975e2SDavid du Colombier 	{L"uArr", 8657},
4979a747e4fSDavid du Colombier 	{L"uacute", 250},
4984ac975e2SDavid du Colombier 	{L"uarr", 8593},
4999a747e4fSDavid du Colombier 	{L"ucirc", 251},
5009a747e4fSDavid du Colombier 	{L"ugrave", 249},
5019a747e4fSDavid du Colombier 	{L"uml", 168},
5024ac975e2SDavid 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},
5124ac975e2SDavid 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},
5174ac975e2SDavid du Colombier 	{L"zeta", 950},
5184ac975e2SDavid du Colombier 	{L"zwj", 8205},
5194ac975e2SDavid 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 
540c93608ccSDavid du Colombier static void	lexinit(void);
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);
543314a20f0SDavid du Colombier static int		getscriptdata(TokenSource* ts, int firstc, int starti, Token* a, int* pai, int findtag);
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
lexinit(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*
newtokensource(uchar * data,int edata,int chset,int mtype)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 {
587c93608ccSDavid 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*
_gettoks(uchar * data,int datalen,int chset,int mtype,int * plen)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 	if(dbglex)
6079a747e4fSDavid du Colombier 		fprint(2, "_gettoks starts, ts.i=%d, ts.edata=%d\n", ts->i, ts->edata);
608c93608ccSDavid du Colombier 	alen = 0;
609c93608ccSDavid du Colombier 	ai = 0;
610c93608ccSDavid du Colombier 	a = 0;
6119a747e4fSDavid du Colombier 	if(ts->mtype == TextHtml) {
6129a747e4fSDavid du Colombier 		for(;;) {
613*edc15dd6SDavid du Colombier 			if(alen - ai < ToksChunk/32) {
6149a747e4fSDavid du Colombier 				alen += ToksChunk;
615c93608ccSDavid du Colombier 				a = erealloc(a, alen*sizeof *a);
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);
623314a20f0SDavid du Colombier 				if(tag == Tscript || tag == Tstyle) {
6249a747e4fSDavid du Colombier 					// special rules for getting Data after....
6259a747e4fSDavid du Colombier 					starti = ts->i;
6269a747e4fSDavid du Colombier 					c = getchar(ts);
627314a20f0SDavid du Colombier 					tag = getscriptdata(ts, c, starti, a, &ai, tag);
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(;;) {
641*edc15dd6SDavid du Colombier 			if(alen - ai < ToksChunk/32) {
6429a747e4fSDavid du Colombier 				alen += ToksChunk;
643c93608ccSDavid du Colombier 				a = erealloc(a, alen*sizeof *a);
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 	}
652314a20f0SDavid du Colombier 	free(ts);
6539a747e4fSDavid du Colombier 	if(dbglex)
6549a747e4fSDavid du Colombier 		fprint(2, "lex: returning %d tokens\n", ai);
6559a747e4fSDavid du Colombier 	*plen = ai;
656c93608ccSDavid du Colombier 	if(ai == 0){
657c93608ccSDavid du Colombier 		free(a);
658c93608ccSDavid du Colombier 		a = 0;
659c93608ccSDavid du Colombier 	}
6609a747e4fSDavid du Colombier 	return a;
6619a747e4fSDavid du Colombier }
6629a747e4fSDavid du Colombier 
6639a747e4fSDavid du Colombier // For case where source isn't HTML.
6649a747e4fSDavid du Colombier // Just make data tokens, one per line (or partial line,
6659a747e4fSDavid du Colombier // at end of buffer), ignoring non-whitespace control
6669a747e4fSDavid du Colombier // characters and dumping \r's.
6679a747e4fSDavid du Colombier // If find non-empty token, fill in a[*pai], bump *pai, and return Data.
6689a747e4fSDavid du Colombier // Otherwise return -1;
6699a747e4fSDavid du Colombier static int
getplaindata(TokenSource * ts,Token * a,int * pai)6709a747e4fSDavid du Colombier getplaindata(TokenSource* ts, Token* a, int* pai)
6719a747e4fSDavid du Colombier {
6729a747e4fSDavid du Colombier 	Rune*	s;
6739a747e4fSDavid du Colombier 	int	j;
6749a747e4fSDavid du Colombier 	int	starti;
6759a747e4fSDavid du Colombier 	int	c;
6769a747e4fSDavid du Colombier 	Token*	tok;
6779a747e4fSDavid du Colombier 	Rune	buf[BIGBUFSIZE];
6789a747e4fSDavid du Colombier 
6799a747e4fSDavid du Colombier 	s = nil;
6809a747e4fSDavid du Colombier 	j = 0;
6819a747e4fSDavid du Colombier 	starti = ts->i;
6829a747e4fSDavid du Colombier 	for(c = getchar(ts); c >= 0; c = getchar(ts)) {
6839a747e4fSDavid du Colombier 		if(c < ' ') {
6849a747e4fSDavid du Colombier 			if(isspace(c)) {
6859a747e4fSDavid du Colombier 				if(c == '\r') {
6869a747e4fSDavid du Colombier 					// ignore it unless no following '\n',
6879a747e4fSDavid du Colombier 					// in which case treat it like '\n'
6889a747e4fSDavid du Colombier 					c = getchar(ts);
6899a747e4fSDavid du Colombier 					if(c != '\n') {
6909a747e4fSDavid du Colombier 						if(c >= 0)
6919a747e4fSDavid du Colombier 							ungetchar(ts, c);
6929a747e4fSDavid du Colombier 						c = '\n';
6939a747e4fSDavid du Colombier 					}
6949a747e4fSDavid du Colombier 				}
6959a747e4fSDavid du Colombier 			}
6969a747e4fSDavid du Colombier 			else
6979a747e4fSDavid du Colombier 				c = 0;
6989a747e4fSDavid du Colombier 		}
6999a747e4fSDavid du Colombier 		if(c != 0) {
7009a747e4fSDavid du Colombier 			buf[j++] = c;
701c93608ccSDavid du Colombier 			if(j == nelem(buf)-1) {
7029a747e4fSDavid du Colombier 				s = buftostr(s, buf, j);
7039a747e4fSDavid du Colombier 				j = 0;
7049a747e4fSDavid du Colombier 			}
7059a747e4fSDavid du Colombier 		}
7069a747e4fSDavid du Colombier 		if(c == '\n')
7079a747e4fSDavid du Colombier 			break;
7089a747e4fSDavid du Colombier 	}
7099a747e4fSDavid du Colombier 	s = buftostr(s, buf, j);
7109a747e4fSDavid du Colombier 	if(s == nil)
7119a747e4fSDavid du Colombier 		return -1;
7129a747e4fSDavid du Colombier 	tok = &a[(*pai)++];
7139a747e4fSDavid du Colombier 	tok->tag = Data;
7149a747e4fSDavid du Colombier 	tok->text = s;
7159a747e4fSDavid du Colombier 	tok->attr = nil;
7169a747e4fSDavid du Colombier 	tok->starti = starti;
7179a747e4fSDavid du Colombier 	return Data;
7189a747e4fSDavid du Colombier }
7199a747e4fSDavid du Colombier 
7209a747e4fSDavid du Colombier // Return concatenation of s and buf[0:j]
7219a747e4fSDavid du Colombier static Rune*
buftostr(Rune * s,Rune * buf,int j)7229a747e4fSDavid du Colombier buftostr(Rune* s, Rune* buf, int j)
7239a747e4fSDavid du Colombier {
724c93608ccSDavid du Colombier 	int i;
725c93608ccSDavid du Colombier 
7269a747e4fSDavid du Colombier 	if(s == nil)
7279a747e4fSDavid du Colombier 		s = _Strndup(buf, j);
728c93608ccSDavid du Colombier 	else {
729c93608ccSDavid du Colombier 		i = _Strlen(s);
730c93608ccSDavid du Colombier 		s = realloc(s, ( i+j+1)*sizeof *s);
731c93608ccSDavid du Colombier 		memcpy(&s[i], buf, j*sizeof *s);
732c93608ccSDavid du Colombier 		s[i+j] = 0;
733c93608ccSDavid du Colombier 	}
7349a747e4fSDavid du Colombier 	return s;
7359a747e4fSDavid du Colombier }
7369a747e4fSDavid du Colombier 
7379a747e4fSDavid du Colombier // Gather data up to next start-of-tag or end-of-buffer.
7389a747e4fSDavid du Colombier // Translate entity references (&amp;).
7399a747e4fSDavid du Colombier // Ignore non-whitespace control characters and get rid of \r's.
7409a747e4fSDavid du Colombier // If find non-empty token, fill in a[*pai], bump *pai, and return Data.
7419a747e4fSDavid du Colombier // Otherwise return -1;
7429a747e4fSDavid du Colombier static int
getdata(TokenSource * ts,int firstc,int starti,Token * a,int * pai)7439a747e4fSDavid du Colombier getdata(TokenSource* ts, int firstc, int starti, Token* a, int* pai)
7449a747e4fSDavid du Colombier {
7459a747e4fSDavid du Colombier 	Rune*	s;
7469a747e4fSDavid du Colombier 	int	j;
7479a747e4fSDavid du Colombier 	int	c;
7489a747e4fSDavid du Colombier 	Token*	tok;
749c93608ccSDavid du Colombier 	Rune	buf[SMALLBUFSIZE];
7509a747e4fSDavid du Colombier 
7519a747e4fSDavid du Colombier 	s = nil;
7529a747e4fSDavid du Colombier 	j = 0;
753c93608ccSDavid du Colombier 	for(c = firstc; c >= 0; c = getchar(ts)){
7549a747e4fSDavid du Colombier 		if(c == '&') {
7559a747e4fSDavid du Colombier 			c = ampersand(ts);
7569a747e4fSDavid du Colombier 			if(c < 0)
7579a747e4fSDavid du Colombier 				break;
7589a747e4fSDavid du Colombier 		}
7599a747e4fSDavid du Colombier 		else if(c < ' ') {
7609a747e4fSDavid du Colombier 			if(isspace(c)) {
7619a747e4fSDavid du Colombier 				if(c == '\r') {
7629a747e4fSDavid du Colombier 					// ignore it unless no following '\n',
7639a747e4fSDavid du Colombier 					// in which case treat it like '\n'
7649a747e4fSDavid du Colombier 					c = getchar(ts);
7659a747e4fSDavid du Colombier 					if(c != '\n') {
7669a747e4fSDavid du Colombier 						if(c >= 0)
7679a747e4fSDavid du Colombier 							ungetchar(ts, c);
7689a747e4fSDavid du Colombier 						c = '\n';
7699a747e4fSDavid du Colombier 					}
7709a747e4fSDavid du Colombier 				}
7719a747e4fSDavid du Colombier 			}
7729a747e4fSDavid du Colombier 			else {
7739a747e4fSDavid du Colombier 				if(warn)
7749a747e4fSDavid du Colombier 					fprint(2, "warning: non-whitespace control character %d ignored\n", c);
7759a747e4fSDavid du Colombier 				c = 0;
7769a747e4fSDavid du Colombier 			}
7779a747e4fSDavid du Colombier 		}
7789a747e4fSDavid du Colombier 		else if(c == '<') {
7799a747e4fSDavid du Colombier 			ungetchar(ts, c);
7809a747e4fSDavid du Colombier 			break;
7819a747e4fSDavid du Colombier 		}
7829a747e4fSDavid du Colombier 		if(c != 0) {
7839a747e4fSDavid du Colombier 			buf[j++] = c;
784c93608ccSDavid du Colombier 			if(j == nelem(buf)-1) {
7859a747e4fSDavid du Colombier 				s = buftostr(s, buf, j);
7869a747e4fSDavid du Colombier 				j = 0;
7879a747e4fSDavid du Colombier 			}
7889a747e4fSDavid du Colombier 		}
7899a747e4fSDavid du Colombier 	}
7909a747e4fSDavid du Colombier 	s = buftostr(s, buf, j);
7919a747e4fSDavid du Colombier 	if(s == nil)
7929a747e4fSDavid du Colombier 		return -1;
7939a747e4fSDavid du Colombier 	tok = &a[(*pai)++];
7949a747e4fSDavid du Colombier 	tok->tag = Data;
7959a747e4fSDavid du Colombier 	tok->text = s;
7969a747e4fSDavid du Colombier 	tok->attr = nil;
7979a747e4fSDavid du Colombier 	tok->starti = starti;
7989a747e4fSDavid du Colombier 	return Data;
7999a747e4fSDavid du Colombier }
8009a747e4fSDavid du Colombier 
8019a747e4fSDavid du Colombier // The rules for lexing scripts are different (ugh).
802314a20f0SDavid du Colombier // Gather up everything until see an "</" tagnames[tok] ">"
8039a747e4fSDavid du Colombier static int
getscriptdata(TokenSource * ts,int firstc,int starti,Token * a,int * pai,int findtag)804314a20f0SDavid du Colombier getscriptdata(TokenSource* ts, int firstc, int starti, Token* a, int* pai, int findtag)
8059a747e4fSDavid du Colombier {
8069a747e4fSDavid du Colombier 	Rune*	s;
8079a747e4fSDavid du Colombier 	int	j;
8089a747e4fSDavid du Colombier 	int	tstarti;
8099a747e4fSDavid du Colombier 	int	savei;
8109a747e4fSDavid du Colombier 	int	c;
8119a747e4fSDavid du Colombier 	int	tag;
8129a747e4fSDavid du Colombier 	int	done;
8139a747e4fSDavid du Colombier 	Token*	tok;
8149a747e4fSDavid du Colombier 	Rune	buf[BIGBUFSIZE];
8159a747e4fSDavid du Colombier 
8169a747e4fSDavid du Colombier 	s = nil;
8179a747e4fSDavid du Colombier 	j = 0;
8189a747e4fSDavid du Colombier 	tstarti = starti;
8199a747e4fSDavid du Colombier 	c = firstc;
8209a747e4fSDavid du Colombier 	done = 0;
8219a747e4fSDavid du Colombier 	while(c >= 0) {
8229a747e4fSDavid du Colombier 		if(c == '<') {
8239a747e4fSDavid du Colombier 			// other browsers ignore stuff to end of line after <!
8249a747e4fSDavid du Colombier 			savei = ts->i;
8259a747e4fSDavid du Colombier 			c = getchar(ts);
8269a747e4fSDavid du Colombier 			if(c == '!') {
827314a20f0SDavid du Colombier 				if(comment(ts) == -1)
828314a20f0SDavid du Colombier 					break;
8299a747e4fSDavid du Colombier 				if(c == '\r')
8309a747e4fSDavid du Colombier 					c = getchar(ts);
8319a747e4fSDavid du Colombier 				if(c == '\n')
8329a747e4fSDavid du Colombier 					c = getchar(ts);
8339a747e4fSDavid du Colombier 			}
8349a747e4fSDavid du Colombier 			else if(c >= 0) {
8359a747e4fSDavid du Colombier 				backup(ts, savei);
8369a747e4fSDavid du Colombier 				tag = gettag(ts, tstarti, a, pai);
8379a747e4fSDavid du Colombier 				if(tag == -1)
8389a747e4fSDavid du Colombier 					break;
8399a747e4fSDavid du Colombier 				if(tag != Comment)
8409a747e4fSDavid du Colombier 					(*pai)--;
8419a747e4fSDavid du Colombier 				backup(ts, tstarti);
842314a20f0SDavid du Colombier 				if(tag == findtag + RBRA) {
8439a747e4fSDavid du Colombier 					done = 1;
8449a747e4fSDavid du Colombier 					break;
8459a747e4fSDavid du Colombier 				}
846314a20f0SDavid du Colombier 				// here tag was not the one we were looking for, so take as regular data
8479a747e4fSDavid du Colombier 				c = getchar(ts);
8489a747e4fSDavid du Colombier 			}
8499a747e4fSDavid du Colombier 		}
8509a747e4fSDavid du Colombier 		if(c < 0)
8519a747e4fSDavid du Colombier 			break;
8529a747e4fSDavid du Colombier 		if(c != 0) {
8539a747e4fSDavid du Colombier 			buf[j++] = c;
854c93608ccSDavid du Colombier 			if(j == nelem(buf)-1) {
8559a747e4fSDavid du Colombier 				s = buftostr(s, buf, j);
8569a747e4fSDavid du Colombier 				j = 0;
8579a747e4fSDavid du Colombier 			}
8589a747e4fSDavid du Colombier 		}
8599a747e4fSDavid du Colombier 		tstarti = ts->i;
8609a747e4fSDavid du Colombier 		c = getchar(ts);
8619a747e4fSDavid du Colombier 	}
8629a747e4fSDavid du Colombier 	if(done || ts->i == ts->edata) {
8639a747e4fSDavid du Colombier 		s = buftostr(s, buf, j);
8649a747e4fSDavid du Colombier 		tok = &a[(*pai)++];
8659a747e4fSDavid du Colombier 		tok->tag = Data;
8669a747e4fSDavid du Colombier 		tok->text = s;
8679a747e4fSDavid du Colombier 		tok->attr = nil;
8689a747e4fSDavid du Colombier 		tok->starti = starti;
8699a747e4fSDavid du Colombier 		return Data;
8709a747e4fSDavid du Colombier 	}
871c93608ccSDavid du Colombier 	free(s);
8729a747e4fSDavid du Colombier 	backup(ts, starti);
8739a747e4fSDavid du Colombier 	return -1;
8749a747e4fSDavid du Colombier }
8759a747e4fSDavid du Colombier 
8769a747e4fSDavid du Colombier // We've just seen a '<'.  Gather up stuff to closing '>' (if buffer
8779a747e4fSDavid du Colombier // ends before then, return -1).
8789a747e4fSDavid du Colombier // If it's a tag, look up the name, gather the attributes, and return
8799a747e4fSDavid du Colombier // the appropriate token.
8809a747e4fSDavid du Colombier // Else it's either just plain data or some kind of ignorable stuff:
8819a747e4fSDavid du Colombier // return Data or Comment as appropriate.
8829a747e4fSDavid du Colombier // If it's not a Comment, put it in a[*pai] and bump *pai.
8839a747e4fSDavid du Colombier static int
gettag(TokenSource * ts,int starti,Token * a,int * pai)8849a747e4fSDavid du Colombier gettag(TokenSource* ts, int starti, Token* a, int* pai)
8859a747e4fSDavid du Colombier {
8869a747e4fSDavid du Colombier 	int	rbra;
8879a747e4fSDavid du Colombier 	int	ans;
8889a747e4fSDavid du Colombier 	Attr*	al;
8899a747e4fSDavid du Colombier 	int	nexti;
8909a747e4fSDavid du Colombier 	int	c;
8919a747e4fSDavid du Colombier 	int	ti;
8929a747e4fSDavid du Colombier 	int	afnd;
8939a747e4fSDavid du Colombier 	int	attid;
8949a747e4fSDavid du Colombier 	int	quote;
8959a747e4fSDavid du Colombier 	Rune*	val;
8969a747e4fSDavid du Colombier 	int	nv;
8979a747e4fSDavid du Colombier 	int	i;
8989a747e4fSDavid du Colombier 	int	tag;
8999a747e4fSDavid du Colombier 	Token*	tok;
9009a747e4fSDavid du Colombier 	Rune	buf[BIGBUFSIZE];
9019a747e4fSDavid du Colombier 
9029a747e4fSDavid du Colombier 	rbra = 0;
9039a747e4fSDavid du Colombier 	nexti = ts->i;
9049a747e4fSDavid du Colombier 	tok = &a[*pai];
9059a747e4fSDavid du Colombier 	tok->tag = Notfound;
9069a747e4fSDavid du Colombier 	tok->text = nil;
9079a747e4fSDavid du Colombier 	tok->attr = nil;
9089a747e4fSDavid du Colombier 	tok->starti = starti;
9099a747e4fSDavid du Colombier 	c = getchar(ts);
9109a747e4fSDavid du Colombier 	if(c == '/') {
9119a747e4fSDavid du Colombier 		rbra = RBRA;
9129a747e4fSDavid du Colombier 		c = getchar(ts);
9139a747e4fSDavid du Colombier 	}
9149a747e4fSDavid du Colombier 	if(c < 0)
9159a747e4fSDavid du Colombier 		goto eob_done;
9169a747e4fSDavid du Colombier 	if(c >= 256 || !isalpha(c)) {
9179a747e4fSDavid du Colombier 		// not a tag
9189a747e4fSDavid du Colombier 		if(c == '!') {
9199a747e4fSDavid du Colombier 			ans = comment(ts);
9209a747e4fSDavid du Colombier 			if(ans != -1)
9219a747e4fSDavid du Colombier 				return ans;
9229a747e4fSDavid du Colombier 			goto eob_done;
9239a747e4fSDavid du Colombier 		}
9249a747e4fSDavid du Colombier 		else {
9259a747e4fSDavid du Colombier 			backup(ts, nexti);
9269a747e4fSDavid du Colombier 			tok->tag = Data;
9279a747e4fSDavid du Colombier 			tok->text = _Strdup(L"<");
9289a747e4fSDavid du Colombier 			(*pai)++;
9299a747e4fSDavid du Colombier 			return Data;
9309a747e4fSDavid du Colombier 		}
9319a747e4fSDavid du Colombier 	}
9329a747e4fSDavid du Colombier 	// c starts a tagname
9339a747e4fSDavid du Colombier 	buf[0] = c;
9349a747e4fSDavid du Colombier 	i = 1;
9359a747e4fSDavid du Colombier 	while(1) {
9369a747e4fSDavid du Colombier 		c = getchar(ts);
9379a747e4fSDavid du Colombier 		if(c < 0)
9389a747e4fSDavid du Colombier 			goto eob_done;
9399a747e4fSDavid du Colombier 		if(!ISNAMCHAR(c))
9409a747e4fSDavid du Colombier 			break;
9419a747e4fSDavid du Colombier 		// if name is bigger than buf it won't be found anyway...
9429a747e4fSDavid du Colombier 		if(i < BIGBUFSIZE)
9439a747e4fSDavid du Colombier 			buf[i++] = c;
9449a747e4fSDavid du Colombier 	}
9459a747e4fSDavid du Colombier 	if(_lookup(tagtable, Numtags, buf, i, &tag))
9469a747e4fSDavid du Colombier 		tok->tag = tag + rbra;
9479a747e4fSDavid du Colombier 	else
9489a747e4fSDavid du Colombier 		tok->text = _Strndup(buf, i);	// for warning print, in build
9499a747e4fSDavid du Colombier 	// attribute gathering loop
9509a747e4fSDavid du Colombier 	al = nil;
9519a747e4fSDavid du Colombier 	while(1) {
9529a747e4fSDavid du Colombier 		// look for "ws name" or "ws name ws = ws val"  (ws=whitespace)
9539a747e4fSDavid du Colombier 		// skip whitespace
9549a747e4fSDavid du Colombier attrloop_continue:
9559a747e4fSDavid du Colombier 		while(c < 256 && isspace(c)) {
9569a747e4fSDavid du Colombier 			c = getchar(ts);
9579a747e4fSDavid du Colombier 			if(c < 0)
9589a747e4fSDavid du Colombier 				goto eob_done;
9599a747e4fSDavid du Colombier 		}
9609a747e4fSDavid du Colombier 		if(c == '>')
9619a747e4fSDavid du Colombier 			goto attrloop_done;
9629a747e4fSDavid du Colombier 		if(c == '<') {
9639a747e4fSDavid du Colombier 			if(warn)
9649a747e4fSDavid du Colombier 				fprint(2, "warning: unclosed tag\n");
9659a747e4fSDavid du Colombier 			ungetchar(ts, c);
9669a747e4fSDavid du Colombier 			goto attrloop_done;
9679a747e4fSDavid du Colombier 		}
9689a747e4fSDavid du Colombier 		if(c >= 256 || !isalpha(c)) {
9699a747e4fSDavid du Colombier 			if(warn)
9709a747e4fSDavid du Colombier 				fprint(2, "warning: expected attribute name\n");
9719a747e4fSDavid du Colombier 			// skipt to next attribute name
9729a747e4fSDavid du Colombier 			while(1) {
9739a747e4fSDavid du Colombier 				c = getchar(ts);
9749a747e4fSDavid du Colombier 				if(c < 0)
9759a747e4fSDavid du Colombier 					goto eob_done;
9769a747e4fSDavid du Colombier 				if(c < 256 && isalpha(c))
9779a747e4fSDavid du Colombier 					goto attrloop_continue;
9789a747e4fSDavid du Colombier 				if(c == '<') {
9799a747e4fSDavid du Colombier 					if(warn)
9809a747e4fSDavid du Colombier 						fprint(2, "warning: unclosed tag\n");
9819a747e4fSDavid du Colombier 					ungetchar(ts, 60);
9829a747e4fSDavid du Colombier 					goto attrloop_done;
9839a747e4fSDavid du Colombier 				}
9849a747e4fSDavid du Colombier 				if(c == '>')
9859a747e4fSDavid du Colombier 					goto attrloop_done;
9869a747e4fSDavid du Colombier 			}
9879a747e4fSDavid du Colombier 		}
9889a747e4fSDavid du Colombier 		// gather attribute name
9899a747e4fSDavid du Colombier 		buf[0] = c;
9909a747e4fSDavid du Colombier 		i = 1;
9919a747e4fSDavid du Colombier 		while(1) {
9929a747e4fSDavid du Colombier 			c = getchar(ts);
9939a747e4fSDavid du Colombier 			if(c < 0)
9949a747e4fSDavid du Colombier 				goto eob_done;
9959a747e4fSDavid du Colombier 			if(!ISNAMCHAR(c))
9969a747e4fSDavid du Colombier 				break;
9979a747e4fSDavid du Colombier 			if(i < BIGBUFSIZE-1)
9989a747e4fSDavid du Colombier 				buf[i++] = c;
9999a747e4fSDavid du Colombier 		}
10009a747e4fSDavid du Colombier 		afnd = _lookup(attrtable, Numattrs, buf, i, &attid);
10019a747e4fSDavid du Colombier 		if(warn && !afnd) {
10029a747e4fSDavid du Colombier 			buf[i] = 0;
10039a747e4fSDavid du Colombier 			fprint(2, "warning: unknown attribute name %S\n", buf);
10049a747e4fSDavid du Colombier 		}
10059a747e4fSDavid du Colombier 		// skip whitespace
10069a747e4fSDavid du Colombier 		while(c < 256 && isspace(c)) {
10079a747e4fSDavid du Colombier 			c = getchar(ts);
10089a747e4fSDavid du Colombier 			if(c < 0)
10099a747e4fSDavid du Colombier 				goto eob_done;
10109a747e4fSDavid du Colombier 		}
10119a747e4fSDavid du Colombier 		if(c != '=') {
10129a747e4fSDavid du Colombier 			if(afnd)
10139a747e4fSDavid du Colombier 				al = newattr(attid, nil, al);
10149a747e4fSDavid du Colombier 			goto attrloop_continue;
10159a747e4fSDavid du Colombier 		}
10169a747e4fSDavid du Colombier 		//# c is '=' here;  skip whitespace
10179a747e4fSDavid du Colombier 		while(1) {
10189a747e4fSDavid du Colombier 			c = getchar(ts);
10199a747e4fSDavid du Colombier 			if(c < 0)
10209a747e4fSDavid du Colombier 				goto eob_done;
10219a747e4fSDavid du Colombier 			if(c >= 256 || !isspace(c))
10229a747e4fSDavid du Colombier 				break;
10239a747e4fSDavid du Colombier 		}
10249a747e4fSDavid du Colombier 		quote = 0;
10259a747e4fSDavid du Colombier 		if(c == '\'' || c == '"') {
10269a747e4fSDavid du Colombier 			quote = c;
10279a747e4fSDavid du Colombier 			c = getchar(ts);
10289a747e4fSDavid du Colombier 			if(c < 0)
10299a747e4fSDavid du Colombier 				goto eob_done;
10309a747e4fSDavid du Colombier 		}
10319a747e4fSDavid du Colombier 		val = nil;
10329a747e4fSDavid du Colombier 		nv = 0;
10339a747e4fSDavid du Colombier 		while(1) {
10349a747e4fSDavid du Colombier valloop_continue:
10359a747e4fSDavid du Colombier 			if(c < 0)
10369a747e4fSDavid du Colombier 				goto eob_done;
10379a747e4fSDavid du Colombier 			if(c == '>') {
10389a747e4fSDavid du Colombier 				if(quote) {
10399a747e4fSDavid du Colombier 					// c might be part of string (though not good style)
10409a747e4fSDavid du Colombier 					// but if line ends before close quote, assume
10419a747e4fSDavid du Colombier 					// there was an unmatched quote
10429a747e4fSDavid du Colombier 					ti = ts->i;
10439a747e4fSDavid du Colombier 					while(1) {
10449a747e4fSDavid du Colombier 						c = getchar(ts);
10459a747e4fSDavid du Colombier 						if(c < 0)
10469a747e4fSDavid du Colombier 							goto eob_done;
10479a747e4fSDavid du Colombier 						if(c == quote) {
10489a747e4fSDavid du Colombier 							backup(ts, ti);
10499a747e4fSDavid du Colombier 							buf[nv++] = '>';
10509a747e4fSDavid du Colombier 							if(nv == BIGBUFSIZE-1) {
10519a747e4fSDavid du Colombier 								val = buftostr(val, buf, nv);
10529a747e4fSDavid du Colombier 								nv = 0;
10539a747e4fSDavid du Colombier 							}
10549a747e4fSDavid du Colombier 							c = getchar(ts);
10559a747e4fSDavid du Colombier 							goto valloop_continue;
10569a747e4fSDavid du Colombier 						}
10579a747e4fSDavid du Colombier 						if(c == '\n') {
10589a747e4fSDavid du Colombier 							if(warn)
10599a747e4fSDavid du Colombier 								fprint(2, "warning: apparent unmatched quote\n");
10609a747e4fSDavid du Colombier 							backup(ts, ti);
10619a747e4fSDavid du Colombier 							c = '>';
10629a747e4fSDavid du Colombier 							goto valloop_done;
10639a747e4fSDavid du Colombier 						}
10649a747e4fSDavid du Colombier 					}
10659a747e4fSDavid du Colombier 				}
10669a747e4fSDavid du Colombier 				else
10679a747e4fSDavid du Colombier 					goto valloop_done;
10689a747e4fSDavid du Colombier 			}
10699a747e4fSDavid du Colombier 			if(quote) {
10709a747e4fSDavid du Colombier 				if(c == quote) {
10719a747e4fSDavid du Colombier 					c = getchar(ts);
10729a747e4fSDavid du Colombier 					if(c < 0)
10739a747e4fSDavid du Colombier 						goto eob_done;
10749a747e4fSDavid du Colombier 					goto valloop_done;
10759a747e4fSDavid du Colombier 				}
10769a747e4fSDavid du Colombier 				if(c == '\r') {
10779a747e4fSDavid du Colombier 					c = getchar(ts);
10789a747e4fSDavid du Colombier 					goto valloop_continue;
10799a747e4fSDavid du Colombier 				}
10809a747e4fSDavid du Colombier 				if(c == '\t' || c == '\n')
10819a747e4fSDavid du Colombier 					c = ' ';
10829a747e4fSDavid du Colombier 			}
10839a747e4fSDavid du Colombier 			else {
10849a747e4fSDavid du Colombier 				if(c < 256 && isspace(c))
10859a747e4fSDavid du Colombier 					goto valloop_done;
10869a747e4fSDavid du Colombier 			}
10879a747e4fSDavid du Colombier 			if(c == '&') {
10889a747e4fSDavid du Colombier 				c = ampersand(ts);
10899a747e4fSDavid du Colombier 				if(c == -1)
10909a747e4fSDavid du Colombier 					goto eob_done;
10919a747e4fSDavid du Colombier 			}
10929a747e4fSDavid du Colombier 			buf[nv++] = c;
10939a747e4fSDavid du Colombier 			if(nv == BIGBUFSIZE-1) {
10949a747e4fSDavid du Colombier 				val = buftostr(val, buf, nv);
10959a747e4fSDavid du Colombier 				nv = 0;
10969a747e4fSDavid du Colombier 			}
10979a747e4fSDavid du Colombier 			c = getchar(ts);
10989a747e4fSDavid du Colombier 		}
10999a747e4fSDavid du Colombier valloop_done:
11009a747e4fSDavid du Colombier 		if(afnd) {
11019a747e4fSDavid du Colombier 			val = buftostr(val, buf, nv);
11029a747e4fSDavid du Colombier 			al = newattr(attid, val, al);
11039a747e4fSDavid du Colombier 		}
11049a747e4fSDavid du Colombier 	}
11059a747e4fSDavid du Colombier 
11069a747e4fSDavid du Colombier attrloop_done:
11079a747e4fSDavid du Colombier 	tok->attr = al;
11089a747e4fSDavid du Colombier 	(*pai)++;
11099a747e4fSDavid du Colombier 	return tok->tag;
11109a747e4fSDavid du Colombier 
11119a747e4fSDavid du Colombier eob_done:
11129a747e4fSDavid du Colombier 	if(warn)
11139a747e4fSDavid du Colombier 		fprint(2, "warning: incomplete tag at end of page\n");
11149a747e4fSDavid du Colombier 	backup(ts, nexti);
11159a747e4fSDavid du Colombier 	tok->tag = Data;
11169a747e4fSDavid du Colombier 	tok->text = _Strdup(L"<");
11179a747e4fSDavid du Colombier 	return Data;
11189a747e4fSDavid du Colombier }
11199a747e4fSDavid du Colombier 
11209a747e4fSDavid du Colombier // We've just read a '<!' at position starti,
11219a747e4fSDavid du Colombier // so this may be a comment or other ignored section, or it may
11229a747e4fSDavid du Colombier // be just a literal string if there is no close before end of file
11239a747e4fSDavid du Colombier // (other browsers do that).
11249a747e4fSDavid du Colombier // The accepted practice seems to be (note: contrary to SGML spec!):
11259a747e4fSDavid du Colombier // If see <!--, look for --> to close, or if none, > to close.
11269a747e4fSDavid du Colombier // If see <!(not --), look for > to close.
11279a747e4fSDavid du Colombier // If no close before end of file, leave original characters in as literal data.
11289a747e4fSDavid du Colombier //
11299a747e4fSDavid du Colombier // If we see ignorable stuff, return Comment.
11309a747e4fSDavid du Colombier // Else return nil (caller should back up and try again when more data arrives,
11319a747e4fSDavid du Colombier // unless at end of file, in which case caller should just make '<' a data token).
11329a747e4fSDavid du Colombier static int
comment(TokenSource * ts)11339a747e4fSDavid du Colombier comment(TokenSource* ts)
11349a747e4fSDavid du Colombier {
11359a747e4fSDavid du Colombier 	int	nexti;
11369a747e4fSDavid du Colombier 	int	havecomment;
11379a747e4fSDavid du Colombier 	int	c;
11389a747e4fSDavid du Colombier 
11399a747e4fSDavid du Colombier 	nexti = ts->i;
11409a747e4fSDavid du Colombier 	havecomment = 0;
11419a747e4fSDavid du Colombier 	c = getchar(ts);
11429a747e4fSDavid du Colombier 	if(c == '-') {
11439a747e4fSDavid du Colombier 		c = getchar(ts);
11449a747e4fSDavid du Colombier 		if(c == '-') {
11459a747e4fSDavid du Colombier 			if(findstr(ts, L"-->"))
11469a747e4fSDavid du Colombier 				havecomment = 1;
11479a747e4fSDavid du Colombier 			else
11489a747e4fSDavid du Colombier 				backup(ts, nexti);
11499a747e4fSDavid du Colombier 		}
11509a747e4fSDavid du Colombier 	}
11519a747e4fSDavid du Colombier 	if(!havecomment) {
11529a747e4fSDavid du Colombier 		if(c == '>')
11539a747e4fSDavid du Colombier 			havecomment = 1;
11549a747e4fSDavid du Colombier 		else if(c >= 0) {
11559a747e4fSDavid du Colombier 			if(findstr(ts, L">"))
11569a747e4fSDavid du Colombier 				havecomment = 1;
11579a747e4fSDavid du Colombier 		}
11589a747e4fSDavid du Colombier 	}
11599a747e4fSDavid du Colombier 	if(havecomment)
11609a747e4fSDavid du Colombier 		return Comment;
11619a747e4fSDavid du Colombier 	return -1;
11629a747e4fSDavid du Colombier }
11639a747e4fSDavid du Colombier 
11649a747e4fSDavid du Colombier // Look for string s in token source.
11659a747e4fSDavid du Colombier // If found, return 1, with buffer at next char after s,
11669a747e4fSDavid du Colombier // else return 0 (caller should back up).
11679a747e4fSDavid du Colombier static int
findstr(TokenSource * ts,Rune * s)11689a747e4fSDavid du Colombier findstr(TokenSource* ts, Rune* s)
11699a747e4fSDavid du Colombier {
11709a747e4fSDavid du Colombier 	int	c0;
11719a747e4fSDavid du Colombier 	int	n;
11729a747e4fSDavid du Colombier 	int	nexti;
11739a747e4fSDavid du Colombier 	int	i;
11749a747e4fSDavid du Colombier 	int	c;
11759a747e4fSDavid du Colombier 
11769a747e4fSDavid du Colombier 	c0 = s[0];
11779a747e4fSDavid du Colombier 	n = runestrlen(s);
11789a747e4fSDavid du Colombier 	while(1) {
11799a747e4fSDavid du Colombier 		c = getchar(ts);
11809a747e4fSDavid du Colombier 		if(c < 0)
11819a747e4fSDavid du Colombier 			break;
11829a747e4fSDavid du Colombier 		if(c == c0) {
11839a747e4fSDavid du Colombier 			if(n == 1)
11849a747e4fSDavid du Colombier 				return 1;
11859a747e4fSDavid du Colombier 			nexti = ts->i;
11869a747e4fSDavid du Colombier 			for(i = 1; i < n; i++) {
11879a747e4fSDavid du Colombier 				c = getchar(ts);
11889a747e4fSDavid du Colombier 				if(c < 0)
11899a747e4fSDavid du Colombier 					goto mainloop_done;
11909a747e4fSDavid du Colombier 				if(c != s[i])
11919a747e4fSDavid du Colombier 					break;
11929a747e4fSDavid du Colombier 			}
11939a747e4fSDavid du Colombier 			if(i == n)
11949a747e4fSDavid du Colombier 				return 1;
11959a747e4fSDavid du Colombier 			backup(ts, nexti);
11969a747e4fSDavid du Colombier 		}
11979a747e4fSDavid du Colombier 	}
11989a747e4fSDavid du Colombier mainloop_done:
11999a747e4fSDavid du Colombier 	return 0;
12009a747e4fSDavid du Colombier }
12019a747e4fSDavid du Colombier 
12029a747e4fSDavid du Colombier // We've just read an '&'; look for an entity reference
12039a747e4fSDavid du Colombier // name, and if found, return translated char.
12049a747e4fSDavid du Colombier // if there is a complete entity name but it isn't known,
1205314a20f0SDavid du Colombier // back up to just past the '&' and return '&'.
12069a747e4fSDavid du Colombier // If the entity can't be completed in the current buffer, back up
12079a747e4fSDavid du Colombier // to the '&' and return -1.
12089a747e4fSDavid du Colombier static int
ampersand(TokenSource * ts)12099a747e4fSDavid du Colombier ampersand(TokenSource* ts)
12109a747e4fSDavid du Colombier {
12119a747e4fSDavid du Colombier 	int	savei;
12129a747e4fSDavid du Colombier 	int	c;
12139a747e4fSDavid du Colombier 	int	fnd;
12149a747e4fSDavid du Colombier 	int	ans;
12159a747e4fSDavid du Colombier 	int	v;
12169a747e4fSDavid du Colombier 	int	k;
1217c93608ccSDavid du Colombier 	Rune	buf[25];
12189a747e4fSDavid du Colombier 
12199a747e4fSDavid du Colombier 	savei = ts->i;
12209a747e4fSDavid du Colombier 	c = getchar(ts);
12219a747e4fSDavid du Colombier 	fnd = 0;
12229a747e4fSDavid du Colombier 	ans = -1;
12239a747e4fSDavid du Colombier 	if(c == '#') {
12249a747e4fSDavid du Colombier 		c = getchar(ts);
12259a747e4fSDavid du Colombier 		v = 0;
1226314a20f0SDavid du Colombier 		if(c == 'X' || c == 'x')
1227314a20f0SDavid du Colombier 			for(c = getchar(ts); c < 256; c = getchar(ts))
1228314a20f0SDavid du Colombier 				if(c >= '0' && c <= '9')
1229314a20f0SDavid du Colombier 					v = v*16+c-'0';
1230314a20f0SDavid du Colombier 				else if(c >= 'A' && c<= 'F')
1231314a20f0SDavid du Colombier 					v = v*16+c-'A'+10;
1232314a20f0SDavid du Colombier 				else if(c >= 'a' && c <= 'f')
1233314a20f0SDavid du Colombier 					v = v*16+c-'a'+10;
1234314a20f0SDavid du Colombier 				else
1235314a20f0SDavid du Colombier 					break;
1236314a20f0SDavid du Colombier 		else
12379a747e4fSDavid du Colombier 			while(c >= 0) {
12389a747e4fSDavid du Colombier 				if(!(c < 256 && isdigit(c)))
12399a747e4fSDavid du Colombier 					break;
12409a747e4fSDavid du Colombier 				v = v*10 + c - 48;
12419a747e4fSDavid du Colombier 				c = getchar(ts);
12429a747e4fSDavid du Colombier 			}
12439a747e4fSDavid du Colombier 		if(c >= 0) {
12449a747e4fSDavid du Colombier 			if(!(c == ';' || c == '\n' || c == '\r'))
12459a747e4fSDavid du Colombier 				ungetchar(ts, c);
12469a747e4fSDavid du Colombier 			c = v;
12479a747e4fSDavid du Colombier 			if(c == 160)
12489a747e4fSDavid du Colombier 				c = 160;
12499a747e4fSDavid du Colombier 			if(c >= Winstart && c <= Winend) {
12509a747e4fSDavid du Colombier 				c = winchars[c - Winstart];
12519a747e4fSDavid du Colombier 			}
12529a747e4fSDavid du Colombier 			ans = c;
12539a747e4fSDavid du Colombier 			fnd = 1;
12549a747e4fSDavid du Colombier 		}
12559a747e4fSDavid du Colombier 	}
12569a747e4fSDavid du Colombier 	else if(c < 256 && isalpha(c)) {
12579a747e4fSDavid du Colombier 		buf[0] = c;
12589a747e4fSDavid du Colombier 		k = 1;
12599a747e4fSDavid du Colombier 		while(1) {
12609a747e4fSDavid du Colombier 			c = getchar(ts);
12619a747e4fSDavid du Colombier 			if(c < 0)
12629a747e4fSDavid du Colombier 				break;
1263314a20f0SDavid du Colombier 			if(c < 256 && (isalpha(c) || isdigit(c))) {
1264c93608ccSDavid du Colombier 				if(k < nelem(buf)-1)
12659a747e4fSDavid du Colombier 					buf[k++] = c;
12669a747e4fSDavid du Colombier 			}
12679a747e4fSDavid du Colombier 			else {
12689a747e4fSDavid du Colombier 				if(!(c == ';' || c == '\n' || c == '\r'))
12699a747e4fSDavid du Colombier 					ungetchar(ts, c);
12709a747e4fSDavid du Colombier 				break;
12719a747e4fSDavid du Colombier 			}
12729a747e4fSDavid du Colombier 		}
1273314a20f0SDavid du Colombier 		if(c >= 256 || c != '=' && !(isalpha(c) || isdigit(c)))
12749a747e4fSDavid du Colombier 			fnd = _lookup(chartab, NCHARTAB, buf, k, &ans);
12759a747e4fSDavid du Colombier 	}
12769a747e4fSDavid du Colombier 	if(!fnd) {
12779a747e4fSDavid du Colombier 		backup(ts, savei);
12789a747e4fSDavid du Colombier 		ans = '&';
12799a747e4fSDavid du Colombier 	}
12809a747e4fSDavid du Colombier 	return ans;
12819a747e4fSDavid du Colombier }
12829a747e4fSDavid du Colombier 
12839a747e4fSDavid du Colombier // Get next char, obeying ts.chset.
12849a747e4fSDavid du Colombier // Returns -1 if no complete character left before current end of data.
12859a747e4fSDavid du Colombier static int
getchar(TokenSource * ts)12869a747e4fSDavid du Colombier getchar(TokenSource* ts)
12879a747e4fSDavid du Colombier {
12889a747e4fSDavid du Colombier 	uchar*	buf;
12899a747e4fSDavid du Colombier 	int	c;
12909a747e4fSDavid du Colombier 	int	n;
12919a747e4fSDavid du Colombier 	int	ok;
12929a747e4fSDavid du Colombier 	Rune	r;
12939a747e4fSDavid du Colombier 
12949a747e4fSDavid du Colombier 	if(ts->i >= ts->edata)
12959a747e4fSDavid du Colombier 		return -1;
12969a747e4fSDavid du Colombier 	buf = ts->data;
12979a747e4fSDavid du Colombier 	c = buf[ts->i];
12989a747e4fSDavid du Colombier 	switch(ts->chset) {
12999a747e4fSDavid du Colombier 	case ISO_8859_1:
13009a747e4fSDavid du Colombier 		if(c >= Winstart && c <= Winend)
13019a747e4fSDavid du Colombier 			c = winchars[c - Winstart];
13029a747e4fSDavid du Colombier 		ts->i++;
13039a747e4fSDavid du Colombier 		break;
13049a747e4fSDavid du Colombier 	case US_Ascii:
13059a747e4fSDavid du Colombier 		if(c > 127) {
13069a747e4fSDavid du Colombier 			if(warn)
13079a747e4fSDavid du Colombier 				fprint(2, "non-ascii char (%x) when US-ASCII specified\n", c);
13089a747e4fSDavid du Colombier 		}
13099a747e4fSDavid du Colombier 		ts->i++;
13109a747e4fSDavid du Colombier 		break;
13119a747e4fSDavid du Colombier 	case UTF_8:
13129a747e4fSDavid du Colombier 		ok = fullrune((char*)(buf+ts->i), ts->edata-ts->i);
13139a747e4fSDavid du Colombier 		n = chartorune(&r, (char*)(buf+ts->i));
13149a747e4fSDavid du Colombier 		if(ok) {
13159a747e4fSDavid du Colombier 			if(warn && c == 0x80)
13169a747e4fSDavid du Colombier 				fprint(2, "warning: invalid utf-8 sequence (starts with %x)\n", ts->data[ts->i]);
13179a747e4fSDavid du Colombier 			ts->i += n;
13189a747e4fSDavid du Colombier 			c = r;
13199a747e4fSDavid du Colombier 		}
13209a747e4fSDavid du Colombier 		else {
13219a747e4fSDavid du Colombier 			// not enough bytes in buf to complete utf-8 char
13229a747e4fSDavid du Colombier 			ts->i = ts->edata;	// mark "all used"
13239a747e4fSDavid du Colombier 			c = -1;
13249a747e4fSDavid du Colombier 		}
13259a747e4fSDavid du Colombier 		break;
13269a747e4fSDavid du Colombier 	case Unicode:
13279a747e4fSDavid du Colombier 		if(ts->i < ts->edata - 1) {
13289a747e4fSDavid du Colombier 			//standards say most-significant byte first
13299a747e4fSDavid du Colombier 			c = (c << 8)|(buf[ts->i + 1]);
13309a747e4fSDavid du Colombier 			ts->i += 2;
13319a747e4fSDavid du Colombier 		}
13329a747e4fSDavid du Colombier 		else {
13339a747e4fSDavid du Colombier 			ts->i = ts->edata;	// mark "all used"
13349a747e4fSDavid du Colombier 			c = -1;
13359a747e4fSDavid du Colombier 		}
13369a747e4fSDavid du Colombier 		break;
1337*edc15dd6SDavid du Colombier 	default:
1338*edc15dd6SDavid du Colombier 		return -1;
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
ungetchar(TokenSource * ts,int c)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
backup(TokenSource * ts,int savei)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
_tokaval(Token * t,int attid,Rune ** pans,int xfer)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
Tconv(Fmt * f)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*
newattr(int attid,Rune * value,Attr * link)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
freeattrs(Attr * ahead)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
_freetokens(Token * tarray,int n)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