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 (&). 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