xref: /plan9/sys/src/libhtml/build.c (revision 684b447ecdf07ea000d8deca0fbcf5ac24894c7f)
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 // A stack for holding integer values
99a747e4fSDavid du Colombier enum {
109a747e4fSDavid du Colombier 	Nestmax = 40	// max nesting level of lists, font styles, etc.
119a747e4fSDavid du Colombier };
129a747e4fSDavid du Colombier 
139a747e4fSDavid du Colombier struct Stack {
149a747e4fSDavid du Colombier 	int		n;				// next available slot (top of stack is stack[n-1])
159a747e4fSDavid du Colombier 	int		slots[Nestmax];	// stack entries
169a747e4fSDavid du Colombier };
179a747e4fSDavid du Colombier 
189a747e4fSDavid du Colombier // Parsing state
199a747e4fSDavid du Colombier struct Pstate
209a747e4fSDavid du Colombier {
219a747e4fSDavid du Colombier 	Pstate*	next;			// in stack of Pstates
229a747e4fSDavid du Colombier 	int		skipping;		// true when we shouldn't add items
239a747e4fSDavid du Colombier 	int		skipwhite;		// true when we should strip leading space
249a747e4fSDavid du Colombier 	int		curfont;		// font index for current font
259a747e4fSDavid du Colombier 	int		curfg;		// current foreground color
269a747e4fSDavid du Colombier 	Background	curbg;	// current background
279a747e4fSDavid du Colombier 	int		curvoff;		// current baseline offset
289a747e4fSDavid du Colombier 	uchar	curul;		// current underline/strike state
299a747e4fSDavid du Colombier 	uchar	curjust;		// current justify state
309a747e4fSDavid du Colombier 	int		curanchor;	// current (href) anchor id (if in one), or 0
319a747e4fSDavid du Colombier 	int		curstate;		// current value of item state
329a747e4fSDavid du Colombier 	int		literal;		// current literal state
339a747e4fSDavid du Colombier 	int		inpar;		// true when in a paragraph-like construct
349a747e4fSDavid du Colombier 	int		adjsize;		// current font size adjustment
359a747e4fSDavid du Colombier 	Item*	items;		// dummy head of item list we're building
369a747e4fSDavid du Colombier 	Item*	lastit;		// tail of item list we're building
379a747e4fSDavid du Colombier 	Item*	prelastit;		// item before lastit
389a747e4fSDavid du Colombier 	Stack	fntstylestk;	// style stack
399a747e4fSDavid du Colombier 	Stack	fntsizestk;		// size stack
409a747e4fSDavid du Colombier 	Stack	fgstk;		// text color stack
419a747e4fSDavid du Colombier 	Stack	ulstk;		// underline stack
429a747e4fSDavid du Colombier 	Stack	voffstk;		// vertical offset stack
439a747e4fSDavid du Colombier 	Stack	listtypestk;	// list type stack
449a747e4fSDavid du Colombier 	Stack	listcntstk;		// list counter stack
459a747e4fSDavid du Colombier 	Stack	juststk;		// justification stack
469a747e4fSDavid du Colombier 	Stack	hangstk;		// hanging stack
479a747e4fSDavid du Colombier };
489a747e4fSDavid du Colombier 
499a747e4fSDavid du Colombier struct ItemSource
509a747e4fSDavid du Colombier {
519a747e4fSDavid du Colombier 	Docinfo*		doc;
529a747e4fSDavid du Colombier 	Pstate*		psstk;
539a747e4fSDavid du Colombier 	int			nforms;
549a747e4fSDavid du Colombier 	int			ntables;
559a747e4fSDavid du Colombier 	int			nanchors;
569a747e4fSDavid du Colombier 	int			nframes;
579a747e4fSDavid du Colombier 	Form*		curform;
589a747e4fSDavid du Colombier 	Map*		curmap;
599a747e4fSDavid du Colombier 	Table*		tabstk;
609a747e4fSDavid du Colombier 	Kidinfo*		kidstk;
619a747e4fSDavid du Colombier };
629a747e4fSDavid du Colombier 
639a747e4fSDavid du Colombier // Some layout parameters
649a747e4fSDavid du Colombier enum {
659a747e4fSDavid du Colombier 	FRKIDMARGIN = 6,	// default margin around kid frames
669a747e4fSDavid du Colombier 	IMGHSPACE = 0,	// default hspace for images (0 matches IE, Netscape)
679a747e4fSDavid du Colombier 	IMGVSPACE = 0,	// default vspace for images
689a747e4fSDavid du Colombier 	FLTIMGHSPACE = 2,	// default hspace for float images
699a747e4fSDavid du Colombier 	TABSP = 5,		// default cellspacing for tables
709a747e4fSDavid du Colombier 	TABPAD = 1,		// default cell padding for tables
719a747e4fSDavid du Colombier 	LISTTAB = 1,		// number of tabs to indent lists
729a747e4fSDavid du Colombier 	BQTAB = 1,		// number of tabs to indent blockquotes
739a747e4fSDavid du Colombier 	HRSZ = 2,			// thickness of horizontal rules
749a747e4fSDavid du Colombier 	SUBOFF = 4,		// vertical offset for subscripts
759a747e4fSDavid du Colombier 	SUPOFF = 6,		// vertical offset for superscripts
769a747e4fSDavid du Colombier 	NBSP = 160		// non-breaking space character
779a747e4fSDavid du Colombier };
789a747e4fSDavid du Colombier 
799a747e4fSDavid du Colombier // These tables must be sorted
809a747e4fSDavid du Colombier static StringInt align_tab[] = {
819a747e4fSDavid du Colombier 	{L"baseline",	ALbaseline},
829a747e4fSDavid du Colombier 	{L"bottom",	ALbottom},
839a747e4fSDavid du Colombier 	{L"center",	ALcenter},
849a747e4fSDavid du Colombier 	{L"char",		ALchar},
859a747e4fSDavid du Colombier 	{L"justify",	ALjustify},
869a747e4fSDavid du Colombier 	{L"left",		ALleft},
879a747e4fSDavid du Colombier 	{L"middle",	ALmiddle},
889a747e4fSDavid du Colombier 	{L"right",		ALright},
899a747e4fSDavid du Colombier 	{L"top",		ALtop}
909a747e4fSDavid du Colombier };
919a747e4fSDavid du Colombier #define NALIGNTAB (sizeof(align_tab)/sizeof(StringInt))
929a747e4fSDavid du Colombier 
939a747e4fSDavid du Colombier static StringInt input_tab[] = {
949a747e4fSDavid du Colombier 	{L"button",	Fbutton},
959a747e4fSDavid du Colombier 	{L"checkbox",	Fcheckbox},
969a747e4fSDavid du Colombier 	{L"file",		Ffile},
979a747e4fSDavid du Colombier 	{L"hidden",	Fhidden},
989a747e4fSDavid du Colombier 	{L"image",	Fimage},
999a747e4fSDavid du Colombier 	{L"password",	Fpassword},
1009a747e4fSDavid du Colombier 	{L"radio",		Fradio},
1019a747e4fSDavid du Colombier 	{L"reset",		Freset},
1029a747e4fSDavid du Colombier 	{L"submit",	Fsubmit},
1039a747e4fSDavid du Colombier 	{L"text",		Ftext}
1049a747e4fSDavid du Colombier };
1059a747e4fSDavid du Colombier #define NINPUTTAB (sizeof(input_tab)/sizeof(StringInt))
1069a747e4fSDavid du Colombier 
1079a747e4fSDavid du Colombier static StringInt clear_tab[] = {
1089a747e4fSDavid du Colombier 	{L"all",	IFcleft|IFcright},
1099a747e4fSDavid du Colombier 	{L"left",	IFcleft},
1109a747e4fSDavid du Colombier 	{L"right",	IFcright}
1119a747e4fSDavid du Colombier };
1129a747e4fSDavid du Colombier #define NCLEARTAB (sizeof(clear_tab)/sizeof(StringInt))
1139a747e4fSDavid du Colombier 
1149a747e4fSDavid du Colombier static StringInt fscroll_tab[] = {
1159a747e4fSDavid du Colombier 	{L"auto",	FRhscrollauto|FRvscrollauto},
1169a747e4fSDavid du Colombier 	{L"no",	FRnoscroll},
1179a747e4fSDavid du Colombier 	{L"yes",	FRhscroll|FRvscroll},
1189a747e4fSDavid du Colombier };
1199a747e4fSDavid du Colombier #define NFSCROLLTAB (sizeof(fscroll_tab)/sizeof(StringInt))
1209a747e4fSDavid du Colombier 
1219a747e4fSDavid du Colombier static StringInt shape_tab[] = {
1229a747e4fSDavid du Colombier 	{L"circ",		SHcircle},
1239a747e4fSDavid du Colombier 	{L"circle",		SHcircle},
1249a747e4fSDavid du Colombier 	{L"poly",		SHpoly},
1259a747e4fSDavid du Colombier 	{L"polygon",	SHpoly},
1269a747e4fSDavid du Colombier 	{L"rect",		SHrect},
1279a747e4fSDavid du Colombier 	{L"rectangle",	SHrect}
1289a747e4fSDavid du Colombier };
1299a747e4fSDavid du Colombier #define NSHAPETAB (sizeof(shape_tab)/sizeof(StringInt))
1309a747e4fSDavid du Colombier 
1319a747e4fSDavid du Colombier static StringInt method_tab[] = {
1329a747e4fSDavid du Colombier 	{L"get",		HGet},
1339a747e4fSDavid du Colombier 	{L"post",		HPost}
1349a747e4fSDavid du Colombier };
1359a747e4fSDavid du Colombier #define NMETHODTAB (sizeof(method_tab)/sizeof(StringInt))
1369a747e4fSDavid du Colombier 
1379a747e4fSDavid du Colombier static Rune* roman[15]= {
1389a747e4fSDavid du Colombier 	L"I", L"II", L"III", L"IV", L"V", L"VI", L"VII", L"VIII", L"IX", L"X",
1399a747e4fSDavid du Colombier 	L"XI", L"XII", L"XIII", L"XIV", L"XV"
1409a747e4fSDavid du Colombier };
1419a747e4fSDavid du Colombier #define NROMAN 15
1429a747e4fSDavid du Colombier 
1439a747e4fSDavid du Colombier // List number types
1449a747e4fSDavid du Colombier enum {
1459a747e4fSDavid du Colombier 	LTdisc, LTsquare, LTcircle, LT1, LTa, LTA, LTi, LTI
1469a747e4fSDavid du Colombier };
1479a747e4fSDavid du Colombier 
1489a747e4fSDavid du Colombier enum {
1499a747e4fSDavid du Colombier 	SPBefore = 2,
1509a747e4fSDavid du Colombier 	SPAfter = 4,
1519a747e4fSDavid du Colombier 	BL = 1,
1529a747e4fSDavid du Colombier 	BLBA = (BL|SPBefore|SPAfter)
1539a747e4fSDavid du Colombier };
1549a747e4fSDavid du Colombier 
1559a747e4fSDavid du Colombier // blockbrk[tag] is break info for a block level element, or one
1569a747e4fSDavid du Colombier // of a few others that get the same treatment re ending open paragraphs
1579a747e4fSDavid du Colombier // and requiring a line break / vertical space before them.
1589a747e4fSDavid du Colombier // If we want a line of space before the given element, SPBefore is OR'd in.
1599a747e4fSDavid du Colombier // If we want a line of space after the given element, SPAfter is OR'd in.
1609a747e4fSDavid du Colombier 
1619a747e4fSDavid du Colombier static uchar blockbrk[Numtags]= {
1629a747e4fSDavid du Colombier 	[Taddress] BLBA, [Tblockquote] BLBA, [Tcenter] BL,
1639a747e4fSDavid du Colombier 	[Tdir] BLBA, [Tdiv] BL, [Tdd] BL, [Tdl] BLBA,
1649a747e4fSDavid du Colombier 	[Tdt] BL, [Tform] BLBA,
1659a747e4fSDavid du Colombier 	// headings and tables get breaks added manually
1669a747e4fSDavid du Colombier 	[Th1] BL, [Th2] BL, [Th3] BL,
1679a747e4fSDavid du Colombier 	[Th4] BL, [Th5] BL, [Th6] BL,
1689a747e4fSDavid du Colombier 	[Thr] BL, [Tisindex] BLBA, [Tli] BL, [Tmenu] BLBA,
1699a747e4fSDavid du Colombier 	[Tol] BLBA, [Tp] BLBA, [Tpre] BLBA,
1709a747e4fSDavid du Colombier 	[Tul] BLBA
1719a747e4fSDavid du Colombier };
1729a747e4fSDavid du Colombier 
1739a747e4fSDavid du Colombier enum {
1749a747e4fSDavid du Colombier 	AGEN = 1
1759a747e4fSDavid du Colombier };
1769a747e4fSDavid du Colombier 
1779a747e4fSDavid du Colombier // attrinfo is information about attributes.
1789a747e4fSDavid du Colombier // The AGEN value means that the attribute is generic (applies to almost all elements)
1799a747e4fSDavid du Colombier static uchar attrinfo[Numattrs]= {
1809a747e4fSDavid du Colombier 	[Aid] AGEN, [Aclass] AGEN, [Astyle] AGEN, [Atitle] AGEN,
1819a747e4fSDavid du Colombier 	[Aonblur] AGEN, [Aonchange] AGEN, [Aonclick] AGEN,
1829a747e4fSDavid du Colombier 	[Aondblclick] AGEN, [Aonfocus] AGEN, [Aonkeypress] AGEN,
1839a747e4fSDavid du Colombier 	[Aonkeyup] AGEN, [Aonload] AGEN, [Aonmousedown] AGEN,
1849a747e4fSDavid du Colombier 	[Aonmousemove] AGEN, [Aonmouseout] AGEN, [Aonmouseover] AGEN,
1859a747e4fSDavid du Colombier 	[Aonmouseup] AGEN, [Aonreset] AGEN, [Aonselect] AGEN,
1869a747e4fSDavid du Colombier 	[Aonsubmit] AGEN, [Aonunload] AGEN
1879a747e4fSDavid du Colombier };
1889a747e4fSDavid du Colombier 
1899a747e4fSDavid du Colombier static uchar scriptev[Numattrs]= {
1909a747e4fSDavid du Colombier 	[Aonblur] SEonblur, [Aonchange] SEonchange, [Aonclick] SEonclick,
1919a747e4fSDavid du Colombier 	[Aondblclick] SEondblclick, [Aonfocus] SEonfocus, [Aonkeypress] SEonkeypress,
1929a747e4fSDavid du Colombier 	[Aonkeyup] SEonkeyup, [Aonload] SEonload, [Aonmousedown] SEonmousedown,
1939a747e4fSDavid du Colombier 	[Aonmousemove] SEonmousemove, [Aonmouseout] SEonmouseout, [Aonmouseover] SEonmouseover,
1949a747e4fSDavid du Colombier 	[Aonmouseup] SEonmouseup, [Aonreset] SEonreset, [Aonselect] SEonselect,
1959a747e4fSDavid du Colombier 	[Aonsubmit] SEonsubmit, [Aonunload] SEonunload
1969a747e4fSDavid du Colombier };
1979a747e4fSDavid du Colombier 
1989a747e4fSDavid du Colombier // Color lookup table
1999a747e4fSDavid du Colombier static StringInt color_tab[] = {
2009a747e4fSDavid du Colombier 	{L"aqua", 0x00FFFF},
2019a747e4fSDavid du Colombier 	{L"black",  0x000000},
2029a747e4fSDavid du Colombier 	{L"blue", 0x0000CC},
2039a747e4fSDavid du Colombier 	{L"fuchsia", 0xFF00FF},
2049a747e4fSDavid du Colombier 	{L"gray", 0x808080},
2059a747e4fSDavid du Colombier 	{L"green", 0x008000},
2069a747e4fSDavid du Colombier 	{L"lime", 0x00FF00},
2079a747e4fSDavid du Colombier 	{L"maroon", 0x800000},
2089a747e4fSDavid du Colombier 	{L"navy", 0x000080,},
2099a747e4fSDavid du Colombier 	{L"olive", 0x808000},
2109a747e4fSDavid du Colombier 	{L"purple", 0x800080},
2119a747e4fSDavid du Colombier 	{L"red", 0xFF0000},
2129a747e4fSDavid du Colombier 	{L"silver", 0xC0C0C0},
2139a747e4fSDavid du Colombier 	{L"teal", 0x008080},
2149a747e4fSDavid du Colombier 	{L"white", 0xFFFFFF},
2159a747e4fSDavid du Colombier 	{L"yellow", 0xFFFF00}
2169a747e4fSDavid du Colombier };
2179a747e4fSDavid du Colombier #define NCOLORS (sizeof(color_tab)/sizeof(StringInt))
2189a747e4fSDavid du Colombier 
2199a747e4fSDavid du Colombier static StringInt 		*targetmap;
2209a747e4fSDavid du Colombier static int			targetmapsize;
2219a747e4fSDavid du Colombier static int			ntargets;
2229a747e4fSDavid du Colombier 
2239a747e4fSDavid du Colombier static int buildinited = 0;
2249a747e4fSDavid du Colombier 
2259a747e4fSDavid du Colombier #define SMALLBUFSIZE 240
2269a747e4fSDavid du Colombier #define BIGBUFSIZE 2000
2279a747e4fSDavid du Colombier 
2289a747e4fSDavid du Colombier int	dbgbuild = 0;
2299a747e4fSDavid du Colombier int	warn = 0;
2309a747e4fSDavid du Colombier 
2319a747e4fSDavid du Colombier static Align		aalign(Token* tok);
2329a747e4fSDavid du Colombier static int			acolorval(Token* tok, int attid, int dflt);
2339a747e4fSDavid du Colombier static void			addbrk(Pstate* ps, int sp, int clr);
2349a747e4fSDavid du Colombier static void			additem(Pstate* ps, Item* it, Token* tok);
2359a747e4fSDavid du Colombier static void			addlinebrk(Pstate* ps, int clr);
2369a747e4fSDavid du Colombier static void			addnbsp(Pstate* ps);
2379a747e4fSDavid du Colombier static void			addtext(Pstate* ps, Rune* s);
2389a747e4fSDavid du Colombier static Dimen		adimen(Token* tok, int attid);
2399a747e4fSDavid du Colombier static int			aflagval(Token* tok, int attid);
2409a747e4fSDavid du Colombier static int			aintval(Token* tok, int attid, int dflt);
2419a747e4fSDavid du Colombier static Rune*		astrval(Token* tok, int attid, Rune* dflt);
2429a747e4fSDavid du Colombier static int			atabval(Token* tok, int attid, StringInt* tab, int ntab, int dflt);
2439a747e4fSDavid du Colombier static int			atargval(Token* tok, int dflt);
2449a747e4fSDavid du Colombier static int			auintval(Token* tok, int attid, int dflt);
2459a747e4fSDavid du Colombier static Rune*		aurlval(Token* tok, int attid, Rune* dflt, Rune* base);
2469a747e4fSDavid du Colombier static Rune*		aval(Token* tok, int attid);
2479a747e4fSDavid du Colombier static void			buildinit(void);
2489a747e4fSDavid du Colombier static Pstate*		cell_pstate(Pstate* oldps, int ishead);
2499a747e4fSDavid du Colombier static void			changehang(Pstate* ps, int delta);
2509a747e4fSDavid du Colombier static void			changeindent(Pstate* ps, int delta);
2519a747e4fSDavid du Colombier static int			color(Rune* s, int dflt);
2529a747e4fSDavid du Colombier static void			copystack(Stack* tostk, Stack* fromstk);
2539a747e4fSDavid du Colombier static int			dimprint(char* buf, int nbuf, Dimen d);
2549a747e4fSDavid du Colombier static Pstate*		finishcell(Table* curtab, Pstate* psstk);
2559a747e4fSDavid du Colombier static void			finish_table(Table* t);
2569a747e4fSDavid du Colombier static void			freeanchor(Anchor* a);
2579a747e4fSDavid du Colombier static void			freedestanchor(DestAnchor* da);
2589a747e4fSDavid du Colombier static void			freeform(Form* f);
2599a747e4fSDavid du Colombier static void			freeformfield(Formfield* ff);
2609a747e4fSDavid du Colombier static void			freeitem(Item* it);
2619a747e4fSDavid du Colombier static void			freepstate(Pstate* p);
2629a747e4fSDavid du Colombier static void			freepstatestack(Pstate* pshead);
2639a747e4fSDavid du Colombier static void			freescriptevents(SEvent* ehead);
2649a747e4fSDavid du Colombier static void			freetable(Table* t);
2659a747e4fSDavid du Colombier static Map*		getmap(Docinfo* di, Rune* name);
2669a747e4fSDavid du Colombier static Rune*		getpcdata(Token* toks, int tokslen, int* ptoki);
2679a747e4fSDavid du Colombier static Pstate*		lastps(Pstate* psl);
2689a747e4fSDavid du Colombier static Rune*		listmark(uchar ty, int n);
2699a747e4fSDavid du Colombier static int			listtyval(Token* tok, int dflt);
2709a747e4fSDavid du Colombier static Align		makealign(int halign, int valign);
2719a747e4fSDavid du Colombier static Background	makebackground(Rune* imgurl, int color);
2729a747e4fSDavid du Colombier static Dimen		makedimen(int kind, int spec);
2739a747e4fSDavid du Colombier static Anchor*		newanchor(int index, Rune* name, Rune* href, int target, Anchor* link);
2749a747e4fSDavid du Colombier static Area*		newarea(int shape, Rune* href, int target, Area* link);
2759a747e4fSDavid du Colombier static DestAnchor*	newdestanchor(int index, Rune* name, Item* item, DestAnchor* link);
2769a747e4fSDavid du Colombier static Docinfo*		newdocinfo(void);
2779a747e4fSDavid du Colombier static Genattr*		newgenattr(Rune* id, Rune* class, Rune* style, Rune* title, Attr* events);
2789a747e4fSDavid du Colombier static Form*		newform(int formid, Rune* name, Rune* action,
2799a747e4fSDavid du Colombier 					int target, int method, Form* link);
2809a747e4fSDavid du Colombier static Formfield*	newformfield(int ftype, int fieldid, Form* form, Rune* name,
2819a747e4fSDavid du Colombier 					Rune* value, int size, int maxlength, Formfield* link);
2829a747e4fSDavid du Colombier static Item*		newifloat(Item* it, int side);
2839a747e4fSDavid du Colombier static Item*		newiformfield(Formfield* ff);
2849a747e4fSDavid du Colombier static Item*		newiimage(Rune* src, Rune* altrep, int align, int width, int height,
2859a747e4fSDavid du Colombier 					int hspace, int vspace, int border, int ismap, Map* map);
286*684b447eSDavid du Colombier static Item*		newirule(int align, int size, int noshade, int color, Dimen wspec);
2879a747e4fSDavid du Colombier static Item*		newispacer(int spkind);
2889a747e4fSDavid du Colombier static Item*		newitable(Table* t);
2899a747e4fSDavid du Colombier static ItemSource*	newitemsource(Docinfo* di);
2909a747e4fSDavid du Colombier static Item*		newitext(Rune* s, int fnt, int fg, int voff, int ul);
2919a747e4fSDavid du Colombier static Kidinfo*		newkidinfo(int isframeset, Kidinfo* link);
2929a747e4fSDavid du Colombier static Option*		newoption(int selected, Rune* value, Rune* display, Option* link);
2939a747e4fSDavid du Colombier static Pstate*		newpstate(Pstate* link);
2945d459b5aSDavid du Colombier static SEvent*		newscriptevent(int type, Rune* script, SEvent* link);
2959a747e4fSDavid du Colombier static Table*		newtable(int tableid, Align align, Dimen width, int border,
2969a747e4fSDavid du Colombier 					int cellspacing, int cellpadding, Background bg, Token* tok, Table* link);
2979a747e4fSDavid du Colombier static Tablecell*	newtablecell(int cellid, int rowspan, int colspan, Align align, Dimen wspec,
2989a747e4fSDavid du Colombier 					int hspec, Background bg, int flags, Tablecell* link);
2999a747e4fSDavid du Colombier static Tablerow*	newtablerow(Align align, Background bg, int flags, Tablerow* link);
3009a747e4fSDavid du Colombier static Dimen		parsedim(Rune* s, int ns);
3019a747e4fSDavid du Colombier static void			pop(Stack* stk);
3029a747e4fSDavid du Colombier static void			popfontsize(Pstate* ps);
3039a747e4fSDavid du Colombier static void			popfontstyle(Pstate* ps);
3049a747e4fSDavid du Colombier static void			popjust(Pstate* ps);
3059a747e4fSDavid du Colombier static int			popretnewtop(Stack* stk, int dflt);
3069a747e4fSDavid du Colombier static int			push(Stack* stk, int val);
3079a747e4fSDavid du Colombier static void			pushfontsize(Pstate* ps, int sz);
3089a747e4fSDavid du Colombier static void			pushfontstyle(Pstate* ps, int sty);
3099a747e4fSDavid du Colombier static void			pushjust(Pstate* ps, int j);
3109a747e4fSDavid du Colombier static Item*		textit(Pstate* ps, Rune* s);
3119a747e4fSDavid du Colombier static Rune*		removeallwhite(Rune* s);
3129a747e4fSDavid du Colombier static void			resetdocinfo(Docinfo* d);
3139a747e4fSDavid du Colombier static void			setcurfont(Pstate* ps);
3149a747e4fSDavid du Colombier static void			setcurjust(Pstate* ps);
3159a747e4fSDavid du Colombier static void			setdimarray(Token* tok, int attid, Dimen** pans, int* panslen);
3169a747e4fSDavid du Colombier static Rune*		stringalign(int a);
3179a747e4fSDavid du Colombier static void			targetmapinit(void);
3189a747e4fSDavid du Colombier static int			toint(Rune* s);
3199a747e4fSDavid du Colombier static int			top(Stack* stk, int dflt);
3209a747e4fSDavid du Colombier static void			trim_cell(Tablecell* c);
3219a747e4fSDavid du Colombier static int			validalign(Align a);
3229a747e4fSDavid du Colombier static int			validdimen(Dimen d);
3239a747e4fSDavid du Colombier static int			validformfield(Formfield* f);
3249a747e4fSDavid du Colombier static int			validhalign(int a);
3259a747e4fSDavid du Colombier static int			validptr(void* p);
3269a747e4fSDavid du Colombier static int			validStr(Rune* s);
3279a747e4fSDavid du Colombier static int			validtable(Table* t);
3289a747e4fSDavid du Colombier static int			validtablerow(Tablerow* r);
3299a747e4fSDavid du Colombier static int			validtablecol(Tablecol* c);
3309a747e4fSDavid du Colombier static int			validtablecell(Tablecell* c);
3319a747e4fSDavid du Colombier static int			validvalign(int a);
3329a747e4fSDavid du Colombier static int			Iconv(Fmt *f);
3339a747e4fSDavid du Colombier 
3349a747e4fSDavid du Colombier static void
buildinit(void)3359a747e4fSDavid du Colombier buildinit(void)
3369a747e4fSDavid du Colombier {
3379a747e4fSDavid du Colombier 	fmtinstall('I', Iconv);
3389a747e4fSDavid du Colombier 	targetmapinit();
3399a747e4fSDavid du Colombier 	buildinited = 1;
3409a747e4fSDavid du Colombier }
3419a747e4fSDavid du Colombier 
3429a747e4fSDavid du Colombier static ItemSource*
newitemsource(Docinfo * di)3439a747e4fSDavid du Colombier newitemsource(Docinfo* di)
3449a747e4fSDavid du Colombier {
3459a747e4fSDavid du Colombier 	ItemSource*	is;
3469a747e4fSDavid du Colombier 	Pstate*	ps;
3479a747e4fSDavid du Colombier 
3489a747e4fSDavid du Colombier 	ps = newpstate(nil);
3499a747e4fSDavid du Colombier 	if(di->mediatype != TextHtml) {
3509a747e4fSDavid du Colombier 		ps->curstate &= ~IFwrap;
3519a747e4fSDavid du Colombier 		ps->literal = 1;
3529a747e4fSDavid du Colombier 		pushfontstyle(ps, FntT);
3539a747e4fSDavid du Colombier 	}
3549a747e4fSDavid du Colombier 	is = (ItemSource*)emalloc(sizeof(ItemSource));
3559a747e4fSDavid du Colombier 	is->doc = di;
3569a747e4fSDavid du Colombier 	is->psstk = ps;
3579a747e4fSDavid du Colombier 	is->nforms = 0;
3589a747e4fSDavid du Colombier 	is->ntables = 0;
3599a747e4fSDavid du Colombier 	is->nanchors = 0;
3609a747e4fSDavid du Colombier 	is->nframes = 0;
3619a747e4fSDavid du Colombier 	is->curform = nil;
3629a747e4fSDavid du Colombier 	is->curmap = nil;
3639a747e4fSDavid du Colombier 	is->tabstk = nil;
3649a747e4fSDavid du Colombier 	is->kidstk = nil;
3659a747e4fSDavid du Colombier 	return is;
3669a747e4fSDavid du Colombier }
3679a747e4fSDavid du Colombier 
3689a747e4fSDavid du Colombier static Item *getitems(ItemSource* is, uchar* data, int datalen);
3699a747e4fSDavid du Colombier 
3709a747e4fSDavid du Colombier // Parse an html document and create a list of layout items.
3719a747e4fSDavid du Colombier // Allocate and return document info in *pdi.
3729a747e4fSDavid du Colombier // When caller is done with the items, it should call
3739a747e4fSDavid du Colombier // freeitems on the returned result, and then
3749a747e4fSDavid du Colombier // freedocinfo(*pdi).
3759a747e4fSDavid du Colombier Item*
parsehtml(uchar * data,int datalen,Rune * pagesrc,int mtype,int chset,Docinfo ** pdi)3769a747e4fSDavid du Colombier parsehtml(uchar* data, int datalen, Rune* pagesrc, int mtype, int chset, Docinfo** pdi)
3779a747e4fSDavid du Colombier {
3789a747e4fSDavid du Colombier 	Item *it;
3799a747e4fSDavid du Colombier 	Docinfo*	di;
3809a747e4fSDavid du Colombier 	ItemSource*	is;
3819a747e4fSDavid du Colombier 
3829a747e4fSDavid du Colombier 	di = newdocinfo();
3839a747e4fSDavid du Colombier 	di->src = _Strdup(pagesrc);
3849a747e4fSDavid du Colombier 	di->base = _Strdup(pagesrc);
3859a747e4fSDavid du Colombier 	di->mediatype = mtype;
3869a747e4fSDavid du Colombier 	di->chset = chset;
3879a747e4fSDavid du Colombier 	*pdi = di;
3889a747e4fSDavid du Colombier 	is = newitemsource(di);
3899a747e4fSDavid du Colombier 	it = getitems(is, data, datalen);
3909a747e4fSDavid du Colombier 	freepstatestack(is->psstk);
3919a747e4fSDavid du Colombier 	free(is);
3929a747e4fSDavid du Colombier 	return it;
3939a747e4fSDavid du Colombier }
3949a747e4fSDavid du Colombier 
3959a747e4fSDavid du Colombier // Get a group of tokens for lexer, parse them, and create
3969a747e4fSDavid du Colombier // a list of layout items.
3979a747e4fSDavid du Colombier // When caller is done with the items, it should call
3989a747e4fSDavid du Colombier // freeitems on the returned result.
3999a747e4fSDavid du Colombier static Item*
getitems(ItemSource * is,uchar * data,int datalen)4009a747e4fSDavid du Colombier getitems(ItemSource* is, uchar* data, int datalen)
4019a747e4fSDavid du Colombier {
4029a747e4fSDavid du Colombier 	int	i;
4039a747e4fSDavid du Colombier 	int	j;
4049a747e4fSDavid du Colombier 	int	nt;
4059a747e4fSDavid du Colombier 	int	pt;
4069a747e4fSDavid du Colombier 	int	doscripts;
4079a747e4fSDavid du Colombier 	int	tokslen;
4089a747e4fSDavid du Colombier 	int	toki;
4099a747e4fSDavid du Colombier 	int	h;
4109a747e4fSDavid du Colombier 	int	sz;
4119a747e4fSDavid du Colombier 	int	method;
4129a747e4fSDavid du Colombier 	int	n;
4139a747e4fSDavid du Colombier 	int	nblank;
4149a747e4fSDavid du Colombier 	int	norsz;
4159a747e4fSDavid du Colombier 	int	bramt;
4169a747e4fSDavid du Colombier 	int	sty;
4179a747e4fSDavid du Colombier 	int	nosh;
418*684b447eSDavid du Colombier 	int	color;
4199a747e4fSDavid du Colombier 	int	oldcuranchor;
4209a747e4fSDavid du Colombier 	int	dfltbd;
4219a747e4fSDavid du Colombier 	int	v;
4229a747e4fSDavid du Colombier 	int	hang;
4239a747e4fSDavid du Colombier 	int	isempty;
4249a747e4fSDavid du Colombier 	int	tag;
4259a747e4fSDavid du Colombier 	int	brksp;
4269a747e4fSDavid du Colombier 	int	target;
4279a747e4fSDavid du Colombier 	uchar	brk;
4289a747e4fSDavid du Colombier 	uchar	flags;
4299a747e4fSDavid du Colombier 	uchar	align;
4309a747e4fSDavid du Colombier 	uchar	al;
4319a747e4fSDavid du Colombier 	uchar	ty;
4329a747e4fSDavid du Colombier 	uchar	ty2;
4339a747e4fSDavid du Colombier 	Pstate*	ps;
4349a747e4fSDavid du Colombier 	Pstate*	nextps;
4359a747e4fSDavid du Colombier 	Pstate*	outerps;
4369a747e4fSDavid du Colombier 	Table*	curtab;
4379a747e4fSDavid du Colombier 	Token*	tok;
4389a747e4fSDavid du Colombier 	Token*	toks;
4399a747e4fSDavid du Colombier 	Docinfo*	di;
4409a747e4fSDavid du Colombier 	Item*	ans;
4419a747e4fSDavid du Colombier 	Item*	img;
4429a747e4fSDavid du Colombier 	Item*	ffit;
4439a747e4fSDavid du Colombier 	Item*	tabitem;
4449a747e4fSDavid du Colombier 	Rune*	s;
4459a747e4fSDavid du Colombier 	Rune*	t;
4469a747e4fSDavid du Colombier 	Rune*	name;
4479a747e4fSDavid du Colombier 	Rune*	enctype;
4489a747e4fSDavid du Colombier 	Rune*	usemap;
4499a747e4fSDavid du Colombier 	Rune*	prompt;
4509a747e4fSDavid du Colombier 	Rune*	equiv;
4519a747e4fSDavid du Colombier 	Rune*	val;
4529a747e4fSDavid du Colombier 	Rune*	nsz;
4539a747e4fSDavid du Colombier 	Rune*	script;
4549a747e4fSDavid du Colombier 	Map*	map;
4559a747e4fSDavid du Colombier 	Form*	frm;
4569a747e4fSDavid du Colombier 	Iimage*	ii;
4579a747e4fSDavid du Colombier 	Kidinfo*	kd;
4589a747e4fSDavid du Colombier 	Kidinfo*	ks;
4599a747e4fSDavid du Colombier 	Kidinfo*	pks;
4609a747e4fSDavid du Colombier 	Dimen	wd;
4619a747e4fSDavid du Colombier 	Option*	option;
4629a747e4fSDavid du Colombier 	Table*	tab;
4639a747e4fSDavid du Colombier 	Tablecell*	c;
4649a747e4fSDavid du Colombier 	Tablerow*	tr;
4659a747e4fSDavid du Colombier 	Formfield*	field;
4669a747e4fSDavid du Colombier 	Formfield*	ff;
4679a747e4fSDavid du Colombier 	Rune*	href;
4689a747e4fSDavid du Colombier 	Rune*	src;
4699a747e4fSDavid du Colombier 	Rune*	scriptsrc;
4709a747e4fSDavid du Colombier 	Rune*	bgurl;
4719a747e4fSDavid du Colombier 	Rune*	action;
4729a747e4fSDavid du Colombier 	Background	bg;
4739a747e4fSDavid du Colombier 
4749a747e4fSDavid du Colombier 	if(!buildinited)
4759a747e4fSDavid du Colombier 		buildinit();
4769a747e4fSDavid du Colombier 	doscripts = 0;	// for now
4779a747e4fSDavid du Colombier 	ps = is->psstk;
4789a747e4fSDavid du Colombier 	curtab = is->tabstk;
4799a747e4fSDavid du Colombier 	di = is->doc;
4809a747e4fSDavid du Colombier 	toks = _gettoks(data, datalen, di->chset, di->mediatype, &tokslen);
4819a747e4fSDavid du Colombier 	toki = 0;
4829a747e4fSDavid du Colombier 	for(; toki < tokslen; toki++) {
4839a747e4fSDavid du Colombier 		tok = &toks[toki];
4849a747e4fSDavid du Colombier 		if(dbgbuild > 1)
4859a747e4fSDavid du Colombier 			fprint(2, "build: curstate %ux, token %T\n", ps->curstate, tok);
4869a747e4fSDavid du Colombier 		tag = tok->tag;
4879a747e4fSDavid du Colombier 		brk = 0;
4889a747e4fSDavid du Colombier 		brksp = 0;
4899a747e4fSDavid du Colombier 		if(tag < Numtags) {
4909a747e4fSDavid du Colombier 			brk = blockbrk[tag];
4919a747e4fSDavid du Colombier 			if(brk&SPBefore)
4929a747e4fSDavid du Colombier 				brksp = 1;
4939a747e4fSDavid du Colombier 		}
4949a747e4fSDavid du Colombier 		else if(tag < Numtags + RBRA) {
4959a747e4fSDavid du Colombier 			brk = blockbrk[tag - RBRA];
4969a747e4fSDavid du Colombier 			if(brk&SPAfter)
4979a747e4fSDavid du Colombier 				brksp = 1;
4989a747e4fSDavid du Colombier 		}
4999a747e4fSDavid du Colombier 		if(brk) {
5009a747e4fSDavid du Colombier 			addbrk(ps, brksp, 0);
5019a747e4fSDavid du Colombier 			if(ps->inpar) {
5029a747e4fSDavid du Colombier 				popjust(ps);
5039a747e4fSDavid du Colombier 				ps->inpar = 0;
5049a747e4fSDavid du Colombier 			}
5059a747e4fSDavid du Colombier 		}
5069a747e4fSDavid du Colombier 		// check common case first (Data), then switch statement on tag
5079a747e4fSDavid du Colombier 		if(tag == Data) {
5089a747e4fSDavid du Colombier 			// Lexing didn't pay attention to SGML record boundary rules:
5099a747e4fSDavid du Colombier 			// \n after start tag or before end tag to be discarded.
5109a747e4fSDavid du Colombier 			// (Lex has already discarded all \r's).
5119a747e4fSDavid du Colombier 			// Some pages assume this doesn't happen in <PRE> text,
5129a747e4fSDavid du Colombier 			// so we won't do it if literal is true.
5139a747e4fSDavid du Colombier 			// BUG: won't discard \n before a start tag that begins
5149a747e4fSDavid du Colombier 			// the next bufferful of tokens.
5159a747e4fSDavid du Colombier 			s = tok->text;
5165d459b5aSDavid du Colombier 			n = _Strlen(s);
5179a747e4fSDavid du Colombier 			if(!ps->literal) {
5189a747e4fSDavid du Colombier 				i = 0;
5199a747e4fSDavid du Colombier 				j = n;
5209a747e4fSDavid du Colombier 				if(toki > 0) {
5219a747e4fSDavid du Colombier 					pt = toks[toki - 1].tag;
5229a747e4fSDavid du Colombier 					// IE and Netscape both ignore this rule (contrary to spec)
5239a747e4fSDavid du Colombier 					// if previous tag was img
5249a747e4fSDavid du Colombier 					if(pt < Numtags && pt != Timg && j > 0 && s[0] == '\n')
5259a747e4fSDavid du Colombier 						i++;
5269a747e4fSDavid du Colombier 				}
5279a747e4fSDavid du Colombier 				if(toki < tokslen - 1) {
5289a747e4fSDavid du Colombier 					nt = toks[toki + 1].tag;
5299a747e4fSDavid du Colombier 					if(nt >= RBRA && nt < Numtags + RBRA && j > i && s[j - 1] == '\n')
5309a747e4fSDavid du Colombier 						j--;
5319a747e4fSDavid du Colombier 				}
5329a747e4fSDavid du Colombier 				if(i > 0 || j < n) {
5339a747e4fSDavid du Colombier 					t = s;
5349a747e4fSDavid du Colombier 					s = _Strsubstr(s, i, j);
5359a747e4fSDavid du Colombier 					free(t);
5369a747e4fSDavid du Colombier 					n = j-i;
5379a747e4fSDavid du Colombier 				}
5389a747e4fSDavid du Colombier 			}
5399a747e4fSDavid du Colombier 			if(ps->skipwhite) {
5409a747e4fSDavid du Colombier 				_trimwhite(s, n, &t, &nt);
5419a747e4fSDavid du Colombier 				if(t == nil) {
5429a747e4fSDavid du Colombier 					free(s);
5439a747e4fSDavid du Colombier 					s = nil;
5449a747e4fSDavid du Colombier 				}
5459a747e4fSDavid du Colombier 				else if(t != s) {
5469a747e4fSDavid du Colombier 					t = _Strndup(t, nt);
5479a747e4fSDavid du Colombier 					free(s);
5489a747e4fSDavid du Colombier 					s = t;
5499a747e4fSDavid du Colombier 				}
5509a747e4fSDavid du Colombier 				if(s != nil)
5519a747e4fSDavid du Colombier 					ps->skipwhite = 0;
5529a747e4fSDavid du Colombier 			}
5539a747e4fSDavid du Colombier 			tok->text = nil;		// token doesn't own string anymore
5549a747e4fSDavid du Colombier 			if(s != nil)
5559a747e4fSDavid du Colombier 				addtext(ps, s);
5569a747e4fSDavid du Colombier 		}
5579a747e4fSDavid du Colombier 		else
5589a747e4fSDavid du Colombier 			switch(tag) {
5599a747e4fSDavid du Colombier 			// Some abbrevs used in following DTD comments
5609a747e4fSDavid du Colombier 			// %text = 	#PCDATA
5619a747e4fSDavid du Colombier 			//		| TT | I | B | U | STRIKE | BIG | SMALL | SUB | SUP
5629a747e4fSDavid du Colombier 			//		| EM | STRONG | DFN | CODE | SAMP | KBD | VAR | CITE
5639a747e4fSDavid du Colombier 			//		| A | IMG | APPLET | FONT | BASEFONT | BR | SCRIPT | MAP
5649a747e4fSDavid du Colombier 			//		| INPUT | SELECT | TEXTAREA
5659a747e4fSDavid du Colombier 			// %block = P | UL | OL | DIR | MENU | DL | PRE | DL | DIV | CENTER
5669a747e4fSDavid du Colombier 			//		| BLOCKQUOTE | FORM | ISINDEX | HR | TABLE
5679a747e4fSDavid du Colombier 			// %flow = (%text | %block)*
5689a747e4fSDavid du Colombier 			// %body.content = (%heading | %text | %block | ADDRESS)*
5699a747e4fSDavid du Colombier 
5709a747e4fSDavid du Colombier 			// <!ELEMENT A - - (%text) -(A)>
5719a747e4fSDavid du Colombier 			// Anchors are not supposed to be nested, but you sometimes see
5729a747e4fSDavid du Colombier 			// href anchors inside destination anchors.
5739a747e4fSDavid du Colombier 			case Ta:
5749a747e4fSDavid du Colombier 				if(ps->curanchor != 0) {
5759a747e4fSDavid du Colombier 					if(warn)
5769a747e4fSDavid du Colombier 						fprint(2, "warning: nested <A> or missing </A>\n");
5779a747e4fSDavid du Colombier 					ps->curanchor = 0;
5789a747e4fSDavid du Colombier 				}
5799a747e4fSDavid du Colombier 				name = aval(tok, Aname);
5809a747e4fSDavid du Colombier 				href = aurlval(tok, Ahref, nil, di->base);
5819a747e4fSDavid du Colombier 				// ignore rel, rev, and title attrs
5829a747e4fSDavid du Colombier 				if(href != nil) {
5839a747e4fSDavid du Colombier 					target = atargval(tok, di->target);
5849a747e4fSDavid du Colombier 					di->anchors = newanchor(++is->nanchors, name, href, target, di->anchors);
5859a747e4fSDavid du Colombier 					if(name != nil)
5869a747e4fSDavid du Colombier 						name = _Strdup(name);	// for DestAnchor construction, below
5879a747e4fSDavid du Colombier 					ps->curanchor = is->nanchors;
5889a747e4fSDavid du Colombier 					ps->curfg = push(&ps->fgstk, di->link);
5899a747e4fSDavid du Colombier 					ps->curul = push(&ps->ulstk, ULunder);
5909a747e4fSDavid du Colombier 				}
5919a747e4fSDavid du Colombier 				if(name != nil) {
5929a747e4fSDavid du Colombier 					// add a null item to be destination
5939a747e4fSDavid du Colombier 					additem(ps, newispacer(ISPnull), tok);
5949a747e4fSDavid du Colombier 					di->dests = newdestanchor(++is->nanchors, name, ps->lastit, di->dests);
5959a747e4fSDavid du Colombier 				}
5969a747e4fSDavid du Colombier 				break;
5979a747e4fSDavid du Colombier 
5989a747e4fSDavid du Colombier 			case Ta+RBRA :
5999a747e4fSDavid du Colombier 				if(ps->curanchor != 0) {
6009a747e4fSDavid du Colombier 					ps->curfg = popretnewtop(&ps->fgstk, di->text);
6019a747e4fSDavid du Colombier 					ps->curul = popretnewtop(&ps->ulstk, ULnone);
6029a747e4fSDavid du Colombier 					ps->curanchor = 0;
6039a747e4fSDavid du Colombier 				}
6049a747e4fSDavid du Colombier 				break;
6059a747e4fSDavid du Colombier 
6069a747e4fSDavid du Colombier 			// <!ELEMENT APPLET - - (PARAM | %text)* >
6079a747e4fSDavid du Colombier 			// We can't do applets, so ignore PARAMS, and let
6089a747e4fSDavid du Colombier 			// the %text contents appear for the alternative rep
6099a747e4fSDavid du Colombier 			case Tapplet:
6109a747e4fSDavid du Colombier 			case Tapplet+RBRA:
6119a747e4fSDavid du Colombier 				if(warn && tag == Tapplet)
6129a747e4fSDavid du Colombier 					fprint(2, "warning: <APPLET> ignored\n");
6139a747e4fSDavid du Colombier 				break;
6149a747e4fSDavid du Colombier 
6159a747e4fSDavid du Colombier 			// <!ELEMENT AREA - O EMPTY>
6169a747e4fSDavid du Colombier 			case Tarea:
6179a747e4fSDavid du Colombier 				map = di->maps;
6189a747e4fSDavid du Colombier 				if(map == nil) {
6199a747e4fSDavid du Colombier 					if(warn)
6209a747e4fSDavid du Colombier 						fprint(2, "warning: <AREA> not inside <MAP>\n");
6219a747e4fSDavid du Colombier 					continue;
6229a747e4fSDavid du Colombier 				}
6239a747e4fSDavid du Colombier 				map->areas = newarea(atabval(tok, Ashape, shape_tab, NSHAPETAB, SHrect),
6249a747e4fSDavid du Colombier 					aurlval(tok, Ahref, nil, di->base),
6259a747e4fSDavid du Colombier 					atargval(tok, di->target),
6269a747e4fSDavid du Colombier 					map->areas);
6279a747e4fSDavid du Colombier 				setdimarray(tok, Acoords, &map->areas->coords, &map->areas->ncoords);
6289a747e4fSDavid du Colombier 				break;
6299a747e4fSDavid du Colombier 
6309a747e4fSDavid du Colombier 			// <!ELEMENT (B|STRONG) - - (%text)*>
6319a747e4fSDavid du Colombier 			case Tb:
6329a747e4fSDavid du Colombier 			case Tstrong:
6339a747e4fSDavid du Colombier 				pushfontstyle(ps, FntB);
6349a747e4fSDavid du Colombier 				break;
6359a747e4fSDavid du Colombier 
6369a747e4fSDavid du Colombier 			case Tb+RBRA:
6379a747e4fSDavid du Colombier 			case Tcite+RBRA:
6389a747e4fSDavid du Colombier 			case Tcode+RBRA:
6399a747e4fSDavid du Colombier 			case Tdfn+RBRA:
6409a747e4fSDavid du Colombier 			case Tem+RBRA:
6419a747e4fSDavid du Colombier 			case Tkbd+RBRA:
6429a747e4fSDavid du Colombier 			case Ti+RBRA:
6439a747e4fSDavid du Colombier 			case Tsamp+RBRA:
6449a747e4fSDavid du Colombier 			case Tstrong+RBRA:
6459a747e4fSDavid du Colombier 			case Ttt+RBRA:
6469a747e4fSDavid du Colombier 			case Tvar+RBRA :
6479a747e4fSDavid du Colombier 			case Taddress+RBRA:
6489a747e4fSDavid du Colombier 				popfontstyle(ps);
6499a747e4fSDavid du Colombier 				break;
6509a747e4fSDavid du Colombier 
6519a747e4fSDavid du Colombier 			// <!ELEMENT BASE - O EMPTY>
6529a747e4fSDavid du Colombier 			case Tbase:
6539a747e4fSDavid du Colombier 				t = di->base;
6549a747e4fSDavid du Colombier 				di->base = aurlval(tok, Ahref, di->base, di->base);
6559a747e4fSDavid du Colombier 				if(t != nil)
6569a747e4fSDavid du Colombier 					free(t);
6579a747e4fSDavid du Colombier 				di->target = atargval(tok, di->target);
6589a747e4fSDavid du Colombier 				break;
6599a747e4fSDavid du Colombier 
6609a747e4fSDavid du Colombier 			// <!ELEMENT BASEFONT - O EMPTY>
6619a747e4fSDavid du Colombier 			case Tbasefont:
6629a747e4fSDavid du Colombier 				ps->adjsize = aintval(tok, Asize, 3) - 3;
6639a747e4fSDavid du Colombier 				break;
6649a747e4fSDavid du Colombier 
6659a747e4fSDavid du Colombier 			// <!ELEMENT (BIG|SMALL) - - (%text)*>
6669a747e4fSDavid du Colombier 			case Tbig:
6679a747e4fSDavid du Colombier 			case Tsmall:
6689a747e4fSDavid du Colombier 				sz = ps->adjsize;
6699a747e4fSDavid du Colombier 				if(tag == Tbig)
6709a747e4fSDavid du Colombier 					sz += Large;
6719a747e4fSDavid du Colombier 				else
6729a747e4fSDavid du Colombier 					sz += Small;
6739a747e4fSDavid du Colombier 				pushfontsize(ps, sz);
6749a747e4fSDavid du Colombier 				break;
6759a747e4fSDavid du Colombier 
6769a747e4fSDavid du Colombier 			case Tbig+RBRA:
6779a747e4fSDavid du Colombier 			case Tsmall+RBRA:
6789a747e4fSDavid du Colombier 				popfontsize(ps);
6799a747e4fSDavid du Colombier 				break;
6809a747e4fSDavid du Colombier 
6819a747e4fSDavid du Colombier 			// <!ELEMENT BLOCKQUOTE - - %body.content>
6829a747e4fSDavid du Colombier 			case Tblockquote:
6839a747e4fSDavid du Colombier 				changeindent(ps, BQTAB);
6849a747e4fSDavid du Colombier 				break;
6859a747e4fSDavid du Colombier 
6869a747e4fSDavid du Colombier 			case Tblockquote+RBRA:
6879a747e4fSDavid du Colombier 				changeindent(ps, -BQTAB);
6889a747e4fSDavid du Colombier 				break;
6899a747e4fSDavid du Colombier 
6909a747e4fSDavid du Colombier 			// <!ELEMENT BODY O O %body.content>
6919a747e4fSDavid du Colombier 			case Tbody:
6929a747e4fSDavid du Colombier 				ps->skipping = 0;
6939a747e4fSDavid du Colombier 				bg = makebackground(nil, acolorval(tok, Abgcolor, di->background.color));
6949a747e4fSDavid du Colombier 				bgurl = aurlval(tok, Abackground, nil, di->base);
6959a747e4fSDavid du Colombier 				if(bgurl != nil) {
6969a747e4fSDavid du Colombier 					if(di->backgrounditem != nil)
6979a747e4fSDavid du Colombier 						freeitem((Item*)di->backgrounditem);
6989a747e4fSDavid du Colombier 						// really should remove old item from di->images list,
6999a747e4fSDavid du Colombier 						// but there should only be one BODY element ...
7009a747e4fSDavid du Colombier 					di->backgrounditem = (Iimage*)newiimage(bgurl, nil, ALnone, 0, 0, 0, 0, 0, 0, nil);
7019a747e4fSDavid du Colombier 					di->backgrounditem->nextimage = di->images;
7029a747e4fSDavid du Colombier 					di->images = di->backgrounditem;
7039a747e4fSDavid du Colombier 				}
7049a747e4fSDavid du Colombier 				ps->curbg = bg;
7059a747e4fSDavid du Colombier 				di->background = bg;
7069a747e4fSDavid du Colombier 				di->text = acolorval(tok, Atext, di->text);
7079a747e4fSDavid du Colombier 				di->link = acolorval(tok, Alink, di->link);
7089a747e4fSDavid du Colombier 				di->vlink = acolorval(tok, Avlink, di->vlink);
7099a747e4fSDavid du Colombier 				di->alink = acolorval(tok, Aalink, di->alink);
7109a747e4fSDavid du Colombier 				if(di->text != ps->curfg) {
7119a747e4fSDavid du Colombier 					ps->curfg = di->text;
7129a747e4fSDavid du Colombier 					ps->fgstk.n = 0;
7139a747e4fSDavid du Colombier 				}
7149a747e4fSDavid du Colombier 				break;
7159a747e4fSDavid du Colombier 
7169a747e4fSDavid du Colombier 			case Tbody+RBRA:
7179a747e4fSDavid du Colombier 				// HTML spec says ignore things after </body>,
7189a747e4fSDavid du Colombier 				// but IE and Netscape don't
7199a747e4fSDavid du Colombier 				// ps.skipping = 1;
7209a747e4fSDavid du Colombier 				break;
7219a747e4fSDavid du Colombier 
7229a747e4fSDavid du Colombier 			// <!ELEMENT BR - O EMPTY>
7239a747e4fSDavid du Colombier 			case Tbr:
7249a747e4fSDavid du Colombier 				addlinebrk(ps, atabval(tok, Aclear, clear_tab, NCLEARTAB, 0));
7259a747e4fSDavid du Colombier 				break;
7269a747e4fSDavid du Colombier 
7279a747e4fSDavid du Colombier 			// <!ELEMENT CAPTION - - (%text;)*>
7289a747e4fSDavid du Colombier 			case Tcaption:
7299a747e4fSDavid du Colombier 				if(curtab == nil) {
7309a747e4fSDavid du Colombier 					if(warn)
7319a747e4fSDavid du Colombier 						fprint(2, "warning: <CAPTION> outside <TABLE>\n");
7329a747e4fSDavid du Colombier 					continue;
7339a747e4fSDavid du Colombier 				}
7349a747e4fSDavid du Colombier 				if(curtab->caption != nil) {
7359a747e4fSDavid du Colombier 					if(warn)
7369a747e4fSDavid du Colombier 						fprint(2, "warning: more than one <CAPTION> in <TABLE>\n");
7379a747e4fSDavid du Colombier 					continue;
7389a747e4fSDavid du Colombier 				}
7399a747e4fSDavid du Colombier 				ps = newpstate(ps);
7409a747e4fSDavid du Colombier 				curtab->caption_place = atabval(tok, Aalign, align_tab, NALIGNTAB, ALtop);
7419a747e4fSDavid du Colombier 				break;
7429a747e4fSDavid du Colombier 
7439a747e4fSDavid du Colombier 			case Tcaption+RBRA:
7449a747e4fSDavid du Colombier 				nextps = ps->next;
7459a747e4fSDavid du Colombier 				if(curtab == nil || nextps == nil) {
7469a747e4fSDavid du Colombier 					if(warn)
7479a747e4fSDavid du Colombier 						fprint(2, "warning: unexpected </CAPTION>\n");
7489a747e4fSDavid du Colombier 					continue;
7499a747e4fSDavid du Colombier 				}
7509a747e4fSDavid du Colombier 				curtab->caption = ps->items->next;
7519a747e4fSDavid du Colombier 				free(ps);
7529a747e4fSDavid du Colombier 				ps = nextps;
7539a747e4fSDavid du Colombier 				break;
7549a747e4fSDavid du Colombier 
7559a747e4fSDavid du Colombier 			case Tcenter:
7569a747e4fSDavid du Colombier 			case Tdiv:
7579a747e4fSDavid du Colombier 				if(tag == Tcenter)
7589a747e4fSDavid du Colombier 					al = ALcenter;
7599a747e4fSDavid du Colombier 				else
7609a747e4fSDavid du Colombier 					al = atabval(tok, Aalign, align_tab, NALIGNTAB, ps->curjust);
7619a747e4fSDavid du Colombier 				pushjust(ps, al);
7629a747e4fSDavid du Colombier 				break;
7639a747e4fSDavid du Colombier 
7649a747e4fSDavid du Colombier 			case Tcenter+RBRA:
7659a747e4fSDavid du Colombier 			case Tdiv+RBRA:
7669a747e4fSDavid du Colombier 				popjust(ps);
7679a747e4fSDavid du Colombier 				break;
7689a747e4fSDavid du Colombier 
7699a747e4fSDavid du Colombier 			// <!ELEMENT DD - O  %flow >
7709a747e4fSDavid du Colombier 			case Tdd:
7719a747e4fSDavid du Colombier 				if(ps->hangstk.n == 0) {
7729a747e4fSDavid du Colombier 					if(warn)
7739a747e4fSDavid du Colombier 						fprint(2, "warning: <DD> not inside <DL\n");
7749a747e4fSDavid du Colombier 					continue;
7759a747e4fSDavid du Colombier 				}
7769a747e4fSDavid du Colombier 				h = top(&ps->hangstk, 0);
7779a747e4fSDavid du Colombier 				if(h != 0)
7789a747e4fSDavid du Colombier 					changehang(ps, -10*LISTTAB);
7799a747e4fSDavid du Colombier 				else
7809a747e4fSDavid du Colombier 					addbrk(ps, 0, 0);
7819a747e4fSDavid du Colombier 				push(&ps->hangstk, 0);
7829a747e4fSDavid du Colombier 				break;
7839a747e4fSDavid du Colombier 
7849a747e4fSDavid du Colombier 			//<!ELEMENT (DIR|MENU) - - (LI)+ -(%block) >
7859a747e4fSDavid du Colombier 			//<!ELEMENT (OL|UL) - - (LI)+>
7869a747e4fSDavid du Colombier 			case Tdir:
7879a747e4fSDavid du Colombier 			case Tmenu:
7889a747e4fSDavid du Colombier 			case Tol:
7899a747e4fSDavid du Colombier 			case Tul:
7909a747e4fSDavid du Colombier 				changeindent(ps, LISTTAB);
7919a747e4fSDavid du Colombier 				push(&ps->listtypestk, listtyval(tok, (tag==Tol)? LT1 : LTdisc));
7929a747e4fSDavid du Colombier 				push(&ps->listcntstk, aintval(tok, Astart, 1));
7939a747e4fSDavid du Colombier 				break;
7949a747e4fSDavid du Colombier 
7959a747e4fSDavid du Colombier 			case Tdir+RBRA:
7969a747e4fSDavid du Colombier 			case Tmenu+RBRA:
7979a747e4fSDavid du Colombier 			case Tol+RBRA:
7989a747e4fSDavid du Colombier 			case Tul+RBRA:
7999a747e4fSDavid du Colombier 				if(ps->listtypestk.n == 0) {
8009a747e4fSDavid du Colombier 					if(warn)
8019a747e4fSDavid du Colombier 						fprint(2, "warning: %T ended no list\n", tok);
8029a747e4fSDavid du Colombier 					continue;
8039a747e4fSDavid du Colombier 				}
8049a747e4fSDavid du Colombier 				addbrk(ps, 0, 0);
8059a747e4fSDavid du Colombier 				pop(&ps->listtypestk);
8069a747e4fSDavid du Colombier 				pop(&ps->listcntstk);
8079a747e4fSDavid du Colombier 				changeindent(ps, -LISTTAB);
8089a747e4fSDavid du Colombier 				break;
8099a747e4fSDavid du Colombier 
8109a747e4fSDavid du Colombier 			// <!ELEMENT DL - - (DT|DD)+ >
8119a747e4fSDavid du Colombier 			case Tdl:
8129a747e4fSDavid du Colombier 				changeindent(ps, LISTTAB);
8139a747e4fSDavid du Colombier 				push(&ps->hangstk, 0);
8149a747e4fSDavid du Colombier 				break;
8159a747e4fSDavid du Colombier 
8169a747e4fSDavid du Colombier 			case Tdl+RBRA:
8179a747e4fSDavid du Colombier 				if(ps->hangstk.n == 0) {
8189a747e4fSDavid du Colombier 					if(warn)
8199a747e4fSDavid du Colombier 						fprint(2, "warning: unexpected </DL>\n");
8209a747e4fSDavid du Colombier 					continue;
8219a747e4fSDavid du Colombier 				}
8229a747e4fSDavid du Colombier 				changeindent(ps, -LISTTAB);
8239a747e4fSDavid du Colombier 				if(top(&ps->hangstk, 0) != 0)
8249a747e4fSDavid du Colombier 					changehang(ps, -10*LISTTAB);
8259a747e4fSDavid du Colombier 				pop(&ps->hangstk);
8269a747e4fSDavid du Colombier 				break;
8279a747e4fSDavid du Colombier 
8289a747e4fSDavid du Colombier 			// <!ELEMENT DT - O (%text)* >
8299a747e4fSDavid du Colombier 			case Tdt:
8309a747e4fSDavid du Colombier 				if(ps->hangstk.n == 0) {
8319a747e4fSDavid du Colombier 					if(warn)
8329a747e4fSDavid du Colombier 						fprint(2, "warning: <DT> not inside <DL>\n");
8339a747e4fSDavid du Colombier 					continue;
8349a747e4fSDavid du Colombier 				}
8359a747e4fSDavid du Colombier 				h = top(&ps->hangstk, 0);
8369a747e4fSDavid du Colombier 				pop(&ps->hangstk);
8379a747e4fSDavid du Colombier 				if(h != 0)
8389a747e4fSDavid du Colombier 					changehang(ps, -10*LISTTAB);
8399a747e4fSDavid du Colombier 				changehang(ps, 10*LISTTAB);
8409a747e4fSDavid du Colombier 				push(&ps->hangstk, 1);
8419a747e4fSDavid du Colombier 				break;
8429a747e4fSDavid du Colombier 
8439a747e4fSDavid du Colombier 			// <!ELEMENT FONT - - (%text)*>
8449a747e4fSDavid du Colombier 			case Tfont:
8459a747e4fSDavid du Colombier 				sz = top(&ps->fntsizestk, Normal);
8469a747e4fSDavid du Colombier 				if(_tokaval(tok, Asize, &nsz, 0)) {
8479a747e4fSDavid du Colombier 					if(_prefix(L"+", nsz))
8489a747e4fSDavid du Colombier 						sz = Normal + _Strtol(nsz+1, nil, 10) + ps->adjsize;
8499a747e4fSDavid du Colombier 					else if(_prefix(L"-", nsz))
8509a747e4fSDavid du Colombier 						sz = Normal - _Strtol(nsz+1, nil, 10) + ps->adjsize;
8519a747e4fSDavid du Colombier 					else if(nsz != nil)
8529a747e4fSDavid du Colombier 						sz = Normal + (_Strtol(nsz, nil, 10) - 3);
8539a747e4fSDavid du Colombier 				}
8549a747e4fSDavid du Colombier 				ps->curfg = push(&ps->fgstk, acolorval(tok, Acolor, ps->curfg));
8559a747e4fSDavid du Colombier 				pushfontsize(ps, sz);
8569a747e4fSDavid du Colombier 				break;
8579a747e4fSDavid du Colombier 
8589a747e4fSDavid du Colombier 			case Tfont+RBRA:
8599a747e4fSDavid du Colombier 				if(ps->fgstk.n == 0) {
8609a747e4fSDavid du Colombier 					if(warn)
8619a747e4fSDavid du Colombier 						fprint(2, "warning: unexpected </FONT>\n");
8629a747e4fSDavid du Colombier 					continue;
8639a747e4fSDavid du Colombier 				}
8649a747e4fSDavid du Colombier 				ps->curfg = popretnewtop(&ps->fgstk, di->text);
8659a747e4fSDavid du Colombier 				popfontsize(ps);
8669a747e4fSDavid du Colombier 				break;
8679a747e4fSDavid du Colombier 
8689a747e4fSDavid du Colombier 			// <!ELEMENT FORM - - %body.content -(FORM) >
8699a747e4fSDavid du Colombier 			case Tform:
8709a747e4fSDavid du Colombier 				if(is->curform != nil) {
8719a747e4fSDavid du Colombier 					if(warn)
8729a747e4fSDavid du Colombier 						fprint(2, "warning: <FORM> nested inside another\n");
8739a747e4fSDavid du Colombier 					continue;
8749a747e4fSDavid du Colombier 				}
8759a747e4fSDavid du Colombier 				action = aurlval(tok, Aaction, di->base, di->base);
8769a747e4fSDavid du Colombier 				s = aval(tok, Aid);
8779a747e4fSDavid du Colombier 				name = astrval(tok, Aname, s);
8789a747e4fSDavid du Colombier 				if(s)
8799a747e4fSDavid du Colombier 					free(s);
8809a747e4fSDavid du Colombier 				target = atargval(tok, di->target);
8819a747e4fSDavid du Colombier 				method = atabval(tok, Amethod, method_tab, NMETHODTAB, HGet);
8829a747e4fSDavid du Colombier 				if(warn && _tokaval(tok, Aenctype, &enctype, 0) &&
8835d459b5aSDavid du Colombier 						_Strcmp(enctype, L"application/x-www-form-urlencoded"))
8849a747e4fSDavid du Colombier 					fprint(2, "form enctype %S not handled\n", enctype);
8859a747e4fSDavid du Colombier 				frm = newform(++is->nforms, name, action, target, method, di->forms);
8869a747e4fSDavid du Colombier 				di->forms = frm;
8879a747e4fSDavid du Colombier 				is->curform = frm;
8889a747e4fSDavid du Colombier 				break;
8899a747e4fSDavid du Colombier 
8909a747e4fSDavid du Colombier 			case Tform+RBRA:
8919a747e4fSDavid du Colombier 				if(is->curform == nil) {
8929a747e4fSDavid du Colombier 					if(warn)
8939a747e4fSDavid du Colombier 						fprint(2, "warning: unexpected </FORM>\n");
8949a747e4fSDavid du Colombier 					continue;
8959a747e4fSDavid du Colombier 				}
8969a747e4fSDavid du Colombier 				// put fields back in input order
8979a747e4fSDavid du Colombier 				is->curform->fields = (Formfield*)_revlist((List*)is->curform->fields);
8989a747e4fSDavid du Colombier 				is->curform = nil;
8999a747e4fSDavid du Colombier 				break;
9009a747e4fSDavid du Colombier 
9019a747e4fSDavid du Colombier 			// <!ELEMENT FRAME - O EMPTY>
9029a747e4fSDavid du Colombier 			case Tframe:
9039a747e4fSDavid du Colombier 				ks = is->kidstk;
9049a747e4fSDavid du Colombier 				if(ks == nil) {
9059a747e4fSDavid du Colombier 					if(warn)
9069a747e4fSDavid du Colombier 						fprint(2, "warning: <FRAME> not in <FRAMESET>\n");
9079a747e4fSDavid du Colombier 					continue;
9089a747e4fSDavid du Colombier 				}
9099a747e4fSDavid du Colombier 				ks->kidinfos = kd = newkidinfo(0, ks->kidinfos);
9109a747e4fSDavid du Colombier 				kd->src = aurlval(tok, Asrc, nil, di->base);
9119a747e4fSDavid du Colombier 				kd->name = aval(tok, Aname);
912c93608ccSDavid du Colombier 				if(kd->name == nil)
913c93608ccSDavid du Colombier 					kd->name = runesmprint("_fr%d", ++is->nframes);
9149a747e4fSDavid du Colombier 				kd->marginw = auintval(tok, Amarginwidth, 0);
9159a747e4fSDavid du Colombier 				kd->marginh = auintval(tok, Amarginheight, 0);
9169a747e4fSDavid du Colombier 				kd->framebd = auintval(tok, Aframeborder, 1);
9179a747e4fSDavid du Colombier 				kd->flags = atabval(tok, Ascrolling, fscroll_tab, NFSCROLLTAB, kd->flags);
9189a747e4fSDavid du Colombier 				norsz = aflagval(tok, Anoresize);
9199a747e4fSDavid du Colombier 				if(norsz)
9209a747e4fSDavid du Colombier 					kd->flags |= FRnoresize;
9219a747e4fSDavid du Colombier 				break;
9229a747e4fSDavid du Colombier 
9239a747e4fSDavid du Colombier 			// <!ELEMENT FRAMESET - - (FRAME|FRAMESET)+>
9249a747e4fSDavid du Colombier 			case Tframeset:
9259a747e4fSDavid du Colombier 				ks = newkidinfo(1, nil);
9269a747e4fSDavid du Colombier 				pks = is->kidstk;
9279a747e4fSDavid du Colombier 				if(pks == nil)
9289a747e4fSDavid du Colombier 					di->kidinfo = ks;
9299a747e4fSDavid du Colombier 				else  {
9309a747e4fSDavid du Colombier 					ks->next = pks->kidinfos;
9319a747e4fSDavid du Colombier 					pks->kidinfos = ks;
9329a747e4fSDavid du Colombier 				}
9339a747e4fSDavid du Colombier 				ks->nextframeset = pks;
9349a747e4fSDavid du Colombier 				is->kidstk = ks;
9359a747e4fSDavid du Colombier 				setdimarray(tok, Arows, &ks->rows, &ks->nrows);
9369a747e4fSDavid du Colombier 				if(ks->nrows == 0) {
9379a747e4fSDavid du Colombier 					ks->rows = (Dimen*)emalloc(sizeof(Dimen));
9389a747e4fSDavid du Colombier 					ks->nrows = 1;
9399a747e4fSDavid du Colombier 					ks->rows[0] = makedimen(Dpercent, 100);
9409a747e4fSDavid du Colombier 				}
9419a747e4fSDavid du Colombier 				setdimarray(tok, Acols, &ks->cols, &ks->ncols);
9429a747e4fSDavid du Colombier 				if(ks->ncols == 0) {
9439a747e4fSDavid du Colombier 					ks->cols = (Dimen*)emalloc(sizeof(Dimen));
9449a747e4fSDavid du Colombier 					ks->ncols = 1;
9459a747e4fSDavid du Colombier 					ks->cols[0] = makedimen(Dpercent, 100);
9469a747e4fSDavid du Colombier 				}
9479a747e4fSDavid du Colombier 				break;
9489a747e4fSDavid du Colombier 
9499a747e4fSDavid du Colombier 			case Tframeset+RBRA:
9509a747e4fSDavid du Colombier 				if(is->kidstk == nil) {
9519a747e4fSDavid du Colombier 					if(warn)
9529a747e4fSDavid du Colombier 						fprint(2, "warning: unexpected </FRAMESET>\n");
9539a747e4fSDavid du Colombier 					continue;
9549a747e4fSDavid du Colombier 				}
9559a747e4fSDavid du Colombier 				ks = is->kidstk;
9569a747e4fSDavid du Colombier 				// put kids back in original order
9579a747e4fSDavid du Colombier 				// and add blank frames to fill out cells
9589a747e4fSDavid du Colombier 				n = ks->nrows*ks->ncols;
9599a747e4fSDavid du Colombier 				nblank = n - _listlen((List*)ks->kidinfos);
9609a747e4fSDavid du Colombier 				while(nblank-- > 0)
9619a747e4fSDavid du Colombier 					ks->kidinfos = newkidinfo(0, ks->kidinfos);
9629a747e4fSDavid du Colombier 				ks->kidinfos = (Kidinfo*)_revlist((List*)ks->kidinfos);
9639a747e4fSDavid du Colombier 				is->kidstk = is->kidstk->nextframeset;
9649a747e4fSDavid du Colombier 				if(is->kidstk == nil) {
9659a747e4fSDavid du Colombier 					// end input
9669a747e4fSDavid du Colombier 					ans = nil;
9679a747e4fSDavid du Colombier 					goto return_ans;
9689a747e4fSDavid du Colombier 				}
9699a747e4fSDavid du Colombier 				break;
9709a747e4fSDavid du Colombier 
9719a747e4fSDavid du Colombier 			// <!ELEMENT H1 - - (%text;)*>, etc.
9729a747e4fSDavid du Colombier 			case Th1:
9739a747e4fSDavid du Colombier 			case Th2:
9749a747e4fSDavid du Colombier 			case Th3:
9759a747e4fSDavid du Colombier 			case Th4:
9769a747e4fSDavid du Colombier 			case Th5:
9779a747e4fSDavid du Colombier 			case Th6:
9789a747e4fSDavid du Colombier 				bramt = 1;
9799a747e4fSDavid du Colombier 				if(ps->items == ps->lastit)
9809a747e4fSDavid du Colombier 					bramt = 0;
9819a747e4fSDavid du Colombier 				addbrk(ps, bramt, IFcleft|IFcright);
9829a747e4fSDavid du Colombier 				sz = Verylarge - (tag - Th1);
9839a747e4fSDavid du Colombier 				if(sz < Tiny)
9849a747e4fSDavid du Colombier 					sz = Tiny;
9859a747e4fSDavid du Colombier 				pushfontsize(ps, sz);
9869a747e4fSDavid du Colombier 				sty = top(&ps->fntstylestk, FntR);
9879a747e4fSDavid du Colombier 				if(tag == Th1)
9889a747e4fSDavid du Colombier 					sty = FntB;
9899a747e4fSDavid du Colombier 				pushfontstyle(ps, sty);
9909a747e4fSDavid du Colombier 				pushjust(ps, atabval(tok, Aalign, align_tab, NALIGNTAB, ps->curjust));
9919a747e4fSDavid du Colombier 				ps->skipwhite = 1;
9929a747e4fSDavid du Colombier 				break;
9939a747e4fSDavid du Colombier 
9949a747e4fSDavid du Colombier 			case Th1+RBRA:
9959a747e4fSDavid du Colombier 			case Th2+RBRA:
9969a747e4fSDavid du Colombier 			case Th3+RBRA:
9979a747e4fSDavid du Colombier 			case Th4+RBRA:
9989a747e4fSDavid du Colombier 			case Th5+RBRA:
9999a747e4fSDavid du Colombier 			case Th6+RBRA:
10009a747e4fSDavid du Colombier 				addbrk(ps, 1, IFcleft|IFcright);
10019a747e4fSDavid du Colombier 				popfontsize(ps);
10029a747e4fSDavid du Colombier 				popfontstyle(ps);
10039a747e4fSDavid du Colombier 				popjust(ps);
10049a747e4fSDavid du Colombier 				break;
10059a747e4fSDavid du Colombier 
10069a747e4fSDavid du Colombier 			case Thead:
10079a747e4fSDavid du Colombier 				// HTML spec says ignore regular markup in head,
10089a747e4fSDavid du Colombier 				// but Netscape and IE don't
10099a747e4fSDavid du Colombier 				// ps.skipping = 1;
10109a747e4fSDavid du Colombier 				break;
10119a747e4fSDavid du Colombier 
10129a747e4fSDavid du Colombier 			case Thead+RBRA:
10139a747e4fSDavid du Colombier 				ps->skipping = 0;
10149a747e4fSDavid du Colombier 				break;
10159a747e4fSDavid du Colombier 
10169a747e4fSDavid du Colombier 			// <!ELEMENT HR - O EMPTY>
10179a747e4fSDavid du Colombier 			case Thr:
10189a747e4fSDavid du Colombier 				al = atabval(tok, Aalign, align_tab, NALIGNTAB, ALcenter);
10199a747e4fSDavid du Colombier 				sz = auintval(tok, Asize, HRSZ);
10209a747e4fSDavid du Colombier 				wd = adimen(tok, Awidth);
10219a747e4fSDavid du Colombier 				if(dimenkind(wd) == Dnone)
10229a747e4fSDavid du Colombier 					wd = makedimen(Dpercent, 100);
10239a747e4fSDavid du Colombier 				nosh = aflagval(tok, Anoshade);
1024*684b447eSDavid du Colombier 				color = acolorval(tok, Acolor, 0);
1025*684b447eSDavid du Colombier 				additem(ps, newirule(al, sz, nosh, color, wd), tok);
10269a747e4fSDavid du Colombier 				addbrk(ps, 0, 0);
10279a747e4fSDavid du Colombier 				break;
10289a747e4fSDavid du Colombier 
10299a747e4fSDavid du Colombier 			case Ti:
10309a747e4fSDavid du Colombier 			case Tcite:
10319a747e4fSDavid du Colombier 			case Tdfn:
10329a747e4fSDavid du Colombier 			case Tem:
10339a747e4fSDavid du Colombier 			case Tvar:
10349a747e4fSDavid du Colombier 			case Taddress:
10359a747e4fSDavid du Colombier 				pushfontstyle(ps, FntI);
10369a747e4fSDavid du Colombier 				break;
10379a747e4fSDavid du Colombier 
10389a747e4fSDavid du Colombier 			// <!ELEMENT IMG - O EMPTY>
10399a747e4fSDavid du Colombier 			case Timg:
10409a747e4fSDavid du Colombier 				map = nil;
10419a747e4fSDavid du Colombier 				oldcuranchor = ps->curanchor;
10429a747e4fSDavid du Colombier 				if(_tokaval(tok, Ausemap, &usemap, 0)) {
10439a747e4fSDavid du Colombier 					if(!_prefix(L"#", usemap)) {
10449a747e4fSDavid du Colombier 						if(warn)
10459a747e4fSDavid du Colombier 							fprint(2, "warning: can't handle non-local map %S\n", usemap);
10469a747e4fSDavid du Colombier 					}
10479a747e4fSDavid du Colombier 					else {
10489a747e4fSDavid du Colombier 						map = getmap(di, usemap+1);
10499a747e4fSDavid du Colombier 						if(ps->curanchor == 0) {
10509a747e4fSDavid du Colombier 							di->anchors = newanchor(++is->nanchors, nil, nil, di->target, di->anchors);
10519a747e4fSDavid du Colombier 							ps->curanchor = is->nanchors;
10529a747e4fSDavid du Colombier 						}
10539a747e4fSDavid du Colombier 					}
10549a747e4fSDavid du Colombier 				}
10559a747e4fSDavid du Colombier 				align = atabval(tok, Aalign, align_tab, NALIGNTAB, ALbottom);
10569a747e4fSDavid du Colombier 				dfltbd = 0;
10579a747e4fSDavid du Colombier 				if(ps->curanchor != 0)
10589a747e4fSDavid du Colombier 					dfltbd = 2;
10599a747e4fSDavid du Colombier 				src = aurlval(tok, Asrc, nil, di->base);
10609a747e4fSDavid du Colombier 				if(src == nil) {
10619a747e4fSDavid du Colombier 					if(warn)
10629a747e4fSDavid du Colombier 						fprint(2, "warning: <img> has no src attribute\n");
10639a747e4fSDavid du Colombier 					ps->curanchor = oldcuranchor;
10649a747e4fSDavid du Colombier 					continue;
10659a747e4fSDavid du Colombier 				}
10669a747e4fSDavid du Colombier 				img = newiimage(src,
10679a747e4fSDavid du Colombier 						aval(tok, Aalt),
10689a747e4fSDavid du Colombier 						align,
10699a747e4fSDavid du Colombier 						auintval(tok, Awidth, 0),
10709a747e4fSDavid du Colombier 						auintval(tok, Aheight, 0),
10719a747e4fSDavid du Colombier 						auintval(tok, Ahspace, IMGHSPACE),
10729a747e4fSDavid du Colombier 						auintval(tok, Avspace, IMGVSPACE),
10739a747e4fSDavid du Colombier 						auintval(tok, Aborder, dfltbd),
10749a747e4fSDavid du Colombier 						aflagval(tok, Aismap),
10759a747e4fSDavid du Colombier 						map);
10769a747e4fSDavid du Colombier 				if(align == ALleft || align == ALright) {
10779a747e4fSDavid du Colombier 					additem(ps, newifloat(img, align), tok);
10789a747e4fSDavid du Colombier 					// if no hspace specified, use FLTIMGHSPACE
10799a747e4fSDavid du Colombier 					if(!_tokaval(tok, Ahspace, &val, 0))
10809a747e4fSDavid du Colombier 						((Iimage*)img)->hspace = FLTIMGHSPACE;
10819a747e4fSDavid du Colombier 				}
10829a747e4fSDavid du Colombier 				else {
10839a747e4fSDavid du Colombier 					ps->skipwhite = 0;
10849a747e4fSDavid du Colombier 					additem(ps, img, tok);
10859a747e4fSDavid du Colombier 				}
10869a747e4fSDavid du Colombier 				if(!ps->skipping) {
10879a747e4fSDavid du Colombier 					((Iimage*)img)->nextimage = di->images;
10889a747e4fSDavid du Colombier 					di->images = (Iimage*)img;
10899a747e4fSDavid du Colombier 				}
10909a747e4fSDavid du Colombier 				ps->curanchor = oldcuranchor;
10919a747e4fSDavid du Colombier 				break;
10929a747e4fSDavid du Colombier 
10939a747e4fSDavid du Colombier 			// <!ELEMENT INPUT - O EMPTY>
10949a747e4fSDavid du Colombier 			case Tinput:
10959a747e4fSDavid du Colombier 				ps->skipwhite = 0;
10969a747e4fSDavid du Colombier 				if(is->curform == nil) {
10979a747e4fSDavid du Colombier 					if(warn)
10989a747e4fSDavid du Colombier 						fprint(2, "<INPUT> not inside <FORM>\n");
10999a747e4fSDavid du Colombier 					continue;
11009a747e4fSDavid du Colombier 				}
11019a747e4fSDavid du Colombier 				is->curform->fields = field = newformfield(
11029a747e4fSDavid du Colombier 						atabval(tok, Atype, input_tab, NINPUTTAB, Ftext),
11039a747e4fSDavid du Colombier 						++is->curform->nfields,
11049a747e4fSDavid du Colombier 						is->curform,
11059a747e4fSDavid du Colombier 						aval(tok, Aname),
11069a747e4fSDavid du Colombier 						aval(tok, Avalue),
11079a747e4fSDavid du Colombier 						auintval(tok, Asize, 0),
11089a747e4fSDavid du Colombier 						auintval(tok, Amaxlength, 1000),
11099a747e4fSDavid du Colombier 						is->curform->fields);
11109a747e4fSDavid du Colombier 				if(aflagval(tok, Achecked))
11119a747e4fSDavid du Colombier 					field->flags = FFchecked;
11129a747e4fSDavid du Colombier 
11139a747e4fSDavid du Colombier 				switch(field->ftype) {
11149a747e4fSDavid du Colombier 				case Ftext:
11159a747e4fSDavid du Colombier 				case Fpassword:
11169a747e4fSDavid du Colombier 				case Ffile:
11179a747e4fSDavid du Colombier 					if(field->size == 0)
11189a747e4fSDavid du Colombier 						field->size = 20;
11199a747e4fSDavid du Colombier 					break;
11209a747e4fSDavid du Colombier 
11219a747e4fSDavid du Colombier 				case Fcheckbox:
11229a747e4fSDavid du Colombier 					if(field->name == nil) {
11239a747e4fSDavid du Colombier 						if(warn)
11249a747e4fSDavid du Colombier 							fprint(2, "warning: checkbox form field missing name\n");
11259a747e4fSDavid du Colombier 						continue;
11269a747e4fSDavid du Colombier 					}
11279a747e4fSDavid du Colombier 					if(field->value == nil)
11289a747e4fSDavid du Colombier 						field->value = _Strdup(L"1");
11299a747e4fSDavid du Colombier 					break;
11309a747e4fSDavid du Colombier 
11319a747e4fSDavid du Colombier 				case Fradio:
11329a747e4fSDavid du Colombier 					if(field->name == nil || field->value == nil) {
11339a747e4fSDavid du Colombier 						if(warn)
11349a747e4fSDavid du Colombier 							fprint(2, "warning: radio form field missing name or value\n");
11359a747e4fSDavid du Colombier 						continue;
11369a747e4fSDavid du Colombier 					}
11379a747e4fSDavid du Colombier 					break;
11389a747e4fSDavid du Colombier 
11399a747e4fSDavid du Colombier 				case Fsubmit:
11409a747e4fSDavid du Colombier 					if(field->value == nil)
11419a747e4fSDavid du Colombier 						field->value = _Strdup(L"Submit");
11429a747e4fSDavid du Colombier 					if(field->name == nil)
11439a747e4fSDavid du Colombier 						field->name = _Strdup(L"_no_name_submit_");
11449a747e4fSDavid du Colombier 					break;
11459a747e4fSDavid du Colombier 
11469a747e4fSDavid du Colombier 				case Fimage:
11479a747e4fSDavid du Colombier 					src = aurlval(tok, Asrc, nil, di->base);
11489a747e4fSDavid du Colombier 					if(src == nil) {
11499a747e4fSDavid du Colombier 						if(warn)
11509a747e4fSDavid du Colombier 							fprint(2, "warning: image form field missing src\n");
11519a747e4fSDavid du Colombier 						continue;
11529a747e4fSDavid du Colombier 					}
11539a747e4fSDavid du Colombier 					// width and height attrs aren't specified in HTML 3.2,
11549a747e4fSDavid du Colombier 					// but some people provide them and they help avoid
11559a747e4fSDavid du Colombier 					// a relayout
11569a747e4fSDavid du Colombier 					field->image = newiimage(src,
11579a747e4fSDavid du Colombier 						astrval(tok, Aalt, L"Submit"),
11589a747e4fSDavid du Colombier 						atabval(tok, Aalign, align_tab, NALIGNTAB, ALbottom),
11599a747e4fSDavid du Colombier 						auintval(tok, Awidth, 0), auintval(tok, Aheight, 0),
11609a747e4fSDavid du Colombier 						0, 0, 0, 0, nil);
11619a747e4fSDavid du Colombier 					ii = (Iimage*)field->image;
11629a747e4fSDavid du Colombier 					ii->nextimage = di->images;
11639a747e4fSDavid du Colombier 					di->images = ii;
11649a747e4fSDavid du Colombier 					break;
11659a747e4fSDavid du Colombier 
11669a747e4fSDavid du Colombier 				case Freset:
11679a747e4fSDavid du Colombier 					if(field->value == nil)
11689a747e4fSDavid du Colombier 						field->value = _Strdup(L"Reset");
11699a747e4fSDavid du Colombier 					break;
11709a747e4fSDavid du Colombier 
11719a747e4fSDavid du Colombier 				case Fbutton:
11729a747e4fSDavid du Colombier 					if(field->value == nil)
11739a747e4fSDavid du Colombier 						field->value = _Strdup(L" ");
11749a747e4fSDavid du Colombier 					break;
11759a747e4fSDavid du Colombier 				}
11769a747e4fSDavid du Colombier 				ffit = newiformfield(field);
11779a747e4fSDavid du Colombier 				additem(ps, ffit, tok);
11789a747e4fSDavid du Colombier 				if(ffit->genattr != nil)
11799a747e4fSDavid du Colombier 					field->events = ffit->genattr->events;
11809a747e4fSDavid du Colombier 				break;
11819a747e4fSDavid du Colombier 
11829a747e4fSDavid du Colombier 			// <!ENTITY ISINDEX - O EMPTY>
11839a747e4fSDavid du Colombier 			case Tisindex:
11849a747e4fSDavid du Colombier 				ps->skipwhite = 0;
11859a747e4fSDavid du Colombier 				prompt = astrval(tok, Aprompt, L"Index search terms:");
11869a747e4fSDavid du Colombier 				target = atargval(tok, di->target);
11879a747e4fSDavid du Colombier 				additem(ps, textit(ps, prompt), tok);
11889a747e4fSDavid du Colombier 				frm = newform(++is->nforms,
11899a747e4fSDavid du Colombier 						nil,
11909a747e4fSDavid du Colombier 						di->base,
11919a747e4fSDavid du Colombier 						target,
11929a747e4fSDavid du Colombier 						HGet,
11939a747e4fSDavid du Colombier 						di->forms);
11949a747e4fSDavid du Colombier 				di->forms = frm;
11959a747e4fSDavid du Colombier 				ff = newformfield(Ftext,
11969a747e4fSDavid du Colombier 						1,
11979a747e4fSDavid du Colombier 						frm,
11989a747e4fSDavid du Colombier 						_Strdup(L"_ISINDEX_"),
11999a747e4fSDavid du Colombier 						nil,
12009a747e4fSDavid du Colombier 						50,
12019a747e4fSDavid du Colombier 						1000,
12029a747e4fSDavid du Colombier 						nil);
12039a747e4fSDavid du Colombier 				frm->fields = ff;
12049a747e4fSDavid du Colombier 				frm->nfields = 1;
12059a747e4fSDavid du Colombier 				additem(ps, newiformfield(ff), tok);
12069a747e4fSDavid du Colombier 				addbrk(ps, 1, 0);
12079a747e4fSDavid du Colombier 				break;
12089a747e4fSDavid du Colombier 
12099a747e4fSDavid du Colombier 			// <!ELEMENT LI - O %flow>
12109a747e4fSDavid du Colombier 			case Tli:
12119a747e4fSDavid du Colombier 				if(ps->listtypestk.n == 0) {
12129a747e4fSDavid du Colombier 					if(warn)
12139a747e4fSDavid du Colombier 						fprint(2, "<LI> not in list\n");
12149a747e4fSDavid du Colombier 					continue;
12159a747e4fSDavid du Colombier 				}
12169a747e4fSDavid du Colombier 				ty = top(&ps->listtypestk, 0);
12179a747e4fSDavid du Colombier 				ty2 = listtyval(tok, ty);
12189a747e4fSDavid du Colombier 				if(ty != ty2) {
12199a747e4fSDavid du Colombier 					ty = ty2;
12209a747e4fSDavid du Colombier 					push(&ps->listtypestk, ty2);
12219a747e4fSDavid du Colombier 				}
12229a747e4fSDavid du Colombier 				v = aintval(tok, Avalue, top(&ps->listcntstk, 1));
12239a747e4fSDavid du Colombier 				if(ty == LTdisc || ty == LTsquare || ty == LTcircle)
12249a747e4fSDavid du Colombier 					hang = 10*LISTTAB - 3;
12259a747e4fSDavid du Colombier 				else
12269a747e4fSDavid du Colombier 					hang = 10*LISTTAB - 1;
12279a747e4fSDavid du Colombier 				changehang(ps, hang);
12289a747e4fSDavid du Colombier 				addtext(ps, listmark(ty, v));
12299a747e4fSDavid du Colombier 				push(&ps->listcntstk, v + 1);
12309a747e4fSDavid du Colombier 				changehang(ps, -hang);
12319a747e4fSDavid du Colombier 				ps->skipwhite = 1;
12329a747e4fSDavid du Colombier 				break;
12339a747e4fSDavid du Colombier 
12349a747e4fSDavid du Colombier 			// <!ELEMENT MAP - - (AREA)+>
12359a747e4fSDavid du Colombier 			case Tmap:
12369a747e4fSDavid du Colombier 				if(_tokaval(tok, Aname, &name, 0))
12379a747e4fSDavid du Colombier 					is->curmap = getmap(di, name);
12389a747e4fSDavid du Colombier 				break;
12399a747e4fSDavid du Colombier 
12409a747e4fSDavid du Colombier 			case Tmap+RBRA:
12419a747e4fSDavid du Colombier 				map = is->curmap;
12429a747e4fSDavid du Colombier 				if(map == nil) {
12439a747e4fSDavid du Colombier 					if(warn)
12449a747e4fSDavid du Colombier 						fprint(2, "warning: unexpected </MAP>\n");
12459a747e4fSDavid du Colombier 					continue;
12469a747e4fSDavid du Colombier 				}
12479a747e4fSDavid du Colombier 				map->areas = (Area*)_revlist((List*)map->areas);
12489a747e4fSDavid du Colombier 				break;
12499a747e4fSDavid du Colombier 
12509a747e4fSDavid du Colombier 			case Tmeta:
12519a747e4fSDavid du Colombier 				if(ps->skipping)
12529a747e4fSDavid du Colombier 					continue;
12539a747e4fSDavid du Colombier 				if(_tokaval(tok, Ahttp_equiv, &equiv, 0)) {
12549a747e4fSDavid du Colombier 					val = aval(tok, Acontent);
12555d459b5aSDavid du Colombier 					n = _Strlen(equiv);
12569a747e4fSDavid du Colombier 					if(!_Strncmpci(equiv, n, L"refresh"))
12579a747e4fSDavid du Colombier 						di->refresh = val;
12589a747e4fSDavid du Colombier 					else if(!_Strncmpci(equiv, n, L"content-script-type")) {
12595d459b5aSDavid du Colombier 						n = _Strlen(val);
12609a747e4fSDavid du Colombier 						if(!_Strncmpci(val, n, L"javascript")
12619a747e4fSDavid du Colombier 						   || !_Strncmpci(val, n, L"jscript1.1")
12629a747e4fSDavid du Colombier 						   || !_Strncmpci(val, n, L"jscript"))
12639a747e4fSDavid du Colombier 							di->scripttype = TextJavascript;
12649a747e4fSDavid du Colombier 						else {
12659a747e4fSDavid du Colombier 							if(warn)
12669a747e4fSDavid du Colombier 								fprint(2, "unimplemented script type %S\n", val);
12679a747e4fSDavid du Colombier 							di->scripttype = UnknownType;
12689a747e4fSDavid du Colombier 						}
12699a747e4fSDavid du Colombier 					}
12709a747e4fSDavid du Colombier 				}
12719a747e4fSDavid du Colombier 				break;
12729a747e4fSDavid du Colombier 
12739a747e4fSDavid du Colombier 			// Nobr is NOT in HMTL 4.0, but it is ubiquitous on the web
12749a747e4fSDavid du Colombier 			case Tnobr:
12759a747e4fSDavid du Colombier 				ps->skipwhite = 0;
12769a747e4fSDavid du Colombier 				ps->curstate &= ~IFwrap;
12779a747e4fSDavid du Colombier 				break;
12789a747e4fSDavid du Colombier 
12799a747e4fSDavid du Colombier 			case Tnobr+RBRA:
12809a747e4fSDavid du Colombier 				ps->curstate |= IFwrap;
12819a747e4fSDavid du Colombier 				break;
12829a747e4fSDavid du Colombier 
12839a747e4fSDavid du Colombier 			// We do frames, so skip stuff in noframes
12849a747e4fSDavid du Colombier 			case Tnoframes:
12859a747e4fSDavid du Colombier 				ps->skipping = 1;
12869a747e4fSDavid du Colombier 				break;
12879a747e4fSDavid du Colombier 
12889a747e4fSDavid du Colombier 			case Tnoframes+RBRA:
12899a747e4fSDavid du Colombier 				ps->skipping = 0;
12909a747e4fSDavid du Colombier 				break;
12919a747e4fSDavid du Colombier 
12929a747e4fSDavid du Colombier 			// We do scripts (if enabled), so skip stuff in noscripts
12939a747e4fSDavid du Colombier 			case Tnoscript:
12949a747e4fSDavid du Colombier 				if(doscripts)
12959a747e4fSDavid du Colombier 					ps->skipping = 1;
12969a747e4fSDavid du Colombier 				break;
12979a747e4fSDavid du Colombier 
12989a747e4fSDavid du Colombier 			case Tnoscript+RBRA:
12999a747e4fSDavid du Colombier 				if(doscripts)
13009a747e4fSDavid du Colombier 					ps->skipping = 0;
13019a747e4fSDavid du Colombier 				break;
13029a747e4fSDavid du Colombier 
13039a747e4fSDavid du Colombier 			// <!ELEMENT OPTION - O (	//PCDATA)>
13049a747e4fSDavid du Colombier 			case Toption:
13059a747e4fSDavid du Colombier 				if(is->curform == nil || is->curform->fields == nil) {
13069a747e4fSDavid du Colombier 					if(warn)
13079a747e4fSDavid du Colombier 						fprint(2, "warning: <OPTION> not in <SELECT>\n");
13089a747e4fSDavid du Colombier 					continue;
13099a747e4fSDavid du Colombier 				}
13109a747e4fSDavid du Colombier 				field = is->curform->fields;
13119a747e4fSDavid du Colombier 				if(field->ftype != Fselect) {
13129a747e4fSDavid du Colombier 					if(warn)
13139a747e4fSDavid du Colombier 						fprint(2, "warning: <OPTION> not in <SELECT>\n");
13149a747e4fSDavid du Colombier 					continue;
13159a747e4fSDavid du Colombier 				}
13169a747e4fSDavid du Colombier 				val = aval(tok, Avalue);
13179a747e4fSDavid du Colombier 				option = newoption(aflagval(tok, Aselected), val, nil, field->options);
13189a747e4fSDavid du Colombier 				field->options = option;
13199a747e4fSDavid du Colombier 				option->display =  getpcdata(toks, tokslen, &toki);
13209a747e4fSDavid du Colombier 				if(val == nil)
13219a747e4fSDavid du Colombier 					option->value = _Strdup(option->display);
13229a747e4fSDavid du Colombier 				break;
13239a747e4fSDavid du Colombier 
13249a747e4fSDavid du Colombier 			// <!ELEMENT P - O (%text)* >
13259a747e4fSDavid du Colombier 			case Tp:
13269a747e4fSDavid du Colombier 				pushjust(ps, atabval(tok, Aalign, align_tab, NALIGNTAB, ps->curjust));
13279a747e4fSDavid du Colombier 				ps->inpar = 1;
13289a747e4fSDavid du Colombier 				ps->skipwhite = 1;
13299a747e4fSDavid du Colombier 				break;
13309a747e4fSDavid du Colombier 
13319a747e4fSDavid du Colombier 			case Tp+RBRA:
13329a747e4fSDavid du Colombier 				break;
13339a747e4fSDavid du Colombier 
13349a747e4fSDavid du Colombier 			// <!ELEMENT PARAM - O EMPTY>
13359a747e4fSDavid du Colombier 			// Do something when we do applets...
13369a747e4fSDavid du Colombier 			case Tparam:
13379a747e4fSDavid du Colombier 				break;
13389a747e4fSDavid du Colombier 
13399a747e4fSDavid du Colombier 			// <!ELEMENT PRE - - (%text)* -(IMG|BIG|SMALL|SUB|SUP|FONT) >
13409a747e4fSDavid du Colombier 			case Tpre:
13419a747e4fSDavid du Colombier 				ps->curstate &= ~IFwrap;
13429a747e4fSDavid du Colombier 				ps->literal = 1;
13439a747e4fSDavid du Colombier 				ps->skipwhite = 0;
13449a747e4fSDavid du Colombier 				pushfontstyle(ps, FntT);
13459a747e4fSDavid du Colombier 				break;
13469a747e4fSDavid du Colombier 
13479a747e4fSDavid du Colombier 			case Tpre+RBRA:
13489a747e4fSDavid du Colombier 				ps->curstate |= IFwrap;
13499a747e4fSDavid du Colombier 				if(ps->literal) {
13509a747e4fSDavid du Colombier 					popfontstyle(ps);
13519a747e4fSDavid du Colombier 					ps->literal = 0;
13529a747e4fSDavid du Colombier 				}
13539a747e4fSDavid du Colombier 				break;
13549a747e4fSDavid du Colombier 
13559a747e4fSDavid du Colombier 			// <!ELEMENT SCRIPT - - CDATA>
13569a747e4fSDavid du Colombier 			case Tscript:
13579a747e4fSDavid du Colombier 				if(doscripts) {
13589a747e4fSDavid du Colombier 					if(!di->hasscripts) {
13599a747e4fSDavid du Colombier 						if(di->scripttype == TextJavascript) {
13609a747e4fSDavid du Colombier 							// TODO: initialize script if nec.
13619a747e4fSDavid du Colombier 							// initjscript(di);
13629a747e4fSDavid du Colombier 							di->hasscripts = 1;
13639a747e4fSDavid du Colombier 						}
13649a747e4fSDavid du Colombier 					}
13659a747e4fSDavid du Colombier 				}
13669a747e4fSDavid du Colombier 				if(!di->hasscripts) {
13679a747e4fSDavid du Colombier 					if(warn)
13689a747e4fSDavid du Colombier 						fprint(2, "warning: <SCRIPT> ignored\n");
13699a747e4fSDavid du Colombier 					ps->skipping = 1;
13709a747e4fSDavid du Colombier 				}
13719a747e4fSDavid du Colombier 				else {
13729a747e4fSDavid du Colombier 					scriptsrc = aurlval(tok, Asrc, nil, di->base);
13739a747e4fSDavid du Colombier 					script = nil;
13749a747e4fSDavid du Colombier 					if(scriptsrc != nil) {
13759a747e4fSDavid du Colombier 						if(warn)
13769a747e4fSDavid du Colombier 							fprint(2, "warning: non-local <SCRIPT> ignored\n");
13779a747e4fSDavid du Colombier 						free(scriptsrc);
13789a747e4fSDavid du Colombier 					}
13799a747e4fSDavid du Colombier 					else {
13809a747e4fSDavid du Colombier 						script = getpcdata(toks, tokslen, &toki);
13819a747e4fSDavid du Colombier 					}
13829a747e4fSDavid du Colombier 					if(script != nil) {
13839a747e4fSDavid du Colombier 						if(warn)
13849a747e4fSDavid du Colombier 							fprint(2, "script ignored\n");
13859a747e4fSDavid du Colombier 						free(script);
13869a747e4fSDavid du Colombier 					}
13879a747e4fSDavid du Colombier 				}
13889a747e4fSDavid du Colombier 				break;
13899a747e4fSDavid du Colombier 
13909a747e4fSDavid du Colombier 			case Tscript+RBRA:
13919a747e4fSDavid du Colombier 				ps->skipping = 0;
13929a747e4fSDavid du Colombier 				break;
13939a747e4fSDavid du Colombier 
13949a747e4fSDavid du Colombier 			// <!ELEMENT SELECT - - (OPTION+)>
13959a747e4fSDavid du Colombier 			case Tselect:
13969a747e4fSDavid du Colombier 				if(is->curform == nil) {
13979a747e4fSDavid du Colombier 					if(warn)
13989a747e4fSDavid du Colombier 						fprint(2, "<SELECT> not inside <FORM>\n");
13999a747e4fSDavid du Colombier 					continue;
14009a747e4fSDavid du Colombier 				}
14019a747e4fSDavid du Colombier 				field = newformfield(Fselect,
14029a747e4fSDavid du Colombier 					++is->curform->nfields,
14039a747e4fSDavid du Colombier 					is->curform,
14049a747e4fSDavid du Colombier 					aval(tok, Aname),
14059a747e4fSDavid du Colombier 					nil,
14069a747e4fSDavid du Colombier 					auintval(tok, Asize, 0),
14079a747e4fSDavid du Colombier 					0,
14089a747e4fSDavid du Colombier 					is->curform->fields);
14099a747e4fSDavid du Colombier 				is->curform->fields = field;
14109a747e4fSDavid du Colombier 				if(aflagval(tok, Amultiple))
14119a747e4fSDavid du Colombier 					field->flags = FFmultiple;
14129a747e4fSDavid du Colombier 				ffit = newiformfield(field);
14139a747e4fSDavid du Colombier 				additem(ps, ffit, tok);
14149a747e4fSDavid du Colombier 				if(ffit->genattr != nil)
14159a747e4fSDavid du Colombier 					field->events = ffit->genattr->events;
14169a747e4fSDavid du Colombier 				// throw away stuff until next tag (should be <OPTION>)
14179a747e4fSDavid du Colombier 				s = getpcdata(toks, tokslen, &toki);
14189a747e4fSDavid du Colombier 				if(s != nil)
14199a747e4fSDavid du Colombier 					free(s);
14209a747e4fSDavid du Colombier 				break;
14219a747e4fSDavid du Colombier 
14229a747e4fSDavid du Colombier 			case Tselect+RBRA:
14239a747e4fSDavid du Colombier 				if(is->curform == nil || is->curform->fields == nil) {
14249a747e4fSDavid du Colombier 					if(warn)
14259a747e4fSDavid du Colombier 						fprint(2, "warning: unexpected </SELECT>\n");
14269a747e4fSDavid du Colombier 					continue;
14279a747e4fSDavid du Colombier 				}
14289a747e4fSDavid du Colombier 				field = is->curform->fields;
14299a747e4fSDavid du Colombier 				if(field->ftype != Fselect)
14309a747e4fSDavid du Colombier 					continue;
14319a747e4fSDavid du Colombier 				// put options back in input order
14329a747e4fSDavid du Colombier 				field->options = (Option*)_revlist((List*)field->options);
14339a747e4fSDavid du Colombier 				break;
14349a747e4fSDavid du Colombier 
14359a747e4fSDavid du Colombier 			// <!ELEMENT (STRIKE|U) - - (%text)*>
14369a747e4fSDavid du Colombier 			case Tstrike:
14379a747e4fSDavid du Colombier 			case Tu:
14389a747e4fSDavid du Colombier 				ps->curul = push(&ps->ulstk, (tag==Tstrike)? ULmid : ULunder);
14399a747e4fSDavid du Colombier 				break;
14409a747e4fSDavid du Colombier 
14419a747e4fSDavid du Colombier 			case Tstrike+RBRA:
14429a747e4fSDavid du Colombier 			case Tu+RBRA:
14439a747e4fSDavid du Colombier 				if(ps->ulstk.n == 0) {
14449a747e4fSDavid du Colombier 					if(warn)
14459a747e4fSDavid du Colombier 						fprint(2, "warning: unexpected %T\n", tok);
14469a747e4fSDavid du Colombier 					continue;
14479a747e4fSDavid du Colombier 				}
14489a747e4fSDavid du Colombier 				ps->curul = popretnewtop(&ps->ulstk, ULnone);
14499a747e4fSDavid du Colombier 				break;
14509a747e4fSDavid du Colombier 
14519a747e4fSDavid du Colombier 			// <!ELEMENT STYLE - - CDATA>
14529a747e4fSDavid du Colombier 			case Tstyle:
14539a747e4fSDavid du Colombier 				if(warn)
14549a747e4fSDavid du Colombier 					fprint(2, "warning: unimplemented <STYLE>\n");
14559a747e4fSDavid du Colombier 				ps->skipping = 1;
14569a747e4fSDavid du Colombier 				break;
14579a747e4fSDavid du Colombier 
14589a747e4fSDavid du Colombier 			case Tstyle+RBRA:
14599a747e4fSDavid du Colombier 				ps->skipping = 0;
14609a747e4fSDavid du Colombier 				break;
14619a747e4fSDavid du Colombier 
14629a747e4fSDavid du Colombier 			// <!ELEMENT (SUB|SUP) - - (%text)*>
14639a747e4fSDavid du Colombier 			case Tsub:
14649a747e4fSDavid du Colombier 			case Tsup:
14659a747e4fSDavid du Colombier 				if(tag == Tsub)
14669a747e4fSDavid du Colombier 					ps->curvoff += SUBOFF;
14679a747e4fSDavid du Colombier 				else
14689a747e4fSDavid du Colombier 					ps->curvoff -= SUPOFF;
14699a747e4fSDavid du Colombier 				push(&ps->voffstk, ps->curvoff);
14709a747e4fSDavid du Colombier 				sz = top(&ps->fntsizestk, Normal);
14719a747e4fSDavid du Colombier 				pushfontsize(ps, sz - 1);
14729a747e4fSDavid du Colombier 				break;
14739a747e4fSDavid du Colombier 
14749a747e4fSDavid du Colombier 			case Tsub+RBRA:
14759a747e4fSDavid du Colombier 			case Tsup+RBRA:
14769a747e4fSDavid du Colombier 				if(ps->voffstk.n == 0) {
14779a747e4fSDavid du Colombier 					if(warn)
14789a747e4fSDavid du Colombier 						fprint(2, "warning: unexpected %T\n", tok);
14799a747e4fSDavid du Colombier 					continue;
14809a747e4fSDavid du Colombier 				}
14819a747e4fSDavid du Colombier 				ps->curvoff = popretnewtop(&ps->voffstk, 0);
14829a747e4fSDavid du Colombier 				popfontsize(ps);
14839a747e4fSDavid du Colombier 				break;
14849a747e4fSDavid du Colombier 
14859a747e4fSDavid du Colombier 			// <!ELEMENT TABLE - - (CAPTION?, TR+)>
14869a747e4fSDavid du Colombier 			case Ttable:
14879a747e4fSDavid du Colombier 				ps->skipwhite = 0;
14889a747e4fSDavid du Colombier 				tab = newtable(++is->ntables,
14899a747e4fSDavid du Colombier 						aalign(tok),
14909a747e4fSDavid du Colombier 						adimen(tok, Awidth),
14919a747e4fSDavid du Colombier 						aflagval(tok, Aborder),
14929a747e4fSDavid du Colombier 						auintval(tok, Acellspacing, TABSP),
14939a747e4fSDavid du Colombier 						auintval(tok, Acellpadding, TABPAD),
14949a747e4fSDavid du Colombier 						makebackground(nil, acolorval(tok, Abgcolor, ps->curbg.color)),
14959a747e4fSDavid du Colombier 						tok,
14969a747e4fSDavid du Colombier 						is->tabstk);
14979a747e4fSDavid du Colombier 				is->tabstk = tab;
14989a747e4fSDavid du Colombier 				curtab = tab;
14999a747e4fSDavid du Colombier 				break;
15009a747e4fSDavid du Colombier 
15019a747e4fSDavid du Colombier 			case Ttable+RBRA:
15029a747e4fSDavid du Colombier 				if(curtab == nil) {
15039a747e4fSDavid du Colombier 					if(warn)
15049a747e4fSDavid du Colombier 						fprint(2, "warning: unexpected </TABLE>\n");
15059a747e4fSDavid du Colombier 					continue;
15069a747e4fSDavid du Colombier 				}
15079a747e4fSDavid du Colombier 				isempty = (curtab->cells == nil);
15089a747e4fSDavid du Colombier 				if(isempty) {
15099a747e4fSDavid du Colombier 					if(warn)
15109a747e4fSDavid du Colombier 						fprint(2, "warning: <TABLE> has no cells\n");
15119a747e4fSDavid du Colombier 				}
15129a747e4fSDavid du Colombier 				else {
15139a747e4fSDavid du Colombier 					ps = finishcell(curtab, ps);
15149a747e4fSDavid du Colombier 					if(curtab->rows != nil)
15159a747e4fSDavid du Colombier 						curtab->rows->flags = 0;
15169a747e4fSDavid du Colombier 					finish_table(curtab);
15179a747e4fSDavid du Colombier 				}
15189a747e4fSDavid du Colombier 				ps->skipping = 0;
15199a747e4fSDavid du Colombier 				if(!isempty) {
15209a747e4fSDavid du Colombier 					tabitem = newitable(curtab);
15219a747e4fSDavid du Colombier 					al = curtab->align.halign;
15229a747e4fSDavid du Colombier 					switch(al) {
15239a747e4fSDavid du Colombier 					case ALleft:
15249a747e4fSDavid du Colombier 					case ALright:
15259a747e4fSDavid du Colombier 						additem(ps, newifloat(tabitem, al), tok);
15269a747e4fSDavid du Colombier 						break;
15279a747e4fSDavid du Colombier 					default:
15289a747e4fSDavid du Colombier 						if(al == ALcenter)
15299a747e4fSDavid du Colombier 							pushjust(ps, ALcenter);
15309a747e4fSDavid du Colombier 						addbrk(ps, 0, 0);
15319a747e4fSDavid du Colombier 						if(ps->inpar) {
15329a747e4fSDavid du Colombier 							popjust(ps);
15339a747e4fSDavid du Colombier 							ps->inpar = 0;
15349a747e4fSDavid du Colombier 						}
15359a747e4fSDavid du Colombier 						additem(ps, tabitem, curtab->tabletok);
15369a747e4fSDavid du Colombier 						if(al == ALcenter)
15379a747e4fSDavid du Colombier 							popjust(ps);
15389a747e4fSDavid du Colombier 						break;
15399a747e4fSDavid du Colombier 					}
15409a747e4fSDavid du Colombier 				}
15419a747e4fSDavid du Colombier 				if(is->tabstk == nil) {
15429a747e4fSDavid du Colombier 					if(warn)
15439a747e4fSDavid du Colombier 						fprint(2, "warning: table stack is wrong\n");
15449a747e4fSDavid du Colombier 				}
15459a747e4fSDavid du Colombier 				else
15469a747e4fSDavid du Colombier 					is->tabstk = is->tabstk->next;
15479a747e4fSDavid du Colombier 				curtab->next = di->tables;
15489a747e4fSDavid du Colombier 				di->tables = curtab;
15499a747e4fSDavid du Colombier 				curtab = is->tabstk;
15509a747e4fSDavid du Colombier 				if(!isempty)
15519a747e4fSDavid du Colombier 					addbrk(ps, 0, 0);
15529a747e4fSDavid du Colombier 				break;
15539a747e4fSDavid du Colombier 
15549a747e4fSDavid du Colombier 			// <!ELEMENT (TH|TD) - O %body.content>
15559a747e4fSDavid du Colombier 			// Cells for a row are accumulated in reverse order.
15569a747e4fSDavid du Colombier 			// We push ps on a stack, and use a new one to accumulate
15579a747e4fSDavid du Colombier 			// the contents of the cell.
15589a747e4fSDavid du Colombier 			case Ttd:
15599a747e4fSDavid du Colombier 			case Tth:
15609a747e4fSDavid du Colombier 				if(curtab == nil) {
15619a747e4fSDavid du Colombier 					if(warn)
15629a747e4fSDavid du Colombier 						fprint(2, "%T outside <TABLE>\n", tok);
15639a747e4fSDavid du Colombier 					continue;
15649a747e4fSDavid du Colombier 				}
15659a747e4fSDavid du Colombier 				if(ps->inpar) {
15669a747e4fSDavid du Colombier 					popjust(ps);
15679a747e4fSDavid du Colombier 					ps->inpar = 0;
15689a747e4fSDavid du Colombier 				}
15699a747e4fSDavid du Colombier 				ps = finishcell(curtab, ps);
15709a747e4fSDavid du Colombier 				tr = nil;
15719a747e4fSDavid du Colombier 				if(curtab->rows != nil)
15729a747e4fSDavid du Colombier 					tr = curtab->rows;
15739a747e4fSDavid du Colombier 				if(tr == nil || !tr->flags) {
15749a747e4fSDavid du Colombier 					if(warn)
15759a747e4fSDavid du Colombier 						fprint(2, "%T outside row\n", tok);
15769a747e4fSDavid du Colombier 					tr = newtablerow(makealign(ALnone, ALnone),
15779a747e4fSDavid du Colombier 							makebackground(nil, curtab->background.color),
15789a747e4fSDavid du Colombier 							TFparsing,
15799a747e4fSDavid du Colombier 							curtab->rows);
15809a747e4fSDavid du Colombier 					curtab->rows = tr;
15819a747e4fSDavid du Colombier 				}
15829a747e4fSDavid du Colombier 				ps = cell_pstate(ps, tag == Tth);
15839a747e4fSDavid du Colombier 				flags = TFparsing;
15849a747e4fSDavid du Colombier 				if(aflagval(tok, Anowrap)) {
15859a747e4fSDavid du Colombier 					flags |= TFnowrap;
15869a747e4fSDavid du Colombier 					ps->curstate &= ~IFwrap;
15879a747e4fSDavid du Colombier 				}
15889a747e4fSDavid du Colombier 				if(tag == Tth)
15899a747e4fSDavid du Colombier 					flags |= TFisth;
15909a747e4fSDavid du Colombier 				c = newtablecell(curtab->cells==nil? 1 : curtab->cells->cellid+1,
15919a747e4fSDavid du Colombier 						auintval(tok, Arowspan, 1),
15929a747e4fSDavid du Colombier 						auintval(tok, Acolspan, 1),
15939a747e4fSDavid du Colombier 						aalign(tok),
15949a747e4fSDavid du Colombier 						adimen(tok, Awidth),
15959a747e4fSDavid du Colombier 						auintval(tok, Aheight, 0),
15969a747e4fSDavid du Colombier 						makebackground(nil, acolorval(tok, Abgcolor, tr->background.color)),
15979a747e4fSDavid du Colombier 						flags,
15989a747e4fSDavid du Colombier 						curtab->cells);
15999a747e4fSDavid du Colombier 				curtab->cells = c;
16009a747e4fSDavid du Colombier 				ps->curbg = c->background;
16019a747e4fSDavid du Colombier 				if(c->align.halign == ALnone) {
16029a747e4fSDavid du Colombier 					if(tr->align.halign != ALnone)
16039a747e4fSDavid du Colombier 						c->align.halign = tr->align.halign;
16049a747e4fSDavid du Colombier 					else if(tag == Tth)
16059a747e4fSDavid du Colombier 						c->align.halign = ALcenter;
16069a747e4fSDavid du Colombier 					else
16079a747e4fSDavid du Colombier 						c->align.halign = ALleft;
16089a747e4fSDavid du Colombier 				}
16099a747e4fSDavid du Colombier 				if(c->align.valign == ALnone) {
16109a747e4fSDavid du Colombier 					if(tr->align.valign != ALnone)
16119a747e4fSDavid du Colombier 						c->align.valign = tr->align.valign;
16129a747e4fSDavid du Colombier 					else
16139a747e4fSDavid du Colombier 						c->align.valign = ALmiddle;
16149a747e4fSDavid du Colombier 				}
16159a747e4fSDavid du Colombier 				c->nextinrow = tr->cells;
16169a747e4fSDavid du Colombier 				tr->cells = c;
16179a747e4fSDavid du Colombier 				break;
16189a747e4fSDavid du Colombier 
16199a747e4fSDavid du Colombier 			case Ttd+RBRA:
16209a747e4fSDavid du Colombier 			case Tth+RBRA:
16219a747e4fSDavid du Colombier 				if(curtab == nil || curtab->cells == nil) {
16229a747e4fSDavid du Colombier 					if(warn)
16239a747e4fSDavid du Colombier 						fprint(2, "unexpected %T\n", tok);
16249a747e4fSDavid du Colombier 					continue;
16259a747e4fSDavid du Colombier 				}
16269a747e4fSDavid du Colombier 				ps = finishcell(curtab, ps);
16279a747e4fSDavid du Colombier 				break;
16289a747e4fSDavid du Colombier 
16299a747e4fSDavid du Colombier 			// <!ELEMENT TEXTAREA - - (	//PCDATA)>
16309a747e4fSDavid du Colombier 			case Ttextarea:
16319a747e4fSDavid du Colombier 				if(is->curform == nil) {
16329a747e4fSDavid du Colombier 					if(warn)
16339a747e4fSDavid du Colombier 						fprint(2, "<TEXTAREA> not inside <FORM>\n");
16349a747e4fSDavid du Colombier 					continue;
16359a747e4fSDavid du Colombier 				}
16369a747e4fSDavid du Colombier 				field = newformfield(Ftextarea,
16379a747e4fSDavid du Colombier 					++is->curform->nfields,
16389a747e4fSDavid du Colombier 					is->curform,
16399a747e4fSDavid du Colombier 					aval(tok, Aname),
16409a747e4fSDavid du Colombier 					nil,
16419a747e4fSDavid du Colombier 					0,
16429a747e4fSDavid du Colombier 					0,
16439a747e4fSDavid du Colombier 					is->curform->fields);
16449a747e4fSDavid du Colombier 				is->curform->fields = field;
16459a747e4fSDavid du Colombier 				field->rows = auintval(tok, Arows, 3);
16469a747e4fSDavid du Colombier 				field->cols = auintval(tok, Acols, 50);
16479a747e4fSDavid du Colombier 				field->value = getpcdata(toks, tokslen, &toki);
16489a747e4fSDavid du Colombier 				if(warn && toki < tokslen - 1 && toks[toki + 1].tag != Ttextarea + RBRA)
16499a747e4fSDavid du Colombier 					fprint(2, "warning: <TEXTAREA> data ended by %T\n", &toks[toki + 1]);
16509a747e4fSDavid du Colombier 				ffit = newiformfield(field);
16519a747e4fSDavid du Colombier 				additem(ps, ffit, tok);
16529a747e4fSDavid du Colombier 				if(ffit->genattr != nil)
16539a747e4fSDavid du Colombier 					field->events = ffit->genattr->events;
16549a747e4fSDavid du Colombier 				break;
16559a747e4fSDavid du Colombier 
16569a747e4fSDavid du Colombier 			// <!ELEMENT TITLE - - (	//PCDATA)* -(%head.misc)>
16579a747e4fSDavid du Colombier 			case Ttitle:
16589a747e4fSDavid du Colombier 				di->doctitle = getpcdata(toks, tokslen, &toki);
16599a747e4fSDavid du Colombier 				if(warn && toki < tokslen - 1 && toks[toki + 1].tag != Ttitle + RBRA)
16609a747e4fSDavid du Colombier 					fprint(2, "warning: <TITLE> data ended by %T\n", &toks[toki + 1]);
16619a747e4fSDavid du Colombier 				break;
16629a747e4fSDavid du Colombier 
16639a747e4fSDavid du Colombier 			// <!ELEMENT TR - O (TH|TD)+>
16649a747e4fSDavid du Colombier 			// rows are accumulated in reverse order in curtab->rows
16659a747e4fSDavid du Colombier 			case Ttr:
16669a747e4fSDavid du Colombier 				if(curtab == nil) {
16679a747e4fSDavid du Colombier 					if(warn)
16689a747e4fSDavid du Colombier 						fprint(2, "warning: <TR> outside <TABLE>\n");
16699a747e4fSDavid du Colombier 					continue;
16709a747e4fSDavid du Colombier 				}
16719a747e4fSDavid du Colombier 				if(ps->inpar) {
16729a747e4fSDavid du Colombier 					popjust(ps);
16739a747e4fSDavid du Colombier 					ps->inpar = 0;
16749a747e4fSDavid du Colombier 				}
16759a747e4fSDavid du Colombier 				ps = finishcell(curtab, ps);
16769a747e4fSDavid du Colombier 				if(curtab->rows != nil)
16779a747e4fSDavid du Colombier 					curtab->rows->flags = 0;
16789a747e4fSDavid du Colombier 				curtab->rows = newtablerow(aalign(tok),
16799a747e4fSDavid du Colombier 					makebackground(nil, acolorval(tok, Abgcolor, curtab->background.color)),
16809a747e4fSDavid du Colombier 					TFparsing,
16819a747e4fSDavid du Colombier 					curtab->rows);
16829a747e4fSDavid du Colombier 				break;
16839a747e4fSDavid du Colombier 
16849a747e4fSDavid du Colombier 			case Ttr+RBRA:
16859a747e4fSDavid du Colombier 				if(curtab == nil || curtab->rows == nil) {
16869a747e4fSDavid du Colombier 					if(warn)
16879a747e4fSDavid du Colombier 						fprint(2, "warning: unexpected </TR>\n");
16889a747e4fSDavid du Colombier 					continue;
16899a747e4fSDavid du Colombier 				}
16909a747e4fSDavid du Colombier 				ps = finishcell(curtab, ps);
16919a747e4fSDavid du Colombier 				tr = curtab->rows;
16929a747e4fSDavid du Colombier 				if(tr->cells == nil) {
16939a747e4fSDavid du Colombier 					if(warn)
16949a747e4fSDavid du Colombier 						fprint(2, "warning: empty row\n");
16959a747e4fSDavid du Colombier 					curtab->rows = tr->next;
16969a747e4fSDavid du Colombier 					tr->next = nil;
16979a747e4fSDavid du Colombier 				}
16989a747e4fSDavid du Colombier 				else
16999a747e4fSDavid du Colombier 					tr->flags = 0;
17009a747e4fSDavid du Colombier 				break;
17019a747e4fSDavid du Colombier 
17029a747e4fSDavid du Colombier 			// <!ELEMENT (TT|CODE|KBD|SAMP) - - (%text)*>
17039a747e4fSDavid du Colombier 			case Ttt:
17049a747e4fSDavid du Colombier 			case Tcode:
17059a747e4fSDavid du Colombier 			case Tkbd:
17069a747e4fSDavid du Colombier 			case Tsamp:
17079a747e4fSDavid du Colombier 				pushfontstyle(ps, FntT);
17089a747e4fSDavid du Colombier 				break;
17099a747e4fSDavid du Colombier 
17109a747e4fSDavid du Colombier 			// Tags that have empty action
17119a747e4fSDavid du Colombier 			case Tabbr:
17129a747e4fSDavid du Colombier 			case Tabbr+RBRA:
17139a747e4fSDavid du Colombier 			case Tacronym:
17149a747e4fSDavid du Colombier 			case Tacronym+RBRA:
17159a747e4fSDavid du Colombier 			case Tarea+RBRA:
17169a747e4fSDavid du Colombier 			case Tbase+RBRA:
17179a747e4fSDavid du Colombier 			case Tbasefont+RBRA:
17189a747e4fSDavid du Colombier 			case Tbr+RBRA:
17199a747e4fSDavid du Colombier 			case Tdd+RBRA:
17209a747e4fSDavid du Colombier 			case Tdt+RBRA:
17219a747e4fSDavid du Colombier 			case Tframe+RBRA:
17229a747e4fSDavid du Colombier 			case Thr+RBRA:
17239a747e4fSDavid du Colombier 			case Thtml:
17249a747e4fSDavid du Colombier 			case Thtml+RBRA:
17259a747e4fSDavid du Colombier 			case Timg+RBRA:
17269a747e4fSDavid du Colombier 			case Tinput+RBRA:
17279a747e4fSDavid du Colombier 			case Tisindex+RBRA:
17289a747e4fSDavid du Colombier 			case Tli+RBRA:
17299a747e4fSDavid du Colombier 			case Tlink:
17309a747e4fSDavid du Colombier 			case Tlink+RBRA:
17319a747e4fSDavid du Colombier 			case Tmeta+RBRA:
17329a747e4fSDavid du Colombier 			case Toption+RBRA:
17339a747e4fSDavid du Colombier 			case Tparam+RBRA:
17349a747e4fSDavid du Colombier 			case Ttextarea+RBRA:
17359a747e4fSDavid du Colombier 			case Ttitle+RBRA:
17369a747e4fSDavid du Colombier 				break;
17379a747e4fSDavid du Colombier 
17389a747e4fSDavid du Colombier 
17399a747e4fSDavid du Colombier 			// Tags not implemented
17409a747e4fSDavid du Colombier 			case Tbdo:
17419a747e4fSDavid du Colombier 			case Tbdo+RBRA:
17429a747e4fSDavid du Colombier 			case Tbutton:
17439a747e4fSDavid du Colombier 			case Tbutton+RBRA:
17449a747e4fSDavid du Colombier 			case Tdel:
17459a747e4fSDavid du Colombier 			case Tdel+RBRA:
17469a747e4fSDavid du Colombier 			case Tfieldset:
17479a747e4fSDavid du Colombier 			case Tfieldset+RBRA:
17489a747e4fSDavid du Colombier 			case Tiframe:
17499a747e4fSDavid du Colombier 			case Tiframe+RBRA:
17509a747e4fSDavid du Colombier 			case Tins:
17519a747e4fSDavid du Colombier 			case Tins+RBRA:
17529a747e4fSDavid du Colombier 			case Tlabel:
17539a747e4fSDavid du Colombier 			case Tlabel+RBRA:
17549a747e4fSDavid du Colombier 			case Tlegend:
17559a747e4fSDavid du Colombier 			case Tlegend+RBRA:
17569a747e4fSDavid du Colombier 			case Tobject:
17579a747e4fSDavid du Colombier 			case Tobject+RBRA:
17589a747e4fSDavid du Colombier 			case Toptgroup:
17599a747e4fSDavid du Colombier 			case Toptgroup+RBRA:
17609a747e4fSDavid du Colombier 			case Tspan:
17619a747e4fSDavid du Colombier 			case Tspan+RBRA:
17629a747e4fSDavid du Colombier 				if(warn) {
17639a747e4fSDavid du Colombier 					if(tag > RBRA)
17649a747e4fSDavid du Colombier 						tag -= RBRA;
17659a747e4fSDavid du Colombier 					fprint(2, "warning: unimplemented HTML tag: %S\n", tagnames[tag]);
17669a747e4fSDavid du Colombier 				}
17679a747e4fSDavid du Colombier 				break;
17689a747e4fSDavid du Colombier 
17699a747e4fSDavid du Colombier 			default:
17709a747e4fSDavid du Colombier 				if(warn)
17719a747e4fSDavid du Colombier 					fprint(2, "warning: unknown HTML tag: %S\n", tok->text);
17729a747e4fSDavid du Colombier 				break;
17739a747e4fSDavid du Colombier 			}
17749a747e4fSDavid du Colombier 	}
17759a747e4fSDavid du Colombier 	// some pages omit trailing </table>
17769a747e4fSDavid du Colombier 	while(curtab != nil) {
17779a747e4fSDavid du Colombier 		if(warn)
17789a747e4fSDavid du Colombier 			fprint(2, "warning: <TABLE> not closed\n");
17799a747e4fSDavid du Colombier 		if(curtab->cells != nil) {
17809a747e4fSDavid du Colombier 			ps = finishcell(curtab, ps);
17819a747e4fSDavid du Colombier 			if(curtab->cells == nil) {
17829a747e4fSDavid du Colombier 				if(warn)
17839a747e4fSDavid du Colombier 					fprint(2, "warning: empty table\n");
17849a747e4fSDavid du Colombier 			}
17859a747e4fSDavid du Colombier 			else {
17869a747e4fSDavid du Colombier 				if(curtab->rows != nil)
17879a747e4fSDavid du Colombier 					curtab->rows->flags = 0;
17889a747e4fSDavid du Colombier 				finish_table(curtab);
17899a747e4fSDavid du Colombier 				ps->skipping = 0;
17909a747e4fSDavid du Colombier 				additem(ps, newitable(curtab), curtab->tabletok);
17919a747e4fSDavid du Colombier 				addbrk(ps, 0, 0);
17929a747e4fSDavid du Colombier 			}
17939a747e4fSDavid du Colombier 		}
17949a747e4fSDavid du Colombier 		if(is->tabstk != nil)
17959a747e4fSDavid du Colombier 			is->tabstk = is->tabstk->next;
17969a747e4fSDavid du Colombier 		curtab->next = di->tables;
17979a747e4fSDavid du Colombier 		di->tables = curtab;
17989a747e4fSDavid du Colombier 		curtab = is->tabstk;
17999a747e4fSDavid du Colombier 	}
18009a747e4fSDavid du Colombier 	outerps = lastps(ps);
18019a747e4fSDavid du Colombier 	ans = outerps->items->next;
18021066d6deSDavid du Colombier 	freeitem(outerps->items);
18039a747e4fSDavid du Colombier 	// note: ans may be nil and di->kids not nil, if there's a frameset!
18049a747e4fSDavid du Colombier 	outerps->items = newispacer(ISPnull);
18059a747e4fSDavid du Colombier 	outerps->lastit = outerps->items;
18069a747e4fSDavid du Colombier 	is->psstk = ps;
18079a747e4fSDavid du Colombier 	if(ans != nil && di->hasscripts) {
18089a747e4fSDavid du Colombier 		// TODO evalscript(nil);
18099a747e4fSDavid du Colombier 		;
18109a747e4fSDavid du Colombier 	}
18119a747e4fSDavid du Colombier 
18129a747e4fSDavid du Colombier return_ans:
18139a747e4fSDavid du Colombier 	if(dbgbuild) {
18149a747e4fSDavid du Colombier 		assert(validitems(ans));
18159a747e4fSDavid du Colombier 		if(ans == nil)
18169a747e4fSDavid du Colombier 			fprint(2, "getitems returning nil\n");
18179a747e4fSDavid du Colombier 		else
18189a747e4fSDavid du Colombier 			printitems(ans, "getitems returning:");
18199a747e4fSDavid du Colombier 	}
18209a747e4fSDavid du Colombier 	return ans;
18219a747e4fSDavid du Colombier }
18229a747e4fSDavid du Colombier 
18239a747e4fSDavid du Colombier // Concatenate together maximal set of Data tokens, starting at toks[toki+1].
18249a747e4fSDavid du Colombier // Lexer has ensured that there will either be a following non-data token or
18259a747e4fSDavid du Colombier // we will be at eof.
18269a747e4fSDavid du Colombier // Return emallocd trimmed concatenation, and update *ptoki to last used toki
18279a747e4fSDavid du Colombier static Rune*
getpcdata(Token * toks,int tokslen,int * ptoki)18289a747e4fSDavid du Colombier getpcdata(Token* toks, int tokslen, int* ptoki)
18299a747e4fSDavid du Colombier {
18309a747e4fSDavid du Colombier 	Rune*	ans;
18319a747e4fSDavid du Colombier 	Rune*	p;
18329a747e4fSDavid du Colombier 	Rune*	trimans;
18339a747e4fSDavid du Colombier 	int	anslen;
18349a747e4fSDavid du Colombier 	int	trimanslen;
18359a747e4fSDavid du Colombier 	int	toki;
18369a747e4fSDavid du Colombier 	Token*	tok;
18379a747e4fSDavid du Colombier 
18389a747e4fSDavid du Colombier 	ans = nil;
18399a747e4fSDavid du Colombier 	anslen = 0;
18409a747e4fSDavid du Colombier 	// first find length of answer
18419a747e4fSDavid du Colombier 	toki = (*ptoki) + 1;
18429a747e4fSDavid du Colombier 	while(toki < tokslen) {
18439a747e4fSDavid du Colombier 		tok = &toks[toki];
18449a747e4fSDavid du Colombier 		if(tok->tag == Data) {
18459a747e4fSDavid du Colombier 			toki++;
18465d459b5aSDavid du Colombier 			anslen += _Strlen(tok->text);
18479a747e4fSDavid du Colombier 		}
18489a747e4fSDavid du Colombier 		else
18499a747e4fSDavid du Colombier 			break;
18509a747e4fSDavid du Colombier 	}
18519a747e4fSDavid du Colombier 	// now make up the initial answer
18529a747e4fSDavid du Colombier 	if(anslen > 0) {
18539a747e4fSDavid du Colombier 		ans = _newstr(anslen);
18549a747e4fSDavid du Colombier 		p = ans;
18559a747e4fSDavid du Colombier 		toki = (*ptoki) + 1;
18569a747e4fSDavid du Colombier 		while(toki < tokslen) {
18579a747e4fSDavid du Colombier 			tok = &toks[toki];
18589a747e4fSDavid du Colombier 			if(tok->tag == Data) {
18599a747e4fSDavid du Colombier 				toki++;
18605d459b5aSDavid du Colombier 				p = _Stradd(p, tok->text, _Strlen(tok->text));
18619a747e4fSDavid du Colombier 			}
18629a747e4fSDavid du Colombier 			else
18639a747e4fSDavid du Colombier 				break;
18649a747e4fSDavid du Colombier 		}
18659a747e4fSDavid du Colombier 		*p = 0;
18669a747e4fSDavid du Colombier 		_trimwhite(ans, anslen, &trimans, &trimanslen);
18679a747e4fSDavid du Colombier 		if(trimanslen != anslen) {
18689a747e4fSDavid du Colombier 			p = ans;
18699a747e4fSDavid du Colombier 			ans = _Strndup(trimans, trimanslen);
18709a747e4fSDavid du Colombier 			free(p);
18719a747e4fSDavid du Colombier 		}
18729a747e4fSDavid du Colombier 	}
18739a747e4fSDavid du Colombier 	*ptoki = toki-1;
18749a747e4fSDavid du Colombier 	return ans;
18759a747e4fSDavid du Colombier }
18769a747e4fSDavid du Colombier 
18779a747e4fSDavid du Colombier // If still parsing head of curtab->cells list, finish it off
18789a747e4fSDavid du Colombier // by transferring the items on the head of psstk to the cell.
18799a747e4fSDavid du Colombier // Then pop the psstk and return the new psstk.
18809a747e4fSDavid du Colombier static Pstate*
finishcell(Table * curtab,Pstate * psstk)18819a747e4fSDavid du Colombier finishcell(Table* curtab, Pstate* psstk)
18829a747e4fSDavid du Colombier {
18839a747e4fSDavid du Colombier 	Tablecell*	c;
18849a747e4fSDavid du Colombier 	Pstate* psstknext;
18859a747e4fSDavid du Colombier 
18869a747e4fSDavid du Colombier 	c = curtab->cells;
18879a747e4fSDavid du Colombier 	if(c != nil) {
18889a747e4fSDavid du Colombier 		if((c->flags&TFparsing)) {
18899a747e4fSDavid du Colombier 			psstknext = psstk->next;
18909a747e4fSDavid du Colombier 			if(psstknext == nil) {
18919a747e4fSDavid du Colombier 				if(warn)
18929a747e4fSDavid du Colombier 					fprint(2, "warning: parse state stack is wrong\n");
18939a747e4fSDavid du Colombier 			}
18949a747e4fSDavid du Colombier 			else {
18959a747e4fSDavid du Colombier 				c->content = psstk->items->next;
18969a747e4fSDavid du Colombier 				c->flags &= ~TFparsing;
18979a747e4fSDavid du Colombier 				freepstate(psstk);
18989a747e4fSDavid du Colombier 				psstk = psstknext;
18999a747e4fSDavid du Colombier 			}
19009a747e4fSDavid du Colombier 		}
19019a747e4fSDavid du Colombier 	}
19029a747e4fSDavid du Colombier 	return psstk;
19039a747e4fSDavid du Colombier }
19049a747e4fSDavid du Colombier 
19059a747e4fSDavid du Colombier // Make a new Pstate for a cell, based on the old pstate, oldps.
19069a747e4fSDavid du Colombier // Also, put the new ps on the head of the oldps stack.
19079a747e4fSDavid du Colombier static Pstate*
cell_pstate(Pstate * oldps,int ishead)19089a747e4fSDavid du Colombier cell_pstate(Pstate* oldps, int ishead)
19099a747e4fSDavid du Colombier {
19109a747e4fSDavid du Colombier 	Pstate*	ps;
19119a747e4fSDavid du Colombier 	int	sty;
19129a747e4fSDavid du Colombier 
19139a747e4fSDavid du Colombier 	ps = newpstate(oldps);
19149a747e4fSDavid du Colombier 	ps->skipwhite = 1;
19159a747e4fSDavid du Colombier 	ps->curanchor = oldps->curanchor;
19169a747e4fSDavid du Colombier 	copystack(&ps->fntstylestk, &oldps->fntstylestk);
19179a747e4fSDavid du Colombier 	copystack(&ps->fntsizestk, &oldps->fntsizestk);
19189a747e4fSDavid du Colombier 	ps->curfont = oldps->curfont;
19199a747e4fSDavid du Colombier 	ps->curfg = oldps->curfg;
19209a747e4fSDavid du Colombier 	ps->curbg = oldps->curbg;
19219a747e4fSDavid du Colombier 	copystack(&ps->fgstk, &oldps->fgstk);
19229a747e4fSDavid du Colombier 	ps->adjsize = oldps->adjsize;
19239a747e4fSDavid du Colombier 	if(ishead) {
19249a747e4fSDavid du Colombier 		sty = ps->curfont%NumSize;
19259a747e4fSDavid du Colombier 		ps->curfont = FntB*NumSize + sty;
19269a747e4fSDavid du Colombier 	}
19279a747e4fSDavid du Colombier 	return ps;
19289a747e4fSDavid du Colombier }
19299a747e4fSDavid du Colombier 
19309a747e4fSDavid du Colombier // Return a new Pstate with default starting state.
19319a747e4fSDavid du Colombier // Use link to add it to head of a list, if any.
19329a747e4fSDavid du Colombier static Pstate*
newpstate(Pstate * link)19339a747e4fSDavid du Colombier newpstate(Pstate* link)
19349a747e4fSDavid du Colombier {
19359a747e4fSDavid du Colombier 	Pstate*	ps;
19369a747e4fSDavid du Colombier 
19379a747e4fSDavid du Colombier 	ps = (Pstate*)emalloc(sizeof(Pstate));
19389a747e4fSDavid du Colombier 	ps->curfont = DefFnt;
19399a747e4fSDavid du Colombier 	ps->curfg = Black;
19409a747e4fSDavid du Colombier 	ps->curbg.image = nil;
19419a747e4fSDavid du Colombier 	ps->curbg.color = White;
19429a747e4fSDavid du Colombier 	ps->curul = ULnone;
19439a747e4fSDavid du Colombier 	ps->curjust = ALleft;
19449a747e4fSDavid du Colombier 	ps->curstate = IFwrap;
19459a747e4fSDavid du Colombier 	ps->items = newispacer(ISPnull);
19469a747e4fSDavid du Colombier 	ps->lastit = ps->items;
19479a747e4fSDavid du Colombier 	ps->prelastit = nil;
19489a747e4fSDavid du Colombier 	ps->next = link;
19499a747e4fSDavid du Colombier 	return ps;
19509a747e4fSDavid du Colombier }
19519a747e4fSDavid du Colombier 
19529a747e4fSDavid du Colombier // Return last Pstate on psl list
19539a747e4fSDavid du Colombier static Pstate*
lastps(Pstate * psl)19549a747e4fSDavid du Colombier lastps(Pstate* psl)
19559a747e4fSDavid du Colombier {
19569a747e4fSDavid du Colombier 	assert(psl != nil);
19579a747e4fSDavid du Colombier 	while(psl->next != nil)
19589a747e4fSDavid du Colombier 		psl = psl->next;
19599a747e4fSDavid du Colombier 	return psl;
19609a747e4fSDavid du Colombier }
19619a747e4fSDavid du Colombier 
19629a747e4fSDavid du Colombier // Add it to end of ps item chain, adding in current state from ps.
19639a747e4fSDavid du Colombier // Also, if tok is not nil, scan it for generic attributes and assign
19649a747e4fSDavid du Colombier // the genattr field of the item accordingly.
19659a747e4fSDavid du Colombier static void
additem(Pstate * ps,Item * it,Token * tok)19669a747e4fSDavid du Colombier additem(Pstate* ps, Item* it, Token* tok)
19679a747e4fSDavid du Colombier {
19689a747e4fSDavid du Colombier 	int	aid;
19699a747e4fSDavid du Colombier 	int	any;
19709a747e4fSDavid du Colombier 	Rune*	i;
19719a747e4fSDavid du Colombier 	Rune*	c;
19729a747e4fSDavid du Colombier 	Rune*	s;
19739a747e4fSDavid du Colombier 	Rune*	t;
19749a747e4fSDavid du Colombier 	Attr*	a;
19755d459b5aSDavid du Colombier 	SEvent*	e;
19769a747e4fSDavid du Colombier 
19779a747e4fSDavid du Colombier 	if(ps->skipping) {
19789a747e4fSDavid du Colombier 		if(warn)
19799a747e4fSDavid du Colombier 			fprint(2, "warning: skipping item: %I\n", it);
19809a747e4fSDavid du Colombier 		return;
19819a747e4fSDavid du Colombier 	}
19829a747e4fSDavid du Colombier 	it->anchorid = ps->curanchor;
19839a747e4fSDavid du Colombier 	it->state |= ps->curstate;
19849a747e4fSDavid du Colombier 	if(tok != nil) {
19859a747e4fSDavid du Colombier 		any = 0;
19869a747e4fSDavid du Colombier 		i = nil;
19879a747e4fSDavid du Colombier 		c = nil;
19889a747e4fSDavid du Colombier 		s = nil;
19899a747e4fSDavid du Colombier 		t = nil;
19909a747e4fSDavid du Colombier 		e = nil;
19919a747e4fSDavid du Colombier 		for(a = tok->attr; a != nil; a = a->next) {
19929a747e4fSDavid du Colombier 			aid = a->attid;
19939a747e4fSDavid du Colombier 			if(!attrinfo[aid])
19949a747e4fSDavid du Colombier 				continue;
19959a747e4fSDavid du Colombier 			switch(aid) {
19969a747e4fSDavid du Colombier 			case Aid:
19979a747e4fSDavid du Colombier 				i = a->value;
19989a747e4fSDavid du Colombier 				break;
19999a747e4fSDavid du Colombier 
20009a747e4fSDavid du Colombier 			case Aclass:
20019a747e4fSDavid du Colombier 				c = a->value;
20029a747e4fSDavid du Colombier 				break;
20039a747e4fSDavid du Colombier 
20049a747e4fSDavid du Colombier 			case Astyle:
20059a747e4fSDavid du Colombier 				s = a->value;
20069a747e4fSDavid du Colombier 				break;
20079a747e4fSDavid du Colombier 
20089a747e4fSDavid du Colombier 			case Atitle:
20099a747e4fSDavid du Colombier 				t = a->value;
20109a747e4fSDavid du Colombier 				break;
20119a747e4fSDavid du Colombier 
20129a747e4fSDavid du Colombier 			default:
20139a747e4fSDavid du Colombier 				assert(aid >= Aonblur && aid <= Aonunload);
20149a747e4fSDavid du Colombier 				e = newscriptevent(scriptev[a->attid], a->value, e);
20159a747e4fSDavid du Colombier 				break;
20169a747e4fSDavid du Colombier 			}
20179a747e4fSDavid du Colombier 			a->value = nil;
20189a747e4fSDavid du Colombier 			any = 1;
20199a747e4fSDavid du Colombier 		}
20209a747e4fSDavid du Colombier 		if(any)
20219a747e4fSDavid du Colombier 			it->genattr = newgenattr(i, c, s, t, e);
20229a747e4fSDavid du Colombier 	}
20239a747e4fSDavid du Colombier 	ps->curstate &= ~(IFbrk|IFbrksp|IFnobrk|IFcleft|IFcright);
20249a747e4fSDavid du Colombier 	ps->prelastit = ps->lastit;
20259a747e4fSDavid du Colombier 	ps->lastit->next = it;
20269a747e4fSDavid du Colombier 	ps->lastit = it;
20279a747e4fSDavid du Colombier }
20289a747e4fSDavid du Colombier 
20299a747e4fSDavid du Colombier // Make a text item out of s,
20309a747e4fSDavid du Colombier // using current font, foreground, vertical offset and underline state.
20319a747e4fSDavid du Colombier static Item*
textit(Pstate * ps,Rune * s)20329a747e4fSDavid du Colombier textit(Pstate* ps, Rune* s)
20339a747e4fSDavid du Colombier {
20349a747e4fSDavid du Colombier 	assert(s != nil);
20359a747e4fSDavid du Colombier 	return newitext(s, ps->curfont, ps->curfg, ps->curvoff + Voffbias, ps->curul);
20369a747e4fSDavid du Colombier }
20379a747e4fSDavid du Colombier 
20389a747e4fSDavid du Colombier // Add text item or items for s, paying attention to
20399a747e4fSDavid du Colombier // current font, foreground, baseline offset, underline state,
20409a747e4fSDavid du Colombier // and literal mode.  Unless we're in literal mode, compress
20419a747e4fSDavid du Colombier // whitespace to single blank, and, if curstate has a break,
20429a747e4fSDavid du Colombier // trim any leading whitespace.  Whether in literal mode or not,
20439a747e4fSDavid du Colombier // turn nonbreaking spaces into spacer items with IFnobrk set.
20449a747e4fSDavid du Colombier //
20459a747e4fSDavid du Colombier // In literal mode, break up s at newlines and add breaks instead.
20469a747e4fSDavid du Colombier // Also replace tabs appropriate number of spaces.
20479a747e4fSDavid du Colombier // In nonliteral mode, break up the items every 100 or so characters
20489a747e4fSDavid du Colombier // just to make the layout algorithm not go quadratic.
20499a747e4fSDavid du Colombier //
20509a747e4fSDavid du Colombier // addtext assumes ownership of s.
20519a747e4fSDavid du Colombier static void
addtext(Pstate * ps,Rune * s)20529a747e4fSDavid du Colombier addtext(Pstate* ps, Rune* s)
20539a747e4fSDavid du Colombier {
20549a747e4fSDavid du Colombier 	int	n;
20559a747e4fSDavid du Colombier 	int	i;
20569a747e4fSDavid du Colombier 	int	j;
20579a747e4fSDavid du Colombier 	int	k;
20589a747e4fSDavid du Colombier 	int	col;
20599a747e4fSDavid du Colombier 	int	c;
20609a747e4fSDavid du Colombier 	int	nsp;
20619a747e4fSDavid du Colombier 	Item*	it;
20629a747e4fSDavid du Colombier 	Rune*	ss;
20639a747e4fSDavid du Colombier 	Rune*	p;
20649a747e4fSDavid du Colombier 	Rune	buf[SMALLBUFSIZE];
20659a747e4fSDavid du Colombier 
20669a747e4fSDavid du Colombier 	assert(s != nil);
20679a747e4fSDavid du Colombier 	n = runestrlen(s);
20689a747e4fSDavid du Colombier 	i = 0;
20699a747e4fSDavid du Colombier 	j = 0;
20709a747e4fSDavid du Colombier 	if(ps->literal) {
20719a747e4fSDavid du Colombier 		col = 0;
20729a747e4fSDavid du Colombier 		while(i < n) {
20739a747e4fSDavid du Colombier 			if(s[i] == '\n') {
20749a747e4fSDavid du Colombier 				if(i > j) {
20759a747e4fSDavid du Colombier 					// trim trailing blanks from line
20769a747e4fSDavid du Colombier 					for(k = i; k > j; k--)
20779a747e4fSDavid du Colombier 						if(s[k - 1] != ' ')
20789a747e4fSDavid du Colombier 							break;
20799a747e4fSDavid du Colombier 					if(k > j)
20809a747e4fSDavid du Colombier 						additem(ps, textit(ps, _Strndup(s+j, k-j)), nil);
20819a747e4fSDavid du Colombier 				}
20829a747e4fSDavid du Colombier 				addlinebrk(ps, 0);
20839a747e4fSDavid du Colombier 				j = i + 1;
20849a747e4fSDavid du Colombier 				col = 0;
20859a747e4fSDavid du Colombier 			}
20869a747e4fSDavid du Colombier 			else {
20879a747e4fSDavid du Colombier 				if(s[i] == '\t') {
20889a747e4fSDavid du Colombier 					col += i - j;
20899a747e4fSDavid du Colombier 					nsp = 8 - (col%8);
20909a747e4fSDavid du Colombier 					// make ss = s[j:i] + nsp spaces
20919a747e4fSDavid du Colombier 					ss = _newstr(i-j+nsp);
20929a747e4fSDavid du Colombier 					p = _Stradd(ss, s+j, i-j);
20939a747e4fSDavid du Colombier 					p = _Stradd(p, L"        ", nsp);
20949a747e4fSDavid du Colombier 					*p = 0;
20959a747e4fSDavid du Colombier 					additem(ps, textit(ps, ss), nil);
20969a747e4fSDavid du Colombier 					col += nsp;
20979a747e4fSDavid du Colombier 					j = i + 1;
20989a747e4fSDavid du Colombier 				}
20999a747e4fSDavid du Colombier 				else if(s[i] == NBSP) {
21009a747e4fSDavid du Colombier 					if(i > j)
21019a747e4fSDavid du Colombier 						additem(ps, textit(ps, _Strndup(s+j, i-j)), nil);
21029a747e4fSDavid du Colombier 					addnbsp(ps);
21039a747e4fSDavid du Colombier 					col += (i - j) + 1;
21049a747e4fSDavid du Colombier 					j = i + 1;
21059a747e4fSDavid du Colombier 				}
21069a747e4fSDavid du Colombier 			}
21079a747e4fSDavid du Colombier 			i++;
21089a747e4fSDavid du Colombier 		}
21099a747e4fSDavid du Colombier 		if(i > j) {
21109a747e4fSDavid du Colombier 			if(j == 0 && i == n) {
21119a747e4fSDavid du Colombier 				// just transfer s over
21129a747e4fSDavid du Colombier 				additem(ps, textit(ps, s), nil);
21139a747e4fSDavid du Colombier 			}
21149a747e4fSDavid du Colombier 			else {
21159a747e4fSDavid du Colombier 				additem(ps, textit(ps, _Strndup(s+j, i-j)), nil);
21169a747e4fSDavid du Colombier 				free(s);
21179a747e4fSDavid du Colombier 			}
21189a747e4fSDavid du Colombier 		}
21199a747e4fSDavid du Colombier 	}
21209a747e4fSDavid du Colombier 	else {	// not literal mode
21219a747e4fSDavid du Colombier 		if((ps->curstate&IFbrk) || ps->lastit == ps->items)
21229a747e4fSDavid du Colombier 			while(i < n) {
21239a747e4fSDavid du Colombier 				c = s[i];
21249a747e4fSDavid du Colombier 				if(c >= 256 || !isspace(c))
21259a747e4fSDavid du Colombier 					break;
21269a747e4fSDavid du Colombier 				i++;
21279a747e4fSDavid du Colombier 			}
21289a747e4fSDavid du Colombier 		p = buf;
21299a747e4fSDavid du Colombier 		for(j = i; i < n; i++) {
21309a747e4fSDavid du Colombier 			assert(p+i-j < buf+SMALLBUFSIZE-1);
21319a747e4fSDavid du Colombier 			c = s[i];
21329a747e4fSDavid du Colombier 			if(c == NBSP) {
21339a747e4fSDavid du Colombier 				if(i > j)
21349a747e4fSDavid du Colombier 					p = _Stradd(p, s+j, i-j);
21359a747e4fSDavid du Colombier 				if(p > buf)
21369a747e4fSDavid du Colombier 					additem(ps, textit(ps, _Strndup(buf, p-buf)), nil);
21379a747e4fSDavid du Colombier 				p = buf;
21389a747e4fSDavid du Colombier 				addnbsp(ps);
21399a747e4fSDavid du Colombier 				j = i + 1;
21409a747e4fSDavid du Colombier 				continue;
21419a747e4fSDavid du Colombier 			}
21429a747e4fSDavid du Colombier 			if(c < 256 && isspace(c)) {
21439a747e4fSDavid du Colombier 				if(i > j)
21449a747e4fSDavid du Colombier 					p = _Stradd(p, s+j, i-j);
21459a747e4fSDavid du Colombier 				*p++ = ' ';
21469a747e4fSDavid du Colombier 				while(i < n - 1) {
21479a747e4fSDavid du Colombier 					c = s[i + 1];
21489a747e4fSDavid du Colombier 					if(c >= 256 || !isspace(c))
21499a747e4fSDavid du Colombier 						break;
21509a747e4fSDavid du Colombier 					i++;
21519a747e4fSDavid du Colombier 				}
21529a747e4fSDavid du Colombier 				j = i + 1;
21539a747e4fSDavid du Colombier 			}
21549a747e4fSDavid du Colombier 			if(i - j >= 100) {
21559a747e4fSDavid du Colombier 				p = _Stradd(p, s+j, i+1-j);
21569a747e4fSDavid du Colombier 				j = i + 1;
21579a747e4fSDavid du Colombier 			}
21589a747e4fSDavid du Colombier 			if(p-buf >= 100) {
21599a747e4fSDavid du Colombier 				additem(ps, textit(ps, _Strndup(buf, p-buf)), nil);
21609a747e4fSDavid du Colombier 				p = buf;
21619a747e4fSDavid du Colombier 			}
21629a747e4fSDavid du Colombier 		}
21639a747e4fSDavid du Colombier 		if(i > j && j < n) {
21649a747e4fSDavid du Colombier 			assert(p+i-j < buf+SMALLBUFSIZE-1);
21659a747e4fSDavid du Colombier 			p = _Stradd(p, s+j, i-j);
21669a747e4fSDavid du Colombier 		}
21679a747e4fSDavid du Colombier 		// don't add a space if previous item ended in a space
21689a747e4fSDavid du Colombier 		if(p-buf == 1 && buf[0] == ' ' && ps->lastit != nil) {
21699a747e4fSDavid du Colombier 			it = ps->lastit;
21709a747e4fSDavid du Colombier 			if(it->tag == Itexttag) {
21719a747e4fSDavid du Colombier 				ss = ((Itext*)it)->s;
21725d459b5aSDavid du Colombier 				k = _Strlen(ss);
21739a747e4fSDavid du Colombier 				if(k > 0 && ss[k] == ' ')
21749a747e4fSDavid du Colombier 					p = buf;
21759a747e4fSDavid du Colombier 			}
21769a747e4fSDavid du Colombier 		}
21779a747e4fSDavid du Colombier 		if(p > buf)
21789a747e4fSDavid du Colombier 			additem(ps, textit(ps, _Strndup(buf, p-buf)), nil);
21799a747e4fSDavid du Colombier 		free(s);
21809a747e4fSDavid du Colombier 	}
21819a747e4fSDavid du Colombier }
21829a747e4fSDavid du Colombier 
21839a747e4fSDavid du Colombier // Add a break to ps->curstate, with extra space if sp is true.
21849a747e4fSDavid du Colombier // If there was a previous break, combine this one's parameters
21859a747e4fSDavid du Colombier // with that to make the amt be the max of the two and the clr
21869a747e4fSDavid du Colombier // be the most general. (amt will be 0 or 1)
21879a747e4fSDavid du Colombier // Also, if the immediately preceding item was a text item,
21889a747e4fSDavid du Colombier // trim any whitespace from the end of it, if not in literal mode.
21899a747e4fSDavid du Colombier // Finally, if this is at the very beginning of the item list
21909a747e4fSDavid du Colombier // (the only thing there is a null spacer), then don't add the space.
21919a747e4fSDavid du Colombier static void
addbrk(Pstate * ps,int sp,int clr)21929a747e4fSDavid du Colombier addbrk(Pstate* ps, int sp, int clr)
21939a747e4fSDavid du Colombier {
21949a747e4fSDavid du Colombier 	int	state;
21959a747e4fSDavid du Colombier 	Rune*	l;
21969a747e4fSDavid du Colombier 	int		nl;
21979a747e4fSDavid du Colombier 	Rune*	r;
21989a747e4fSDavid du Colombier 	int		nr;
21999a747e4fSDavid du Colombier 	Itext*	t;
22009a747e4fSDavid du Colombier 	Rune*	s;
22019a747e4fSDavid du Colombier 
22029a747e4fSDavid du Colombier 	state = ps->curstate;
22039a747e4fSDavid du Colombier 	clr = clr|(state&(IFcleft|IFcright));
22049a747e4fSDavid du Colombier 	if(sp && !(ps->lastit == ps->items))
22059a747e4fSDavid du Colombier 		sp = IFbrksp;
22069a747e4fSDavid du Colombier 	else
22079a747e4fSDavid du Colombier 		sp = 0;
22089a747e4fSDavid du Colombier 	ps->curstate = IFbrk|sp|(state&~(IFcleft|IFcright))|clr;
22099a747e4fSDavid du Colombier 	if(ps->lastit != ps->items) {
22109a747e4fSDavid du Colombier 		if(!ps->literal && ps->lastit->tag == Itexttag) {
22119a747e4fSDavid du Colombier 			t = (Itext*)ps->lastit;
22125d459b5aSDavid du Colombier 			_splitr(t->s, _Strlen(t->s), notwhitespace, &l, &nl, &r, &nr);
22139a747e4fSDavid du Colombier 			// try to avoid making empty items
22149a747e4fSDavid du Colombier 			// but not crucial f the occasional one gets through
22159a747e4fSDavid du Colombier 			if(nl == 0 && ps->prelastit != nil) {
22169a747e4fSDavid du Colombier 				ps->lastit = ps->prelastit;
22179a747e4fSDavid du Colombier 				ps->lastit->next = nil;
22189a747e4fSDavid du Colombier 				ps->prelastit = nil;
22199a747e4fSDavid du Colombier 			}
22209a747e4fSDavid du Colombier 			else {
22219a747e4fSDavid du Colombier 				s = t->s;
22229a747e4fSDavid du Colombier 				if(nl == 0) {
22239a747e4fSDavid du Colombier 					// need a non-nil pointer to empty string
22249a747e4fSDavid du Colombier 					// (_Strdup(L"") returns nil)
22259a747e4fSDavid du Colombier 					t->s = emalloc(sizeof(Rune));
22269a747e4fSDavid du Colombier 					t->s[0] = 0;
22279a747e4fSDavid du Colombier 				}
22289a747e4fSDavid du Colombier 				else
22299a747e4fSDavid du Colombier 					t->s = _Strndup(l, nl);
22309a747e4fSDavid du Colombier 				if(s)
22319a747e4fSDavid du Colombier 					free(s);
22329a747e4fSDavid du Colombier 			}
22339a747e4fSDavid du Colombier 		}
22349a747e4fSDavid du Colombier 	}
22359a747e4fSDavid du Colombier }
22369a747e4fSDavid du Colombier 
22379a747e4fSDavid du Colombier // Add break due to a <br> or a newline within a preformatted section.
22389a747e4fSDavid du Colombier // We add a null item first, with current font's height and ascent, to make
22399a747e4fSDavid du Colombier // sure that the current line takes up at least that amount of vertical space.
22409a747e4fSDavid du Colombier // This ensures that <br>s on empty lines cause blank lines, and that
22419a747e4fSDavid du Colombier // multiple <br>s in a row give multiple blank lines.
22429a747e4fSDavid du Colombier // However don't add the spacer if the previous item was something that
22439a747e4fSDavid du Colombier // takes up space itself.
22449a747e4fSDavid du Colombier static void
addlinebrk(Pstate * ps,int clr)22459a747e4fSDavid du Colombier addlinebrk(Pstate* ps, int clr)
22469a747e4fSDavid du Colombier {
22479a747e4fSDavid du Colombier 	int	obrkstate;
22489a747e4fSDavid du Colombier 	int	b;
22499a747e4fSDavid du Colombier 
22509a747e4fSDavid du Colombier 	// don't want break before our null item unless the previous item
22519a747e4fSDavid du Colombier 	// was also a null item for the purposes of line breaking
22529a747e4fSDavid du Colombier 	obrkstate = ps->curstate&(IFbrk|IFbrksp);
22539a747e4fSDavid du Colombier 	b = IFnobrk;
22549a747e4fSDavid du Colombier 	if(ps->lastit != nil) {
22559a747e4fSDavid du Colombier 		if(ps->lastit->tag == Ispacertag) {
22569a747e4fSDavid du Colombier 			if(((Ispacer*)ps->lastit)->spkind == ISPvline)
22579a747e4fSDavid du Colombier 				b = IFbrk;
22589a747e4fSDavid du Colombier 		}
22599a747e4fSDavid du Colombier 	}
22609a747e4fSDavid du Colombier 	ps->curstate = (ps->curstate&~(IFbrk|IFbrksp))|b;
22619a747e4fSDavid du Colombier 	additem(ps, newispacer(ISPvline), nil);
22629a747e4fSDavid du Colombier 	ps->curstate = (ps->curstate&~(IFbrk|IFbrksp))|obrkstate;
22639a747e4fSDavid du Colombier 	addbrk(ps, 0, clr);
22649a747e4fSDavid du Colombier }
22659a747e4fSDavid du Colombier 
22669a747e4fSDavid du Colombier // Add a nonbreakable space
22679a747e4fSDavid du Colombier static void
addnbsp(Pstate * ps)22689a747e4fSDavid du Colombier addnbsp(Pstate* ps)
22699a747e4fSDavid du Colombier {
22709a747e4fSDavid du Colombier 	// if nbsp comes right where a break was specified,
22719a747e4fSDavid du Colombier 	// do the break anyway (nbsp is being used to generate undiscardable
22729a747e4fSDavid du Colombier 	// space rather than to prevent a break)
22739a747e4fSDavid du Colombier 	if((ps->curstate&IFbrk) == 0)
22749a747e4fSDavid du Colombier 		ps->curstate |= IFnobrk;
22759a747e4fSDavid du Colombier 	additem(ps, newispacer(ISPhspace), nil);
22769a747e4fSDavid du Colombier 	// but definitely no break on next item
22779a747e4fSDavid du Colombier 	ps->curstate |= IFnobrk;
22789a747e4fSDavid du Colombier }
22799a747e4fSDavid du Colombier 
22809a747e4fSDavid du Colombier // Change hang in ps.curstate by delta.
22819a747e4fSDavid du Colombier // The amount is in 1/10ths of tabs, and is the amount that
22829a747e4fSDavid du Colombier // the current contiguous set of items with a hang value set
22839a747e4fSDavid du Colombier // is to be shifted left from its normal (indented) place.
22849a747e4fSDavid du Colombier static void
changehang(Pstate * ps,int delta)22859a747e4fSDavid du Colombier changehang(Pstate* ps, int delta)
22869a747e4fSDavid du Colombier {
22879a747e4fSDavid du Colombier 	int	amt;
22889a747e4fSDavid du Colombier 
22899a747e4fSDavid du Colombier 	amt = (ps->curstate&IFhangmask) + delta;
22909a747e4fSDavid du Colombier 	if(amt < 0) {
22919a747e4fSDavid du Colombier 		if(warn)
22929a747e4fSDavid du Colombier 			fprint(2, "warning: hang went negative\n");
22939a747e4fSDavid du Colombier 		amt = 0;
22949a747e4fSDavid du Colombier 	}
22959a747e4fSDavid du Colombier 	ps->curstate = (ps->curstate&~IFhangmask)|amt;
22969a747e4fSDavid du Colombier }
22979a747e4fSDavid du Colombier 
22989a747e4fSDavid du Colombier // Change indent in ps.curstate by delta.
22999a747e4fSDavid du Colombier static void
changeindent(Pstate * ps,int delta)23009a747e4fSDavid du Colombier changeindent(Pstate* ps, int delta)
23019a747e4fSDavid du Colombier {
23029a747e4fSDavid du Colombier 	int	amt;
23039a747e4fSDavid du Colombier 
23049a747e4fSDavid du Colombier 	amt = ((ps->curstate&IFindentmask) >> IFindentshift) + delta;
23059a747e4fSDavid du Colombier 	if(amt < 0) {
23069a747e4fSDavid du Colombier 		if(warn)
23079a747e4fSDavid du Colombier 			fprint(2, "warning: indent went negative\n");
23089a747e4fSDavid du Colombier 		amt = 0;
23099a747e4fSDavid du Colombier 	}
23109a747e4fSDavid du Colombier 	ps->curstate = (ps->curstate&~IFindentmask)|(amt << IFindentshift);
23119a747e4fSDavid du Colombier }
23129a747e4fSDavid du Colombier 
23139a747e4fSDavid du Colombier // Push val on top of stack, and also return value pushed
23149a747e4fSDavid du Colombier static int
push(Stack * stk,int val)23159a747e4fSDavid du Colombier push(Stack* stk, int val)
23169a747e4fSDavid du Colombier {
23179a747e4fSDavid du Colombier 	if(stk->n == Nestmax) {
23189a747e4fSDavid du Colombier 		if(warn)
23199a747e4fSDavid du Colombier 			fprint(2, "warning: build stack overflow\n");
23209a747e4fSDavid du Colombier 	}
23219a747e4fSDavid du Colombier 	else
23229a747e4fSDavid du Colombier 		stk->slots[stk->n++] = val;
23239a747e4fSDavid du Colombier 	return val;
23249a747e4fSDavid du Colombier }
23259a747e4fSDavid du Colombier 
23269a747e4fSDavid du Colombier // Pop top of stack
23279a747e4fSDavid du Colombier static void
pop(Stack * stk)23289a747e4fSDavid du Colombier pop(Stack* stk)
23299a747e4fSDavid du Colombier {
23309a747e4fSDavid du Colombier 	if(stk->n > 0)
23319a747e4fSDavid du Colombier 		--stk->n;
23329a747e4fSDavid du Colombier }
23339a747e4fSDavid du Colombier 
23349a747e4fSDavid du Colombier //Return top of stack, using dflt if stack is empty
23359a747e4fSDavid du Colombier static int
top(Stack * stk,int dflt)23369a747e4fSDavid du Colombier top(Stack* stk, int dflt)
23379a747e4fSDavid du Colombier {
23389a747e4fSDavid du Colombier 	if(stk->n == 0)
23399a747e4fSDavid du Colombier 		return dflt;
23409a747e4fSDavid du Colombier 	return stk->slots[stk->n-1];
23419a747e4fSDavid du Colombier }
23429a747e4fSDavid du Colombier 
23439a747e4fSDavid du Colombier // pop, then return new top, with dflt if empty
23449a747e4fSDavid du Colombier static int
popretnewtop(Stack * stk,int dflt)23459a747e4fSDavid du Colombier popretnewtop(Stack* stk, int dflt)
23469a747e4fSDavid du Colombier {
23479a747e4fSDavid du Colombier 	if(stk->n == 0)
23489a747e4fSDavid du Colombier 		return dflt;
23499a747e4fSDavid du Colombier 	stk->n--;
23509a747e4fSDavid du Colombier 	if(stk->n == 0)
23519a747e4fSDavid du Colombier 		return dflt;
23529a747e4fSDavid du Colombier 	return stk->slots[stk->n-1];
23539a747e4fSDavid du Colombier }
23549a747e4fSDavid du Colombier 
23559a747e4fSDavid du Colombier // Copy fromstk entries into tostk
23569a747e4fSDavid du Colombier static void
copystack(Stack * tostk,Stack * fromstk)23579a747e4fSDavid du Colombier copystack(Stack* tostk, Stack* fromstk)
23589a747e4fSDavid du Colombier {
23599a747e4fSDavid du Colombier 	int n;
23609a747e4fSDavid du Colombier 
23619a747e4fSDavid du Colombier 	n = fromstk->n;
23629a747e4fSDavid du Colombier 	tostk->n = n;
23639a747e4fSDavid du Colombier 	memmove(tostk->slots, fromstk->slots, n*sizeof(int));
23649a747e4fSDavid du Colombier }
23659a747e4fSDavid du Colombier 
23669a747e4fSDavid du Colombier static void
popfontstyle(Pstate * ps)23679a747e4fSDavid du Colombier popfontstyle(Pstate* ps)
23689a747e4fSDavid du Colombier {
23699a747e4fSDavid du Colombier 	pop(&ps->fntstylestk);
23709a747e4fSDavid du Colombier 	setcurfont(ps);
23719a747e4fSDavid du Colombier }
23729a747e4fSDavid du Colombier 
23739a747e4fSDavid du Colombier static void
pushfontstyle(Pstate * ps,int sty)23749a747e4fSDavid du Colombier pushfontstyle(Pstate* ps, int sty)
23759a747e4fSDavid du Colombier {
23769a747e4fSDavid du Colombier 	push(&ps->fntstylestk, sty);
23779a747e4fSDavid du Colombier 	setcurfont(ps);
23789a747e4fSDavid du Colombier }
23799a747e4fSDavid du Colombier 
23809a747e4fSDavid du Colombier static void
popfontsize(Pstate * ps)23819a747e4fSDavid du Colombier popfontsize(Pstate* ps)
23829a747e4fSDavid du Colombier {
23839a747e4fSDavid du Colombier 	pop(&ps->fntsizestk);
23849a747e4fSDavid du Colombier 	setcurfont(ps);
23859a747e4fSDavid du Colombier }
23869a747e4fSDavid du Colombier 
23879a747e4fSDavid du Colombier static void
pushfontsize(Pstate * ps,int sz)23889a747e4fSDavid du Colombier pushfontsize(Pstate* ps, int sz)
23899a747e4fSDavid du Colombier {
23909a747e4fSDavid du Colombier 	push(&ps->fntsizestk, sz);
23919a747e4fSDavid du Colombier 	setcurfont(ps);
23929a747e4fSDavid du Colombier }
23939a747e4fSDavid du Colombier 
23949a747e4fSDavid du Colombier static void
setcurfont(Pstate * ps)23959a747e4fSDavid du Colombier setcurfont(Pstate* ps)
23969a747e4fSDavid du Colombier {
23979a747e4fSDavid du Colombier 	int	sty;
23989a747e4fSDavid du Colombier 	int	sz;
23999a747e4fSDavid du Colombier 
24009a747e4fSDavid du Colombier 	sty = top(&ps->fntstylestk, FntR);
24019a747e4fSDavid du Colombier 	sz = top(&ps->fntsizestk, Normal);
24029a747e4fSDavid du Colombier 	if(sz < Tiny)
24039a747e4fSDavid du Colombier 		sz = Tiny;
24049a747e4fSDavid du Colombier 	if(sz > Verylarge)
24059a747e4fSDavid du Colombier 		sz = Verylarge;
24069a747e4fSDavid du Colombier 	ps->curfont = sty*NumSize + sz;
24079a747e4fSDavid du Colombier }
24089a747e4fSDavid du Colombier 
24099a747e4fSDavid du Colombier static void
popjust(Pstate * ps)24109a747e4fSDavid du Colombier popjust(Pstate* ps)
24119a747e4fSDavid du Colombier {
24129a747e4fSDavid du Colombier 	pop(&ps->juststk);
24139a747e4fSDavid du Colombier 	setcurjust(ps);
24149a747e4fSDavid du Colombier }
24159a747e4fSDavid du Colombier 
24169a747e4fSDavid du Colombier static void
pushjust(Pstate * ps,int j)24179a747e4fSDavid du Colombier pushjust(Pstate* ps, int j)
24189a747e4fSDavid du Colombier {
24199a747e4fSDavid du Colombier 	push(&ps->juststk, j);
24209a747e4fSDavid du Colombier 	setcurjust(ps);
24219a747e4fSDavid du Colombier }
24229a747e4fSDavid du Colombier 
24239a747e4fSDavid du Colombier static void
setcurjust(Pstate * ps)24249a747e4fSDavid du Colombier setcurjust(Pstate* ps)
24259a747e4fSDavid du Colombier {
24269a747e4fSDavid du Colombier 	int	j;
24279a747e4fSDavid du Colombier 	int	state;
24289a747e4fSDavid du Colombier 
24299a747e4fSDavid du Colombier 	j = top(&ps->juststk, ALleft);
24309a747e4fSDavid du Colombier 	if(j != ps->curjust) {
24319a747e4fSDavid du Colombier 		ps->curjust = j;
24329a747e4fSDavid du Colombier 		state = ps->curstate;
24339a747e4fSDavid du Colombier 		state &= ~(IFrjust|IFcjust);
24349a747e4fSDavid du Colombier 		if(j == ALcenter)
24359a747e4fSDavid du Colombier 			state |= IFcjust;
24369a747e4fSDavid du Colombier 		else if(j == ALright)
24379a747e4fSDavid du Colombier 			state |= IFrjust;
24389a747e4fSDavid du Colombier 		ps->curstate = state;
24399a747e4fSDavid du Colombier 	}
24409a747e4fSDavid du Colombier }
24419a747e4fSDavid du Colombier 
24429a747e4fSDavid du Colombier // Do final rearrangement after table parsing is finished
24439a747e4fSDavid du Colombier // and assign cells to grid points
24449a747e4fSDavid du Colombier static void
finish_table(Table * t)24459a747e4fSDavid du Colombier finish_table(Table* t)
24469a747e4fSDavid du Colombier {
24479a747e4fSDavid du Colombier 	int	ncol;
24489a747e4fSDavid du Colombier 	int	nrow;
24499a747e4fSDavid du Colombier 	int	r;
24509a747e4fSDavid du Colombier 	Tablerow*	rl;
24519a747e4fSDavid du Colombier 	Tablecell*	cl;
24529a747e4fSDavid du Colombier 	int*	rowspancnt;
24539a747e4fSDavid du Colombier 	Tablecell**	rowspancell;
24549a747e4fSDavid du Colombier 	int	ri;
24559a747e4fSDavid du Colombier 	int	ci;
24569a747e4fSDavid du Colombier 	Tablecell*	c;
24579a747e4fSDavid du Colombier 	Tablecell*	cnext;
24589a747e4fSDavid du Colombier 	Tablerow*	row;
24599a747e4fSDavid du Colombier 	Tablerow*	rownext;
24609a747e4fSDavid du Colombier 	int	rcols;
24619a747e4fSDavid du Colombier 	int	newncol;
24629a747e4fSDavid du Colombier 	int	k;
24639a747e4fSDavid du Colombier 	int	j;
24649a747e4fSDavid du Colombier 	int	cspan;
24659a747e4fSDavid du Colombier 	int	rspan;
24669a747e4fSDavid du Colombier 	int	i;
24679a747e4fSDavid du Colombier 
24689a747e4fSDavid du Colombier 	rl = t->rows;
24699a747e4fSDavid du Colombier 	t->nrow = nrow = _listlen((List*)rl);
24709a747e4fSDavid du Colombier 	t->rows = (Tablerow*)emalloc(nrow * sizeof(Tablerow));
24719a747e4fSDavid du Colombier 	ncol = 0;
24729a747e4fSDavid du Colombier 	r = nrow - 1;
24739a747e4fSDavid du Colombier 	for(row = rl; row != nil; row = rownext) {
24749a747e4fSDavid du Colombier 		// copy the data from the allocated Tablerow into the array slot
24759a747e4fSDavid du Colombier 		t->rows[r] = *row;
24769a747e4fSDavid du Colombier 		rownext = row->next;
24779a747e4fSDavid du Colombier 		row = &t->rows[r];
24789a747e4fSDavid du Colombier 		r--;
24799a747e4fSDavid du Colombier 		rcols = 0;
24809a747e4fSDavid du Colombier 		c = row->cells;
24819a747e4fSDavid du Colombier 
24829a747e4fSDavid du Colombier 		// If rowspan is > 1 but this is the last row,
24839a747e4fSDavid du Colombier 		// reset the rowspan
24849a747e4fSDavid du Colombier 		if(c != nil && c->rowspan > 1 && r == nrow-2)
24859a747e4fSDavid du Colombier 				c->rowspan = 1;
24869a747e4fSDavid du Colombier 
24879a747e4fSDavid du Colombier 		// reverse row->cells list (along nextinrow pointers)
24889a747e4fSDavid du Colombier 		row->cells = nil;
24899a747e4fSDavid du Colombier 		while(c != nil) {
24909a747e4fSDavid du Colombier 			cnext = c->nextinrow;
24919a747e4fSDavid du Colombier 			c->nextinrow = row->cells;
24929a747e4fSDavid du Colombier 			row->cells = c;
24939a747e4fSDavid du Colombier 			rcols += c->colspan;
24949a747e4fSDavid du Colombier 			c = cnext;
24959a747e4fSDavid du Colombier 		}
24969a747e4fSDavid du Colombier 		if(rcols > ncol)
24979a747e4fSDavid du Colombier 			ncol = rcols;
24989a747e4fSDavid du Colombier 	}
24999a747e4fSDavid du Colombier 	t->ncol = ncol;
25009a747e4fSDavid du Colombier 	t->cols = (Tablecol*)emalloc(ncol * sizeof(Tablecol));
25019a747e4fSDavid du Colombier 
25029a747e4fSDavid du Colombier 	// Reverse cells just so they are drawn in source order.
25039a747e4fSDavid du Colombier 	// Also, trim their contents so they don't end in whitespace.
25049a747e4fSDavid du Colombier 	t->cells = (Tablecell*)_revlist((List*)t->cells);
25059a747e4fSDavid du Colombier 	for(c = t->cells; c != nil; c= c->next)
25069a747e4fSDavid du Colombier 		trim_cell(c);
25079a747e4fSDavid du Colombier 	t->grid = (Tablecell***)emalloc(nrow * sizeof(Tablecell**));
25089a747e4fSDavid du Colombier 	for(i = 0; i < nrow; i++)
25099a747e4fSDavid du Colombier 		t->grid[i] = (Tablecell**)emalloc(ncol * sizeof(Tablecell*));
25109a747e4fSDavid du Colombier 
25119a747e4fSDavid du Colombier 	// The following arrays keep track of cells that are spanning
25129a747e4fSDavid du Colombier 	// multiple rows;  rowspancnt[i] is the number of rows left
25139a747e4fSDavid du Colombier 	// to be spanned in column i.
25149a747e4fSDavid du Colombier 	// When done, cell's (row,col) is upper left grid point.
25159a747e4fSDavid du Colombier 	rowspancnt = (int*)emalloc(ncol * sizeof(int));
25169a747e4fSDavid du Colombier 	rowspancell = (Tablecell**)emalloc(ncol * sizeof(Tablecell*));
25179a747e4fSDavid du Colombier 	for(ri = 0; ri < nrow; ri++) {
25189a747e4fSDavid du Colombier 		row = &t->rows[ri];
25199a747e4fSDavid du Colombier 		cl = row->cells;
25209a747e4fSDavid du Colombier 		ci = 0;
25219a747e4fSDavid du Colombier 		while(ci < ncol || cl != nil) {
25229a747e4fSDavid du Colombier 			if(ci < ncol && rowspancnt[ci] > 0) {
25239a747e4fSDavid du Colombier 				t->grid[ri][ci] = rowspancell[ci];
25249a747e4fSDavid du Colombier 				rowspancnt[ci]--;
25259a747e4fSDavid du Colombier 				ci++;
25269a747e4fSDavid du Colombier 			}
25279a747e4fSDavid du Colombier 			else {
25289a747e4fSDavid du Colombier 				if(cl == nil) {
25299a747e4fSDavid du Colombier 					ci++;
25309a747e4fSDavid du Colombier 					continue;
25319a747e4fSDavid du Colombier 				}
25329a747e4fSDavid du Colombier 				c = cl;
25339a747e4fSDavid du Colombier 				cl = cl->nextinrow;
25349a747e4fSDavid du Colombier 				cspan = c->colspan;
25359a747e4fSDavid du Colombier 				rspan = c->rowspan;
25369a747e4fSDavid du Colombier 				if(ci + cspan > ncol) {
25379a747e4fSDavid du Colombier 					// because of row spanning, we calculated
25389a747e4fSDavid du Colombier 					// ncol incorrectly; adjust it
25399a747e4fSDavid du Colombier 					newncol = ci + cspan;
25409a747e4fSDavid du Colombier 					t->cols = (Tablecol*)erealloc(t->cols, newncol * sizeof(Tablecol));
25419a747e4fSDavid du Colombier 					rowspancnt = (int*)erealloc(rowspancnt, newncol * sizeof(int));
25429a747e4fSDavid du Colombier 					rowspancell = (Tablecell**)erealloc(rowspancell, newncol * sizeof(Tablecell*));
25439a747e4fSDavid du Colombier 					k = newncol-ncol;
25449a747e4fSDavid du Colombier 					memset(t->cols+ncol, 0, k*sizeof(Tablecol));
25459a747e4fSDavid du Colombier 					memset(rowspancnt+ncol, 0, k*sizeof(int));
25469a747e4fSDavid du Colombier 					memset(rowspancell+ncol, 0, k*sizeof(Tablecell*));
25479a747e4fSDavid du Colombier 					for(j = 0; j < nrow; j++) {
25489a747e4fSDavid du Colombier 						t->grid[j] = (Tablecell**)erealloc(t->grid[j], newncol * sizeof(Tablecell*));
25499a747e4fSDavid du Colombier 						memset(t->grid[j], 0, k*sizeof(Tablecell*));
25509a747e4fSDavid du Colombier 					}
25519a747e4fSDavid du Colombier 					t->ncol = ncol = newncol;
25529a747e4fSDavid du Colombier 				}
25539a747e4fSDavid du Colombier 				c->row = ri;
25549a747e4fSDavid du Colombier 				c->col = ci;
25559a747e4fSDavid du Colombier 				for(i = 0; i < cspan; i++) {
25569a747e4fSDavid du Colombier 					t->grid[ri][ci] = c;
25579a747e4fSDavid du Colombier 					if(rspan > 1) {
25589a747e4fSDavid du Colombier 						rowspancnt[ci] = rspan - 1;
25599a747e4fSDavid du Colombier 						rowspancell[ci] = c;
25609a747e4fSDavid du Colombier 					}
25619a747e4fSDavid du Colombier 					ci++;
25629a747e4fSDavid du Colombier 				}
25639a747e4fSDavid du Colombier 			}
25649a747e4fSDavid du Colombier 		}
25659a747e4fSDavid du Colombier 	}
25661066d6deSDavid du Colombier 	free(rowspancnt);
25671066d6deSDavid du Colombier 	free(rowspancell);
25689a747e4fSDavid du Colombier }
25699a747e4fSDavid du Colombier 
25709a747e4fSDavid du Colombier // Remove tail of cell content until it isn't whitespace.
25719a747e4fSDavid du Colombier static void
trim_cell(Tablecell * c)25729a747e4fSDavid du Colombier trim_cell(Tablecell* c)
25739a747e4fSDavid du Colombier {
25749a747e4fSDavid du Colombier 	int	dropping;
25759a747e4fSDavid du Colombier 	Rune*	s;
25769a747e4fSDavid du Colombier 	Rune*	x;
25779a747e4fSDavid du Colombier 	Rune*	y;
25789a747e4fSDavid du Colombier 	int		nx;
25799a747e4fSDavid du Colombier 	int		ny;
25809a747e4fSDavid du Colombier 	Item*	p;
25819a747e4fSDavid du Colombier 	Itext*	q;
25829a747e4fSDavid du Colombier 	Item*	pprev;
25839a747e4fSDavid du Colombier 
25849a747e4fSDavid du Colombier 	dropping = 1;
25859a747e4fSDavid du Colombier 	while(c->content != nil && dropping) {
25869a747e4fSDavid du Colombier 		p = c->content;
25879a747e4fSDavid du Colombier 		pprev = nil;
25889a747e4fSDavid du Colombier 		while(p->next != nil) {
25899a747e4fSDavid du Colombier 			pprev = p;
25909a747e4fSDavid du Colombier 			p = p->next;
25919a747e4fSDavid du Colombier 		}
25929a747e4fSDavid du Colombier 		dropping = 0;
25939a747e4fSDavid du Colombier 		if(!(p->state&IFnobrk)) {
25949a747e4fSDavid du Colombier 			if(p->tag == Itexttag) {
25959a747e4fSDavid du Colombier 				q = (Itext*)p;
25969a747e4fSDavid du Colombier 				s = q->s;
25975d459b5aSDavid du Colombier 				_splitr(s, _Strlen(s), notwhitespace, &x, &nx, &y, &ny);
25989a747e4fSDavid du Colombier 				if(nx != 0 && ny != 0) {
25999a747e4fSDavid du Colombier 					q->s = _Strndup(x, nx);
26009a747e4fSDavid du Colombier 					free(s);
26019a747e4fSDavid du Colombier 				}
26029a747e4fSDavid du Colombier 				break;
26039a747e4fSDavid du Colombier 			}
26049a747e4fSDavid du Colombier 		}
26059a747e4fSDavid du Colombier 		if(dropping) {
26069a747e4fSDavid du Colombier 			if(pprev == nil)
26079a747e4fSDavid du Colombier 				c->content = nil;
26089a747e4fSDavid du Colombier 			else
26099a747e4fSDavid du Colombier 				pprev->next = nil;
26109a747e4fSDavid du Colombier 			freeitem(p);
26119a747e4fSDavid du Colombier 		}
26129a747e4fSDavid du Colombier 	}
26139a747e4fSDavid du Colombier }
26149a747e4fSDavid du Colombier 
26159a747e4fSDavid du Colombier // Caller must free answer (eventually).
26169a747e4fSDavid du Colombier static Rune*
listmark(uchar ty,int n)26179a747e4fSDavid du Colombier listmark(uchar ty, int n)
26189a747e4fSDavid du Colombier {
26199a747e4fSDavid du Colombier 	Rune*	s;
26209a747e4fSDavid du Colombier 	Rune*	t;
26219a747e4fSDavid du Colombier 	int	n2;
26229a747e4fSDavid du Colombier 	int	i;
26239a747e4fSDavid du Colombier 
26249a747e4fSDavid du Colombier 	s = nil;
26259a747e4fSDavid du Colombier 	switch(ty) {
26269a747e4fSDavid du Colombier 	case LTdisc:
26279a747e4fSDavid du Colombier 	case LTsquare:
26289a747e4fSDavid du Colombier 	case LTcircle:
26299a747e4fSDavid du Colombier 		s = _newstr(1);
26309a747e4fSDavid du Colombier 		s[0] = (ty == LTdisc)? 0x2022		// bullet
26319a747e4fSDavid du Colombier 			: ((ty == LTsquare)? 0x220e	// filled square
26329a747e4fSDavid du Colombier 			    : 0x2218);				// degree
26339a747e4fSDavid du Colombier 		s[1] = 0;
26349a747e4fSDavid du Colombier 		break;
26359a747e4fSDavid du Colombier 
26369a747e4fSDavid du Colombier 	case LT1:
2637c93608ccSDavid du Colombier 		s = runesmprint("%d.", n);
26389a747e4fSDavid du Colombier 		break;
26399a747e4fSDavid du Colombier 
26409a747e4fSDavid du Colombier 	case LTa:
26419a747e4fSDavid du Colombier 	case LTA:
26429a747e4fSDavid du Colombier 		n--;
26439a747e4fSDavid du Colombier 		i = 0;
26449a747e4fSDavid du Colombier 		if(n < 0)
26459a747e4fSDavid du Colombier 			n = 0;
26469a747e4fSDavid du Colombier 		s = _newstr((n <= 25)? 2 : 3);
26479a747e4fSDavid du Colombier 		if(n > 25) {
26489a747e4fSDavid du Colombier 			n2 = n%26;
26499a747e4fSDavid du Colombier 			n /= 26;
26509a747e4fSDavid du Colombier 			if(n2 > 25)
26519a747e4fSDavid du Colombier 				n2 = 25;
26529a747e4fSDavid du Colombier 			s[i++] = n2 + (ty == LTa)? 'a' : 'A';
26539a747e4fSDavid du Colombier 		}
26549a747e4fSDavid du Colombier 		s[i++] = n + (ty == LTa)? 'a' : 'A';
26559a747e4fSDavid du Colombier 		s[i++] = '.';
26569a747e4fSDavid du Colombier 		s[i] = 0;
26579a747e4fSDavid du Colombier 		break;
26589a747e4fSDavid du Colombier 
26599a747e4fSDavid du Colombier 	case LTi:
26609a747e4fSDavid du Colombier 	case LTI:
26619a747e4fSDavid du Colombier 		if(n >= NROMAN) {
26629a747e4fSDavid du Colombier 			if(warn)
26639a747e4fSDavid du Colombier 				fprint(2, "warning: unimplemented roman number > %d\n", NROMAN);
26649a747e4fSDavid du Colombier 			n = NROMAN;
26659a747e4fSDavid du Colombier 		}
26669a747e4fSDavid du Colombier 		t = roman[n - 1];
26675d459b5aSDavid du Colombier 		n2 = _Strlen(t);
26689a747e4fSDavid du Colombier 		s = _newstr(n2+1);
26699a747e4fSDavid du Colombier 		for(i = 0; i < n2; i++)
26709a747e4fSDavid du Colombier 			s[i] = (ty == LTi)? tolower(t[i]) : t[i];
26719a747e4fSDavid du Colombier 		s[i++] = '.';
26729a747e4fSDavid du Colombier 		s[i] = 0;
26739a747e4fSDavid du Colombier 		break;
26749a747e4fSDavid du Colombier 	}
26759a747e4fSDavid du Colombier 	return s;
26769a747e4fSDavid du Colombier }
26779a747e4fSDavid du Colombier 
26789a747e4fSDavid du Colombier // Find map with given name in di.maps.
26799a747e4fSDavid du Colombier // If not there, add one, copying name.
26809a747e4fSDavid du Colombier // Ownership of map remains with di->maps list.
26819a747e4fSDavid du Colombier static Map*
getmap(Docinfo * di,Rune * name)26829a747e4fSDavid du Colombier getmap(Docinfo* di, Rune* name)
26839a747e4fSDavid du Colombier {
26849a747e4fSDavid du Colombier 	Map*	m;
26859a747e4fSDavid du Colombier 
26869a747e4fSDavid du Colombier 	for(m = di->maps; m != nil; m = m->next) {
26875d459b5aSDavid du Colombier 		if(!_Strcmp(name, m->name))
26889a747e4fSDavid du Colombier 			return m;
26899a747e4fSDavid du Colombier 	}
26909a747e4fSDavid du Colombier 	m = (Map*)emalloc(sizeof(Map));
26919a747e4fSDavid du Colombier 	m->name = _Strdup(name);
26929a747e4fSDavid du Colombier 	m->areas = nil;
26939a747e4fSDavid du Colombier 	m->next = di->maps;
26949a747e4fSDavid du Colombier 	di->maps = m;
26959a747e4fSDavid du Colombier 	return m;
26969a747e4fSDavid du Colombier }
26979a747e4fSDavid du Colombier 
26989a747e4fSDavid du Colombier // Transfers ownership of href to Area
26999a747e4fSDavid du Colombier static Area*
newarea(int shape,Rune * href,int target,Area * link)27009a747e4fSDavid du Colombier newarea(int shape, Rune* href, int target, Area* link)
27019a747e4fSDavid du Colombier {
27029a747e4fSDavid du Colombier 	Area* a;
27039a747e4fSDavid du Colombier 
27049a747e4fSDavid du Colombier 	a = (Area*)emalloc(sizeof(Area));
27059a747e4fSDavid du Colombier 	a->shape = shape;
27069a747e4fSDavid du Colombier 	a->href = href;
27079a747e4fSDavid du Colombier 	a->target = target;
27089a747e4fSDavid du Colombier 	a->next = link;
27099a747e4fSDavid du Colombier 	return a;
27109a747e4fSDavid du Colombier }
27119a747e4fSDavid du Colombier 
27129a747e4fSDavid du Colombier // Return string value associated with attid in tok, nil if none.
27139a747e4fSDavid du Colombier // Caller must free the result (eventually).
27149a747e4fSDavid du Colombier static Rune*
aval(Token * tok,int attid)27159a747e4fSDavid du Colombier aval(Token* tok, int attid)
27169a747e4fSDavid du Colombier {
27179a747e4fSDavid du Colombier 	Rune*	ans;
27189a747e4fSDavid du Colombier 
27199a747e4fSDavid du Colombier 	_tokaval(tok, attid, &ans, 1);	// transfers string ownership from token to ans
27209a747e4fSDavid du Colombier 	return ans;
27219a747e4fSDavid du Colombier }
27229a747e4fSDavid du Colombier 
27239a747e4fSDavid du Colombier // Like aval, but use dflt if there was no such attribute in tok.
27249a747e4fSDavid du Colombier // Caller must free the result (eventually).
27259a747e4fSDavid du Colombier static Rune*
astrval(Token * tok,int attid,Rune * dflt)27269a747e4fSDavid du Colombier astrval(Token* tok, int attid, Rune* dflt)
27279a747e4fSDavid du Colombier {
27289a747e4fSDavid du Colombier 	Rune*	ans;
27299a747e4fSDavid du Colombier 
27309a747e4fSDavid du Colombier 	if(_tokaval(tok, attid, &ans, 1))
27319a747e4fSDavid du Colombier 		return ans;	// transfers string ownership from token to ans
27329a747e4fSDavid du Colombier 	else
27339a747e4fSDavid du Colombier 		return _Strdup(dflt);
27349a747e4fSDavid du Colombier }
27359a747e4fSDavid du Colombier 
27369a747e4fSDavid du Colombier // Here we're supposed to convert to an int,
27379a747e4fSDavid du Colombier // and have a default when not found
27389a747e4fSDavid du Colombier static int
aintval(Token * tok,int attid,int dflt)27399a747e4fSDavid du Colombier aintval(Token* tok, int attid, int dflt)
27409a747e4fSDavid du Colombier {
27419a747e4fSDavid du Colombier 	Rune*	ans;
27429a747e4fSDavid du Colombier 
27439a747e4fSDavid du Colombier 	if(!_tokaval(tok, attid, &ans, 0) || ans == nil)
27449a747e4fSDavid du Colombier 		return dflt;
27459a747e4fSDavid du Colombier 	else
27469a747e4fSDavid du Colombier 		return toint(ans);
27479a747e4fSDavid du Colombier }
27489a747e4fSDavid du Colombier 
27499a747e4fSDavid du Colombier // Like aintval, but result should be >= 0
27509a747e4fSDavid du Colombier static int
auintval(Token * tok,int attid,int dflt)27519a747e4fSDavid du Colombier auintval(Token* tok, int attid, int dflt)
27529a747e4fSDavid du Colombier {
27539a747e4fSDavid du Colombier 	Rune* ans;
27549a747e4fSDavid du Colombier 	int v;
27559a747e4fSDavid du Colombier 
27569a747e4fSDavid du Colombier 	if(!_tokaval(tok, attid, &ans, 0) || ans == nil)
27579a747e4fSDavid du Colombier 		return dflt;
27589a747e4fSDavid du Colombier 	else {
27599a747e4fSDavid du Colombier 		v = toint(ans);
27609a747e4fSDavid du Colombier 		return v >= 0? v : 0;
27619a747e4fSDavid du Colombier 	}
27629a747e4fSDavid du Colombier }
27639a747e4fSDavid du Colombier 
27649a747e4fSDavid du Colombier // int conversion, but with possible error check (if warning)
27659a747e4fSDavid du Colombier static int
toint(Rune * s)27669a747e4fSDavid du Colombier toint(Rune* s)
27679a747e4fSDavid du Colombier {
27689a747e4fSDavid du Colombier 	int ans;
27699a747e4fSDavid du Colombier 	Rune* eptr;
27709a747e4fSDavid du Colombier 
27719a747e4fSDavid du Colombier 	ans = _Strtol(s, &eptr, 10);
27729a747e4fSDavid du Colombier 	if(warn) {
27739a747e4fSDavid du Colombier 		if(*eptr != 0) {
27749a747e4fSDavid du Colombier 			eptr = _Strclass(eptr, notwhitespace);
27759a747e4fSDavid du Colombier 			if(eptr != nil)
27769a747e4fSDavid du Colombier 				fprint(2, "warning: expected integer, got %S\n", s);
27779a747e4fSDavid du Colombier 		}
27789a747e4fSDavid du Colombier 	}
27799a747e4fSDavid du Colombier 	return ans;
27809a747e4fSDavid du Colombier }
27819a747e4fSDavid du Colombier 
27829a747e4fSDavid du Colombier // Attribute value when need a table to convert strings to ints
27839a747e4fSDavid du Colombier static int
atabval(Token * tok,int attid,StringInt * tab,int ntab,int dflt)27849a747e4fSDavid du Colombier atabval(Token* tok, int attid, StringInt* tab, int ntab, int dflt)
27859a747e4fSDavid du Colombier {
27869a747e4fSDavid du Colombier 	Rune*	aval;
27879a747e4fSDavid du Colombier 	int	ans;
27889a747e4fSDavid du Colombier 
27899a747e4fSDavid du Colombier 	ans = dflt;
27909a747e4fSDavid du Colombier 	if(_tokaval(tok, attid, &aval, 0)) {
27915d459b5aSDavid du Colombier 		if(!_lookup(tab, ntab, aval, _Strlen(aval), &ans)) {
27929a747e4fSDavid du Colombier 			ans = dflt;
27939a747e4fSDavid du Colombier 			if(warn)
27949a747e4fSDavid du Colombier 				fprint(2, "warning: name not found in table lookup: %S\n", aval);
27959a747e4fSDavid du Colombier 		}
27969a747e4fSDavid du Colombier 	}
27979a747e4fSDavid du Colombier 	return ans;
27989a747e4fSDavid du Colombier }
27999a747e4fSDavid du Colombier 
28009a747e4fSDavid du Colombier // Attribute value when supposed to be a color
28019a747e4fSDavid du Colombier static int
acolorval(Token * tok,int attid,int dflt)28029a747e4fSDavid du Colombier acolorval(Token* tok, int attid, int dflt)
28039a747e4fSDavid du Colombier {
28049a747e4fSDavid du Colombier 	Rune*	aval;
28059a747e4fSDavid du Colombier 	int	ans;
28069a747e4fSDavid du Colombier 
28079a747e4fSDavid du Colombier 	ans = dflt;
28089a747e4fSDavid du Colombier 	if(_tokaval(tok, attid, &aval, 0))
28099a747e4fSDavid du Colombier 		ans = color(aval, dflt);
28109a747e4fSDavid du Colombier 	return ans;
28119a747e4fSDavid du Colombier }
28129a747e4fSDavid du Colombier 
28139a747e4fSDavid du Colombier // Attribute value when supposed to be a target frame name
28149a747e4fSDavid du Colombier static int
atargval(Token * tok,int dflt)28159a747e4fSDavid du Colombier atargval(Token* tok, int dflt)
28169a747e4fSDavid du Colombier {
28179a747e4fSDavid du Colombier 	int	ans;
28189a747e4fSDavid du Colombier 	Rune*	aval;
28199a747e4fSDavid du Colombier 
28209a747e4fSDavid du Colombier 	ans = dflt;
28219a747e4fSDavid du Colombier 	if(_tokaval(tok, Atarget, &aval, 0)){
28229a747e4fSDavid du Colombier 		ans = targetid(aval);
28239a747e4fSDavid du Colombier 	}
28249a747e4fSDavid du Colombier 	return ans;
28259a747e4fSDavid du Colombier }
28269a747e4fSDavid du Colombier 
28279a747e4fSDavid du Colombier // special for list types, where "i" and "I" are different,
28289a747e4fSDavid du Colombier // but "square" and "SQUARE" are the same
28299a747e4fSDavid du Colombier static int
listtyval(Token * tok,int dflt)28309a747e4fSDavid du Colombier listtyval(Token* tok, int dflt)
28319a747e4fSDavid du Colombier {
28329a747e4fSDavid du Colombier 	Rune*	aval;
28339a747e4fSDavid du Colombier 	int	ans;
28349a747e4fSDavid du Colombier 	int	n;
28359a747e4fSDavid du Colombier 
28369a747e4fSDavid du Colombier 	ans = dflt;
28379a747e4fSDavid du Colombier 	if(_tokaval(tok, Atype, &aval, 0)) {
28385d459b5aSDavid du Colombier 		n = _Strlen(aval);
28399a747e4fSDavid du Colombier 		if(n == 1) {
28409a747e4fSDavid du Colombier 			switch(aval[0]) {
28419a747e4fSDavid du Colombier 			case '1':
28429a747e4fSDavid du Colombier 				ans = LT1;
28439a747e4fSDavid du Colombier 				break;
28449a747e4fSDavid du Colombier 			case 'A':
28459a747e4fSDavid du Colombier 				ans = LTA;
28469a747e4fSDavid du Colombier 				break;
28479a747e4fSDavid du Colombier 			case 'I':
28489a747e4fSDavid du Colombier 				ans = LTI;
28499a747e4fSDavid du Colombier 				break;
28509a747e4fSDavid du Colombier 			case 'a':
28519a747e4fSDavid du Colombier 				ans = LTa;
28529a747e4fSDavid du Colombier 				break;
28539a747e4fSDavid du Colombier 			case 'i':
28549a747e4fSDavid du Colombier 				ans = LTi;
28559a747e4fSDavid du Colombier 			default:
28569a747e4fSDavid du Colombier 				if(warn)
28579a747e4fSDavid du Colombier 					fprint(2, "warning: unknown list element type %c\n", aval[0]);
28589a747e4fSDavid du Colombier 			}
28599a747e4fSDavid du Colombier 		}
28609a747e4fSDavid du Colombier 		else {
28619a747e4fSDavid du Colombier 			if(!_Strncmpci(aval, n, L"circle"))
28629a747e4fSDavid du Colombier 				ans = LTcircle;
28639a747e4fSDavid du Colombier 			else if(!_Strncmpci(aval, n, L"disc"))
28649a747e4fSDavid du Colombier 				ans = LTdisc;
28659a747e4fSDavid du Colombier 			else if(!_Strncmpci(aval, n, L"square"))
28669a747e4fSDavid du Colombier 				ans = LTsquare;
28679a747e4fSDavid du Colombier 			else {
28689a747e4fSDavid du Colombier 				if(warn)
28699a747e4fSDavid du Colombier 					fprint(2, "warning: unknown list element type %S\n", aval);
28709a747e4fSDavid du Colombier 			}
28719a747e4fSDavid du Colombier 		}
28729a747e4fSDavid du Colombier 	}
28739a747e4fSDavid du Colombier 	return ans;
28749a747e4fSDavid du Colombier }
28759a747e4fSDavid du Colombier 
28769a747e4fSDavid du Colombier // Attribute value when value is a URL, possibly relative to base.
28779a747e4fSDavid du Colombier // FOR NOW: leave the url relative.
28789a747e4fSDavid du Colombier // Caller must free the result (eventually).
28799a747e4fSDavid du Colombier static Rune*
aurlval(Token * tok,int attid,Rune * dflt,Rune * base)28809a747e4fSDavid du Colombier aurlval(Token* tok, int attid, Rune* dflt, Rune* base)
28819a747e4fSDavid du Colombier {
28829a747e4fSDavid du Colombier 	Rune*	ans;
28839a747e4fSDavid du Colombier 	Rune*	url;
28849a747e4fSDavid du Colombier 
28859a747e4fSDavid du Colombier 	USED(base);
28869a747e4fSDavid du Colombier 	ans = nil;
28879a747e4fSDavid du Colombier 	if(_tokaval(tok, attid, &url, 0) && url != nil)
28889a747e4fSDavid du Colombier 		ans = removeallwhite(url);
28899a747e4fSDavid du Colombier 	if(ans == nil)
28909a747e4fSDavid du Colombier 		ans = _Strdup(dflt);
28919a747e4fSDavid du Colombier 	return ans;
28929a747e4fSDavid du Colombier }
28939a747e4fSDavid du Colombier 
28949a747e4fSDavid du Colombier // Return copy of s but with all whitespace (even internal) removed.
28959a747e4fSDavid du Colombier // This fixes some buggy URL specification strings.
28969a747e4fSDavid du Colombier static Rune*
removeallwhite(Rune * s)28979a747e4fSDavid du Colombier removeallwhite(Rune* s)
28989a747e4fSDavid du Colombier {
28999a747e4fSDavid du Colombier 	int	j;
29009a747e4fSDavid du Colombier 	int	n;
29019a747e4fSDavid du Colombier 	int	i;
29029a747e4fSDavid du Colombier 	int	c;
29039a747e4fSDavid du Colombier 	Rune*	ans;
29049a747e4fSDavid du Colombier 
29059a747e4fSDavid du Colombier 	j = 0;
29065d459b5aSDavid du Colombier 	n = _Strlen(s);
29079a747e4fSDavid du Colombier 	for(i = 0; i < n; i++) {
29089a747e4fSDavid du Colombier 		c = s[i];
29099a747e4fSDavid du Colombier 		if(c >= 256 || !isspace(c))
29109a747e4fSDavid du Colombier 			j++;
29119a747e4fSDavid du Colombier 	}
29129a747e4fSDavid du Colombier 	if(j < n) {
29139a747e4fSDavid du Colombier 		ans = _newstr(j);
29149a747e4fSDavid du Colombier 		j = 0;
29159a747e4fSDavid du Colombier 		for(i = 0; i < n; i++) {
29169a747e4fSDavid du Colombier 			c = s[i];
29179a747e4fSDavid du Colombier 			if(c >= 256 || !isspace(c))
29189a747e4fSDavid du Colombier 				ans[j++] = c;
29199a747e4fSDavid du Colombier 		}
29209a747e4fSDavid du Colombier 		ans[j] = 0;
29219a747e4fSDavid du Colombier 	}
29229a747e4fSDavid du Colombier 	else
29239a747e4fSDavid du Colombier 		ans = _Strdup(s);
29249a747e4fSDavid du Colombier 	return ans;
29259a747e4fSDavid du Colombier }
29269a747e4fSDavid du Colombier 
29279a747e4fSDavid du Colombier // Attribute value when mere presence of attr implies value of 1,
29289a747e4fSDavid du Colombier // but if there is an integer there, return it as the value.
29299a747e4fSDavid du Colombier static int
aflagval(Token * tok,int attid)29309a747e4fSDavid du Colombier aflagval(Token* tok, int attid)
29319a747e4fSDavid du Colombier {
29329a747e4fSDavid du Colombier 	int	val;
29339a747e4fSDavid du Colombier 	Rune*	sval;
29349a747e4fSDavid du Colombier 
29359a747e4fSDavid du Colombier 	val = 0;
29369a747e4fSDavid du Colombier 	if(_tokaval(tok, attid, &sval, 0)) {
29379a747e4fSDavid du Colombier 		val = 1;
29389a747e4fSDavid du Colombier 		if(sval != nil)
29399a747e4fSDavid du Colombier 			val = toint(sval);
29409a747e4fSDavid du Colombier 	}
29419a747e4fSDavid du Colombier 	return val;
29429a747e4fSDavid du Colombier }
29439a747e4fSDavid du Colombier 
29449a747e4fSDavid du Colombier static Align
makealign(int halign,int valign)29459a747e4fSDavid du Colombier makealign(int halign, int valign)
29469a747e4fSDavid du Colombier {
29479a747e4fSDavid du Colombier 	Align	al;
29489a747e4fSDavid du Colombier 
29499a747e4fSDavid du Colombier 	al.halign = halign;
29509a747e4fSDavid du Colombier 	al.valign = valign;
29519a747e4fSDavid du Colombier 	return al;
29529a747e4fSDavid du Colombier }
29539a747e4fSDavid du Colombier 
29549a747e4fSDavid du Colombier // Make an Align (two alignments, horizontal and vertical)
29559a747e4fSDavid du Colombier static Align
aalign(Token * tok)29569a747e4fSDavid du Colombier aalign(Token* tok)
29579a747e4fSDavid du Colombier {
29589a747e4fSDavid du Colombier 	return makealign(
29599a747e4fSDavid du Colombier 		atabval(tok, Aalign, align_tab, NALIGNTAB, ALnone),
29609a747e4fSDavid du Colombier 		atabval(tok, Avalign, align_tab, NALIGNTAB, ALnone));
29619a747e4fSDavid du Colombier }
29629a747e4fSDavid du Colombier 
29639a747e4fSDavid du Colombier // Make a Dimen, based on value of attid attr
29649a747e4fSDavid du Colombier static Dimen
adimen(Token * tok,int attid)29659a747e4fSDavid du Colombier adimen(Token* tok, int attid)
29669a747e4fSDavid du Colombier {
29679a747e4fSDavid du Colombier 	Rune*	wd;
29689a747e4fSDavid du Colombier 
29699a747e4fSDavid du Colombier 	if(_tokaval(tok, attid, &wd, 0))
29705d459b5aSDavid du Colombier 		return parsedim(wd, _Strlen(wd));
29719a747e4fSDavid du Colombier 	else
29729a747e4fSDavid du Colombier 		return makedimen(Dnone, 0);
29739a747e4fSDavid du Colombier }
29749a747e4fSDavid du Colombier 
29759a747e4fSDavid du Colombier // Parse s[0:n] as num[.[num]][unit][%|*]
29769a747e4fSDavid du Colombier static Dimen
parsedim(Rune * s,int ns)29779a747e4fSDavid du Colombier parsedim(Rune* s, int ns)
29789a747e4fSDavid du Colombier {
29799a747e4fSDavid du Colombier 	int	kind;
29809a747e4fSDavid du Colombier 	int	spec;
29819a747e4fSDavid du Colombier 	Rune*	l;
29829a747e4fSDavid du Colombier 	int	nl;
29839a747e4fSDavid du Colombier 	Rune*	r;
29849a747e4fSDavid du Colombier 	int	nr;
29859a747e4fSDavid du Colombier 	int	mul;
29869a747e4fSDavid du Colombier 	int	i;
29879a747e4fSDavid du Colombier 	Rune*	f;
29889a747e4fSDavid du Colombier 	int	nf;
29899a747e4fSDavid du Colombier 	int	Tkdpi;
29909a747e4fSDavid du Colombier 	Rune*	units;
29919a747e4fSDavid du Colombier 
29929a747e4fSDavid du Colombier 	kind = Dnone;
29939a747e4fSDavid du Colombier 	spec = 0;
29949a747e4fSDavid du Colombier 	_splitl(s, ns, L"^0-9", &l, &nl, &r, &nr);
29959a747e4fSDavid du Colombier 	if(nl != 0) {
29969a747e4fSDavid du Colombier 		spec = 1000*_Strtol(l, nil, 10);
29979a747e4fSDavid du Colombier 		if(nr > 0 && r[0] == '.') {
29989a747e4fSDavid du Colombier 			_splitl(r+1, nr-1, L"^0-9", &f, &nf, &r, &nr);
29999a747e4fSDavid du Colombier 			if(nf != 0) {
30009a747e4fSDavid du Colombier 				mul = 100;
30019a747e4fSDavid du Colombier 				for(i = 0; i < nf; i++) {
30029a747e4fSDavid du Colombier 					spec = spec + mul*(f[i]-'0');
30039a747e4fSDavid du Colombier 					mul = mul/10;
30049a747e4fSDavid du Colombier 				}
30059a747e4fSDavid du Colombier 			}
30069a747e4fSDavid du Colombier 		}
30079a747e4fSDavid du Colombier 		kind = Dpixels;
30089a747e4fSDavid du Colombier 		if(nr != 0) {
30099a747e4fSDavid du Colombier 			if(nr >= 2) {
30109a747e4fSDavid du Colombier 				Tkdpi = 100;
30119a747e4fSDavid du Colombier 				units = r;
30129a747e4fSDavid du Colombier 				r = r+2;
30139a747e4fSDavid du Colombier 				nr -= 2;
30149a747e4fSDavid du Colombier 				if(!_Strncmpci(units, 2, L"pt"))
30159a747e4fSDavid du Colombier 					spec = (spec*Tkdpi)/72;
30169a747e4fSDavid du Colombier 				else if(!_Strncmpci(units, 2, L"pi"))
30179a747e4fSDavid du Colombier 					spec = (spec*12*Tkdpi)/72;
30189a747e4fSDavid du Colombier 				else if(!_Strncmpci(units, 2, L"in"))
30199a747e4fSDavid du Colombier 					spec = spec*Tkdpi;
30209a747e4fSDavid du Colombier 				else if(!_Strncmpci(units, 2, L"cm"))
30219a747e4fSDavid du Colombier 					spec = (spec*100*Tkdpi)/254;
30229a747e4fSDavid du Colombier 				else if(!_Strncmpci(units, 2, L"mm"))
30239a747e4fSDavid du Colombier 					spec = (spec*10*Tkdpi)/254;
30249a747e4fSDavid du Colombier 				else if(!_Strncmpci(units, 2, L"em"))
30259a747e4fSDavid du Colombier 					spec = spec*15;
30269a747e4fSDavid du Colombier 				else {
30279a747e4fSDavid du Colombier 					if(warn)
30289a747e4fSDavid du Colombier 						fprint(2, "warning: unknown units %C%Cs\n", units[0], units[1]);
30299a747e4fSDavid du Colombier 				}
30309a747e4fSDavid du Colombier 			}
30319a747e4fSDavid du Colombier 			if(nr >= 1) {
30329a747e4fSDavid du Colombier 				if(r[0] == '%')
30339a747e4fSDavid du Colombier 					kind = Dpercent;
30349a747e4fSDavid du Colombier 				else if(r[0] == '*')
30359a747e4fSDavid du Colombier 					kind = Drelative;
30369a747e4fSDavid du Colombier 			}
30379a747e4fSDavid du Colombier 		}
30389a747e4fSDavid du Colombier 		spec = spec/1000;
30399a747e4fSDavid du Colombier 	}
30409a747e4fSDavid du Colombier 	else if(nr == 1 && r[0] == '*') {
30419a747e4fSDavid du Colombier 		spec = 1;
30429a747e4fSDavid du Colombier 		kind = Drelative;
30439a747e4fSDavid du Colombier 	}
30449a747e4fSDavid du Colombier 	return makedimen(kind, spec);
30459a747e4fSDavid du Colombier }
30469a747e4fSDavid du Colombier 
30479a747e4fSDavid du Colombier static void
setdimarray(Token * tok,int attid,Dimen ** pans,int * panslen)30489a747e4fSDavid du Colombier setdimarray(Token* tok, int attid, Dimen** pans, int* panslen)
30499a747e4fSDavid du Colombier {
30509a747e4fSDavid du Colombier 	Rune*	s;
30519a747e4fSDavid du Colombier 	Dimen*	d;
30529a747e4fSDavid du Colombier 	int	k;
30539a747e4fSDavid du Colombier 	int	nc;
30549a747e4fSDavid du Colombier 	Rune* a[SMALLBUFSIZE];
30559a747e4fSDavid du Colombier 	int	an[SMALLBUFSIZE];
30569a747e4fSDavid du Colombier 
30579a747e4fSDavid du Colombier 	if(_tokaval(tok, attid, &s, 0)) {
30585d459b5aSDavid du Colombier 		nc = _splitall(s, _Strlen(s), L", ", a, an, SMALLBUFSIZE);
30599a747e4fSDavid du Colombier 		if(nc > 0) {
30609a747e4fSDavid du Colombier 			d = (Dimen*)emalloc(nc * sizeof(Dimen));
30619a747e4fSDavid du Colombier 			for(k = 0; k < nc; k++) {
30629a747e4fSDavid du Colombier 				d[k] = parsedim(a[k], an[k]);
30639a747e4fSDavid du Colombier 			}
30649a747e4fSDavid du Colombier 			*pans = d;
30659a747e4fSDavid du Colombier 			*panslen = nc;
30669a747e4fSDavid du Colombier 			return;
30679a747e4fSDavid du Colombier 		}
30689a747e4fSDavid du Colombier 	}
30699a747e4fSDavid du Colombier 	*pans = nil;
30709a747e4fSDavid du Colombier 	*panslen = 0;
30719a747e4fSDavid du Colombier }
30729a747e4fSDavid du Colombier 
30739a747e4fSDavid du Colombier static Background
makebackground(Rune * imageurl,int color)30749a747e4fSDavid du Colombier makebackground(Rune* imageurl, int color)
30759a747e4fSDavid du Colombier {
30769a747e4fSDavid du Colombier 	Background bg;
30779a747e4fSDavid du Colombier 
30789a747e4fSDavid du Colombier 	bg.image = imageurl;
30799a747e4fSDavid du Colombier 	bg.color = color;
30809a747e4fSDavid du Colombier 	return bg;
30819a747e4fSDavid du Colombier }
30829a747e4fSDavid du Colombier 
30839a747e4fSDavid du Colombier static Item*
newitext(Rune * s,int fnt,int fg,int voff,int ul)30849a747e4fSDavid du Colombier newitext(Rune* s, int fnt, int fg, int voff, int ul)
30859a747e4fSDavid du Colombier {
30869a747e4fSDavid du Colombier 	Itext* t;
30879a747e4fSDavid du Colombier 
30889a747e4fSDavid du Colombier 	assert(s != nil);
30899a747e4fSDavid du Colombier 	t = (Itext*)emalloc(sizeof(Itext));
30909a747e4fSDavid du Colombier 	t->tag = Itexttag;
30919a747e4fSDavid du Colombier 	t->s = s;
30929a747e4fSDavid du Colombier 	t->fnt = fnt;
30939a747e4fSDavid du Colombier 	t->fg = fg;
30949a747e4fSDavid du Colombier 	t->voff = voff;
30959a747e4fSDavid du Colombier 	t->ul = ul;
30969a747e4fSDavid du Colombier 	return (Item*)t;
30979a747e4fSDavid du Colombier }
30989a747e4fSDavid du Colombier 
30999a747e4fSDavid du Colombier static Item*
newirule(int align,int size,int noshade,int color,Dimen wspec)3100*684b447eSDavid du Colombier newirule(int align, int size, int noshade, int color, Dimen wspec)
31019a747e4fSDavid du Colombier {
31029a747e4fSDavid du Colombier 	Irule* r;
31039a747e4fSDavid du Colombier 
31049a747e4fSDavid du Colombier 	r = (Irule*)emalloc(sizeof(Irule));
31059a747e4fSDavid du Colombier 	r->tag = Iruletag;
31069a747e4fSDavid du Colombier 	r->align = align;
31079a747e4fSDavid du Colombier 	r->size = size;
31089a747e4fSDavid du Colombier 	r->noshade = noshade;
3109*684b447eSDavid du Colombier 	r->color = color;
31109a747e4fSDavid du Colombier 	r->wspec = wspec;
31119a747e4fSDavid du Colombier 	return (Item*)r;
31129a747e4fSDavid du Colombier }
31139a747e4fSDavid du Colombier 
31149a747e4fSDavid du Colombier // Map is owned elsewhere.
31159a747e4fSDavid du Colombier static Item*
newiimage(Rune * src,Rune * altrep,int align,int width,int height,int hspace,int vspace,int border,int ismap,Map * map)31169a747e4fSDavid du Colombier newiimage(Rune* src, Rune* altrep, int align, int width, int height,
31179a747e4fSDavid du Colombier 		int hspace, int vspace, int border, int ismap, Map* map)
31189a747e4fSDavid du Colombier {
31199a747e4fSDavid du Colombier 	Iimage* i;
31209a747e4fSDavid du Colombier 	int	state;
31219a747e4fSDavid du Colombier 
31229a747e4fSDavid du Colombier 	state = 0;
31239a747e4fSDavid du Colombier 	if(ismap)
31249a747e4fSDavid du Colombier 		state = IFsmap;
31259a747e4fSDavid du Colombier 	i = (Iimage*)emalloc(sizeof(Iimage));
31269a747e4fSDavid du Colombier 	i->tag = Iimagetag;
31279a747e4fSDavid du Colombier 	i->state = state;
31289a747e4fSDavid du Colombier 	i->imsrc = src;
31299a747e4fSDavid du Colombier 	i->altrep = altrep;
31309a747e4fSDavid du Colombier 	i->align = align;
31319a747e4fSDavid du Colombier 	i->imwidth = width;
31329a747e4fSDavid du Colombier 	i->imheight = height;
31339a747e4fSDavid du Colombier 	i->hspace = hspace;
31349a747e4fSDavid du Colombier 	i->vspace = vspace;
31359a747e4fSDavid du Colombier 	i->border = border;
31369a747e4fSDavid du Colombier 	i->map = map;
31379a747e4fSDavid du Colombier 	i->ctlid = -1;
31389a747e4fSDavid du Colombier 	return (Item*)i;
31399a747e4fSDavid du Colombier }
31409a747e4fSDavid du Colombier 
31419a747e4fSDavid du Colombier static Item*
newiformfield(Formfield * ff)31429a747e4fSDavid du Colombier newiformfield(Formfield* ff)
31439a747e4fSDavid du Colombier {
31449a747e4fSDavid du Colombier 	Iformfield* f;
31459a747e4fSDavid du Colombier 
31469a747e4fSDavid du Colombier 	f = (Iformfield*)emalloc(sizeof(Iformfield));
31479a747e4fSDavid du Colombier 	f->tag = Iformfieldtag;
31489a747e4fSDavid du Colombier 	f->formfield = ff;
31499a747e4fSDavid du Colombier 	return (Item*)f;
31509a747e4fSDavid du Colombier }
31519a747e4fSDavid du Colombier 
31529a747e4fSDavid du Colombier static Item*
newitable(Table * tab)31539a747e4fSDavid du Colombier newitable(Table* tab)
31549a747e4fSDavid du Colombier {
31559a747e4fSDavid du Colombier 	Itable* t;
31569a747e4fSDavid du Colombier 
31579a747e4fSDavid du Colombier 	t = (Itable*)emalloc(sizeof(Itable));
31589a747e4fSDavid du Colombier 	t->tag = Itabletag;
31599a747e4fSDavid du Colombier 	t->table = tab;
31609a747e4fSDavid du Colombier 	return (Item*)t;
31619a747e4fSDavid du Colombier }
31629a747e4fSDavid du Colombier 
31639a747e4fSDavid du Colombier static Item*
newifloat(Item * it,int side)31649a747e4fSDavid du Colombier newifloat(Item* it, int side)
31659a747e4fSDavid du Colombier {
31669a747e4fSDavid du Colombier 	Ifloat* f;
31679a747e4fSDavid du Colombier 
31689a747e4fSDavid du Colombier 	f = (Ifloat*)emalloc(sizeof(Ifloat));
31699a747e4fSDavid du Colombier 	f->tag = Ifloattag;
31709a747e4fSDavid du Colombier 	f->state = IFwrap;
31719a747e4fSDavid du Colombier 	f->item = it;
31729a747e4fSDavid du Colombier 	f->side = side;
31739a747e4fSDavid du Colombier 	return (Item*)f;
31749a747e4fSDavid du Colombier }
31759a747e4fSDavid du Colombier 
31769a747e4fSDavid du Colombier static Item*
newispacer(int spkind)31779a747e4fSDavid du Colombier newispacer(int spkind)
31789a747e4fSDavid du Colombier {
31799a747e4fSDavid du Colombier 	Ispacer* s;
31809a747e4fSDavid du Colombier 
31819a747e4fSDavid du Colombier 	s = (Ispacer*)emalloc(sizeof(Ispacer));
31829a747e4fSDavid du Colombier 	s->tag = Ispacertag;
31839a747e4fSDavid du Colombier 	s->spkind = spkind;
31849a747e4fSDavid du Colombier 	return (Item*)s;
31859a747e4fSDavid du Colombier }
31869a747e4fSDavid du Colombier 
31879a747e4fSDavid du Colombier // Free one item (caller must deal with next pointer)
31889a747e4fSDavid du Colombier static void
freeitem(Item * it)31899a747e4fSDavid du Colombier freeitem(Item* it)
31909a747e4fSDavid du Colombier {
31919a747e4fSDavid du Colombier 	Iimage* ii;
31929a747e4fSDavid du Colombier 	Genattr* ga;
31939a747e4fSDavid du Colombier 
31949a747e4fSDavid du Colombier 	if(it == nil)
31959a747e4fSDavid du Colombier 		return;
31969a747e4fSDavid du Colombier 
31979a747e4fSDavid du Colombier 	switch(it->tag) {
31989a747e4fSDavid du Colombier 	case Itexttag:
31999a747e4fSDavid du Colombier 		free(((Itext*)it)->s);
32009a747e4fSDavid du Colombier 		break;
32019a747e4fSDavid du Colombier 	case Iimagetag:
32029a747e4fSDavid du Colombier 		ii = (Iimage*)it;
32039a747e4fSDavid du Colombier 		free(ii->imsrc);
32049a747e4fSDavid du Colombier 		free(ii->altrep);
32059a747e4fSDavid du Colombier 		break;
32069a747e4fSDavid du Colombier 	case Iformfieldtag:
32079a747e4fSDavid du Colombier 		freeformfield(((Iformfield*)it)->formfield);
32089a747e4fSDavid du Colombier 		break;
32099a747e4fSDavid du Colombier 	case Itabletag:
32109a747e4fSDavid du Colombier 		freetable(((Itable*)it)->table);
32119a747e4fSDavid du Colombier 		break;
32129a747e4fSDavid du Colombier 	case Ifloattag:
32139a747e4fSDavid du Colombier 		freeitem(((Ifloat*)it)->item);
32149a747e4fSDavid du Colombier 		break;
32159a747e4fSDavid du Colombier 	}
32169a747e4fSDavid du Colombier 	ga = it->genattr;
32179a747e4fSDavid du Colombier 	if(ga != nil) {
32189a747e4fSDavid du Colombier 		free(ga->id);
32199a747e4fSDavid du Colombier 		free(ga->class);
32209a747e4fSDavid du Colombier 		free(ga->style);
32219a747e4fSDavid du Colombier 		free(ga->title);
32229a747e4fSDavid du Colombier 		freescriptevents(ga->events);
32239a747e4fSDavid du Colombier 	}
32249a747e4fSDavid du Colombier 	free(it);
32259a747e4fSDavid du Colombier }
32269a747e4fSDavid du Colombier 
32279a747e4fSDavid du Colombier // Free list of items chained through next pointer
32289a747e4fSDavid du Colombier void
freeitems(Item * ithead)32299a747e4fSDavid du Colombier freeitems(Item* ithead)
32309a747e4fSDavid du Colombier {
32319a747e4fSDavid du Colombier 	Item* it;
32329a747e4fSDavid du Colombier 	Item* itnext;
32339a747e4fSDavid du Colombier 
32349a747e4fSDavid du Colombier 	it = ithead;
32359a747e4fSDavid du Colombier 	while(it != nil) {
32369a747e4fSDavid du Colombier 		itnext = it->next;
32379a747e4fSDavid du Colombier 		freeitem(it);
32389a747e4fSDavid du Colombier 		it = itnext;
32399a747e4fSDavid du Colombier 	}
32409a747e4fSDavid du Colombier }
32419a747e4fSDavid du Colombier 
32429a747e4fSDavid du Colombier static void
freeformfield(Formfield * ff)32439a747e4fSDavid du Colombier freeformfield(Formfield* ff)
32449a747e4fSDavid du Colombier {
32459a747e4fSDavid du Colombier 	Option* o;
32469a747e4fSDavid du Colombier 	Option* onext;
32479a747e4fSDavid du Colombier 
32489a747e4fSDavid du Colombier 	if(ff == nil)
32499a747e4fSDavid du Colombier 		return;
32509a747e4fSDavid du Colombier 
32519a747e4fSDavid du Colombier 	free(ff->name);
32529a747e4fSDavid du Colombier 	free(ff->value);
32539a747e4fSDavid du Colombier 	for(o = ff->options; o != nil; o = onext) {
32549a747e4fSDavid du Colombier 		onext = o->next;
32559a747e4fSDavid du Colombier 		free(o->value);
32569a747e4fSDavid du Colombier 		free(o->display);
32579a747e4fSDavid du Colombier 	}
32589a747e4fSDavid du Colombier 	free(ff);
32599a747e4fSDavid du Colombier }
32609a747e4fSDavid du Colombier 
32619a747e4fSDavid du Colombier static void
freetable(Table * t)32629a747e4fSDavid du Colombier freetable(Table* t)
32639a747e4fSDavid du Colombier {
32649a747e4fSDavid du Colombier 	int i;
32659a747e4fSDavid du Colombier 	Tablecell* c;
32669a747e4fSDavid du Colombier 	Tablecell* cnext;
32679a747e4fSDavid du Colombier 
32689a747e4fSDavid du Colombier 	if(t == nil)
32699a747e4fSDavid du Colombier 		return;
32709a747e4fSDavid du Colombier 
32719a747e4fSDavid du Colombier 	// We'll find all the unique cells via t->cells and next pointers.
32729a747e4fSDavid du Colombier 	// (Other pointers to cells in the table are duplicates of these)
32739a747e4fSDavid du Colombier 	for(c = t->cells; c != nil; c = cnext) {
32749a747e4fSDavid du Colombier 		cnext = c->next;
32759a747e4fSDavid du Colombier 		freeitems(c->content);
32769a747e4fSDavid du Colombier 	}
32779a747e4fSDavid du Colombier 	if(t->grid != nil) {
32789a747e4fSDavid du Colombier 		for(i = 0; i < t->nrow; i++)
32799a747e4fSDavid du Colombier 			free(t->grid[i]);
32809a747e4fSDavid du Colombier 		free(t->grid);
32819a747e4fSDavid du Colombier 	}
32829a747e4fSDavid du Colombier 	free(t->rows);
32839a747e4fSDavid du Colombier 	free(t->cols);
32849a747e4fSDavid du Colombier 	freeitems(t->caption);
32859a747e4fSDavid du Colombier 	free(t);
32869a747e4fSDavid du Colombier }
32879a747e4fSDavid du Colombier 
32889a747e4fSDavid du Colombier static void
freeform(Form * f)32899a747e4fSDavid du Colombier freeform(Form* f)
32909a747e4fSDavid du Colombier {
32919a747e4fSDavid du Colombier 	if(f == nil)
32929a747e4fSDavid du Colombier 		return;
32939a747e4fSDavid du Colombier 
32949a747e4fSDavid du Colombier 	free(f->name);
32959a747e4fSDavid du Colombier 	free(f->action);
32969a747e4fSDavid du Colombier 	// Form doesn't own its fields (Iformfield items do)
32979a747e4fSDavid du Colombier 	free(f);
32989a747e4fSDavid du Colombier }
32999a747e4fSDavid du Colombier 
33009a747e4fSDavid du Colombier static void
freeforms(Form * fhead)33019a747e4fSDavid du Colombier freeforms(Form* fhead)
33029a747e4fSDavid du Colombier {
33039a747e4fSDavid du Colombier 	Form* f;
33049a747e4fSDavid du Colombier 	Form* fnext;
33059a747e4fSDavid du Colombier 
33069a747e4fSDavid du Colombier 	for(f = fhead; f != nil; f = fnext) {
33079a747e4fSDavid du Colombier 		fnext = f->next;
33089a747e4fSDavid du Colombier 		freeform(f);
33099a747e4fSDavid du Colombier 	}
33109a747e4fSDavid du Colombier }
33119a747e4fSDavid du Colombier 
33129a747e4fSDavid du Colombier static void
freeanchor(Anchor * a)33139a747e4fSDavid du Colombier freeanchor(Anchor* a)
33149a747e4fSDavid du Colombier {
33159a747e4fSDavid du Colombier 	if(a == nil)
33169a747e4fSDavid du Colombier 		return;
33179a747e4fSDavid du Colombier 
33189a747e4fSDavid du Colombier 	free(a->name);
33199a747e4fSDavid du Colombier 	free(a->href);
33209a747e4fSDavid du Colombier 	free(a);
33219a747e4fSDavid du Colombier }
33229a747e4fSDavid du Colombier 
33239a747e4fSDavid du Colombier static void
freeanchors(Anchor * ahead)33249a747e4fSDavid du Colombier freeanchors(Anchor* ahead)
33259a747e4fSDavid du Colombier {
33269a747e4fSDavid du Colombier 	Anchor* a;
33279a747e4fSDavid du Colombier 	Anchor* anext;
33289a747e4fSDavid du Colombier 
33299a747e4fSDavid du Colombier 	for(a = ahead; a != nil; a = anext) {
33309a747e4fSDavid du Colombier 		anext = a->next;
33319a747e4fSDavid du Colombier 		freeanchor(a);
33329a747e4fSDavid du Colombier 	}
33339a747e4fSDavid du Colombier }
33349a747e4fSDavid du Colombier 
33359a747e4fSDavid du Colombier static void
freedestanchor(DestAnchor * da)33369a747e4fSDavid du Colombier freedestanchor(DestAnchor* da)
33379a747e4fSDavid du Colombier {
33389a747e4fSDavid du Colombier 	if(da == nil)
33399a747e4fSDavid du Colombier 		return;
33409a747e4fSDavid du Colombier 
33419a747e4fSDavid du Colombier 	free(da->name);
33429a747e4fSDavid du Colombier 	free(da);
33439a747e4fSDavid du Colombier }
33449a747e4fSDavid du Colombier 
33459a747e4fSDavid du Colombier static void
freedestanchors(DestAnchor * dahead)33469a747e4fSDavid du Colombier freedestanchors(DestAnchor* dahead)
33479a747e4fSDavid du Colombier {
33489a747e4fSDavid du Colombier 	DestAnchor* da;
33499a747e4fSDavid du Colombier 	DestAnchor* danext;
33509a747e4fSDavid du Colombier 
33519a747e4fSDavid du Colombier 	for(da = dahead; da != nil; da = danext) {
33529a747e4fSDavid du Colombier 		danext = da->next;
33539a747e4fSDavid du Colombier 		freedestanchor(da);
33549a747e4fSDavid du Colombier 	}
33559a747e4fSDavid du Colombier }
33569a747e4fSDavid du Colombier 
33579a747e4fSDavid du Colombier static void
freearea(Area * a)33589a747e4fSDavid du Colombier freearea(Area* a)
33599a747e4fSDavid du Colombier {
33609a747e4fSDavid du Colombier 	if(a == nil)
33619a747e4fSDavid du Colombier 		return;
33629a747e4fSDavid du Colombier 	free(a->href);
33639a747e4fSDavid du Colombier 	free(a->coords);
33649a747e4fSDavid du Colombier }
33659a747e4fSDavid du Colombier 
33669a747e4fSDavid du Colombier static void freekidinfos(Kidinfo* khead);
33679a747e4fSDavid du Colombier 
33689a747e4fSDavid du Colombier static void
freekidinfo(Kidinfo * k)33699a747e4fSDavid du Colombier freekidinfo(Kidinfo* k)
33709a747e4fSDavid du Colombier {
33719a747e4fSDavid du Colombier 	if(k->isframeset) {
33729a747e4fSDavid du Colombier 		free(k->rows);
33739a747e4fSDavid du Colombier 		free(k->cols);
33749a747e4fSDavid du Colombier 		freekidinfos(k->kidinfos);
33759a747e4fSDavid du Colombier 	}
33769a747e4fSDavid du Colombier 	else {
33779a747e4fSDavid du Colombier 		free(k->src);
33789a747e4fSDavid du Colombier 		free(k->name);
33799a747e4fSDavid du Colombier 	}
33809a747e4fSDavid du Colombier 	free(k);
33819a747e4fSDavid du Colombier }
33829a747e4fSDavid du Colombier 
33839a747e4fSDavid du Colombier static void
freekidinfos(Kidinfo * khead)33849a747e4fSDavid du Colombier freekidinfos(Kidinfo* khead)
33859a747e4fSDavid du Colombier {
33869a747e4fSDavid du Colombier 	Kidinfo* k;
33879a747e4fSDavid du Colombier 	Kidinfo* knext;
33889a747e4fSDavid du Colombier 
33899a747e4fSDavid du Colombier 	for(k = khead; k != nil; k = knext) {
33909a747e4fSDavid du Colombier 		knext = k->next;
33919a747e4fSDavid du Colombier 		freekidinfo(k);
33929a747e4fSDavid du Colombier 	}
33939a747e4fSDavid du Colombier }
33949a747e4fSDavid du Colombier 
33959a747e4fSDavid du Colombier static void
freemap(Map * m)33969a747e4fSDavid du Colombier freemap(Map* m)
33979a747e4fSDavid du Colombier {
33989a747e4fSDavid du Colombier 	Area* a;
33999a747e4fSDavid du Colombier 	Area* anext;
34009a747e4fSDavid du Colombier 
34019a747e4fSDavid du Colombier 	if(m == nil)
34029a747e4fSDavid du Colombier 		return;
34039a747e4fSDavid du Colombier 
34049a747e4fSDavid du Colombier 	free(m->name);
34059a747e4fSDavid du Colombier 	for(a = m->areas; a != nil; a = anext) {
34069a747e4fSDavid du Colombier 		anext = a->next;
34079a747e4fSDavid du Colombier 		freearea(a);
34089a747e4fSDavid du Colombier 	}
34099a747e4fSDavid du Colombier 	free(m);
34109a747e4fSDavid du Colombier }
34119a747e4fSDavid du Colombier 
34129a747e4fSDavid du Colombier static void
freemaps(Map * mhead)34139a747e4fSDavid du Colombier freemaps(Map* mhead)
34149a747e4fSDavid du Colombier {
34159a747e4fSDavid du Colombier 	Map* m;
34169a747e4fSDavid du Colombier 	Map* mnext;
34179a747e4fSDavid du Colombier 
34189a747e4fSDavid du Colombier 	for(m = mhead; m != nil; m = mnext) {
34199a747e4fSDavid du Colombier 		mnext = m->next;
34209a747e4fSDavid du Colombier 		freemap(m);
34219a747e4fSDavid du Colombier 	}
34229a747e4fSDavid du Colombier }
34239a747e4fSDavid du Colombier 
34249a747e4fSDavid du Colombier void
freedocinfo(Docinfo * d)34259a747e4fSDavid du Colombier freedocinfo(Docinfo* d)
34269a747e4fSDavid du Colombier {
34279a747e4fSDavid du Colombier 	if(d == nil)
34289a747e4fSDavid du Colombier 		return;
34299a747e4fSDavid du Colombier 	free(d->src);
34309a747e4fSDavid du Colombier 	free(d->base);
34319a747e4fSDavid du Colombier 	freeitem((Item*)d->backgrounditem);
34329a747e4fSDavid du Colombier 	free(d->refresh);
34339a747e4fSDavid du Colombier 	freekidinfos(d->kidinfo);
34349a747e4fSDavid du Colombier 	freeanchors(d->anchors);
34359a747e4fSDavid du Colombier 	freedestanchors(d->dests);
34369a747e4fSDavid du Colombier 	freeforms(d->forms);
34379a747e4fSDavid du Colombier 	freemaps(d->maps);
34389a747e4fSDavid du Colombier 	// tables, images, and formfields are freed when
34399a747e4fSDavid du Colombier 	// the items pointing at them are freed
34409a747e4fSDavid du Colombier 	free(d);
34419a747e4fSDavid du Colombier }
34429a747e4fSDavid du Colombier 
34439a747e4fSDavid du Colombier // Currently, someone else owns all the memory
34449a747e4fSDavid du Colombier // pointed to by things in a Pstate.
34459a747e4fSDavid du Colombier static void
freepstate(Pstate * p)34469a747e4fSDavid du Colombier freepstate(Pstate* p)
34479a747e4fSDavid du Colombier {
34489a747e4fSDavid du Colombier 	free(p);
34499a747e4fSDavid du Colombier }
34509a747e4fSDavid du Colombier 
34519a747e4fSDavid du Colombier static void
freepstatestack(Pstate * pshead)34529a747e4fSDavid du Colombier freepstatestack(Pstate* pshead)
34539a747e4fSDavid du Colombier {
34549a747e4fSDavid du Colombier 	Pstate* p;
34559a747e4fSDavid du Colombier 	Pstate* pnext;
34569a747e4fSDavid du Colombier 
34579a747e4fSDavid du Colombier 	for(p = pshead; p != nil; p = pnext) {
34589a747e4fSDavid du Colombier 		pnext = p->next;
34599a747e4fSDavid du Colombier 		free(p);
34609a747e4fSDavid du Colombier 	}
34619a747e4fSDavid du Colombier }
34629a747e4fSDavid du Colombier 
34639a747e4fSDavid du Colombier static int
Iconv(Fmt * f)34649a747e4fSDavid du Colombier Iconv(Fmt *f)
34659a747e4fSDavid du Colombier {
34669a747e4fSDavid du Colombier 	Item*	it;
34679a747e4fSDavid du Colombier 	Itext*	t;
34689a747e4fSDavid du Colombier 	Irule*	r;
34699a747e4fSDavid du Colombier 	Iimage*	i;
34709a747e4fSDavid du Colombier 	Ifloat*	fl;
34719a747e4fSDavid du Colombier 	int	state;
34729a747e4fSDavid du Colombier 	Formfield*	ff;
34739a747e4fSDavid du Colombier 	Rune*	ty;
34749a747e4fSDavid du Colombier 	Tablecell*	c;
34759a747e4fSDavid du Colombier 	Table*	tab;
34769a747e4fSDavid du Colombier 	char*	p;
34779a747e4fSDavid du Colombier 	int	cl;
34789a747e4fSDavid du Colombier 	int	hang;
34799a747e4fSDavid du Colombier 	int	indent;
34809a747e4fSDavid du Colombier 	int	bi;
34819a747e4fSDavid du Colombier 	int	nbuf;
34829a747e4fSDavid du Colombier 	char	buf[BIGBUFSIZE];
34839a747e4fSDavid du Colombier 
34849a747e4fSDavid du Colombier 	it = va_arg(f->args, Item*);
34859a747e4fSDavid du Colombier 	bi = 0;
34869a747e4fSDavid du Colombier 	nbuf = sizeof(buf);
34879a747e4fSDavid du Colombier 	state = it->state;
34889a747e4fSDavid du Colombier 	nbuf = nbuf-1;
34899a747e4fSDavid du Colombier 	if(state&IFbrk) {
34909a747e4fSDavid du Colombier 		cl = state&(IFcleft|IFcright);
34919a747e4fSDavid du Colombier 		p = "";
34929a747e4fSDavid du Colombier 		if(cl) {
34939a747e4fSDavid du Colombier 			if(cl == (IFcleft|IFcright))
34949a747e4fSDavid du Colombier 				p = " both";
34959a747e4fSDavid du Colombier 			else if(cl == IFcleft)
34969a747e4fSDavid du Colombier 				p = " left";
34979a747e4fSDavid du Colombier 			else
34989a747e4fSDavid du Colombier 				p = " right";
34999a747e4fSDavid du Colombier 		}
35009a747e4fSDavid du Colombier 		bi = snprint(buf, nbuf, "brk(%d%s)", (state&IFbrksp)? 1 : 0, p);
35019a747e4fSDavid du Colombier 	}
35029a747e4fSDavid du Colombier 	if(state&IFnobrk)
35039a747e4fSDavid du Colombier 		bi += snprint(buf+bi, nbuf-bi, " nobrk");
35049a747e4fSDavid du Colombier 	if(!(state&IFwrap))
35059a747e4fSDavid du Colombier 		bi += snprint(buf+bi, nbuf-bi, " nowrap");
35069a747e4fSDavid du Colombier 	if(state&IFrjust)
35079a747e4fSDavid du Colombier 		bi += snprint(buf+bi, nbuf-bi, " rjust");
35089a747e4fSDavid du Colombier 	if(state&IFcjust)
35099a747e4fSDavid du Colombier 		bi += snprint(buf+bi, nbuf-bi, " cjust");
35109a747e4fSDavid du Colombier 	if(state&IFsmap)
35119a747e4fSDavid du Colombier 		bi += snprint(buf+bi, nbuf-bi, " smap");
35129a747e4fSDavid du Colombier 	indent = (state&IFindentmask) >> IFindentshift;
35139a747e4fSDavid du Colombier 	if(indent > 0)
35149a747e4fSDavid du Colombier 		bi += snprint(buf+bi, nbuf-bi, " indent=%d", indent);
35159a747e4fSDavid du Colombier 	hang = state&IFhangmask;
35169a747e4fSDavid du Colombier 	if(hang > 0)
35179a747e4fSDavid du Colombier 		bi += snprint(buf+bi, nbuf-bi, " hang=%d", hang);
35189a747e4fSDavid du Colombier 
35199a747e4fSDavid du Colombier 	switch(it->tag) {
35209a747e4fSDavid du Colombier 	case Itexttag:
35219a747e4fSDavid du Colombier 		t = (Itext*)it;
35229a747e4fSDavid du Colombier 		bi += snprint(buf+bi, nbuf-bi, " Text '%S', fnt=%d, fg=%x", t->s, t->fnt, t->fg);
35239a747e4fSDavid du Colombier 		break;
35249a747e4fSDavid du Colombier 
35259a747e4fSDavid du Colombier 	case Iruletag:
35269a747e4fSDavid du Colombier 		r = (Irule*)it;
35279a747e4fSDavid du Colombier 		bi += snprint(buf+bi, nbuf-bi, "Rule size=%d, al=%S, wspec=", r->size, stringalign(r->align));
35289a747e4fSDavid du Colombier 		bi += dimprint(buf+bi, nbuf-bi, r->wspec);
35299a747e4fSDavid du Colombier 		break;
35309a747e4fSDavid du Colombier 
35319a747e4fSDavid du Colombier 	case Iimagetag:
35329a747e4fSDavid du Colombier 		i = (Iimage*)it;
35339a747e4fSDavid du Colombier 		bi += snprint(buf+bi, nbuf-bi,
35349a747e4fSDavid du Colombier 			"Image src=%S, alt=%S, al=%S, w=%d, h=%d hsp=%d, vsp=%d, bd=%d, map=%S",
35359a747e4fSDavid du Colombier 			i->imsrc, i->altrep? i->altrep : L"", stringalign(i->align), i->imwidth, i->imheight,
35369a747e4fSDavid du Colombier 			i->hspace, i->vspace, i->border, i->map? i->map->name : L"");
35379a747e4fSDavid du Colombier 		break;
35389a747e4fSDavid du Colombier 
35399a747e4fSDavid du Colombier 	case Iformfieldtag:
35409a747e4fSDavid du Colombier 		ff = ((Iformfield*)it)->formfield;
35419a747e4fSDavid du Colombier 		if(ff->ftype == Ftextarea)
35429a747e4fSDavid du Colombier 			ty = L"textarea";
35439a747e4fSDavid du Colombier 		else if(ff->ftype == Fselect)
35449a747e4fSDavid du Colombier 			ty = L"select";
35459a747e4fSDavid du Colombier 		else {
35469a747e4fSDavid du Colombier 			ty = _revlookup(input_tab, NINPUTTAB, ff->ftype);
35479a747e4fSDavid du Colombier 			if(ty == nil)
35489a747e4fSDavid du Colombier 				ty = L"none";
35499a747e4fSDavid du Colombier 		}
35509a747e4fSDavid du Colombier 		bi += snprint(buf+bi, nbuf-bi, "Formfield %S, fieldid=%d, formid=%d, name=%S, value=%S",
35519a747e4fSDavid du Colombier 			ty, ff->fieldid, ff->form->formid, ff->name? ff->name : L"",
35529a747e4fSDavid du Colombier 			ff->value? ff->value : L"");
35539a747e4fSDavid du Colombier 		break;
35549a747e4fSDavid du Colombier 
35559a747e4fSDavid du Colombier 	case Itabletag:
35569a747e4fSDavid du Colombier 		tab = ((Itable*)it)->table;
35579a747e4fSDavid du Colombier 		bi += snprint(buf+bi, nbuf-bi, "Table tableid=%d, width=", tab->tableid);
35589a747e4fSDavid du Colombier 		bi += dimprint(buf+bi, nbuf-bi, tab->width);
35599a747e4fSDavid du Colombier 		bi += snprint(buf+bi, nbuf-bi, ", nrow=%d, ncol=%d, ncell=%d, totw=%d, toth=%d\n",
35609a747e4fSDavid du Colombier 			tab->nrow, tab->ncol, tab->ncell, tab->totw, tab->toth);
35619a747e4fSDavid du Colombier 		for(c = tab->cells; c != nil; c = c->next)
35629a747e4fSDavid du Colombier 			bi += snprint(buf+bi, nbuf-bi, "Cell %d.%d, at (%d,%d) ",
35639a747e4fSDavid du Colombier 					tab->tableid, c->cellid, c->row, c->col);
35649a747e4fSDavid du Colombier 		bi += snprint(buf+bi, nbuf-bi, "End of Table %d", tab->tableid);
35659a747e4fSDavid du Colombier 		break;
35669a747e4fSDavid du Colombier 
35679a747e4fSDavid du Colombier 	case Ifloattag:
35689a747e4fSDavid du Colombier 		fl = (Ifloat*)it;
35699a747e4fSDavid du Colombier 		bi += snprint(buf+bi, nbuf-bi, "Float, x=%d y=%d, side=%S, it=%I",
35709a747e4fSDavid du Colombier 			fl->x, fl->y, stringalign(fl->side), fl->item);
35719a747e4fSDavid du Colombier 		bi += snprint(buf+bi, nbuf-bi, "\n\t");
35729a747e4fSDavid du Colombier 		break;
35739a747e4fSDavid du Colombier 
35749a747e4fSDavid du Colombier 	case Ispacertag:
35759a747e4fSDavid du Colombier 		p = "";
35769a747e4fSDavid du Colombier 		switch(((Ispacer*)it)->spkind) {
35779a747e4fSDavid du Colombier 		case ISPnull:
35789a747e4fSDavid du Colombier 			p = "null";
35799a747e4fSDavid du Colombier 			break;
35809a747e4fSDavid du Colombier 		case ISPvline:
35819a747e4fSDavid du Colombier 			p = "vline";
35829a747e4fSDavid du Colombier 			break;
35839a747e4fSDavid du Colombier 		case ISPhspace:
35849a747e4fSDavid du Colombier 			p = "hspace";
35859a747e4fSDavid du Colombier 			break;
35869a747e4fSDavid du Colombier 		}
35879a747e4fSDavid du Colombier 		bi += snprint(buf+bi, nbuf-bi, "Spacer %s ", p);
35889a747e4fSDavid du Colombier 		break;
35899a747e4fSDavid du Colombier 	}
35909a747e4fSDavid du Colombier 	bi += snprint(buf+bi, nbuf-bi, " w=%d, h=%d, a=%d, anchor=%d\n",
35919a747e4fSDavid du Colombier 			it->width, it->height, it->ascent, it->anchorid);
35929a747e4fSDavid du Colombier 	buf[bi] = 0;
35939a747e4fSDavid du Colombier 	return fmtstrcpy(f, buf);
35949a747e4fSDavid du Colombier }
35959a747e4fSDavid du Colombier 
35969a747e4fSDavid du Colombier // String version of alignment 'a'
35979a747e4fSDavid du Colombier static Rune*
stringalign(int a)35989a747e4fSDavid du Colombier stringalign(int a)
35999a747e4fSDavid du Colombier {
36009a747e4fSDavid du Colombier 	Rune*	s;
36019a747e4fSDavid du Colombier 
36029a747e4fSDavid du Colombier 	s = _revlookup(align_tab, NALIGNTAB, a);
36035d459b5aSDavid du Colombier 	if(s == nil)
36049a747e4fSDavid du Colombier 		s = L"none";
36059a747e4fSDavid du Colombier 	return s;
36069a747e4fSDavid du Colombier }
36079a747e4fSDavid du Colombier 
36089a747e4fSDavid du Colombier // Put at most nbuf chars of representation of d into buf,
36099a747e4fSDavid du Colombier // and return number of characters put
36109a747e4fSDavid du Colombier static int
dimprint(char * buf,int nbuf,Dimen d)36119a747e4fSDavid du Colombier dimprint(char* buf, int nbuf, Dimen d)
36129a747e4fSDavid du Colombier {
36139a747e4fSDavid du Colombier 	int	n;
36149a747e4fSDavid du Colombier 	int	k;
36159a747e4fSDavid du Colombier 
36169a747e4fSDavid du Colombier 	n = 0;
36179a747e4fSDavid du Colombier 	n += snprint(buf, nbuf, "%d", dimenspec(d));
36189a747e4fSDavid du Colombier 	k = dimenkind(d);
36199a747e4fSDavid du Colombier 	if(k == Dpercent)
36209a747e4fSDavid du Colombier 		buf[n++] = '%';
36219a747e4fSDavid du Colombier 	if(k == Drelative)
36229a747e4fSDavid du Colombier 		buf[n++] = '*';
36239a747e4fSDavid du Colombier 	return n;
36249a747e4fSDavid du Colombier }
36259a747e4fSDavid du Colombier 
36269a747e4fSDavid du Colombier void
printitems(Item * items,char * msg)36279a747e4fSDavid du Colombier printitems(Item* items, char* msg)
36289a747e4fSDavid du Colombier {
36299a747e4fSDavid du Colombier 	Item*	il;
36309a747e4fSDavid du Colombier 
36319a747e4fSDavid du Colombier 	fprint(2, "%s\n", msg);
36329a747e4fSDavid du Colombier 	il = items;
36339a747e4fSDavid du Colombier 	while(il != nil) {
36349a747e4fSDavid du Colombier 		fprint(2, "%I", il);
36359a747e4fSDavid du Colombier 		il = il->next;
36369a747e4fSDavid du Colombier 	}
36379a747e4fSDavid du Colombier }
36389a747e4fSDavid du Colombier 
36399a747e4fSDavid du Colombier static Genattr*
newgenattr(Rune * id,Rune * class,Rune * style,Rune * title,SEvent * events)36405d459b5aSDavid du Colombier newgenattr(Rune* id, Rune* class, Rune* style, Rune* title, SEvent* events)
36419a747e4fSDavid du Colombier {
36429a747e4fSDavid du Colombier 	Genattr* g;
36439a747e4fSDavid du Colombier 
36449a747e4fSDavid du Colombier 	g = (Genattr*)emalloc(sizeof(Genattr));
36459a747e4fSDavid du Colombier 	g->id = id;
36469a747e4fSDavid du Colombier 	g->class = class;
36479a747e4fSDavid du Colombier 	g->style = style;
36489a747e4fSDavid du Colombier 	g->title = title;
36499a747e4fSDavid du Colombier 	g->events = events;
36509a747e4fSDavid du Colombier 	return g;
36519a747e4fSDavid du Colombier }
36529a747e4fSDavid du Colombier 
36539a747e4fSDavid du Colombier static Formfield*
newformfield(int ftype,int fieldid,Form * form,Rune * name,Rune * value,int size,int maxlength,Formfield * link)36549a747e4fSDavid du Colombier newformfield(int ftype, int fieldid, Form* form, Rune* name,
36559a747e4fSDavid du Colombier 		Rune* value, int size, int maxlength, Formfield* link)
36569a747e4fSDavid du Colombier {
36579a747e4fSDavid du Colombier 	Formfield* ff;
36589a747e4fSDavid du Colombier 
36599a747e4fSDavid du Colombier 	ff = (Formfield*)emalloc(sizeof(Formfield));
36609a747e4fSDavid du Colombier 	ff->ftype = ftype;
36619a747e4fSDavid du Colombier 	ff->fieldid = fieldid;
36629a747e4fSDavid du Colombier 	ff->form = form;
36639a747e4fSDavid du Colombier 	ff->name = name;
36649a747e4fSDavid du Colombier 	ff->value = value;
36659a747e4fSDavid du Colombier 	ff->size = size;
36669a747e4fSDavid du Colombier 	ff->maxlength = maxlength;
36679a747e4fSDavid du Colombier 	ff->ctlid = -1;
36689a747e4fSDavid du Colombier 	ff->next = link;
36699a747e4fSDavid du Colombier 	return ff;
36709a747e4fSDavid du Colombier }
36719a747e4fSDavid du Colombier 
36729a747e4fSDavid du Colombier // Transfers ownership of value and display to Option.
36739a747e4fSDavid du Colombier static Option*
newoption(int selected,Rune * value,Rune * display,Option * link)36749a747e4fSDavid du Colombier newoption(int selected, Rune* value, Rune* display, Option* link)
36759a747e4fSDavid du Colombier {
36769a747e4fSDavid du Colombier 	Option *o;
36779a747e4fSDavid du Colombier 
36789a747e4fSDavid du Colombier 	o = (Option*)emalloc(sizeof(Option));
36799a747e4fSDavid du Colombier 	o->selected = selected;
36809a747e4fSDavid du Colombier 	o->value = value;
36819a747e4fSDavid du Colombier 	o->display = display;
36829a747e4fSDavid du Colombier 	o->next = link;
36839a747e4fSDavid du Colombier 	return o;
36849a747e4fSDavid du Colombier }
36859a747e4fSDavid du Colombier 
36869a747e4fSDavid du Colombier static Form*
newform(int formid,Rune * name,Rune * action,int target,int method,Form * link)36879a747e4fSDavid du Colombier newform(int formid, Rune* name, Rune* action, int target, int method, Form* link)
36889a747e4fSDavid du Colombier {
36899a747e4fSDavid du Colombier 	Form* f;
36909a747e4fSDavid du Colombier 
36919a747e4fSDavid du Colombier 	f = (Form*)emalloc(sizeof(Form));
36929a747e4fSDavid du Colombier 	f->formid = formid;
36939a747e4fSDavid du Colombier 	f->name = name;
36949a747e4fSDavid du Colombier 	f->action = action;
36959a747e4fSDavid du Colombier 	f->target = target;
36969a747e4fSDavid du Colombier 	f->method = method;
36979a747e4fSDavid du Colombier 	f->nfields = 0;
36989a747e4fSDavid du Colombier 	f->fields = nil;
36999a747e4fSDavid du Colombier 	f->next = link;
37009a747e4fSDavid du Colombier 	return f;
37019a747e4fSDavid du Colombier }
37029a747e4fSDavid du Colombier 
37039a747e4fSDavid du Colombier static Table*
newtable(int tableid,Align align,Dimen width,int border,int cellspacing,int cellpadding,Background bg,Token * tok,Table * link)37049a747e4fSDavid du Colombier newtable(int tableid, Align align, Dimen width, int border,
37059a747e4fSDavid du Colombier 	int cellspacing, int cellpadding, Background bg, Token* tok, Table* link)
37069a747e4fSDavid du Colombier {
37079a747e4fSDavid du Colombier 	Table* t;
37089a747e4fSDavid du Colombier 
37099a747e4fSDavid du Colombier 	t = (Table*)emalloc(sizeof(Table));
37109a747e4fSDavid du Colombier 	t->tableid = tableid;
37119a747e4fSDavid du Colombier 	t->align = align;
37129a747e4fSDavid du Colombier 	t->width = width;
37139a747e4fSDavid du Colombier 	t->border = border;
37149a747e4fSDavid du Colombier 	t->cellspacing = cellspacing;
37159a747e4fSDavid du Colombier 	t->cellpadding = cellpadding;
37169a747e4fSDavid du Colombier 	t->background = bg;
37179a747e4fSDavid du Colombier 	t->caption_place = ALbottom;
37189a747e4fSDavid du Colombier 	t->caption_lay = nil;
37199a747e4fSDavid du Colombier 	t->tabletok = tok;
37209a747e4fSDavid du Colombier 	t->tabletok = nil;
37219a747e4fSDavid du Colombier 	t->next = link;
37229a747e4fSDavid du Colombier 	return t;
37239a747e4fSDavid du Colombier }
37249a747e4fSDavid du Colombier 
37259a747e4fSDavid du Colombier static Tablerow*
newtablerow(Align align,Background bg,int flags,Tablerow * link)37269a747e4fSDavid du Colombier newtablerow(Align align, Background bg, int flags, Tablerow* link)
37279a747e4fSDavid du Colombier {
37289a747e4fSDavid du Colombier 	Tablerow* tr;
37299a747e4fSDavid du Colombier 
37309a747e4fSDavid du Colombier 	tr = (Tablerow*)emalloc(sizeof(Tablerow));
37319a747e4fSDavid du Colombier 	tr->align = align;
37329a747e4fSDavid du Colombier 	tr->background = bg;
37339a747e4fSDavid du Colombier 	tr->flags = flags;
37349a747e4fSDavid du Colombier 	tr->next = link;
37359a747e4fSDavid du Colombier 	return tr;
37369a747e4fSDavid du Colombier }
37379a747e4fSDavid du Colombier 
37389a747e4fSDavid du Colombier static Tablecell*
newtablecell(int cellid,int rowspan,int colspan,Align align,Dimen wspec,int hspec,Background bg,int flags,Tablecell * link)37399a747e4fSDavid du Colombier newtablecell(int cellid, int rowspan, int colspan, Align align, Dimen wspec, int hspec,
37409a747e4fSDavid du Colombier 		Background bg, int flags, Tablecell* link)
37419a747e4fSDavid du Colombier {
37429a747e4fSDavid du Colombier 	Tablecell* c;
37439a747e4fSDavid du Colombier 
37449a747e4fSDavid du Colombier 	c = (Tablecell*)emalloc(sizeof(Tablecell));
37459a747e4fSDavid du Colombier 	c->cellid = cellid;
37469a747e4fSDavid du Colombier 	c->lay = nil;
37479a747e4fSDavid du Colombier 	c->rowspan = rowspan;
37489a747e4fSDavid du Colombier 	c->colspan = colspan;
37499a747e4fSDavid du Colombier 	c->align = align;
37509a747e4fSDavid du Colombier 	c->flags = flags;
37519a747e4fSDavid du Colombier 	c->wspec = wspec;
37529a747e4fSDavid du Colombier 	c->hspec = hspec;
37539a747e4fSDavid du Colombier 	c->background = bg;
37549a747e4fSDavid du Colombier 	c->next = link;
37559a747e4fSDavid du Colombier 	return c;
37569a747e4fSDavid du Colombier }
37579a747e4fSDavid du Colombier 
37589a747e4fSDavid du Colombier static Anchor*
newanchor(int index,Rune * name,Rune * href,int target,Anchor * link)37599a747e4fSDavid du Colombier newanchor(int index, Rune* name, Rune* href, int target, Anchor* link)
37609a747e4fSDavid du Colombier {
37619a747e4fSDavid du Colombier 	Anchor* a;
37629a747e4fSDavid du Colombier 
37639a747e4fSDavid du Colombier 	a = (Anchor*)emalloc(sizeof(Anchor));
37649a747e4fSDavid du Colombier 	a->index = index;
37659a747e4fSDavid du Colombier 	a->name = name;
37669a747e4fSDavid du Colombier 	a->href = href;
37679a747e4fSDavid du Colombier 	a->target = target;
37689a747e4fSDavid du Colombier 	a->next = link;
37699a747e4fSDavid du Colombier 	return a;
37709a747e4fSDavid du Colombier }
37719a747e4fSDavid du Colombier 
37729a747e4fSDavid du Colombier static DestAnchor*
newdestanchor(int index,Rune * name,Item * item,DestAnchor * link)37739a747e4fSDavid du Colombier newdestanchor(int index, Rune* name, Item* item, DestAnchor* link)
37749a747e4fSDavid du Colombier {
37759a747e4fSDavid du Colombier 	DestAnchor* d;
37769a747e4fSDavid du Colombier 
37779a747e4fSDavid du Colombier 	d = (DestAnchor*)emalloc(sizeof(DestAnchor));
37789a747e4fSDavid du Colombier 	d->index = index;
37799a747e4fSDavid du Colombier 	d->name = name;
37809a747e4fSDavid du Colombier 	d->item = item;
37819a747e4fSDavid du Colombier 	d->next = link;
37829a747e4fSDavid du Colombier 	return d;
37839a747e4fSDavid du Colombier }
37849a747e4fSDavid du Colombier 
37859a747e4fSDavid du Colombier static SEvent*
newscriptevent(int type,Rune * script,SEvent * link)37865d459b5aSDavid du Colombier newscriptevent(int type, Rune* script, SEvent* link)
37879a747e4fSDavid du Colombier {
37889a747e4fSDavid du Colombier 	SEvent* ans;
37899a747e4fSDavid du Colombier 
37905d459b5aSDavid du Colombier 	ans = (SEvent*)emalloc(sizeof(SEvent));
37919a747e4fSDavid du Colombier 	ans->type = type;
37929a747e4fSDavid du Colombier 	ans->script = script;
37939a747e4fSDavid du Colombier 	ans->next = link;
37949a747e4fSDavid du Colombier 	return ans;
37959a747e4fSDavid du Colombier }
37969a747e4fSDavid du Colombier 
37979a747e4fSDavid du Colombier static void
freescriptevents(SEvent * ehead)37989a747e4fSDavid du Colombier freescriptevents(SEvent* ehead)
37999a747e4fSDavid du Colombier {
38009a747e4fSDavid du Colombier 	SEvent* e;
38019a747e4fSDavid du Colombier 	SEvent* nexte;
38029a747e4fSDavid du Colombier 
38039a747e4fSDavid du Colombier 	e = ehead;
38049a747e4fSDavid du Colombier 	while(e != nil) {
38059a747e4fSDavid du Colombier 		nexte = e->next;
38069a747e4fSDavid du Colombier 		free(e->script);
38079a747e4fSDavid du Colombier 		free(e);
38089a747e4fSDavid du Colombier 		e = nexte;
38099a747e4fSDavid du Colombier 	}
38109a747e4fSDavid du Colombier }
38119a747e4fSDavid du Colombier 
38129a747e4fSDavid du Colombier static Dimen
makedimen(int kind,int spec)38139a747e4fSDavid du Colombier makedimen(int kind, int spec)
38149a747e4fSDavid du Colombier {
38159a747e4fSDavid du Colombier 	Dimen d;
38169a747e4fSDavid du Colombier 
38179a747e4fSDavid du Colombier 	if(spec&Dkindmask) {
38189a747e4fSDavid du Colombier 		if(warn)
38199a747e4fSDavid du Colombier 			fprint(2, "warning: dimension spec too big: %d\n", spec);
38209a747e4fSDavid du Colombier 		spec = 0;
38219a747e4fSDavid du Colombier 	}
38229a747e4fSDavid du Colombier 	d.kindspec = kind|spec;
38239a747e4fSDavid du Colombier 	return d;
38249a747e4fSDavid du Colombier }
38259a747e4fSDavid du Colombier 
38269a747e4fSDavid du Colombier int
dimenkind(Dimen d)38279a747e4fSDavid du Colombier dimenkind(Dimen d)
38289a747e4fSDavid du Colombier {
38299a747e4fSDavid du Colombier 	return (d.kindspec&Dkindmask);
38309a747e4fSDavid du Colombier }
38319a747e4fSDavid du Colombier 
38329a747e4fSDavid du Colombier int
dimenspec(Dimen d)38339a747e4fSDavid du Colombier dimenspec(Dimen d)
38349a747e4fSDavid du Colombier {
38359a747e4fSDavid du Colombier 	return (d.kindspec&Dspecmask);
38369a747e4fSDavid du Colombier }
38379a747e4fSDavid du Colombier 
38389a747e4fSDavid du Colombier static Kidinfo*
newkidinfo(int isframeset,Kidinfo * link)38399a747e4fSDavid du Colombier newkidinfo(int isframeset, Kidinfo* link)
38409a747e4fSDavid du Colombier {
38419a747e4fSDavid du Colombier 	Kidinfo*	ki;
38429a747e4fSDavid du Colombier 
38439a747e4fSDavid du Colombier 	ki = (Kidinfo*)emalloc(sizeof(Kidinfo));
38449a747e4fSDavid du Colombier 	ki->isframeset = isframeset;
38459a747e4fSDavid du Colombier 	if(!isframeset) {
38469a747e4fSDavid du Colombier 		ki->flags = FRhscrollauto|FRvscrollauto;
38479a747e4fSDavid du Colombier 		ki->marginw = FRKIDMARGIN;
38489a747e4fSDavid du Colombier 		ki->marginh = FRKIDMARGIN;
38499a747e4fSDavid du Colombier 		ki->framebd = 1;
38509a747e4fSDavid du Colombier 	}
38519a747e4fSDavid du Colombier 	ki->next = link;
38529a747e4fSDavid du Colombier 	return ki;
38539a747e4fSDavid du Colombier }
38549a747e4fSDavid du Colombier 
38559a747e4fSDavid du Colombier static Docinfo*
newdocinfo(void)38569a747e4fSDavid du Colombier newdocinfo(void)
38579a747e4fSDavid du Colombier {
38589a747e4fSDavid du Colombier 	Docinfo*	d;
38599a747e4fSDavid du Colombier 
38609a747e4fSDavid du Colombier 	d = (Docinfo*)emalloc(sizeof(Docinfo));
38619a747e4fSDavid du Colombier 	resetdocinfo(d);
38629a747e4fSDavid du Colombier 	return d;
38639a747e4fSDavid du Colombier }
38649a747e4fSDavid du Colombier 
38659a747e4fSDavid du Colombier static void
resetdocinfo(Docinfo * d)38669a747e4fSDavid du Colombier resetdocinfo(Docinfo* d)
38679a747e4fSDavid du Colombier {
38689a747e4fSDavid du Colombier 	memset(d, 0, sizeof(Docinfo));
38699a747e4fSDavid du Colombier 	d->background = makebackground(nil, White);
38709a747e4fSDavid du Colombier 	d->text = Black;
38719a747e4fSDavid du Colombier 	d->link = Blue;
38729a747e4fSDavid du Colombier 	d->vlink = Blue;
38739a747e4fSDavid du Colombier 	d->alink = Blue;
38749a747e4fSDavid du Colombier 	d->target = FTself;
38759a747e4fSDavid du Colombier 	d->chset = ISO_8859_1;
38769a747e4fSDavid du Colombier 	d->scripttype = TextJavascript;
38779a747e4fSDavid du Colombier 	d->frameid = -1;
38789a747e4fSDavid du Colombier }
38799a747e4fSDavid du Colombier 
38809a747e4fSDavid du Colombier // Use targetmap array to keep track of name <-> targetid mapping.
38819a747e4fSDavid du Colombier // Use real malloc(), and never free
38829a747e4fSDavid du Colombier static void
targetmapinit(void)38839a747e4fSDavid du Colombier targetmapinit(void)
38849a747e4fSDavid du Colombier {
3885c93608ccSDavid du Colombier 	int l;
3886c93608ccSDavid du Colombier 
38879a747e4fSDavid du Colombier 	targetmapsize = 10;
3888c93608ccSDavid du Colombier 	l = targetmapsize*sizeof *targetmap;
3889c93608ccSDavid du Colombier 	targetmap = emalloc(l);
3890c93608ccSDavid du Colombier 	memset(targetmap, 0, l);
38919a747e4fSDavid du Colombier 	targetmap[0].key = _Strdup(L"_top");
38929a747e4fSDavid du Colombier 	targetmap[0].val = FTtop;
38939a747e4fSDavid du Colombier 	targetmap[1].key = _Strdup(L"_self");
38949a747e4fSDavid du Colombier 	targetmap[1].val = FTself;
38959a747e4fSDavid du Colombier 	targetmap[2].key = _Strdup(L"_parent");
38969a747e4fSDavid du Colombier 	targetmap[2].val = FTparent;
38979a747e4fSDavid du Colombier 	targetmap[3].key = _Strdup(L"_blank");
38989a747e4fSDavid du Colombier 	targetmap[3].val = FTblank;
38999a747e4fSDavid du Colombier 	ntargets = 4;
39009a747e4fSDavid du Colombier }
39019a747e4fSDavid du Colombier 
39029a747e4fSDavid du Colombier int
targetid(Rune * s)39039a747e4fSDavid du Colombier targetid(Rune* s)
39049a747e4fSDavid du Colombier {
39059a747e4fSDavid du Colombier 	int i;
39069a747e4fSDavid du Colombier 	int n;
39079a747e4fSDavid du Colombier 
39085d459b5aSDavid du Colombier 	n = _Strlen(s);
39099a747e4fSDavid du Colombier 	if(n == 0)
39109a747e4fSDavid du Colombier 		return FTself;
39119a747e4fSDavid du Colombier 	for(i = 0; i < ntargets; i++)
39125d459b5aSDavid du Colombier 		if(_Strcmp(s, targetmap[i].key) == 0)
39139a747e4fSDavid du Colombier 			return targetmap[i].val;
3914c93608ccSDavid du Colombier 	if(i == targetmapsize) {
39159a747e4fSDavid du Colombier 		targetmapsize += 10;
3916c93608ccSDavid du Colombier 		targetmap = erealloc(targetmap, targetmapsize*sizeof(StringInt));
39179a747e4fSDavid du Colombier 	}
3918c93608ccSDavid du Colombier 	targetmap[i].key = _Strdup(s);
39199a747e4fSDavid du Colombier 	targetmap[i].val = i;
39209a747e4fSDavid du Colombier 	ntargets++;
39219a747e4fSDavid du Colombier 	return i;
39229a747e4fSDavid du Colombier }
39239a747e4fSDavid du Colombier 
39249a747e4fSDavid du Colombier Rune*
targetname(int targid)39259a747e4fSDavid du Colombier targetname(int targid)
39269a747e4fSDavid du Colombier {
39279a747e4fSDavid du Colombier 	int i;
39289a747e4fSDavid du Colombier 
39299a747e4fSDavid du Colombier 	for(i = 0; i < ntargets; i++)
39309a747e4fSDavid du Colombier 		if(targetmap[i].val == targid)
39319a747e4fSDavid du Colombier 			return targetmap[i].key;
39329a747e4fSDavid du Colombier 	return L"?";
39339a747e4fSDavid du Colombier }
39349a747e4fSDavid du Colombier 
39359a747e4fSDavid du Colombier // Convert HTML color spec to RGB value, returning dflt if can't.
39369a747e4fSDavid du Colombier // Argument is supposed to be a valid HTML color, or "".
39379a747e4fSDavid du Colombier // Return the RGB value of the color, using dflt if s
39389a747e4fSDavid du Colombier // is nil or an invalid color.
39399a747e4fSDavid du Colombier static int
color(Rune * s,int dflt)39409a747e4fSDavid du Colombier color(Rune* s, int dflt)
39419a747e4fSDavid du Colombier {
39429a747e4fSDavid du Colombier 	int v;
39439a747e4fSDavid du Colombier 	Rune* rest;
39449a747e4fSDavid du Colombier 
39459a747e4fSDavid du Colombier 	if(s == nil)
39469a747e4fSDavid du Colombier 		return dflt;
39475d459b5aSDavid du Colombier 	if(_lookup(color_tab, NCOLORS, s, _Strlen(s), &v))
39489a747e4fSDavid du Colombier 		return v;
39499a747e4fSDavid du Colombier 	if(s[0] == '#')
39509a747e4fSDavid du Colombier 		s++;
39519a747e4fSDavid du Colombier 	v = _Strtol(s, &rest, 16);
39529a747e4fSDavid du Colombier 	if(*rest == 0)
39539a747e4fSDavid du Colombier 		return v;
39549a747e4fSDavid du Colombier 	return dflt;
39559a747e4fSDavid du Colombier }
39569a747e4fSDavid du Colombier 
39579a747e4fSDavid du Colombier // Debugging
39589a747e4fSDavid du Colombier 
39599a747e4fSDavid du Colombier #define HUGEPIX 10000
39609a747e4fSDavid du Colombier 
39619a747e4fSDavid du Colombier // A "shallow" validitem, that doesn't follow next links
39629a747e4fSDavid du Colombier // or descend into tables.
39639a747e4fSDavid du Colombier static int
validitem(Item * i)39649a747e4fSDavid du Colombier validitem(Item* i)
39659a747e4fSDavid du Colombier {
39669a747e4fSDavid du Colombier 	int ok;
39679a747e4fSDavid du Colombier 	Itext* ti;
39689a747e4fSDavid du Colombier 	Irule* ri;
39699a747e4fSDavid du Colombier 	Iimage* ii;
39709a747e4fSDavid du Colombier 	Ifloat* fi;
39719a747e4fSDavid du Colombier 	int a;
39729a747e4fSDavid du Colombier 
39739a747e4fSDavid du Colombier 	ok = (i->tag >= Itexttag && i->tag <= Ispacertag) &&
39749a747e4fSDavid du Colombier 		(i->next == nil || validptr(i->next)) &&
39759a747e4fSDavid du Colombier 		(i->width >= 0 && i->width < HUGEPIX) &&
39769a747e4fSDavid du Colombier 		(i->height >= 0 && i->height < HUGEPIX) &&
39779a747e4fSDavid du Colombier 		(i->ascent > -HUGEPIX && i->ascent < HUGEPIX) &&
39789a747e4fSDavid du Colombier 		(i->anchorid >= 0) &&
39799a747e4fSDavid du Colombier 		(i->genattr == nil || validptr(i->genattr));
39809a747e4fSDavid du Colombier 	// also, could check state for ridiculous combinations
39819a747e4fSDavid du Colombier 	// also, could check anchorid for within-doc-range
39829a747e4fSDavid du Colombier 	if(ok)
39839a747e4fSDavid du Colombier 		switch(i->tag) {
39849a747e4fSDavid du Colombier 		case Itexttag:
39859a747e4fSDavid du Colombier 			ti = (Itext*)i;
39869a747e4fSDavid du Colombier 			ok = validStr(ti->s) &&
39879a747e4fSDavid du Colombier 				(ti->fnt >= 0 && ti->fnt < NumStyle*NumSize) &&
39889a747e4fSDavid du Colombier 				(ti->ul == ULnone || ti->ul == ULunder || ti->ul == ULmid);
39899a747e4fSDavid du Colombier 			break;
39909a747e4fSDavid du Colombier 		case Iruletag:
39919a747e4fSDavid du Colombier 			ri = (Irule*)i;
39929a747e4fSDavid du Colombier 			ok = (validvalign(ri->align) || validhalign(ri->align)) &&
39939a747e4fSDavid du Colombier 				(ri->size >=0 && ri->size < HUGEPIX);
39949a747e4fSDavid du Colombier 			break;
39959a747e4fSDavid du Colombier 		case Iimagetag:
39969a747e4fSDavid du Colombier 			ii = (Iimage*)i;
39979a747e4fSDavid du Colombier 			ok = (ii->imsrc == nil || validptr(ii->imsrc)) &&
39989a747e4fSDavid du Colombier 				(ii->width >= 0 && ii->width < HUGEPIX) &&
39999a747e4fSDavid du Colombier 				(ii->height >= 0 && ii->height < HUGEPIX) &&
40009a747e4fSDavid du Colombier 				(ii->imwidth >= 0 && ii->imwidth < HUGEPIX) &&
40019a747e4fSDavid du Colombier 				(ii->imheight >= 0 && ii->imheight < HUGEPIX) &&
40029a747e4fSDavid du Colombier 				(ii->altrep == nil || validStr(ii->altrep)) &&
40039a747e4fSDavid du Colombier 				(ii->map == nil || validptr(ii->map)) &&
40049a747e4fSDavid du Colombier 				(validvalign(ii->align) || validhalign(ii->align)) &&
40059a747e4fSDavid du Colombier 				(ii->nextimage == nil || validptr(ii->nextimage));
40069a747e4fSDavid du Colombier 			break;
40079a747e4fSDavid du Colombier 		case Iformfieldtag:
40089a747e4fSDavid du Colombier 			ok = validformfield(((Iformfield*)i)->formfield);
40099a747e4fSDavid du Colombier 			break;
40109a747e4fSDavid du Colombier 		case Itabletag:
40119a747e4fSDavid du Colombier 			ok = validptr((Itable*)i);
40129a747e4fSDavid du Colombier 			break;
40139a747e4fSDavid du Colombier 		case Ifloattag:
40149a747e4fSDavid du Colombier 			fi = (Ifloat*)i;
40159a747e4fSDavid du Colombier 			ok = (fi->side == ALleft || fi->side == ALright) &&
40169a747e4fSDavid du Colombier 				validitem(fi->item) &&
40179a747e4fSDavid du Colombier 				(fi->item->tag == Iimagetag || fi->item->tag == Itabletag);
40189a747e4fSDavid du Colombier 			break;
40199a747e4fSDavid du Colombier 		case Ispacertag:
40209a747e4fSDavid du Colombier 			a = ((Ispacer*)i)->spkind;
40219a747e4fSDavid du Colombier 			ok = a==ISPnull || a==ISPvline || a==ISPhspace || a==ISPgeneral;
40229a747e4fSDavid du Colombier 			break;
40239a747e4fSDavid du Colombier 		default:
40249a747e4fSDavid du Colombier 			ok = 0;
40259a747e4fSDavid du Colombier 		}
40269a747e4fSDavid du Colombier 	return ok;
40279a747e4fSDavid du Colombier }
40289a747e4fSDavid du Colombier 
40299a747e4fSDavid du Colombier // "deep" validation, that checks whole list of items,
40309a747e4fSDavid du Colombier // and descends into tables and floated tables.
40319a747e4fSDavid du Colombier // nil is ok for argument.
40329a747e4fSDavid du Colombier int
validitems(Item * i)40339a747e4fSDavid du Colombier validitems(Item* i)
40349a747e4fSDavid du Colombier {
40359a747e4fSDavid du Colombier 	int ok;
40369a747e4fSDavid du Colombier 	Item* ii;
40379a747e4fSDavid du Colombier 
40389a747e4fSDavid du Colombier 	ok = 1;
40399a747e4fSDavid du Colombier 	while(i != nil && ok) {
40409a747e4fSDavid du Colombier 		ok = validitem(i);
40419a747e4fSDavid du Colombier 		if(ok) {
40429a747e4fSDavid du Colombier 			if(i->tag == Itabletag) {
40439a747e4fSDavid du Colombier 				ok = validtable(((Itable*)i)->table);
40449a747e4fSDavid du Colombier 			}
40459a747e4fSDavid du Colombier 			else if(i->tag == Ifloattag) {
40469a747e4fSDavid du Colombier 				ii = ((Ifloat*)i)->item;
40479a747e4fSDavid du Colombier 				if(ii->tag == Itabletag)
40489a747e4fSDavid du Colombier 					ok = validtable(((Itable*)ii)->table);
40499a747e4fSDavid du Colombier 			}
40509a747e4fSDavid du Colombier 		}
40519a747e4fSDavid du Colombier 		if(!ok) {
40529a747e4fSDavid du Colombier 			fprint(2, "invalid item: %I\n", i);
40539a747e4fSDavid du Colombier 		}
40549a747e4fSDavid du Colombier 		i = i->next;
40559a747e4fSDavid du Colombier 	}
40569a747e4fSDavid du Colombier 	return ok;
40579a747e4fSDavid du Colombier }
40589a747e4fSDavid du Colombier 
40599a747e4fSDavid du Colombier static int
validformfield(Formfield * f)40609a747e4fSDavid du Colombier validformfield(Formfield* f)
40619a747e4fSDavid du Colombier {
40629a747e4fSDavid du Colombier 	int ok;
40639a747e4fSDavid du Colombier 
40649a747e4fSDavid du Colombier 	ok = (f->next == nil || validptr(f->next)) &&
40659a747e4fSDavid du Colombier 		(f->ftype >= 0 && f->ftype <= Ftextarea) &&
40669a747e4fSDavid du Colombier 		f->fieldid >= 0 &&
40679a747e4fSDavid du Colombier 		(f->form == nil || validptr(f->form)) &&
40689a747e4fSDavid du Colombier 		(f->name == nil || validStr(f->name)) &&
40699a747e4fSDavid du Colombier 		(f->value == nil || validStr(f->value)) &&
40709a747e4fSDavid du Colombier 		(f->options == nil || validptr(f->options)) &&
40719a747e4fSDavid du Colombier 		(f->image == nil || validitem(f->image)) &&
40729a747e4fSDavid du Colombier 		(f->events == nil || validptr(f->events));
40739a747e4fSDavid du Colombier 	// when all built, should have f->fieldid < f->form->nfields,
40749a747e4fSDavid du Colombier 	// but this may be called during build...
40759a747e4fSDavid du Colombier 	return ok;
40769a747e4fSDavid du Colombier }
40779a747e4fSDavid du Colombier 
40789a747e4fSDavid du Colombier // "deep" validation -- checks cell contents too
40799a747e4fSDavid du Colombier static int
validtable(Table * t)40809a747e4fSDavid du Colombier validtable(Table* t)
40819a747e4fSDavid du Colombier {
40829a747e4fSDavid du Colombier 	int ok;
40839a747e4fSDavid du Colombier 	int i, j;
40849a747e4fSDavid du Colombier 	Tablecell* c;
40859a747e4fSDavid du Colombier 
40869a747e4fSDavid du Colombier 	ok = (t->next == nil || validptr(t->next)) &&
40879a747e4fSDavid du Colombier 		t->nrow >= 0 &&
40889a747e4fSDavid du Colombier 		t->ncol >= 0 &&
40899a747e4fSDavid du Colombier 		t->ncell >= 0 &&
40909a747e4fSDavid du Colombier 		validalign(t->align) &&
40919a747e4fSDavid du Colombier 		validdimen(t->width) &&
40929a747e4fSDavid du Colombier 		(t->border >= 0 && t->border < HUGEPIX) &&
40939a747e4fSDavid du Colombier 		(t->cellspacing >= 0 && t->cellspacing < HUGEPIX) &&
40949a747e4fSDavid du Colombier 		(t->cellpadding >= 0 && t->cellpadding < HUGEPIX) &&
40959a747e4fSDavid du Colombier 		validitems(t->caption) &&
40969a747e4fSDavid du Colombier 		(t->caption_place == ALtop || t->caption_place == ALbottom) &&
40979a747e4fSDavid du Colombier 		(t->totw >= 0 && t->totw < HUGEPIX) &&
40989a747e4fSDavid du Colombier 		(t->toth >= 0 && t->toth < HUGEPIX) &&
40999a747e4fSDavid du Colombier 		(t->tabletok == nil || validptr(t->tabletok));
41009a747e4fSDavid du Colombier 	// during parsing, t->rows has list;
41019a747e4fSDavid du Colombier 	// only when parsing is done is t->nrow set > 0
41029a747e4fSDavid du Colombier 	if(ok && t->nrow > 0 && t->ncol > 0) {
41039a747e4fSDavid du Colombier 		// table is "finished"
41049a747e4fSDavid du Colombier 		for(i = 0; i < t->nrow && ok; i++)
41059a747e4fSDavid du Colombier 			ok = validtablerow(t->rows+i);
41069a747e4fSDavid du Colombier 		for(j = 0; j < t->ncol && ok; j++)
41079a747e4fSDavid du Colombier 			ok = validtablecol(t->cols+j);
41089a747e4fSDavid du Colombier 		for(c = t->cells; c != nil && ok; c = c->next)
41099a747e4fSDavid du Colombier 			ok = validtablecell(c);
41109a747e4fSDavid du Colombier 		for(i = 0; i < t->nrow && ok; i++)
41119a747e4fSDavid du Colombier 			for(j = 0; j < t->ncol && ok; j++)
41129a747e4fSDavid du Colombier 				ok = validptr(t->grid[i][j]);
41139a747e4fSDavid du Colombier 	}
41149a747e4fSDavid du Colombier 	return ok;
41159a747e4fSDavid du Colombier }
41169a747e4fSDavid du Colombier 
41179a747e4fSDavid du Colombier static int
validvalign(int a)41189a747e4fSDavid du Colombier validvalign(int a)
41199a747e4fSDavid du Colombier {
41209a747e4fSDavid du Colombier 	return a == ALnone || a == ALmiddle || a == ALbottom || a == ALtop || a == ALbaseline;
41219a747e4fSDavid du Colombier }
41229a747e4fSDavid du Colombier 
41239a747e4fSDavid du Colombier static int
validhalign(int a)41249a747e4fSDavid du Colombier validhalign(int a)
41259a747e4fSDavid du Colombier {
41269a747e4fSDavid du Colombier 	return a == ALnone || a == ALleft || a == ALcenter || a == ALright ||
41279a747e4fSDavid du Colombier 			a == ALjustify || a == ALchar;
41289a747e4fSDavid du Colombier }
41299a747e4fSDavid du Colombier 
41309a747e4fSDavid du Colombier static int
validalign(Align a)41319a747e4fSDavid du Colombier validalign(Align a)
41329a747e4fSDavid du Colombier {
41339a747e4fSDavid du Colombier 	return validhalign(a.halign) && validvalign(a.valign);
41349a747e4fSDavid du Colombier }
41359a747e4fSDavid du Colombier 
41369a747e4fSDavid du Colombier static int
validdimen(Dimen d)41379a747e4fSDavid du Colombier validdimen(Dimen d)
41389a747e4fSDavid du Colombier {
41399a747e4fSDavid du Colombier 	int ok;
41409a747e4fSDavid du Colombier 	int s;
41419a747e4fSDavid du Colombier 
41429a747e4fSDavid du Colombier 	ok = 0;
41439a747e4fSDavid du Colombier 	s = d.kindspec&Dspecmask;
41449a747e4fSDavid du Colombier 	switch(d.kindspec&Dkindmask) {
41459a747e4fSDavid du Colombier 	case Dnone:
41469a747e4fSDavid du Colombier 		ok = s==0;
41479a747e4fSDavid du Colombier 		break;
41489a747e4fSDavid du Colombier 	case Dpixels:
41499a747e4fSDavid du Colombier 		ok = s < HUGEPIX;
41509a747e4fSDavid du Colombier 		break;
41519a747e4fSDavid du Colombier 	case Dpercent:
41529a747e4fSDavid du Colombier 	case Drelative:
41539a747e4fSDavid du Colombier 		ok = 1;
41549a747e4fSDavid du Colombier 		break;
41559a747e4fSDavid du Colombier 	}
41569a747e4fSDavid du Colombier 	return ok;
41579a747e4fSDavid du Colombier }
41589a747e4fSDavid du Colombier 
41599a747e4fSDavid du Colombier static int
validtablerow(Tablerow * r)41609a747e4fSDavid du Colombier validtablerow(Tablerow* r)
41619a747e4fSDavid du Colombier {
41629a747e4fSDavid du Colombier 	return (r->cells == nil || validptr(r->cells)) &&
41639a747e4fSDavid du Colombier 		(r->height >= 0 && r->height < HUGEPIX) &&
41649a747e4fSDavid du Colombier 		(r->ascent > -HUGEPIX && r->ascent < HUGEPIX) &&
41659a747e4fSDavid du Colombier 		validalign(r->align);
41669a747e4fSDavid du Colombier }
41679a747e4fSDavid du Colombier 
41689a747e4fSDavid du Colombier static int
validtablecol(Tablecol * c)41699a747e4fSDavid du Colombier validtablecol(Tablecol* c)
41709a747e4fSDavid du Colombier {
41719a747e4fSDavid du Colombier 	return c->width >= 0 && c->width < HUGEPIX
41729a747e4fSDavid du Colombier 		&& validalign(c->align);
41739a747e4fSDavid du Colombier }
41749a747e4fSDavid du Colombier 
41759a747e4fSDavid du Colombier static int
validtablecell(Tablecell * c)41769a747e4fSDavid du Colombier validtablecell(Tablecell* c)
41779a747e4fSDavid du Colombier {
41789a747e4fSDavid du Colombier 	int ok;
41799a747e4fSDavid du Colombier 
41809a747e4fSDavid du Colombier 	ok = (c->next == nil || validptr(c->next)) &&
41819a747e4fSDavid du Colombier 		(c->nextinrow == nil || validptr(c->nextinrow)) &&
41829a747e4fSDavid du Colombier 		(c->content == nil || validptr(c->content)) &&
41839a747e4fSDavid du Colombier 		(c->lay == nil || validptr(c->lay)) &&
41849a747e4fSDavid du Colombier 		c->rowspan >= 0 &&
41859a747e4fSDavid du Colombier 		c->colspan >= 0 &&
41869a747e4fSDavid du Colombier 		validalign(c->align) &&
41879a747e4fSDavid du Colombier 		validdimen(c->wspec) &&
41889a747e4fSDavid du Colombier 		c->row >= 0 &&
41899a747e4fSDavid du Colombier 		c->col >= 0;
41909a747e4fSDavid du Colombier 	if(ok) {
41919a747e4fSDavid du Colombier 		if(c->content != nil)
41929a747e4fSDavid du Colombier 			ok = validitems(c->content);
41939a747e4fSDavid du Colombier 	}
41949a747e4fSDavid du Colombier 	return ok;
41959a747e4fSDavid du Colombier }
41969a747e4fSDavid du Colombier 
41979a747e4fSDavid du Colombier static int
validptr(void * p)41989a747e4fSDavid du Colombier validptr(void* p)
41999a747e4fSDavid du Colombier {
42009a747e4fSDavid du Colombier 	// TODO: a better job of this.
42019a747e4fSDavid du Colombier 	// For now, just dereference, which cause a bomb
42029a747e4fSDavid du Colombier 	// if not valid
42039a747e4fSDavid du Colombier 	static char c;
42049a747e4fSDavid du Colombier 
42059a747e4fSDavid du Colombier 	c = *((char*)p);
42069a747e4fSDavid du Colombier 	return 1;
42079a747e4fSDavid du Colombier }
42089a747e4fSDavid du Colombier 
42099a747e4fSDavid du Colombier static int
validStr(Rune * s)42109a747e4fSDavid du Colombier validStr(Rune* s)
42119a747e4fSDavid du Colombier {
42129a747e4fSDavid du Colombier 	return s != nil && validptr(s);
42139a747e4fSDavid du Colombier }
4214