1 #include <u.h> 2 #include <libc.h> 3 #include <draw.h> 4 #include <ctype.h> 5 #include <html.h> 6 #include "impl.h" 7 8 // A stack for holding integer values 9 enum { 10 Nestmax = 40 // max nesting level of lists, font styles, etc. 11 }; 12 13 struct Stack { 14 int n; // next available slot (top of stack is stack[n-1]) 15 int slots[Nestmax]; // stack entries 16 }; 17 18 // Parsing state 19 struct Pstate 20 { 21 Pstate* next; // in stack of Pstates 22 int skipping; // true when we shouldn't add items 23 int skipwhite; // true when we should strip leading space 24 int curfont; // font index for current font 25 int curfg; // current foreground color 26 Background curbg; // current background 27 int curvoff; // current baseline offset 28 uchar curul; // current underline/strike state 29 uchar curjust; // current justify state 30 int curanchor; // current (href) anchor id (if in one), or 0 31 int curstate; // current value of item state 32 int literal; // current literal state 33 int inpar; // true when in a paragraph-like construct 34 int adjsize; // current font size adjustment 35 Item* items; // dummy head of item list we're building 36 Item* lastit; // tail of item list we're building 37 Item* prelastit; // item before lastit 38 Stack fntstylestk; // style stack 39 Stack fntsizestk; // size stack 40 Stack fgstk; // text color stack 41 Stack ulstk; // underline stack 42 Stack voffstk; // vertical offset stack 43 Stack listtypestk; // list type stack 44 Stack listcntstk; // list counter stack 45 Stack juststk; // justification stack 46 Stack hangstk; // hanging stack 47 }; 48 49 struct ItemSource 50 { 51 Docinfo* doc; 52 Pstate* psstk; 53 int nforms; 54 int ntables; 55 int nanchors; 56 int nframes; 57 Form* curform; 58 Map* curmap; 59 Table* tabstk; 60 Kidinfo* kidstk; 61 }; 62 63 // Some layout parameters 64 enum { 65 FRKIDMARGIN = 6, // default margin around kid frames 66 IMGHSPACE = 0, // default hspace for images (0 matches IE, Netscape) 67 IMGVSPACE = 0, // default vspace for images 68 FLTIMGHSPACE = 2, // default hspace for float images 69 TABSP = 5, // default cellspacing for tables 70 TABPAD = 1, // default cell padding for tables 71 LISTTAB = 1, // number of tabs to indent lists 72 BQTAB = 1, // number of tabs to indent blockquotes 73 HRSZ = 2, // thickness of horizontal rules 74 SUBOFF = 4, // vertical offset for subscripts 75 SUPOFF = 6, // vertical offset for superscripts 76 NBSP = 160 // non-breaking space character 77 }; 78 79 // These tables must be sorted 80 static StringInt align_tab[] = { 81 {L"baseline", ALbaseline}, 82 {L"bottom", ALbottom}, 83 {L"center", ALcenter}, 84 {L"char", ALchar}, 85 {L"justify", ALjustify}, 86 {L"left", ALleft}, 87 {L"middle", ALmiddle}, 88 {L"right", ALright}, 89 {L"top", ALtop} 90 }; 91 #define NALIGNTAB (sizeof(align_tab)/sizeof(StringInt)) 92 93 static StringInt input_tab[] = { 94 {L"button", Fbutton}, 95 {L"checkbox", Fcheckbox}, 96 {L"file", Ffile}, 97 {L"hidden", Fhidden}, 98 {L"image", Fimage}, 99 {L"password", Fpassword}, 100 {L"radio", Fradio}, 101 {L"reset", Freset}, 102 {L"submit", Fsubmit}, 103 {L"text", Ftext} 104 }; 105 #define NINPUTTAB (sizeof(input_tab)/sizeof(StringInt)) 106 107 static StringInt clear_tab[] = { 108 {L"all", IFcleft|IFcright}, 109 {L"left", IFcleft}, 110 {L"right", IFcright} 111 }; 112 #define NCLEARTAB (sizeof(clear_tab)/sizeof(StringInt)) 113 114 static StringInt fscroll_tab[] = { 115 {L"auto", FRhscrollauto|FRvscrollauto}, 116 {L"no", FRnoscroll}, 117 {L"yes", FRhscroll|FRvscroll}, 118 }; 119 #define NFSCROLLTAB (sizeof(fscroll_tab)/sizeof(StringInt)) 120 121 static StringInt shape_tab[] = { 122 {L"circ", SHcircle}, 123 {L"circle", SHcircle}, 124 {L"poly", SHpoly}, 125 {L"polygon", SHpoly}, 126 {L"rect", SHrect}, 127 {L"rectangle", SHrect} 128 }; 129 #define NSHAPETAB (sizeof(shape_tab)/sizeof(StringInt)) 130 131 static StringInt method_tab[] = { 132 {L"get", HGet}, 133 {L"post", HPost} 134 }; 135 #define NMETHODTAB (sizeof(method_tab)/sizeof(StringInt)) 136 137 static Rune* roman[15]= { 138 L"I", L"II", L"III", L"IV", L"V", L"VI", L"VII", L"VIII", L"IX", L"X", 139 L"XI", L"XII", L"XIII", L"XIV", L"XV" 140 }; 141 #define NROMAN 15 142 143 // List number types 144 enum { 145 LTdisc, LTsquare, LTcircle, LT1, LTa, LTA, LTi, LTI 146 }; 147 148 enum { 149 SPBefore = 2, 150 SPAfter = 4, 151 BL = 1, 152 BLBA = (BL|SPBefore|SPAfter) 153 }; 154 155 // blockbrk[tag] is break info for a block level element, or one 156 // of a few others that get the same treatment re ending open paragraphs 157 // and requiring a line break / vertical space before them. 158 // If we want a line of space before the given element, SPBefore is OR'd in. 159 // If we want a line of space after the given element, SPAfter is OR'd in. 160 161 static uchar blockbrk[Numtags]= { 162 [Taddress] BLBA, [Tblockquote] BLBA, [Tcenter] BL, 163 [Tdir] BLBA, [Tdiv] BL, [Tdd] BL, [Tdl] BLBA, 164 [Tdt] BL, [Tform] BLBA, 165 // headings and tables get breaks added manually 166 [Th1] BL, [Th2] BL, [Th3] BL, 167 [Th4] BL, [Th5] BL, [Th6] BL, 168 [Thr] BL, [Tisindex] BLBA, [Tli] BL, [Tmenu] BLBA, 169 [Tol] BLBA, [Tp] BLBA, [Tpre] BLBA, 170 [Tul] BLBA 171 }; 172 173 enum { 174 AGEN = 1 175 }; 176 177 // attrinfo is information about attributes. 178 // The AGEN value means that the attribute is generic (applies to almost all elements) 179 static uchar attrinfo[Numattrs]= { 180 [Aid] AGEN, [Aclass] AGEN, [Astyle] AGEN, [Atitle] AGEN, 181 [Aonblur] AGEN, [Aonchange] AGEN, [Aonclick] AGEN, 182 [Aondblclick] AGEN, [Aonfocus] AGEN, [Aonkeypress] AGEN, 183 [Aonkeyup] AGEN, [Aonload] AGEN, [Aonmousedown] AGEN, 184 [Aonmousemove] AGEN, [Aonmouseout] AGEN, [Aonmouseover] AGEN, 185 [Aonmouseup] AGEN, [Aonreset] AGEN, [Aonselect] AGEN, 186 [Aonsubmit] AGEN, [Aonunload] AGEN 187 }; 188 189 static uchar scriptev[Numattrs]= { 190 [Aonblur] SEonblur, [Aonchange] SEonchange, [Aonclick] SEonclick, 191 [Aondblclick] SEondblclick, [Aonfocus] SEonfocus, [Aonkeypress] SEonkeypress, 192 [Aonkeyup] SEonkeyup, [Aonload] SEonload, [Aonmousedown] SEonmousedown, 193 [Aonmousemove] SEonmousemove, [Aonmouseout] SEonmouseout, [Aonmouseover] SEonmouseover, 194 [Aonmouseup] SEonmouseup, [Aonreset] SEonreset, [Aonselect] SEonselect, 195 [Aonsubmit] SEonsubmit, [Aonunload] SEonunload 196 }; 197 198 // Color lookup table 199 static StringInt color_tab[] = { 200 {L"aqua", 0x00FFFF}, 201 {L"black", 0x000000}, 202 {L"blue", 0x0000CC}, 203 {L"fuchsia", 0xFF00FF}, 204 {L"gray", 0x808080}, 205 {L"green", 0x008000}, 206 {L"lime", 0x00FF00}, 207 {L"maroon", 0x800000}, 208 {L"navy", 0x000080,}, 209 {L"olive", 0x808000}, 210 {L"purple", 0x800080}, 211 {L"red", 0xFF0000}, 212 {L"silver", 0xC0C0C0}, 213 {L"teal", 0x008080}, 214 {L"white", 0xFFFFFF}, 215 {L"yellow", 0xFFFF00} 216 }; 217 #define NCOLORS (sizeof(color_tab)/sizeof(StringInt)) 218 219 static StringInt *targetmap; 220 static int targetmapsize; 221 static int ntargets; 222 223 static int buildinited = 0; 224 225 #define SMALLBUFSIZE 240 226 #define BIGBUFSIZE 2000 227 228 int dbgbuild = 0; 229 int warn = 0; 230 231 static Align aalign(Token* tok); 232 static int acolorval(Token* tok, int attid, int dflt); 233 static void addbrk(Pstate* ps, int sp, int clr); 234 static void additem(Pstate* ps, Item* it, Token* tok); 235 static void addlinebrk(Pstate* ps, int clr); 236 static void addnbsp(Pstate* ps); 237 static void addtext(Pstate* ps, Rune* s); 238 static Dimen adimen(Token* tok, int attid); 239 static int aflagval(Token* tok, int attid); 240 static int aintval(Token* tok, int attid, int dflt); 241 static Rune* astrval(Token* tok, int attid, Rune* dflt); 242 static int atabval(Token* tok, int attid, StringInt* tab, int ntab, int dflt); 243 static int atargval(Token* tok, int dflt); 244 static int auintval(Token* tok, int attid, int dflt); 245 static Rune* aurlval(Token* tok, int attid, Rune* dflt, Rune* base); 246 static Rune* aval(Token* tok, int attid); 247 static void buildinit(void); 248 static Pstate* cell_pstate(Pstate* oldps, int ishead); 249 static void changehang(Pstate* ps, int delta); 250 static void changeindent(Pstate* ps, int delta); 251 static int color(Rune* s, int dflt); 252 static void copystack(Stack* tostk, Stack* fromstk); 253 static int dimprint(char* buf, int nbuf, Dimen d); 254 static Pstate* finishcell(Table* curtab, Pstate* psstk); 255 static void finish_table(Table* t); 256 static void freeanchor(Anchor* a); 257 static void freedestanchor(DestAnchor* da); 258 static void freeform(Form* f); 259 static void freeformfield(Formfield* ff); 260 static void freeitem(Item* it); 261 static void freepstate(Pstate* p); 262 static void freepstatestack(Pstate* pshead); 263 static void freescriptevents(SEvent* ehead); 264 static void freetable(Table* t); 265 static Map* getmap(Docinfo* di, Rune* name); 266 static Rune* getpcdata(Token* toks, int tokslen, int* ptoki); 267 static Pstate* lastps(Pstate* psl); 268 static Rune* listmark(uchar ty, int n); 269 static int listtyval(Token* tok, int dflt); 270 static Align makealign(int halign, int valign); 271 static Background makebackground(Rune* imgurl, int color); 272 static Dimen makedimen(int kind, int spec); 273 static Anchor* newanchor(int index, Rune* name, Rune* href, int target, Anchor* link); 274 static Area* newarea(int shape, Rune* href, int target, Area* link); 275 static DestAnchor* newdestanchor(int index, Rune* name, Item* item, DestAnchor* link); 276 static Docinfo* newdocinfo(void); 277 static Genattr* newgenattr(Rune* id, Rune* class, Rune* style, Rune* title, Attr* events); 278 static Form* newform(int formid, Rune* name, Rune* action, 279 int target, int method, Form* link); 280 static Formfield* newformfield(int ftype, int fieldid, Form* form, Rune* name, 281 Rune* value, int size, int maxlength, Formfield* link); 282 static Item* newifloat(Item* it, int side); 283 static Item* newiformfield(Formfield* ff); 284 static Item* newiimage(Rune* src, Rune* altrep, int align, int width, int height, 285 int hspace, int vspace, int border, int ismap, Map* map); 286 static Item* newirule(int align, int size, int noshade, Dimen wspec); 287 static Item* newispacer(int spkind); 288 static Item* newitable(Table* t); 289 static ItemSource* newitemsource(Docinfo* di); 290 static Item* newitext(Rune* s, int fnt, int fg, int voff, int ul); 291 static Kidinfo* newkidinfo(int isframeset, Kidinfo* link); 292 static Option* newoption(int selected, Rune* value, Rune* display, Option* link); 293 static Pstate* newpstate(Pstate* link); 294 static SEvent* newscriptevent(int type, Rune* script, SEvent* link); 295 static Table* newtable(int tableid, Align align, Dimen width, int border, 296 int cellspacing, int cellpadding, Background bg, Token* tok, Table* link); 297 static Tablecell* newtablecell(int cellid, int rowspan, int colspan, Align align, Dimen wspec, 298 int hspec, Background bg, int flags, Tablecell* link); 299 static Tablerow* newtablerow(Align align, Background bg, int flags, Tablerow* link); 300 static Dimen parsedim(Rune* s, int ns); 301 static void pop(Stack* stk); 302 static void popfontsize(Pstate* ps); 303 static void popfontstyle(Pstate* ps); 304 static void popjust(Pstate* ps); 305 static int popretnewtop(Stack* stk, int dflt); 306 static int push(Stack* stk, int val); 307 static void pushfontsize(Pstate* ps, int sz); 308 static void pushfontstyle(Pstate* ps, int sty); 309 static void pushjust(Pstate* ps, int j); 310 static Item* textit(Pstate* ps, Rune* s); 311 static Rune* removeallwhite(Rune* s); 312 static void resetdocinfo(Docinfo* d); 313 static void setcurfont(Pstate* ps); 314 static void setcurjust(Pstate* ps); 315 static void setdimarray(Token* tok, int attid, Dimen** pans, int* panslen); 316 static Rune* stringalign(int a); 317 static void targetmapinit(void); 318 static int toint(Rune* s); 319 static int top(Stack* stk, int dflt); 320 static void trim_cell(Tablecell* c); 321 static int validalign(Align a); 322 static int validdimen(Dimen d); 323 static int validformfield(Formfield* f); 324 static int validhalign(int a); 325 static int validptr(void* p); 326 static int validStr(Rune* s); 327 static int validtable(Table* t); 328 static int validtablerow(Tablerow* r); 329 static int validtablecol(Tablecol* c); 330 static int validtablecell(Tablecell* c); 331 static int validvalign(int a); 332 static int Iconv(Fmt *f); 333 334 static void 335 buildinit(void) 336 { 337 fmtinstall('I', Iconv); 338 targetmapinit(); 339 buildinited = 1; 340 } 341 342 static ItemSource* 343 newitemsource(Docinfo* di) 344 { 345 ItemSource* is; 346 Pstate* ps; 347 348 ps = newpstate(nil); 349 if(di->mediatype != TextHtml) { 350 ps->curstate &= ~IFwrap; 351 ps->literal = 1; 352 pushfontstyle(ps, FntT); 353 } 354 is = (ItemSource*)emalloc(sizeof(ItemSource)); 355 is->doc = di; 356 is->psstk = ps; 357 is->nforms = 0; 358 is->ntables = 0; 359 is->nanchors = 0; 360 is->nframes = 0; 361 is->curform = nil; 362 is->curmap = nil; 363 is->tabstk = nil; 364 is->kidstk = nil; 365 return is; 366 } 367 368 static Item *getitems(ItemSource* is, uchar* data, int datalen); 369 370 // Parse an html document and create a list of layout items. 371 // Allocate and return document info in *pdi. 372 // When caller is done with the items, it should call 373 // freeitems on the returned result, and then 374 // freedocinfo(*pdi). 375 Item* 376 parsehtml(uchar* data, int datalen, Rune* pagesrc, int mtype, int chset, Docinfo** pdi) 377 { 378 Item *it; 379 Docinfo* di; 380 ItemSource* is; 381 382 di = newdocinfo(); 383 di->src = _Strdup(pagesrc); 384 di->base = _Strdup(pagesrc); 385 di->mediatype = mtype; 386 di->chset = chset; 387 *pdi = di; 388 is = newitemsource(di); 389 it = getitems(is, data, datalen); 390 freepstatestack(is->psstk); 391 free(is); 392 return it; 393 } 394 395 // Get a group of tokens for lexer, parse them, and create 396 // a list of layout items. 397 // When caller is done with the items, it should call 398 // freeitems on the returned result. 399 static Item* 400 getitems(ItemSource* is, uchar* data, int datalen) 401 { 402 int i; 403 int j; 404 int nt; 405 int pt; 406 int doscripts; 407 int tokslen; 408 int toki; 409 int h; 410 int sz; 411 int method; 412 int n; 413 int nblank; 414 int norsz; 415 int bramt; 416 int sty; 417 int nosh; 418 int oldcuranchor; 419 int dfltbd; 420 int v; 421 int hang; 422 int isempty; 423 int tag; 424 int brksp; 425 int target; 426 uchar brk; 427 uchar flags; 428 uchar align; 429 uchar al; 430 uchar ty; 431 uchar ty2; 432 Pstate* ps; 433 Pstate* nextps; 434 Pstate* outerps; 435 Table* curtab; 436 Token* tok; 437 Token* toks; 438 Docinfo* di; 439 Item* ans; 440 Item* img; 441 Item* ffit; 442 Item* tabitem; 443 Rune* s; 444 Rune* t; 445 Rune* name; 446 Rune* enctype; 447 Rune* usemap; 448 Rune* prompt; 449 Rune* equiv; 450 Rune* val; 451 Rune* nsz; 452 Rune* script; 453 Map* map; 454 Form* frm; 455 Iimage* ii; 456 Kidinfo* kd; 457 Kidinfo* ks; 458 Kidinfo* pks; 459 Dimen wd; 460 Option* option; 461 Table* tab; 462 Tablecell* c; 463 Tablerow* tr; 464 Formfield* field; 465 Formfield* ff; 466 Rune* href; 467 Rune* src; 468 Rune* scriptsrc; 469 Rune* bgurl; 470 Rune* action; 471 Background bg; 472 473 if(!buildinited) 474 buildinit(); 475 doscripts = 0; // for now 476 ps = is->psstk; 477 curtab = is->tabstk; 478 di = is->doc; 479 toks = _gettoks(data, datalen, di->chset, di->mediatype, &tokslen); 480 toki = 0; 481 for(; toki < tokslen; toki++) { 482 tok = &toks[toki]; 483 if(dbgbuild > 1) 484 fprint(2, "build: curstate %ux, token %T\n", ps->curstate, tok); 485 tag = tok->tag; 486 brk = 0; 487 brksp = 0; 488 if(tag < Numtags) { 489 brk = blockbrk[tag]; 490 if(brk&SPBefore) 491 brksp = 1; 492 } 493 else if(tag < Numtags + RBRA) { 494 brk = blockbrk[tag - RBRA]; 495 if(brk&SPAfter) 496 brksp = 1; 497 } 498 if(brk) { 499 addbrk(ps, brksp, 0); 500 if(ps->inpar) { 501 popjust(ps); 502 ps->inpar = 0; 503 } 504 } 505 // check common case first (Data), then switch statement on tag 506 if(tag == Data) { 507 // Lexing didn't pay attention to SGML record boundary rules: 508 // \n after start tag or before end tag to be discarded. 509 // (Lex has already discarded all \r's). 510 // Some pages assume this doesn't happen in <PRE> text, 511 // so we won't do it if literal is true. 512 // BUG: won't discard \n before a start tag that begins 513 // the next bufferful of tokens. 514 s = tok->text; 515 n = _Strlen(s); 516 if(!ps->literal) { 517 i = 0; 518 j = n; 519 if(toki > 0) { 520 pt = toks[toki - 1].tag; 521 // IE and Netscape both ignore this rule (contrary to spec) 522 // if previous tag was img 523 if(pt < Numtags && pt != Timg && j > 0 && s[0] == '\n') 524 i++; 525 } 526 if(toki < tokslen - 1) { 527 nt = toks[toki + 1].tag; 528 if(nt >= RBRA && nt < Numtags + RBRA && j > i && s[j - 1] == '\n') 529 j--; 530 } 531 if(i > 0 || j < n) { 532 t = s; 533 s = _Strsubstr(s, i, j); 534 free(t); 535 n = j-i; 536 } 537 } 538 if(ps->skipwhite) { 539 _trimwhite(s, n, &t, &nt); 540 if(t == nil) { 541 free(s); 542 s = nil; 543 } 544 else if(t != s) { 545 t = _Strndup(t, nt); 546 free(s); 547 s = t; 548 } 549 if(s != nil) 550 ps->skipwhite = 0; 551 } 552 tok->text = nil; // token doesn't own string anymore 553 if(s != nil) 554 addtext(ps, s); 555 } 556 else 557 switch(tag) { 558 // Some abbrevs used in following DTD comments 559 // %text = #PCDATA 560 // | TT | I | B | U | STRIKE | BIG | SMALL | SUB | SUP 561 // | EM | STRONG | DFN | CODE | SAMP | KBD | VAR | CITE 562 // | A | IMG | APPLET | FONT | BASEFONT | BR | SCRIPT | MAP 563 // | INPUT | SELECT | TEXTAREA 564 // %block = P | UL | OL | DIR | MENU | DL | PRE | DL | DIV | CENTER 565 // | BLOCKQUOTE | FORM | ISINDEX | HR | TABLE 566 // %flow = (%text | %block)* 567 // %body.content = (%heading | %text | %block | ADDRESS)* 568 569 // <!ELEMENT A - - (%text) -(A)> 570 // Anchors are not supposed to be nested, but you sometimes see 571 // href anchors inside destination anchors. 572 case Ta: 573 if(ps->curanchor != 0) { 574 if(warn) 575 fprint(2, "warning: nested <A> or missing </A>\n"); 576 ps->curanchor = 0; 577 } 578 name = aval(tok, Aname); 579 href = aurlval(tok, Ahref, nil, di->base); 580 // ignore rel, rev, and title attrs 581 if(href != nil) { 582 target = atargval(tok, di->target); 583 di->anchors = newanchor(++is->nanchors, name, href, target, di->anchors); 584 if(name != nil) 585 name = _Strdup(name); // for DestAnchor construction, below 586 ps->curanchor = is->nanchors; 587 ps->curfg = push(&ps->fgstk, di->link); 588 ps->curul = push(&ps->ulstk, ULunder); 589 } 590 if(name != nil) { 591 // add a null item to be destination 592 additem(ps, newispacer(ISPnull), tok); 593 di->dests = newdestanchor(++is->nanchors, name, ps->lastit, di->dests); 594 } 595 break; 596 597 case Ta+RBRA : 598 if(ps->curanchor != 0) { 599 ps->curfg = popretnewtop(&ps->fgstk, di->text); 600 ps->curul = popretnewtop(&ps->ulstk, ULnone); 601 ps->curanchor = 0; 602 } 603 break; 604 605 // <!ELEMENT APPLET - - (PARAM | %text)* > 606 // We can't do applets, so ignore PARAMS, and let 607 // the %text contents appear for the alternative rep 608 case Tapplet: 609 case Tapplet+RBRA: 610 if(warn && tag == Tapplet) 611 fprint(2, "warning: <APPLET> ignored\n"); 612 break; 613 614 // <!ELEMENT AREA - O EMPTY> 615 case Tarea: 616 map = di->maps; 617 if(map == nil) { 618 if(warn) 619 fprint(2, "warning: <AREA> not inside <MAP>\n"); 620 continue; 621 } 622 map->areas = newarea(atabval(tok, Ashape, shape_tab, NSHAPETAB, SHrect), 623 aurlval(tok, Ahref, nil, di->base), 624 atargval(tok, di->target), 625 map->areas); 626 setdimarray(tok, Acoords, &map->areas->coords, &map->areas->ncoords); 627 break; 628 629 // <!ELEMENT (B|STRONG) - - (%text)*> 630 case Tb: 631 case Tstrong: 632 pushfontstyle(ps, FntB); 633 break; 634 635 case Tb+RBRA: 636 case Tcite+RBRA: 637 case Tcode+RBRA: 638 case Tdfn+RBRA: 639 case Tem+RBRA: 640 case Tkbd+RBRA: 641 case Ti+RBRA: 642 case Tsamp+RBRA: 643 case Tstrong+RBRA: 644 case Ttt+RBRA: 645 case Tvar+RBRA : 646 case Taddress+RBRA: 647 popfontstyle(ps); 648 break; 649 650 // <!ELEMENT BASE - O EMPTY> 651 case Tbase: 652 t = di->base; 653 di->base = aurlval(tok, Ahref, di->base, di->base); 654 if(t != nil) 655 free(t); 656 di->target = atargval(tok, di->target); 657 break; 658 659 // <!ELEMENT BASEFONT - O EMPTY> 660 case Tbasefont: 661 ps->adjsize = aintval(tok, Asize, 3) - 3; 662 break; 663 664 // <!ELEMENT (BIG|SMALL) - - (%text)*> 665 case Tbig: 666 case Tsmall: 667 sz = ps->adjsize; 668 if(tag == Tbig) 669 sz += Large; 670 else 671 sz += Small; 672 pushfontsize(ps, sz); 673 break; 674 675 case Tbig+RBRA: 676 case Tsmall+RBRA: 677 popfontsize(ps); 678 break; 679 680 // <!ELEMENT BLOCKQUOTE - - %body.content> 681 case Tblockquote: 682 changeindent(ps, BQTAB); 683 break; 684 685 case Tblockquote+RBRA: 686 changeindent(ps, -BQTAB); 687 break; 688 689 // <!ELEMENT BODY O O %body.content> 690 case Tbody: 691 ps->skipping = 0; 692 bg = makebackground(nil, acolorval(tok, Abgcolor, di->background.color)); 693 bgurl = aurlval(tok, Abackground, nil, di->base); 694 if(bgurl != nil) { 695 if(di->backgrounditem != nil) 696 freeitem((Item*)di->backgrounditem); 697 // really should remove old item from di->images list, 698 // but there should only be one BODY element ... 699 di->backgrounditem = (Iimage*)newiimage(bgurl, nil, ALnone, 0, 0, 0, 0, 0, 0, nil); 700 di->backgrounditem->nextimage = di->images; 701 di->images = di->backgrounditem; 702 } 703 ps->curbg = bg; 704 di->background = bg; 705 di->text = acolorval(tok, Atext, di->text); 706 di->link = acolorval(tok, Alink, di->link); 707 di->vlink = acolorval(tok, Avlink, di->vlink); 708 di->alink = acolorval(tok, Aalink, di->alink); 709 if(di->text != ps->curfg) { 710 ps->curfg = di->text; 711 ps->fgstk.n = 0; 712 } 713 break; 714 715 case Tbody+RBRA: 716 // HTML spec says ignore things after </body>, 717 // but IE and Netscape don't 718 // ps.skipping = 1; 719 break; 720 721 // <!ELEMENT BR - O EMPTY> 722 case Tbr: 723 addlinebrk(ps, atabval(tok, Aclear, clear_tab, NCLEARTAB, 0)); 724 break; 725 726 // <!ELEMENT CAPTION - - (%text;)*> 727 case Tcaption: 728 if(curtab == nil) { 729 if(warn) 730 fprint(2, "warning: <CAPTION> outside <TABLE>\n"); 731 continue; 732 } 733 if(curtab->caption != nil) { 734 if(warn) 735 fprint(2, "warning: more than one <CAPTION> in <TABLE>\n"); 736 continue; 737 } 738 ps = newpstate(ps); 739 curtab->caption_place = atabval(tok, Aalign, align_tab, NALIGNTAB, ALtop); 740 break; 741 742 case Tcaption+RBRA: 743 nextps = ps->next; 744 if(curtab == nil || nextps == nil) { 745 if(warn) 746 fprint(2, "warning: unexpected </CAPTION>\n"); 747 continue; 748 } 749 curtab->caption = ps->items->next; 750 free(ps); 751 ps = nextps; 752 break; 753 754 case Tcenter: 755 case Tdiv: 756 if(tag == Tcenter) 757 al = ALcenter; 758 else 759 al = atabval(tok, Aalign, align_tab, NALIGNTAB, ps->curjust); 760 pushjust(ps, al); 761 break; 762 763 case Tcenter+RBRA: 764 case Tdiv+RBRA: 765 popjust(ps); 766 break; 767 768 // <!ELEMENT DD - O %flow > 769 case Tdd: 770 if(ps->hangstk.n == 0) { 771 if(warn) 772 fprint(2, "warning: <DD> not inside <DL\n"); 773 continue; 774 } 775 h = top(&ps->hangstk, 0); 776 if(h != 0) 777 changehang(ps, -10*LISTTAB); 778 else 779 addbrk(ps, 0, 0); 780 push(&ps->hangstk, 0); 781 break; 782 783 //<!ELEMENT (DIR|MENU) - - (LI)+ -(%block) > 784 //<!ELEMENT (OL|UL) - - (LI)+> 785 case Tdir: 786 case Tmenu: 787 case Tol: 788 case Tul: 789 changeindent(ps, LISTTAB); 790 push(&ps->listtypestk, listtyval(tok, (tag==Tol)? LT1 : LTdisc)); 791 push(&ps->listcntstk, aintval(tok, Astart, 1)); 792 break; 793 794 case Tdir+RBRA: 795 case Tmenu+RBRA: 796 case Tol+RBRA: 797 case Tul+RBRA: 798 if(ps->listtypestk.n == 0) { 799 if(warn) 800 fprint(2, "warning: %T ended no list\n", tok); 801 continue; 802 } 803 addbrk(ps, 0, 0); 804 pop(&ps->listtypestk); 805 pop(&ps->listcntstk); 806 changeindent(ps, -LISTTAB); 807 break; 808 809 // <!ELEMENT DL - - (DT|DD)+ > 810 case Tdl: 811 changeindent(ps, LISTTAB); 812 push(&ps->hangstk, 0); 813 break; 814 815 case Tdl+RBRA: 816 if(ps->hangstk.n == 0) { 817 if(warn) 818 fprint(2, "warning: unexpected </DL>\n"); 819 continue; 820 } 821 changeindent(ps, -LISTTAB); 822 if(top(&ps->hangstk, 0) != 0) 823 changehang(ps, -10*LISTTAB); 824 pop(&ps->hangstk); 825 break; 826 827 // <!ELEMENT DT - O (%text)* > 828 case Tdt: 829 if(ps->hangstk.n == 0) { 830 if(warn) 831 fprint(2, "warning: <DT> not inside <DL>\n"); 832 continue; 833 } 834 h = top(&ps->hangstk, 0); 835 pop(&ps->hangstk); 836 if(h != 0) 837 changehang(ps, -10*LISTTAB); 838 changehang(ps, 10*LISTTAB); 839 push(&ps->hangstk, 1); 840 break; 841 842 // <!ELEMENT FONT - - (%text)*> 843 case Tfont: 844 sz = top(&ps->fntsizestk, Normal); 845 if(_tokaval(tok, Asize, &nsz, 0)) { 846 if(_prefix(L"+", nsz)) 847 sz = Normal + _Strtol(nsz+1, nil, 10) + ps->adjsize; 848 else if(_prefix(L"-", nsz)) 849 sz = Normal - _Strtol(nsz+1, nil, 10) + ps->adjsize; 850 else if(nsz != nil) 851 sz = Normal + (_Strtol(nsz, nil, 10) - 3); 852 } 853 ps->curfg = push(&ps->fgstk, acolorval(tok, Acolor, ps->curfg)); 854 pushfontsize(ps, sz); 855 break; 856 857 case Tfont+RBRA: 858 if(ps->fgstk.n == 0) { 859 if(warn) 860 fprint(2, "warning: unexpected </FONT>\n"); 861 continue; 862 } 863 ps->curfg = popretnewtop(&ps->fgstk, di->text); 864 popfontsize(ps); 865 break; 866 867 // <!ELEMENT FORM - - %body.content -(FORM) > 868 case Tform: 869 if(is->curform != nil) { 870 if(warn) 871 fprint(2, "warning: <FORM> nested inside another\n"); 872 continue; 873 } 874 action = aurlval(tok, Aaction, di->base, di->base); 875 s = aval(tok, Aid); 876 name = astrval(tok, Aname, s); 877 if(s) 878 free(s); 879 target = atargval(tok, di->target); 880 method = atabval(tok, Amethod, method_tab, NMETHODTAB, HGet); 881 if(warn && _tokaval(tok, Aenctype, &enctype, 0) && 882 _Strcmp(enctype, L"application/x-www-form-urlencoded")) 883 fprint(2, "form enctype %S not handled\n", enctype); 884 frm = newform(++is->nforms, name, action, target, method, di->forms); 885 di->forms = frm; 886 is->curform = frm; 887 break; 888 889 case Tform+RBRA: 890 if(is->curform == nil) { 891 if(warn) 892 fprint(2, "warning: unexpected </FORM>\n"); 893 continue; 894 } 895 // put fields back in input order 896 is->curform->fields = (Formfield*)_revlist((List*)is->curform->fields); 897 is->curform = nil; 898 break; 899 900 // <!ELEMENT FRAME - O EMPTY> 901 case Tframe: 902 ks = is->kidstk; 903 if(ks == nil) { 904 if(warn) 905 fprint(2, "warning: <FRAME> not in <FRAMESET>\n"); 906 continue; 907 } 908 ks->kidinfos = kd = newkidinfo(0, ks->kidinfos); 909 kd->src = aurlval(tok, Asrc, nil, di->base); 910 kd->name = aval(tok, Aname); 911 if(kd->name == nil) 912 kd->name = runesmprint("_fr%d", ++is->nframes); 913 kd->marginw = auintval(tok, Amarginwidth, 0); 914 kd->marginh = auintval(tok, Amarginheight, 0); 915 kd->framebd = auintval(tok, Aframeborder, 1); 916 kd->flags = atabval(tok, Ascrolling, fscroll_tab, NFSCROLLTAB, kd->flags); 917 norsz = aflagval(tok, Anoresize); 918 if(norsz) 919 kd->flags |= FRnoresize; 920 break; 921 922 // <!ELEMENT FRAMESET - - (FRAME|FRAMESET)+> 923 case Tframeset: 924 ks = newkidinfo(1, nil); 925 pks = is->kidstk; 926 if(pks == nil) 927 di->kidinfo = ks; 928 else { 929 ks->next = pks->kidinfos; 930 pks->kidinfos = ks; 931 } 932 ks->nextframeset = pks; 933 is->kidstk = ks; 934 setdimarray(tok, Arows, &ks->rows, &ks->nrows); 935 if(ks->nrows == 0) { 936 ks->rows = (Dimen*)emalloc(sizeof(Dimen)); 937 ks->nrows = 1; 938 ks->rows[0] = makedimen(Dpercent, 100); 939 } 940 setdimarray(tok, Acols, &ks->cols, &ks->ncols); 941 if(ks->ncols == 0) { 942 ks->cols = (Dimen*)emalloc(sizeof(Dimen)); 943 ks->ncols = 1; 944 ks->cols[0] = makedimen(Dpercent, 100); 945 } 946 break; 947 948 case Tframeset+RBRA: 949 if(is->kidstk == nil) { 950 if(warn) 951 fprint(2, "warning: unexpected </FRAMESET>\n"); 952 continue; 953 } 954 ks = is->kidstk; 955 // put kids back in original order 956 // and add blank frames to fill out cells 957 n = ks->nrows*ks->ncols; 958 nblank = n - _listlen((List*)ks->kidinfos); 959 while(nblank-- > 0) 960 ks->kidinfos = newkidinfo(0, ks->kidinfos); 961 ks->kidinfos = (Kidinfo*)_revlist((List*)ks->kidinfos); 962 is->kidstk = is->kidstk->nextframeset; 963 if(is->kidstk == nil) { 964 // end input 965 ans = nil; 966 goto return_ans; 967 } 968 break; 969 970 // <!ELEMENT H1 - - (%text;)*>, etc. 971 case Th1: 972 case Th2: 973 case Th3: 974 case Th4: 975 case Th5: 976 case Th6: 977 bramt = 1; 978 if(ps->items == ps->lastit) 979 bramt = 0; 980 addbrk(ps, bramt, IFcleft|IFcright); 981 sz = Verylarge - (tag - Th1); 982 if(sz < Tiny) 983 sz = Tiny; 984 pushfontsize(ps, sz); 985 sty = top(&ps->fntstylestk, FntR); 986 if(tag == Th1) 987 sty = FntB; 988 pushfontstyle(ps, sty); 989 pushjust(ps, atabval(tok, Aalign, align_tab, NALIGNTAB, ps->curjust)); 990 ps->skipwhite = 1; 991 break; 992 993 case Th1+RBRA: 994 case Th2+RBRA: 995 case Th3+RBRA: 996 case Th4+RBRA: 997 case Th5+RBRA: 998 case Th6+RBRA: 999 addbrk(ps, 1, IFcleft|IFcright); 1000 popfontsize(ps); 1001 popfontstyle(ps); 1002 popjust(ps); 1003 break; 1004 1005 case Thead: 1006 // HTML spec says ignore regular markup in head, 1007 // but Netscape and IE don't 1008 // ps.skipping = 1; 1009 break; 1010 1011 case Thead+RBRA: 1012 ps->skipping = 0; 1013 break; 1014 1015 // <!ELEMENT HR - O EMPTY> 1016 case Thr: 1017 al = atabval(tok, Aalign, align_tab, NALIGNTAB, ALcenter); 1018 sz = auintval(tok, Asize, HRSZ); 1019 wd = adimen(tok, Awidth); 1020 if(dimenkind(wd) == Dnone) 1021 wd = makedimen(Dpercent, 100); 1022 nosh = aflagval(tok, Anoshade); 1023 additem(ps, newirule(al, sz, nosh, wd), tok); 1024 addbrk(ps, 0, 0); 1025 break; 1026 1027 case Ti: 1028 case Tcite: 1029 case Tdfn: 1030 case Tem: 1031 case Tvar: 1032 case Taddress: 1033 pushfontstyle(ps, FntI); 1034 break; 1035 1036 // <!ELEMENT IMG - O EMPTY> 1037 case Timg: 1038 map = nil; 1039 oldcuranchor = ps->curanchor; 1040 if(_tokaval(tok, Ausemap, &usemap, 0)) { 1041 if(!_prefix(L"#", usemap)) { 1042 if(warn) 1043 fprint(2, "warning: can't handle non-local map %S\n", usemap); 1044 } 1045 else { 1046 map = getmap(di, usemap+1); 1047 if(ps->curanchor == 0) { 1048 di->anchors = newanchor(++is->nanchors, nil, nil, di->target, di->anchors); 1049 ps->curanchor = is->nanchors; 1050 } 1051 } 1052 } 1053 align = atabval(tok, Aalign, align_tab, NALIGNTAB, ALbottom); 1054 dfltbd = 0; 1055 if(ps->curanchor != 0) 1056 dfltbd = 2; 1057 src = aurlval(tok, Asrc, nil, di->base); 1058 if(src == nil) { 1059 if(warn) 1060 fprint(2, "warning: <img> has no src attribute\n"); 1061 ps->curanchor = oldcuranchor; 1062 continue; 1063 } 1064 img = newiimage(src, 1065 aval(tok, Aalt), 1066 align, 1067 auintval(tok, Awidth, 0), 1068 auintval(tok, Aheight, 0), 1069 auintval(tok, Ahspace, IMGHSPACE), 1070 auintval(tok, Avspace, IMGVSPACE), 1071 auintval(tok, Aborder, dfltbd), 1072 aflagval(tok, Aismap), 1073 map); 1074 if(align == ALleft || align == ALright) { 1075 additem(ps, newifloat(img, align), tok); 1076 // if no hspace specified, use FLTIMGHSPACE 1077 if(!_tokaval(tok, Ahspace, &val, 0)) 1078 ((Iimage*)img)->hspace = FLTIMGHSPACE; 1079 } 1080 else { 1081 ps->skipwhite = 0; 1082 additem(ps, img, tok); 1083 } 1084 if(!ps->skipping) { 1085 ((Iimage*)img)->nextimage = di->images; 1086 di->images = (Iimage*)img; 1087 } 1088 ps->curanchor = oldcuranchor; 1089 break; 1090 1091 // <!ELEMENT INPUT - O EMPTY> 1092 case Tinput: 1093 ps->skipwhite = 0; 1094 if(is->curform == nil) { 1095 if(warn) 1096 fprint(2, "<INPUT> not inside <FORM>\n"); 1097 continue; 1098 } 1099 is->curform->fields = field = newformfield( 1100 atabval(tok, Atype, input_tab, NINPUTTAB, Ftext), 1101 ++is->curform->nfields, 1102 is->curform, 1103 aval(tok, Aname), 1104 aval(tok, Avalue), 1105 auintval(tok, Asize, 0), 1106 auintval(tok, Amaxlength, 1000), 1107 is->curform->fields); 1108 if(aflagval(tok, Achecked)) 1109 field->flags = FFchecked; 1110 1111 switch(field->ftype) { 1112 case Ftext: 1113 case Fpassword: 1114 case Ffile: 1115 if(field->size == 0) 1116 field->size = 20; 1117 break; 1118 1119 case Fcheckbox: 1120 if(field->name == nil) { 1121 if(warn) 1122 fprint(2, "warning: checkbox form field missing name\n"); 1123 continue; 1124 } 1125 if(field->value == nil) 1126 field->value = _Strdup(L"1"); 1127 break; 1128 1129 case Fradio: 1130 if(field->name == nil || field->value == nil) { 1131 if(warn) 1132 fprint(2, "warning: radio form field missing name or value\n"); 1133 continue; 1134 } 1135 break; 1136 1137 case Fsubmit: 1138 if(field->value == nil) 1139 field->value = _Strdup(L"Submit"); 1140 if(field->name == nil) 1141 field->name = _Strdup(L"_no_name_submit_"); 1142 break; 1143 1144 case Fimage: 1145 src = aurlval(tok, Asrc, nil, di->base); 1146 if(src == nil) { 1147 if(warn) 1148 fprint(2, "warning: image form field missing src\n"); 1149 continue; 1150 } 1151 // width and height attrs aren't specified in HTML 3.2, 1152 // but some people provide them and they help avoid 1153 // a relayout 1154 field->image = newiimage(src, 1155 astrval(tok, Aalt, L"Submit"), 1156 atabval(tok, Aalign, align_tab, NALIGNTAB, ALbottom), 1157 auintval(tok, Awidth, 0), auintval(tok, Aheight, 0), 1158 0, 0, 0, 0, nil); 1159 ii = (Iimage*)field->image; 1160 ii->nextimage = di->images; 1161 di->images = ii; 1162 break; 1163 1164 case Freset: 1165 if(field->value == nil) 1166 field->value = _Strdup(L"Reset"); 1167 break; 1168 1169 case Fbutton: 1170 if(field->value == nil) 1171 field->value = _Strdup(L" "); 1172 break; 1173 } 1174 ffit = newiformfield(field); 1175 additem(ps, ffit, tok); 1176 if(ffit->genattr != nil) 1177 field->events = ffit->genattr->events; 1178 break; 1179 1180 // <!ENTITY ISINDEX - O EMPTY> 1181 case Tisindex: 1182 ps->skipwhite = 0; 1183 prompt = astrval(tok, Aprompt, L"Index search terms:"); 1184 target = atargval(tok, di->target); 1185 additem(ps, textit(ps, prompt), tok); 1186 frm = newform(++is->nforms, 1187 nil, 1188 di->base, 1189 target, 1190 HGet, 1191 di->forms); 1192 di->forms = frm; 1193 ff = newformfield(Ftext, 1194 1, 1195 frm, 1196 _Strdup(L"_ISINDEX_"), 1197 nil, 1198 50, 1199 1000, 1200 nil); 1201 frm->fields = ff; 1202 frm->nfields = 1; 1203 additem(ps, newiformfield(ff), tok); 1204 addbrk(ps, 1, 0); 1205 break; 1206 1207 // <!ELEMENT LI - O %flow> 1208 case Tli: 1209 if(ps->listtypestk.n == 0) { 1210 if(warn) 1211 fprint(2, "<LI> not in list\n"); 1212 continue; 1213 } 1214 ty = top(&ps->listtypestk, 0); 1215 ty2 = listtyval(tok, ty); 1216 if(ty != ty2) { 1217 ty = ty2; 1218 push(&ps->listtypestk, ty2); 1219 } 1220 v = aintval(tok, Avalue, top(&ps->listcntstk, 1)); 1221 if(ty == LTdisc || ty == LTsquare || ty == LTcircle) 1222 hang = 10*LISTTAB - 3; 1223 else 1224 hang = 10*LISTTAB - 1; 1225 changehang(ps, hang); 1226 addtext(ps, listmark(ty, v)); 1227 push(&ps->listcntstk, v + 1); 1228 changehang(ps, -hang); 1229 ps->skipwhite = 1; 1230 break; 1231 1232 // <!ELEMENT MAP - - (AREA)+> 1233 case Tmap: 1234 if(_tokaval(tok, Aname, &name, 0)) 1235 is->curmap = getmap(di, name); 1236 break; 1237 1238 case Tmap+RBRA: 1239 map = is->curmap; 1240 if(map == nil) { 1241 if(warn) 1242 fprint(2, "warning: unexpected </MAP>\n"); 1243 continue; 1244 } 1245 map->areas = (Area*)_revlist((List*)map->areas); 1246 break; 1247 1248 case Tmeta: 1249 if(ps->skipping) 1250 continue; 1251 if(_tokaval(tok, Ahttp_equiv, &equiv, 0)) { 1252 val = aval(tok, Acontent); 1253 n = _Strlen(equiv); 1254 if(!_Strncmpci(equiv, n, L"refresh")) 1255 di->refresh = val; 1256 else if(!_Strncmpci(equiv, n, L"content-script-type")) { 1257 n = _Strlen(val); 1258 if(!_Strncmpci(val, n, L"javascript") 1259 || !_Strncmpci(val, n, L"jscript1.1") 1260 || !_Strncmpci(val, n, L"jscript")) 1261 di->scripttype = TextJavascript; 1262 else { 1263 if(warn) 1264 fprint(2, "unimplemented script type %S\n", val); 1265 di->scripttype = UnknownType; 1266 } 1267 } 1268 } 1269 break; 1270 1271 // Nobr is NOT in HMTL 4.0, but it is ubiquitous on the web 1272 case Tnobr: 1273 ps->skipwhite = 0; 1274 ps->curstate &= ~IFwrap; 1275 break; 1276 1277 case Tnobr+RBRA: 1278 ps->curstate |= IFwrap; 1279 break; 1280 1281 // We do frames, so skip stuff in noframes 1282 case Tnoframes: 1283 ps->skipping = 1; 1284 break; 1285 1286 case Tnoframes+RBRA: 1287 ps->skipping = 0; 1288 break; 1289 1290 // We do scripts (if enabled), so skip stuff in noscripts 1291 case Tnoscript: 1292 if(doscripts) 1293 ps->skipping = 1; 1294 break; 1295 1296 case Tnoscript+RBRA: 1297 if(doscripts) 1298 ps->skipping = 0; 1299 break; 1300 1301 // <!ELEMENT OPTION - O ( //PCDATA)> 1302 case Toption: 1303 if(is->curform == nil || is->curform->fields == nil) { 1304 if(warn) 1305 fprint(2, "warning: <OPTION> not in <SELECT>\n"); 1306 continue; 1307 } 1308 field = is->curform->fields; 1309 if(field->ftype != Fselect) { 1310 if(warn) 1311 fprint(2, "warning: <OPTION> not in <SELECT>\n"); 1312 continue; 1313 } 1314 val = aval(tok, Avalue); 1315 option = newoption(aflagval(tok, Aselected), val, nil, field->options); 1316 field->options = option; 1317 option->display = getpcdata(toks, tokslen, &toki); 1318 if(val == nil) 1319 option->value = _Strdup(option->display); 1320 break; 1321 1322 // <!ELEMENT P - O (%text)* > 1323 case Tp: 1324 pushjust(ps, atabval(tok, Aalign, align_tab, NALIGNTAB, ps->curjust)); 1325 ps->inpar = 1; 1326 ps->skipwhite = 1; 1327 break; 1328 1329 case Tp+RBRA: 1330 break; 1331 1332 // <!ELEMENT PARAM - O EMPTY> 1333 // Do something when we do applets... 1334 case Tparam: 1335 break; 1336 1337 // <!ELEMENT PRE - - (%text)* -(IMG|BIG|SMALL|SUB|SUP|FONT) > 1338 case Tpre: 1339 ps->curstate &= ~IFwrap; 1340 ps->literal = 1; 1341 ps->skipwhite = 0; 1342 pushfontstyle(ps, FntT); 1343 break; 1344 1345 case Tpre+RBRA: 1346 ps->curstate |= IFwrap; 1347 if(ps->literal) { 1348 popfontstyle(ps); 1349 ps->literal = 0; 1350 } 1351 break; 1352 1353 // <!ELEMENT SCRIPT - - CDATA> 1354 case Tscript: 1355 if(doscripts) { 1356 if(!di->hasscripts) { 1357 if(di->scripttype == TextJavascript) { 1358 // TODO: initialize script if nec. 1359 // initjscript(di); 1360 di->hasscripts = 1; 1361 } 1362 } 1363 } 1364 if(!di->hasscripts) { 1365 if(warn) 1366 fprint(2, "warning: <SCRIPT> ignored\n"); 1367 ps->skipping = 1; 1368 } 1369 else { 1370 scriptsrc = aurlval(tok, Asrc, nil, di->base); 1371 script = nil; 1372 if(scriptsrc != nil) { 1373 if(warn) 1374 fprint(2, "warning: non-local <SCRIPT> ignored\n"); 1375 free(scriptsrc); 1376 } 1377 else { 1378 script = getpcdata(toks, tokslen, &toki); 1379 } 1380 if(script != nil) { 1381 if(warn) 1382 fprint(2, "script ignored\n"); 1383 free(script); 1384 } 1385 } 1386 break; 1387 1388 case Tscript+RBRA: 1389 ps->skipping = 0; 1390 break; 1391 1392 // <!ELEMENT SELECT - - (OPTION+)> 1393 case Tselect: 1394 if(is->curform == nil) { 1395 if(warn) 1396 fprint(2, "<SELECT> not inside <FORM>\n"); 1397 continue; 1398 } 1399 field = newformfield(Fselect, 1400 ++is->curform->nfields, 1401 is->curform, 1402 aval(tok, Aname), 1403 nil, 1404 auintval(tok, Asize, 0), 1405 0, 1406 is->curform->fields); 1407 is->curform->fields = field; 1408 if(aflagval(tok, Amultiple)) 1409 field->flags = FFmultiple; 1410 ffit = newiformfield(field); 1411 additem(ps, ffit, tok); 1412 if(ffit->genattr != nil) 1413 field->events = ffit->genattr->events; 1414 // throw away stuff until next tag (should be <OPTION>) 1415 s = getpcdata(toks, tokslen, &toki); 1416 if(s != nil) 1417 free(s); 1418 break; 1419 1420 case Tselect+RBRA: 1421 if(is->curform == nil || is->curform->fields == nil) { 1422 if(warn) 1423 fprint(2, "warning: unexpected </SELECT>\n"); 1424 continue; 1425 } 1426 field = is->curform->fields; 1427 if(field->ftype != Fselect) 1428 continue; 1429 // put options back in input order 1430 field->options = (Option*)_revlist((List*)field->options); 1431 break; 1432 1433 // <!ELEMENT (STRIKE|U) - - (%text)*> 1434 case Tstrike: 1435 case Tu: 1436 ps->curul = push(&ps->ulstk, (tag==Tstrike)? ULmid : ULunder); 1437 break; 1438 1439 case Tstrike+RBRA: 1440 case Tu+RBRA: 1441 if(ps->ulstk.n == 0) { 1442 if(warn) 1443 fprint(2, "warning: unexpected %T\n", tok); 1444 continue; 1445 } 1446 ps->curul = popretnewtop(&ps->ulstk, ULnone); 1447 break; 1448 1449 // <!ELEMENT STYLE - - CDATA> 1450 case Tstyle: 1451 if(warn) 1452 fprint(2, "warning: unimplemented <STYLE>\n"); 1453 ps->skipping = 1; 1454 break; 1455 1456 case Tstyle+RBRA: 1457 ps->skipping = 0; 1458 break; 1459 1460 // <!ELEMENT (SUB|SUP) - - (%text)*> 1461 case Tsub: 1462 case Tsup: 1463 if(tag == Tsub) 1464 ps->curvoff += SUBOFF; 1465 else 1466 ps->curvoff -= SUPOFF; 1467 push(&ps->voffstk, ps->curvoff); 1468 sz = top(&ps->fntsizestk, Normal); 1469 pushfontsize(ps, sz - 1); 1470 break; 1471 1472 case Tsub+RBRA: 1473 case Tsup+RBRA: 1474 if(ps->voffstk.n == 0) { 1475 if(warn) 1476 fprint(2, "warning: unexpected %T\n", tok); 1477 continue; 1478 } 1479 ps->curvoff = popretnewtop(&ps->voffstk, 0); 1480 popfontsize(ps); 1481 break; 1482 1483 // <!ELEMENT TABLE - - (CAPTION?, TR+)> 1484 case Ttable: 1485 ps->skipwhite = 0; 1486 tab = newtable(++is->ntables, 1487 aalign(tok), 1488 adimen(tok, Awidth), 1489 aflagval(tok, Aborder), 1490 auintval(tok, Acellspacing, TABSP), 1491 auintval(tok, Acellpadding, TABPAD), 1492 makebackground(nil, acolorval(tok, Abgcolor, ps->curbg.color)), 1493 tok, 1494 is->tabstk); 1495 is->tabstk = tab; 1496 curtab = tab; 1497 break; 1498 1499 case Ttable+RBRA: 1500 if(curtab == nil) { 1501 if(warn) 1502 fprint(2, "warning: unexpected </TABLE>\n"); 1503 continue; 1504 } 1505 isempty = (curtab->cells == nil); 1506 if(isempty) { 1507 if(warn) 1508 fprint(2, "warning: <TABLE> has no cells\n"); 1509 } 1510 else { 1511 ps = finishcell(curtab, ps); 1512 if(curtab->rows != nil) 1513 curtab->rows->flags = 0; 1514 finish_table(curtab); 1515 } 1516 ps->skipping = 0; 1517 if(!isempty) { 1518 tabitem = newitable(curtab); 1519 al = curtab->align.halign; 1520 switch(al) { 1521 case ALleft: 1522 case ALright: 1523 additem(ps, newifloat(tabitem, al), tok); 1524 break; 1525 default: 1526 if(al == ALcenter) 1527 pushjust(ps, ALcenter); 1528 addbrk(ps, 0, 0); 1529 if(ps->inpar) { 1530 popjust(ps); 1531 ps->inpar = 0; 1532 } 1533 additem(ps, tabitem, curtab->tabletok); 1534 if(al == ALcenter) 1535 popjust(ps); 1536 break; 1537 } 1538 } 1539 if(is->tabstk == nil) { 1540 if(warn) 1541 fprint(2, "warning: table stack is wrong\n"); 1542 } 1543 else 1544 is->tabstk = is->tabstk->next; 1545 curtab->next = di->tables; 1546 di->tables = curtab; 1547 curtab = is->tabstk; 1548 if(!isempty) 1549 addbrk(ps, 0, 0); 1550 break; 1551 1552 // <!ELEMENT (TH|TD) - O %body.content> 1553 // Cells for a row are accumulated in reverse order. 1554 // We push ps on a stack, and use a new one to accumulate 1555 // the contents of the cell. 1556 case Ttd: 1557 case Tth: 1558 if(curtab == nil) { 1559 if(warn) 1560 fprint(2, "%T outside <TABLE>\n", tok); 1561 continue; 1562 } 1563 if(ps->inpar) { 1564 popjust(ps); 1565 ps->inpar = 0; 1566 } 1567 ps = finishcell(curtab, ps); 1568 tr = nil; 1569 if(curtab->rows != nil) 1570 tr = curtab->rows; 1571 if(tr == nil || !tr->flags) { 1572 if(warn) 1573 fprint(2, "%T outside row\n", tok); 1574 tr = newtablerow(makealign(ALnone, ALnone), 1575 makebackground(nil, curtab->background.color), 1576 TFparsing, 1577 curtab->rows); 1578 curtab->rows = tr; 1579 } 1580 ps = cell_pstate(ps, tag == Tth); 1581 flags = TFparsing; 1582 if(aflagval(tok, Anowrap)) { 1583 flags |= TFnowrap; 1584 ps->curstate &= ~IFwrap; 1585 } 1586 if(tag == Tth) 1587 flags |= TFisth; 1588 c = newtablecell(curtab->cells==nil? 1 : curtab->cells->cellid+1, 1589 auintval(tok, Arowspan, 1), 1590 auintval(tok, Acolspan, 1), 1591 aalign(tok), 1592 adimen(tok, Awidth), 1593 auintval(tok, Aheight, 0), 1594 makebackground(nil, acolorval(tok, Abgcolor, tr->background.color)), 1595 flags, 1596 curtab->cells); 1597 curtab->cells = c; 1598 ps->curbg = c->background; 1599 if(c->align.halign == ALnone) { 1600 if(tr->align.halign != ALnone) 1601 c->align.halign = tr->align.halign; 1602 else if(tag == Tth) 1603 c->align.halign = ALcenter; 1604 else 1605 c->align.halign = ALleft; 1606 } 1607 if(c->align.valign == ALnone) { 1608 if(tr->align.valign != ALnone) 1609 c->align.valign = tr->align.valign; 1610 else 1611 c->align.valign = ALmiddle; 1612 } 1613 c->nextinrow = tr->cells; 1614 tr->cells = c; 1615 break; 1616 1617 case Ttd+RBRA: 1618 case Tth+RBRA: 1619 if(curtab == nil || curtab->cells == nil) { 1620 if(warn) 1621 fprint(2, "unexpected %T\n", tok); 1622 continue; 1623 } 1624 ps = finishcell(curtab, ps); 1625 break; 1626 1627 // <!ELEMENT TEXTAREA - - ( //PCDATA)> 1628 case Ttextarea: 1629 if(is->curform == nil) { 1630 if(warn) 1631 fprint(2, "<TEXTAREA> not inside <FORM>\n"); 1632 continue; 1633 } 1634 field = newformfield(Ftextarea, 1635 ++is->curform->nfields, 1636 is->curform, 1637 aval(tok, Aname), 1638 nil, 1639 0, 1640 0, 1641 is->curform->fields); 1642 is->curform->fields = field; 1643 field->rows = auintval(tok, Arows, 3); 1644 field->cols = auintval(tok, Acols, 50); 1645 field->value = getpcdata(toks, tokslen, &toki); 1646 if(warn && toki < tokslen - 1 && toks[toki + 1].tag != Ttextarea + RBRA) 1647 fprint(2, "warning: <TEXTAREA> data ended by %T\n", &toks[toki + 1]); 1648 ffit = newiformfield(field); 1649 additem(ps, ffit, tok); 1650 if(ffit->genattr != nil) 1651 field->events = ffit->genattr->events; 1652 break; 1653 1654 // <!ELEMENT TITLE - - ( //PCDATA)* -(%head.misc)> 1655 case Ttitle: 1656 di->doctitle = getpcdata(toks, tokslen, &toki); 1657 if(warn && toki < tokslen - 1 && toks[toki + 1].tag != Ttitle + RBRA) 1658 fprint(2, "warning: <TITLE> data ended by %T\n", &toks[toki + 1]); 1659 break; 1660 1661 // <!ELEMENT TR - O (TH|TD)+> 1662 // rows are accumulated in reverse order in curtab->rows 1663 case Ttr: 1664 if(curtab == nil) { 1665 if(warn) 1666 fprint(2, "warning: <TR> outside <TABLE>\n"); 1667 continue; 1668 } 1669 if(ps->inpar) { 1670 popjust(ps); 1671 ps->inpar = 0; 1672 } 1673 ps = finishcell(curtab, ps); 1674 if(curtab->rows != nil) 1675 curtab->rows->flags = 0; 1676 curtab->rows = newtablerow(aalign(tok), 1677 makebackground(nil, acolorval(tok, Abgcolor, curtab->background.color)), 1678 TFparsing, 1679 curtab->rows); 1680 break; 1681 1682 case Ttr+RBRA: 1683 if(curtab == nil || curtab->rows == nil) { 1684 if(warn) 1685 fprint(2, "warning: unexpected </TR>\n"); 1686 continue; 1687 } 1688 ps = finishcell(curtab, ps); 1689 tr = curtab->rows; 1690 if(tr->cells == nil) { 1691 if(warn) 1692 fprint(2, "warning: empty row\n"); 1693 curtab->rows = tr->next; 1694 tr->next = nil; 1695 } 1696 else 1697 tr->flags = 0; 1698 break; 1699 1700 // <!ELEMENT (TT|CODE|KBD|SAMP) - - (%text)*> 1701 case Ttt: 1702 case Tcode: 1703 case Tkbd: 1704 case Tsamp: 1705 pushfontstyle(ps, FntT); 1706 break; 1707 1708 // Tags that have empty action 1709 case Tabbr: 1710 case Tabbr+RBRA: 1711 case Tacronym: 1712 case Tacronym+RBRA: 1713 case Tarea+RBRA: 1714 case Tbase+RBRA: 1715 case Tbasefont+RBRA: 1716 case Tbr+RBRA: 1717 case Tdd+RBRA: 1718 case Tdt+RBRA: 1719 case Tframe+RBRA: 1720 case Thr+RBRA: 1721 case Thtml: 1722 case Thtml+RBRA: 1723 case Timg+RBRA: 1724 case Tinput+RBRA: 1725 case Tisindex+RBRA: 1726 case Tli+RBRA: 1727 case Tlink: 1728 case Tlink+RBRA: 1729 case Tmeta+RBRA: 1730 case Toption+RBRA: 1731 case Tparam+RBRA: 1732 case Ttextarea+RBRA: 1733 case Ttitle+RBRA: 1734 break; 1735 1736 1737 // Tags not implemented 1738 case Tbdo: 1739 case Tbdo+RBRA: 1740 case Tbutton: 1741 case Tbutton+RBRA: 1742 case Tdel: 1743 case Tdel+RBRA: 1744 case Tfieldset: 1745 case Tfieldset+RBRA: 1746 case Tiframe: 1747 case Tiframe+RBRA: 1748 case Tins: 1749 case Tins+RBRA: 1750 case Tlabel: 1751 case Tlabel+RBRA: 1752 case Tlegend: 1753 case Tlegend+RBRA: 1754 case Tobject: 1755 case Tobject+RBRA: 1756 case Toptgroup: 1757 case Toptgroup+RBRA: 1758 case Tspan: 1759 case Tspan+RBRA: 1760 if(warn) { 1761 if(tag > RBRA) 1762 tag -= RBRA; 1763 fprint(2, "warning: unimplemented HTML tag: %S\n", tagnames[tag]); 1764 } 1765 break; 1766 1767 default: 1768 if(warn) 1769 fprint(2, "warning: unknown HTML tag: %S\n", tok->text); 1770 break; 1771 } 1772 } 1773 // some pages omit trailing </table> 1774 while(curtab != nil) { 1775 if(warn) 1776 fprint(2, "warning: <TABLE> not closed\n"); 1777 if(curtab->cells != nil) { 1778 ps = finishcell(curtab, ps); 1779 if(curtab->cells == nil) { 1780 if(warn) 1781 fprint(2, "warning: empty table\n"); 1782 } 1783 else { 1784 if(curtab->rows != nil) 1785 curtab->rows->flags = 0; 1786 finish_table(curtab); 1787 ps->skipping = 0; 1788 additem(ps, newitable(curtab), curtab->tabletok); 1789 addbrk(ps, 0, 0); 1790 } 1791 } 1792 if(is->tabstk != nil) 1793 is->tabstk = is->tabstk->next; 1794 curtab->next = di->tables; 1795 di->tables = curtab; 1796 curtab = is->tabstk; 1797 } 1798 outerps = lastps(ps); 1799 ans = outerps->items->next; 1800 freeitem(outerps->items); 1801 // note: ans may be nil and di->kids not nil, if there's a frameset! 1802 outerps->items = newispacer(ISPnull); 1803 outerps->lastit = outerps->items; 1804 is->psstk = ps; 1805 if(ans != nil && di->hasscripts) { 1806 // TODO evalscript(nil); 1807 ; 1808 } 1809 1810 return_ans: 1811 if(dbgbuild) { 1812 assert(validitems(ans)); 1813 if(ans == nil) 1814 fprint(2, "getitems returning nil\n"); 1815 else 1816 printitems(ans, "getitems returning:"); 1817 } 1818 return ans; 1819 } 1820 1821 // Concatenate together maximal set of Data tokens, starting at toks[toki+1]. 1822 // Lexer has ensured that there will either be a following non-data token or 1823 // we will be at eof. 1824 // Return emallocd trimmed concatenation, and update *ptoki to last used toki 1825 static Rune* 1826 getpcdata(Token* toks, int tokslen, int* ptoki) 1827 { 1828 Rune* ans; 1829 Rune* p; 1830 Rune* trimans; 1831 int anslen; 1832 int trimanslen; 1833 int toki; 1834 Token* tok; 1835 1836 ans = nil; 1837 anslen = 0; 1838 // first find length of answer 1839 toki = (*ptoki) + 1; 1840 while(toki < tokslen) { 1841 tok = &toks[toki]; 1842 if(tok->tag == Data) { 1843 toki++; 1844 anslen += _Strlen(tok->text); 1845 } 1846 else 1847 break; 1848 } 1849 // now make up the initial answer 1850 if(anslen > 0) { 1851 ans = _newstr(anslen); 1852 p = ans; 1853 toki = (*ptoki) + 1; 1854 while(toki < tokslen) { 1855 tok = &toks[toki]; 1856 if(tok->tag == Data) { 1857 toki++; 1858 p = _Stradd(p, tok->text, _Strlen(tok->text)); 1859 } 1860 else 1861 break; 1862 } 1863 *p = 0; 1864 _trimwhite(ans, anslen, &trimans, &trimanslen); 1865 if(trimanslen != anslen) { 1866 p = ans; 1867 ans = _Strndup(trimans, trimanslen); 1868 free(p); 1869 } 1870 } 1871 *ptoki = toki-1; 1872 return ans; 1873 } 1874 1875 // If still parsing head of curtab->cells list, finish it off 1876 // by transferring the items on the head of psstk to the cell. 1877 // Then pop the psstk and return the new psstk. 1878 static Pstate* 1879 finishcell(Table* curtab, Pstate* psstk) 1880 { 1881 Tablecell* c; 1882 Pstate* psstknext; 1883 1884 c = curtab->cells; 1885 if(c != nil) { 1886 if((c->flags&TFparsing)) { 1887 psstknext = psstk->next; 1888 if(psstknext == nil) { 1889 if(warn) 1890 fprint(2, "warning: parse state stack is wrong\n"); 1891 } 1892 else { 1893 c->content = psstk->items->next; 1894 c->flags &= ~TFparsing; 1895 freepstate(psstk); 1896 psstk = psstknext; 1897 } 1898 } 1899 } 1900 return psstk; 1901 } 1902 1903 // Make a new Pstate for a cell, based on the old pstate, oldps. 1904 // Also, put the new ps on the head of the oldps stack. 1905 static Pstate* 1906 cell_pstate(Pstate* oldps, int ishead) 1907 { 1908 Pstate* ps; 1909 int sty; 1910 1911 ps = newpstate(oldps); 1912 ps->skipwhite = 1; 1913 ps->curanchor = oldps->curanchor; 1914 copystack(&ps->fntstylestk, &oldps->fntstylestk); 1915 copystack(&ps->fntsizestk, &oldps->fntsizestk); 1916 ps->curfont = oldps->curfont; 1917 ps->curfg = oldps->curfg; 1918 ps->curbg = oldps->curbg; 1919 copystack(&ps->fgstk, &oldps->fgstk); 1920 ps->adjsize = oldps->adjsize; 1921 if(ishead) { 1922 sty = ps->curfont%NumSize; 1923 ps->curfont = FntB*NumSize + sty; 1924 } 1925 return ps; 1926 } 1927 1928 // Return a new Pstate with default starting state. 1929 // Use link to add it to head of a list, if any. 1930 static Pstate* 1931 newpstate(Pstate* link) 1932 { 1933 Pstate* ps; 1934 1935 ps = (Pstate*)emalloc(sizeof(Pstate)); 1936 ps->curfont = DefFnt; 1937 ps->curfg = Black; 1938 ps->curbg.image = nil; 1939 ps->curbg.color = White; 1940 ps->curul = ULnone; 1941 ps->curjust = ALleft; 1942 ps->curstate = IFwrap; 1943 ps->items = newispacer(ISPnull); 1944 ps->lastit = ps->items; 1945 ps->prelastit = nil; 1946 ps->next = link; 1947 return ps; 1948 } 1949 1950 // Return last Pstate on psl list 1951 static Pstate* 1952 lastps(Pstate* psl) 1953 { 1954 assert(psl != nil); 1955 while(psl->next != nil) 1956 psl = psl->next; 1957 return psl; 1958 } 1959 1960 // Add it to end of ps item chain, adding in current state from ps. 1961 // Also, if tok is not nil, scan it for generic attributes and assign 1962 // the genattr field of the item accordingly. 1963 static void 1964 additem(Pstate* ps, Item* it, Token* tok) 1965 { 1966 int aid; 1967 int any; 1968 Rune* i; 1969 Rune* c; 1970 Rune* s; 1971 Rune* t; 1972 Attr* a; 1973 SEvent* e; 1974 1975 if(ps->skipping) { 1976 if(warn) 1977 fprint(2, "warning: skipping item: %I\n", it); 1978 return; 1979 } 1980 it->anchorid = ps->curanchor; 1981 it->state |= ps->curstate; 1982 if(tok != nil) { 1983 any = 0; 1984 i = nil; 1985 c = nil; 1986 s = nil; 1987 t = nil; 1988 e = nil; 1989 for(a = tok->attr; a != nil; a = a->next) { 1990 aid = a->attid; 1991 if(!attrinfo[aid]) 1992 continue; 1993 switch(aid) { 1994 case Aid: 1995 i = a->value; 1996 break; 1997 1998 case Aclass: 1999 c = a->value; 2000 break; 2001 2002 case Astyle: 2003 s = a->value; 2004 break; 2005 2006 case Atitle: 2007 t = a->value; 2008 break; 2009 2010 default: 2011 assert(aid >= Aonblur && aid <= Aonunload); 2012 e = newscriptevent(scriptev[a->attid], a->value, e); 2013 break; 2014 } 2015 a->value = nil; 2016 any = 1; 2017 } 2018 if(any) 2019 it->genattr = newgenattr(i, c, s, t, e); 2020 } 2021 ps->curstate &= ~(IFbrk|IFbrksp|IFnobrk|IFcleft|IFcright); 2022 ps->prelastit = ps->lastit; 2023 ps->lastit->next = it; 2024 ps->lastit = it; 2025 } 2026 2027 // Make a text item out of s, 2028 // using current font, foreground, vertical offset and underline state. 2029 static Item* 2030 textit(Pstate* ps, Rune* s) 2031 { 2032 assert(s != nil); 2033 return newitext(s, ps->curfont, ps->curfg, ps->curvoff + Voffbias, ps->curul); 2034 } 2035 2036 // Add text item or items for s, paying attention to 2037 // current font, foreground, baseline offset, underline state, 2038 // and literal mode. Unless we're in literal mode, compress 2039 // whitespace to single blank, and, if curstate has a break, 2040 // trim any leading whitespace. Whether in literal mode or not, 2041 // turn nonbreaking spaces into spacer items with IFnobrk set. 2042 // 2043 // In literal mode, break up s at newlines and add breaks instead. 2044 // Also replace tabs appropriate number of spaces. 2045 // In nonliteral mode, break up the items every 100 or so characters 2046 // just to make the layout algorithm not go quadratic. 2047 // 2048 // addtext assumes ownership of s. 2049 static void 2050 addtext(Pstate* ps, Rune* s) 2051 { 2052 int n; 2053 int i; 2054 int j; 2055 int k; 2056 int col; 2057 int c; 2058 int nsp; 2059 Item* it; 2060 Rune* ss; 2061 Rune* p; 2062 Rune buf[SMALLBUFSIZE]; 2063 2064 assert(s != nil); 2065 n = runestrlen(s); 2066 i = 0; 2067 j = 0; 2068 if(ps->literal) { 2069 col = 0; 2070 while(i < n) { 2071 if(s[i] == '\n') { 2072 if(i > j) { 2073 // trim trailing blanks from line 2074 for(k = i; k > j; k--) 2075 if(s[k - 1] != ' ') 2076 break; 2077 if(k > j) 2078 additem(ps, textit(ps, _Strndup(s+j, k-j)), nil); 2079 } 2080 addlinebrk(ps, 0); 2081 j = i + 1; 2082 col = 0; 2083 } 2084 else { 2085 if(s[i] == '\t') { 2086 col += i - j; 2087 nsp = 8 - (col%8); 2088 // make ss = s[j:i] + nsp spaces 2089 ss = _newstr(i-j+nsp); 2090 p = _Stradd(ss, s+j, i-j); 2091 p = _Stradd(p, L" ", nsp); 2092 *p = 0; 2093 additem(ps, textit(ps, ss), nil); 2094 col += nsp; 2095 j = i + 1; 2096 } 2097 else if(s[i] == NBSP) { 2098 if(i > j) 2099 additem(ps, textit(ps, _Strndup(s+j, i-j)), nil); 2100 addnbsp(ps); 2101 col += (i - j) + 1; 2102 j = i + 1; 2103 } 2104 } 2105 i++; 2106 } 2107 if(i > j) { 2108 if(j == 0 && i == n) { 2109 // just transfer s over 2110 additem(ps, textit(ps, s), nil); 2111 } 2112 else { 2113 additem(ps, textit(ps, _Strndup(s+j, i-j)), nil); 2114 free(s); 2115 } 2116 } 2117 } 2118 else { // not literal mode 2119 if((ps->curstate&IFbrk) || ps->lastit == ps->items) 2120 while(i < n) { 2121 c = s[i]; 2122 if(c >= 256 || !isspace(c)) 2123 break; 2124 i++; 2125 } 2126 p = buf; 2127 for(j = i; i < n; i++) { 2128 assert(p+i-j < buf+SMALLBUFSIZE-1); 2129 c = s[i]; 2130 if(c == NBSP) { 2131 if(i > j) 2132 p = _Stradd(p, s+j, i-j); 2133 if(p > buf) 2134 additem(ps, textit(ps, _Strndup(buf, p-buf)), nil); 2135 p = buf; 2136 addnbsp(ps); 2137 j = i + 1; 2138 continue; 2139 } 2140 if(c < 256 && isspace(c)) { 2141 if(i > j) 2142 p = _Stradd(p, s+j, i-j); 2143 *p++ = ' '; 2144 while(i < n - 1) { 2145 c = s[i + 1]; 2146 if(c >= 256 || !isspace(c)) 2147 break; 2148 i++; 2149 } 2150 j = i + 1; 2151 } 2152 if(i - j >= 100) { 2153 p = _Stradd(p, s+j, i+1-j); 2154 j = i + 1; 2155 } 2156 if(p-buf >= 100) { 2157 additem(ps, textit(ps, _Strndup(buf, p-buf)), nil); 2158 p = buf; 2159 } 2160 } 2161 if(i > j && j < n) { 2162 assert(p+i-j < buf+SMALLBUFSIZE-1); 2163 p = _Stradd(p, s+j, i-j); 2164 } 2165 // don't add a space if previous item ended in a space 2166 if(p-buf == 1 && buf[0] == ' ' && ps->lastit != nil) { 2167 it = ps->lastit; 2168 if(it->tag == Itexttag) { 2169 ss = ((Itext*)it)->s; 2170 k = _Strlen(ss); 2171 if(k > 0 && ss[k] == ' ') 2172 p = buf; 2173 } 2174 } 2175 if(p > buf) 2176 additem(ps, textit(ps, _Strndup(buf, p-buf)), nil); 2177 free(s); 2178 } 2179 } 2180 2181 // Add a break to ps->curstate, with extra space if sp is true. 2182 // If there was a previous break, combine this one's parameters 2183 // with that to make the amt be the max of the two and the clr 2184 // be the most general. (amt will be 0 or 1) 2185 // Also, if the immediately preceding item was a text item, 2186 // trim any whitespace from the end of it, if not in literal mode. 2187 // Finally, if this is at the very beginning of the item list 2188 // (the only thing there is a null spacer), then don't add the space. 2189 static void 2190 addbrk(Pstate* ps, int sp, int clr) 2191 { 2192 int state; 2193 Rune* l; 2194 int nl; 2195 Rune* r; 2196 int nr; 2197 Itext* t; 2198 Rune* s; 2199 2200 state = ps->curstate; 2201 clr = clr|(state&(IFcleft|IFcright)); 2202 if(sp && !(ps->lastit == ps->items)) 2203 sp = IFbrksp; 2204 else 2205 sp = 0; 2206 ps->curstate = IFbrk|sp|(state&~(IFcleft|IFcright))|clr; 2207 if(ps->lastit != ps->items) { 2208 if(!ps->literal && ps->lastit->tag == Itexttag) { 2209 t = (Itext*)ps->lastit; 2210 _splitr(t->s, _Strlen(t->s), notwhitespace, &l, &nl, &r, &nr); 2211 // try to avoid making empty items 2212 // but not crucial f the occasional one gets through 2213 if(nl == 0 && ps->prelastit != nil) { 2214 ps->lastit = ps->prelastit; 2215 ps->lastit->next = nil; 2216 ps->prelastit = nil; 2217 } 2218 else { 2219 s = t->s; 2220 if(nl == 0) { 2221 // need a non-nil pointer to empty string 2222 // (_Strdup(L"") returns nil) 2223 t->s = emalloc(sizeof(Rune)); 2224 t->s[0] = 0; 2225 } 2226 else 2227 t->s = _Strndup(l, nl); 2228 if(s) 2229 free(s); 2230 } 2231 } 2232 } 2233 } 2234 2235 // Add break due to a <br> or a newline within a preformatted section. 2236 // We add a null item first, with current font's height and ascent, to make 2237 // sure that the current line takes up at least that amount of vertical space. 2238 // This ensures that <br>s on empty lines cause blank lines, and that 2239 // multiple <br>s in a row give multiple blank lines. 2240 // However don't add the spacer if the previous item was something that 2241 // takes up space itself. 2242 static void 2243 addlinebrk(Pstate* ps, int clr) 2244 { 2245 int obrkstate; 2246 int b; 2247 2248 // don't want break before our null item unless the previous item 2249 // was also a null item for the purposes of line breaking 2250 obrkstate = ps->curstate&(IFbrk|IFbrksp); 2251 b = IFnobrk; 2252 if(ps->lastit != nil) { 2253 if(ps->lastit->tag == Ispacertag) { 2254 if(((Ispacer*)ps->lastit)->spkind == ISPvline) 2255 b = IFbrk; 2256 } 2257 } 2258 ps->curstate = (ps->curstate&~(IFbrk|IFbrksp))|b; 2259 additem(ps, newispacer(ISPvline), nil); 2260 ps->curstate = (ps->curstate&~(IFbrk|IFbrksp))|obrkstate; 2261 addbrk(ps, 0, clr); 2262 } 2263 2264 // Add a nonbreakable space 2265 static void 2266 addnbsp(Pstate* ps) 2267 { 2268 // if nbsp comes right where a break was specified, 2269 // do the break anyway (nbsp is being used to generate undiscardable 2270 // space rather than to prevent a break) 2271 if((ps->curstate&IFbrk) == 0) 2272 ps->curstate |= IFnobrk; 2273 additem(ps, newispacer(ISPhspace), nil); 2274 // but definitely no break on next item 2275 ps->curstate |= IFnobrk; 2276 } 2277 2278 // Change hang in ps.curstate by delta. 2279 // The amount is in 1/10ths of tabs, and is the amount that 2280 // the current contiguous set of items with a hang value set 2281 // is to be shifted left from its normal (indented) place. 2282 static void 2283 changehang(Pstate* ps, int delta) 2284 { 2285 int amt; 2286 2287 amt = (ps->curstate&IFhangmask) + delta; 2288 if(amt < 0) { 2289 if(warn) 2290 fprint(2, "warning: hang went negative\n"); 2291 amt = 0; 2292 } 2293 ps->curstate = (ps->curstate&~IFhangmask)|amt; 2294 } 2295 2296 // Change indent in ps.curstate by delta. 2297 static void 2298 changeindent(Pstate* ps, int delta) 2299 { 2300 int amt; 2301 2302 amt = ((ps->curstate&IFindentmask) >> IFindentshift) + delta; 2303 if(amt < 0) { 2304 if(warn) 2305 fprint(2, "warning: indent went negative\n"); 2306 amt = 0; 2307 } 2308 ps->curstate = (ps->curstate&~IFindentmask)|(amt << IFindentshift); 2309 } 2310 2311 // Push val on top of stack, and also return value pushed 2312 static int 2313 push(Stack* stk, int val) 2314 { 2315 if(stk->n == Nestmax) { 2316 if(warn) 2317 fprint(2, "warning: build stack overflow\n"); 2318 } 2319 else 2320 stk->slots[stk->n++] = val; 2321 return val; 2322 } 2323 2324 // Pop top of stack 2325 static void 2326 pop(Stack* stk) 2327 { 2328 if(stk->n > 0) 2329 --stk->n; 2330 } 2331 2332 //Return top of stack, using dflt if stack is empty 2333 static int 2334 top(Stack* stk, int dflt) 2335 { 2336 if(stk->n == 0) 2337 return dflt; 2338 return stk->slots[stk->n-1]; 2339 } 2340 2341 // pop, then return new top, with dflt if empty 2342 static int 2343 popretnewtop(Stack* stk, int dflt) 2344 { 2345 if(stk->n == 0) 2346 return dflt; 2347 stk->n--; 2348 if(stk->n == 0) 2349 return dflt; 2350 return stk->slots[stk->n-1]; 2351 } 2352 2353 // Copy fromstk entries into tostk 2354 static void 2355 copystack(Stack* tostk, Stack* fromstk) 2356 { 2357 int n; 2358 2359 n = fromstk->n; 2360 tostk->n = n; 2361 memmove(tostk->slots, fromstk->slots, n*sizeof(int)); 2362 } 2363 2364 static void 2365 popfontstyle(Pstate* ps) 2366 { 2367 pop(&ps->fntstylestk); 2368 setcurfont(ps); 2369 } 2370 2371 static void 2372 pushfontstyle(Pstate* ps, int sty) 2373 { 2374 push(&ps->fntstylestk, sty); 2375 setcurfont(ps); 2376 } 2377 2378 static void 2379 popfontsize(Pstate* ps) 2380 { 2381 pop(&ps->fntsizestk); 2382 setcurfont(ps); 2383 } 2384 2385 static void 2386 pushfontsize(Pstate* ps, int sz) 2387 { 2388 push(&ps->fntsizestk, sz); 2389 setcurfont(ps); 2390 } 2391 2392 static void 2393 setcurfont(Pstate* ps) 2394 { 2395 int sty; 2396 int sz; 2397 2398 sty = top(&ps->fntstylestk, FntR); 2399 sz = top(&ps->fntsizestk, Normal); 2400 if(sz < Tiny) 2401 sz = Tiny; 2402 if(sz > Verylarge) 2403 sz = Verylarge; 2404 ps->curfont = sty*NumSize + sz; 2405 } 2406 2407 static void 2408 popjust(Pstate* ps) 2409 { 2410 pop(&ps->juststk); 2411 setcurjust(ps); 2412 } 2413 2414 static void 2415 pushjust(Pstate* ps, int j) 2416 { 2417 push(&ps->juststk, j); 2418 setcurjust(ps); 2419 } 2420 2421 static void 2422 setcurjust(Pstate* ps) 2423 { 2424 int j; 2425 int state; 2426 2427 j = top(&ps->juststk, ALleft); 2428 if(j != ps->curjust) { 2429 ps->curjust = j; 2430 state = ps->curstate; 2431 state &= ~(IFrjust|IFcjust); 2432 if(j == ALcenter) 2433 state |= IFcjust; 2434 else if(j == ALright) 2435 state |= IFrjust; 2436 ps->curstate = state; 2437 } 2438 } 2439 2440 // Do final rearrangement after table parsing is finished 2441 // and assign cells to grid points 2442 static void 2443 finish_table(Table* t) 2444 { 2445 int ncol; 2446 int nrow; 2447 int r; 2448 Tablerow* rl; 2449 Tablecell* cl; 2450 int* rowspancnt; 2451 Tablecell** rowspancell; 2452 int ri; 2453 int ci; 2454 Tablecell* c; 2455 Tablecell* cnext; 2456 Tablerow* row; 2457 Tablerow* rownext; 2458 int rcols; 2459 int newncol; 2460 int k; 2461 int j; 2462 int cspan; 2463 int rspan; 2464 int i; 2465 2466 rl = t->rows; 2467 t->nrow = nrow = _listlen((List*)rl); 2468 t->rows = (Tablerow*)emalloc(nrow * sizeof(Tablerow)); 2469 ncol = 0; 2470 r = nrow - 1; 2471 for(row = rl; row != nil; row = rownext) { 2472 // copy the data from the allocated Tablerow into the array slot 2473 t->rows[r] = *row; 2474 rownext = row->next; 2475 row = &t->rows[r]; 2476 r--; 2477 rcols = 0; 2478 c = row->cells; 2479 2480 // If rowspan is > 1 but this is the last row, 2481 // reset the rowspan 2482 if(c != nil && c->rowspan > 1 && r == nrow-2) 2483 c->rowspan = 1; 2484 2485 // reverse row->cells list (along nextinrow pointers) 2486 row->cells = nil; 2487 while(c != nil) { 2488 cnext = c->nextinrow; 2489 c->nextinrow = row->cells; 2490 row->cells = c; 2491 rcols += c->colspan; 2492 c = cnext; 2493 } 2494 if(rcols > ncol) 2495 ncol = rcols; 2496 } 2497 t->ncol = ncol; 2498 t->cols = (Tablecol*)emalloc(ncol * sizeof(Tablecol)); 2499 2500 // Reverse cells just so they are drawn in source order. 2501 // Also, trim their contents so they don't end in whitespace. 2502 t->cells = (Tablecell*)_revlist((List*)t->cells); 2503 for(c = t->cells; c != nil; c= c->next) 2504 trim_cell(c); 2505 t->grid = (Tablecell***)emalloc(nrow * sizeof(Tablecell**)); 2506 for(i = 0; i < nrow; i++) 2507 t->grid[i] = (Tablecell**)emalloc(ncol * sizeof(Tablecell*)); 2508 2509 // The following arrays keep track of cells that are spanning 2510 // multiple rows; rowspancnt[i] is the number of rows left 2511 // to be spanned in column i. 2512 // When done, cell's (row,col) is upper left grid point. 2513 rowspancnt = (int*)emalloc(ncol * sizeof(int)); 2514 rowspancell = (Tablecell**)emalloc(ncol * sizeof(Tablecell*)); 2515 for(ri = 0; ri < nrow; ri++) { 2516 row = &t->rows[ri]; 2517 cl = row->cells; 2518 ci = 0; 2519 while(ci < ncol || cl != nil) { 2520 if(ci < ncol && rowspancnt[ci] > 0) { 2521 t->grid[ri][ci] = rowspancell[ci]; 2522 rowspancnt[ci]--; 2523 ci++; 2524 } 2525 else { 2526 if(cl == nil) { 2527 ci++; 2528 continue; 2529 } 2530 c = cl; 2531 cl = cl->nextinrow; 2532 cspan = c->colspan; 2533 rspan = c->rowspan; 2534 if(ci + cspan > ncol) { 2535 // because of row spanning, we calculated 2536 // ncol incorrectly; adjust it 2537 newncol = ci + cspan; 2538 t->cols = (Tablecol*)erealloc(t->cols, newncol * sizeof(Tablecol)); 2539 rowspancnt = (int*)erealloc(rowspancnt, newncol * sizeof(int)); 2540 rowspancell = (Tablecell**)erealloc(rowspancell, newncol * sizeof(Tablecell*)); 2541 k = newncol-ncol; 2542 memset(t->cols+ncol, 0, k*sizeof(Tablecol)); 2543 memset(rowspancnt+ncol, 0, k*sizeof(int)); 2544 memset(rowspancell+ncol, 0, k*sizeof(Tablecell*)); 2545 for(j = 0; j < nrow; j++) { 2546 t->grid[j] = (Tablecell**)erealloc(t->grid[j], newncol * sizeof(Tablecell*)); 2547 memset(t->grid[j], 0, k*sizeof(Tablecell*)); 2548 } 2549 t->ncol = ncol = newncol; 2550 } 2551 c->row = ri; 2552 c->col = ci; 2553 for(i = 0; i < cspan; i++) { 2554 t->grid[ri][ci] = c; 2555 if(rspan > 1) { 2556 rowspancnt[ci] = rspan - 1; 2557 rowspancell[ci] = c; 2558 } 2559 ci++; 2560 } 2561 } 2562 } 2563 } 2564 free(rowspancnt); 2565 free(rowspancell); 2566 } 2567 2568 // Remove tail of cell content until it isn't whitespace. 2569 static void 2570 trim_cell(Tablecell* c) 2571 { 2572 int dropping; 2573 Rune* s; 2574 Rune* x; 2575 Rune* y; 2576 int nx; 2577 int ny; 2578 Item* p; 2579 Itext* q; 2580 Item* pprev; 2581 2582 dropping = 1; 2583 while(c->content != nil && dropping) { 2584 p = c->content; 2585 pprev = nil; 2586 while(p->next != nil) { 2587 pprev = p; 2588 p = p->next; 2589 } 2590 dropping = 0; 2591 if(!(p->state&IFnobrk)) { 2592 if(p->tag == Itexttag) { 2593 q = (Itext*)p; 2594 s = q->s; 2595 _splitr(s, _Strlen(s), notwhitespace, &x, &nx, &y, &ny); 2596 if(nx != 0 && ny != 0) { 2597 q->s = _Strndup(x, nx); 2598 free(s); 2599 } 2600 break; 2601 } 2602 } 2603 if(dropping) { 2604 if(pprev == nil) 2605 c->content = nil; 2606 else 2607 pprev->next = nil; 2608 freeitem(p); 2609 } 2610 } 2611 } 2612 2613 // Caller must free answer (eventually). 2614 static Rune* 2615 listmark(uchar ty, int n) 2616 { 2617 Rune* s; 2618 Rune* t; 2619 int n2; 2620 int i; 2621 2622 s = nil; 2623 switch(ty) { 2624 case LTdisc: 2625 case LTsquare: 2626 case LTcircle: 2627 s = _newstr(1); 2628 s[0] = (ty == LTdisc)? 0x2022 // bullet 2629 : ((ty == LTsquare)? 0x220e // filled square 2630 : 0x2218); // degree 2631 s[1] = 0; 2632 break; 2633 2634 case LT1: 2635 s = runesmprint("%d.", n); 2636 break; 2637 2638 case LTa: 2639 case LTA: 2640 n--; 2641 i = 0; 2642 if(n < 0) 2643 n = 0; 2644 s = _newstr((n <= 25)? 2 : 3); 2645 if(n > 25) { 2646 n2 = n%26; 2647 n /= 26; 2648 if(n2 > 25) 2649 n2 = 25; 2650 s[i++] = n2 + (ty == LTa)? 'a' : 'A'; 2651 } 2652 s[i++] = n + (ty == LTa)? 'a' : 'A'; 2653 s[i++] = '.'; 2654 s[i] = 0; 2655 break; 2656 2657 case LTi: 2658 case LTI: 2659 if(n >= NROMAN) { 2660 if(warn) 2661 fprint(2, "warning: unimplemented roman number > %d\n", NROMAN); 2662 n = NROMAN; 2663 } 2664 t = roman[n - 1]; 2665 n2 = _Strlen(t); 2666 s = _newstr(n2+1); 2667 for(i = 0; i < n2; i++) 2668 s[i] = (ty == LTi)? tolower(t[i]) : t[i]; 2669 s[i++] = '.'; 2670 s[i] = 0; 2671 break; 2672 } 2673 return s; 2674 } 2675 2676 // Find map with given name in di.maps. 2677 // If not there, add one, copying name. 2678 // Ownership of map remains with di->maps list. 2679 static Map* 2680 getmap(Docinfo* di, Rune* name) 2681 { 2682 Map* m; 2683 2684 for(m = di->maps; m != nil; m = m->next) { 2685 if(!_Strcmp(name, m->name)) 2686 return m; 2687 } 2688 m = (Map*)emalloc(sizeof(Map)); 2689 m->name = _Strdup(name); 2690 m->areas = nil; 2691 m->next = di->maps; 2692 di->maps = m; 2693 return m; 2694 } 2695 2696 // Transfers ownership of href to Area 2697 static Area* 2698 newarea(int shape, Rune* href, int target, Area* link) 2699 { 2700 Area* a; 2701 2702 a = (Area*)emalloc(sizeof(Area)); 2703 a->shape = shape; 2704 a->href = href; 2705 a->target = target; 2706 a->next = link; 2707 return a; 2708 } 2709 2710 // Return string value associated with attid in tok, nil if none. 2711 // Caller must free the result (eventually). 2712 static Rune* 2713 aval(Token* tok, int attid) 2714 { 2715 Rune* ans; 2716 2717 _tokaval(tok, attid, &ans, 1); // transfers string ownership from token to ans 2718 return ans; 2719 } 2720 2721 // Like aval, but use dflt if there was no such attribute in tok. 2722 // Caller must free the result (eventually). 2723 static Rune* 2724 astrval(Token* tok, int attid, Rune* dflt) 2725 { 2726 Rune* ans; 2727 2728 if(_tokaval(tok, attid, &ans, 1)) 2729 return ans; // transfers string ownership from token to ans 2730 else 2731 return _Strdup(dflt); 2732 } 2733 2734 // Here we're supposed to convert to an int, 2735 // and have a default when not found 2736 static int 2737 aintval(Token* tok, int attid, int dflt) 2738 { 2739 Rune* ans; 2740 2741 if(!_tokaval(tok, attid, &ans, 0) || ans == nil) 2742 return dflt; 2743 else 2744 return toint(ans); 2745 } 2746 2747 // Like aintval, but result should be >= 0 2748 static int 2749 auintval(Token* tok, int attid, int dflt) 2750 { 2751 Rune* ans; 2752 int v; 2753 2754 if(!_tokaval(tok, attid, &ans, 0) || ans == nil) 2755 return dflt; 2756 else { 2757 v = toint(ans); 2758 return v >= 0? v : 0; 2759 } 2760 } 2761 2762 // int conversion, but with possible error check (if warning) 2763 static int 2764 toint(Rune* s) 2765 { 2766 int ans; 2767 Rune* eptr; 2768 2769 ans = _Strtol(s, &eptr, 10); 2770 if(warn) { 2771 if(*eptr != 0) { 2772 eptr = _Strclass(eptr, notwhitespace); 2773 if(eptr != nil) 2774 fprint(2, "warning: expected integer, got %S\n", s); 2775 } 2776 } 2777 return ans; 2778 } 2779 2780 // Attribute value when need a table to convert strings to ints 2781 static int 2782 atabval(Token* tok, int attid, StringInt* tab, int ntab, int dflt) 2783 { 2784 Rune* aval; 2785 int ans; 2786 2787 ans = dflt; 2788 if(_tokaval(tok, attid, &aval, 0)) { 2789 if(!_lookup(tab, ntab, aval, _Strlen(aval), &ans)) { 2790 ans = dflt; 2791 if(warn) 2792 fprint(2, "warning: name not found in table lookup: %S\n", aval); 2793 } 2794 } 2795 return ans; 2796 } 2797 2798 // Attribute value when supposed to be a color 2799 static int 2800 acolorval(Token* tok, int attid, int dflt) 2801 { 2802 Rune* aval; 2803 int ans; 2804 2805 ans = dflt; 2806 if(_tokaval(tok, attid, &aval, 0)) 2807 ans = color(aval, dflt); 2808 return ans; 2809 } 2810 2811 // Attribute value when supposed to be a target frame name 2812 static int 2813 atargval(Token* tok, int dflt) 2814 { 2815 int ans; 2816 Rune* aval; 2817 2818 ans = dflt; 2819 if(_tokaval(tok, Atarget, &aval, 0)){ 2820 ans = targetid(aval); 2821 } 2822 return ans; 2823 } 2824 2825 // special for list types, where "i" and "I" are different, 2826 // but "square" and "SQUARE" are the same 2827 static int 2828 listtyval(Token* tok, int dflt) 2829 { 2830 Rune* aval; 2831 int ans; 2832 int n; 2833 2834 ans = dflt; 2835 if(_tokaval(tok, Atype, &aval, 0)) { 2836 n = _Strlen(aval); 2837 if(n == 1) { 2838 switch(aval[0]) { 2839 case '1': 2840 ans = LT1; 2841 break; 2842 case 'A': 2843 ans = LTA; 2844 break; 2845 case 'I': 2846 ans = LTI; 2847 break; 2848 case 'a': 2849 ans = LTa; 2850 break; 2851 case 'i': 2852 ans = LTi; 2853 default: 2854 if(warn) 2855 fprint(2, "warning: unknown list element type %c\n", aval[0]); 2856 } 2857 } 2858 else { 2859 if(!_Strncmpci(aval, n, L"circle")) 2860 ans = LTcircle; 2861 else if(!_Strncmpci(aval, n, L"disc")) 2862 ans = LTdisc; 2863 else if(!_Strncmpci(aval, n, L"square")) 2864 ans = LTsquare; 2865 else { 2866 if(warn) 2867 fprint(2, "warning: unknown list element type %S\n", aval); 2868 } 2869 } 2870 } 2871 return ans; 2872 } 2873 2874 // Attribute value when value is a URL, possibly relative to base. 2875 // FOR NOW: leave the url relative. 2876 // Caller must free the result (eventually). 2877 static Rune* 2878 aurlval(Token* tok, int attid, Rune* dflt, Rune* base) 2879 { 2880 Rune* ans; 2881 Rune* url; 2882 2883 USED(base); 2884 ans = nil; 2885 if(_tokaval(tok, attid, &url, 0) && url != nil) 2886 ans = removeallwhite(url); 2887 if(ans == nil) 2888 ans = _Strdup(dflt); 2889 return ans; 2890 } 2891 2892 // Return copy of s but with all whitespace (even internal) removed. 2893 // This fixes some buggy URL specification strings. 2894 static Rune* 2895 removeallwhite(Rune* s) 2896 { 2897 int j; 2898 int n; 2899 int i; 2900 int c; 2901 Rune* ans; 2902 2903 j = 0; 2904 n = _Strlen(s); 2905 for(i = 0; i < n; i++) { 2906 c = s[i]; 2907 if(c >= 256 || !isspace(c)) 2908 j++; 2909 } 2910 if(j < n) { 2911 ans = _newstr(j); 2912 j = 0; 2913 for(i = 0; i < n; i++) { 2914 c = s[i]; 2915 if(c >= 256 || !isspace(c)) 2916 ans[j++] = c; 2917 } 2918 ans[j] = 0; 2919 } 2920 else 2921 ans = _Strdup(s); 2922 return ans; 2923 } 2924 2925 // Attribute value when mere presence of attr implies value of 1, 2926 // but if there is an integer there, return it as the value. 2927 static int 2928 aflagval(Token* tok, int attid) 2929 { 2930 int val; 2931 Rune* sval; 2932 2933 val = 0; 2934 if(_tokaval(tok, attid, &sval, 0)) { 2935 val = 1; 2936 if(sval != nil) 2937 val = toint(sval); 2938 } 2939 return val; 2940 } 2941 2942 static Align 2943 makealign(int halign, int valign) 2944 { 2945 Align al; 2946 2947 al.halign = halign; 2948 al.valign = valign; 2949 return al; 2950 } 2951 2952 // Make an Align (two alignments, horizontal and vertical) 2953 static Align 2954 aalign(Token* tok) 2955 { 2956 return makealign( 2957 atabval(tok, Aalign, align_tab, NALIGNTAB, ALnone), 2958 atabval(tok, Avalign, align_tab, NALIGNTAB, ALnone)); 2959 } 2960 2961 // Make a Dimen, based on value of attid attr 2962 static Dimen 2963 adimen(Token* tok, int attid) 2964 { 2965 Rune* wd; 2966 2967 if(_tokaval(tok, attid, &wd, 0)) 2968 return parsedim(wd, _Strlen(wd)); 2969 else 2970 return makedimen(Dnone, 0); 2971 } 2972 2973 // Parse s[0:n] as num[.[num]][unit][%|*] 2974 static Dimen 2975 parsedim(Rune* s, int ns) 2976 { 2977 int kind; 2978 int spec; 2979 Rune* l; 2980 int nl; 2981 Rune* r; 2982 int nr; 2983 int mul; 2984 int i; 2985 Rune* f; 2986 int nf; 2987 int Tkdpi; 2988 Rune* units; 2989 2990 kind = Dnone; 2991 spec = 0; 2992 _splitl(s, ns, L"^0-9", &l, &nl, &r, &nr); 2993 if(nl != 0) { 2994 spec = 1000*_Strtol(l, nil, 10); 2995 if(nr > 0 && r[0] == '.') { 2996 _splitl(r+1, nr-1, L"^0-9", &f, &nf, &r, &nr); 2997 if(nf != 0) { 2998 mul = 100; 2999 for(i = 0; i < nf; i++) { 3000 spec = spec + mul*(f[i]-'0'); 3001 mul = mul/10; 3002 } 3003 } 3004 } 3005 kind = Dpixels; 3006 if(nr != 0) { 3007 if(nr >= 2) { 3008 Tkdpi = 100; 3009 units = r; 3010 r = r+2; 3011 nr -= 2; 3012 if(!_Strncmpci(units, 2, L"pt")) 3013 spec = (spec*Tkdpi)/72; 3014 else if(!_Strncmpci(units, 2, L"pi")) 3015 spec = (spec*12*Tkdpi)/72; 3016 else if(!_Strncmpci(units, 2, L"in")) 3017 spec = spec*Tkdpi; 3018 else if(!_Strncmpci(units, 2, L"cm")) 3019 spec = (spec*100*Tkdpi)/254; 3020 else if(!_Strncmpci(units, 2, L"mm")) 3021 spec = (spec*10*Tkdpi)/254; 3022 else if(!_Strncmpci(units, 2, L"em")) 3023 spec = spec*15; 3024 else { 3025 if(warn) 3026 fprint(2, "warning: unknown units %C%Cs\n", units[0], units[1]); 3027 } 3028 } 3029 if(nr >= 1) { 3030 if(r[0] == '%') 3031 kind = Dpercent; 3032 else if(r[0] == '*') 3033 kind = Drelative; 3034 } 3035 } 3036 spec = spec/1000; 3037 } 3038 else if(nr == 1 && r[0] == '*') { 3039 spec = 1; 3040 kind = Drelative; 3041 } 3042 return makedimen(kind, spec); 3043 } 3044 3045 static void 3046 setdimarray(Token* tok, int attid, Dimen** pans, int* panslen) 3047 { 3048 Rune* s; 3049 Dimen* d; 3050 int k; 3051 int nc; 3052 Rune* a[SMALLBUFSIZE]; 3053 int an[SMALLBUFSIZE]; 3054 3055 if(_tokaval(tok, attid, &s, 0)) { 3056 nc = _splitall(s, _Strlen(s), L", ", a, an, SMALLBUFSIZE); 3057 if(nc > 0) { 3058 d = (Dimen*)emalloc(nc * sizeof(Dimen)); 3059 for(k = 0; k < nc; k++) { 3060 d[k] = parsedim(a[k], an[k]); 3061 } 3062 *pans = d; 3063 *panslen = nc; 3064 return; 3065 } 3066 } 3067 *pans = nil; 3068 *panslen = 0; 3069 } 3070 3071 static Background 3072 makebackground(Rune* imageurl, int color) 3073 { 3074 Background bg; 3075 3076 bg.image = imageurl; 3077 bg.color = color; 3078 return bg; 3079 } 3080 3081 static Item* 3082 newitext(Rune* s, int fnt, int fg, int voff, int ul) 3083 { 3084 Itext* t; 3085 3086 assert(s != nil); 3087 t = (Itext*)emalloc(sizeof(Itext)); 3088 t->tag = Itexttag; 3089 t->s = s; 3090 t->fnt = fnt; 3091 t->fg = fg; 3092 t->voff = voff; 3093 t->ul = ul; 3094 return (Item*)t; 3095 } 3096 3097 static Item* 3098 newirule(int align, int size, int noshade, Dimen wspec) 3099 { 3100 Irule* r; 3101 3102 r = (Irule*)emalloc(sizeof(Irule)); 3103 r->tag = Iruletag; 3104 r->align = align; 3105 r->size = size; 3106 r->noshade = noshade; 3107 r->wspec = wspec; 3108 return (Item*)r; 3109 } 3110 3111 // Map is owned elsewhere. 3112 static Item* 3113 newiimage(Rune* src, Rune* altrep, int align, int width, int height, 3114 int hspace, int vspace, int border, int ismap, Map* map) 3115 { 3116 Iimage* i; 3117 int state; 3118 3119 state = 0; 3120 if(ismap) 3121 state = IFsmap; 3122 i = (Iimage*)emalloc(sizeof(Iimage)); 3123 i->tag = Iimagetag; 3124 i->state = state; 3125 i->imsrc = src; 3126 i->altrep = altrep; 3127 i->align = align; 3128 i->imwidth = width; 3129 i->imheight = height; 3130 i->hspace = hspace; 3131 i->vspace = vspace; 3132 i->border = border; 3133 i->map = map; 3134 i->ctlid = -1; 3135 return (Item*)i; 3136 } 3137 3138 static Item* 3139 newiformfield(Formfield* ff) 3140 { 3141 Iformfield* f; 3142 3143 f = (Iformfield*)emalloc(sizeof(Iformfield)); 3144 f->tag = Iformfieldtag; 3145 f->formfield = ff; 3146 return (Item*)f; 3147 } 3148 3149 static Item* 3150 newitable(Table* tab) 3151 { 3152 Itable* t; 3153 3154 t = (Itable*)emalloc(sizeof(Itable)); 3155 t->tag = Itabletag; 3156 t->table = tab; 3157 return (Item*)t; 3158 } 3159 3160 static Item* 3161 newifloat(Item* it, int side) 3162 { 3163 Ifloat* f; 3164 3165 f = (Ifloat*)emalloc(sizeof(Ifloat)); 3166 f->tag = Ifloattag; 3167 f->state = IFwrap; 3168 f->item = it; 3169 f->side = side; 3170 return (Item*)f; 3171 } 3172 3173 static Item* 3174 newispacer(int spkind) 3175 { 3176 Ispacer* s; 3177 3178 s = (Ispacer*)emalloc(sizeof(Ispacer)); 3179 s->tag = Ispacertag; 3180 s->spkind = spkind; 3181 return (Item*)s; 3182 } 3183 3184 // Free one item (caller must deal with next pointer) 3185 static void 3186 freeitem(Item* it) 3187 { 3188 Iimage* ii; 3189 Genattr* ga; 3190 3191 if(it == nil) 3192 return; 3193 3194 switch(it->tag) { 3195 case Itexttag: 3196 free(((Itext*)it)->s); 3197 break; 3198 case Iimagetag: 3199 ii = (Iimage*)it; 3200 free(ii->imsrc); 3201 free(ii->altrep); 3202 break; 3203 case Iformfieldtag: 3204 freeformfield(((Iformfield*)it)->formfield); 3205 break; 3206 case Itabletag: 3207 freetable(((Itable*)it)->table); 3208 break; 3209 case Ifloattag: 3210 freeitem(((Ifloat*)it)->item); 3211 break; 3212 } 3213 ga = it->genattr; 3214 if(ga != nil) { 3215 free(ga->id); 3216 free(ga->class); 3217 free(ga->style); 3218 free(ga->title); 3219 freescriptevents(ga->events); 3220 } 3221 free(it); 3222 } 3223 3224 // Free list of items chained through next pointer 3225 void 3226 freeitems(Item* ithead) 3227 { 3228 Item* it; 3229 Item* itnext; 3230 3231 it = ithead; 3232 while(it != nil) { 3233 itnext = it->next; 3234 freeitem(it); 3235 it = itnext; 3236 } 3237 } 3238 3239 static void 3240 freeformfield(Formfield* ff) 3241 { 3242 Option* o; 3243 Option* onext; 3244 3245 if(ff == nil) 3246 return; 3247 3248 free(ff->name); 3249 free(ff->value); 3250 for(o = ff->options; o != nil; o = onext) { 3251 onext = o->next; 3252 free(o->value); 3253 free(o->display); 3254 } 3255 free(ff); 3256 } 3257 3258 static void 3259 freetable(Table* t) 3260 { 3261 int i; 3262 Tablecell* c; 3263 Tablecell* cnext; 3264 3265 if(t == nil) 3266 return; 3267 3268 // We'll find all the unique cells via t->cells and next pointers. 3269 // (Other pointers to cells in the table are duplicates of these) 3270 for(c = t->cells; c != nil; c = cnext) { 3271 cnext = c->next; 3272 freeitems(c->content); 3273 } 3274 if(t->grid != nil) { 3275 for(i = 0; i < t->nrow; i++) 3276 free(t->grid[i]); 3277 free(t->grid); 3278 } 3279 free(t->rows); 3280 free(t->cols); 3281 freeitems(t->caption); 3282 free(t); 3283 } 3284 3285 static void 3286 freeform(Form* f) 3287 { 3288 if(f == nil) 3289 return; 3290 3291 free(f->name); 3292 free(f->action); 3293 // Form doesn't own its fields (Iformfield items do) 3294 free(f); 3295 } 3296 3297 static void 3298 freeforms(Form* fhead) 3299 { 3300 Form* f; 3301 Form* fnext; 3302 3303 for(f = fhead; f != nil; f = fnext) { 3304 fnext = f->next; 3305 freeform(f); 3306 } 3307 } 3308 3309 static void 3310 freeanchor(Anchor* a) 3311 { 3312 if(a == nil) 3313 return; 3314 3315 free(a->name); 3316 free(a->href); 3317 free(a); 3318 } 3319 3320 static void 3321 freeanchors(Anchor* ahead) 3322 { 3323 Anchor* a; 3324 Anchor* anext; 3325 3326 for(a = ahead; a != nil; a = anext) { 3327 anext = a->next; 3328 freeanchor(a); 3329 } 3330 } 3331 3332 static void 3333 freedestanchor(DestAnchor* da) 3334 { 3335 if(da == nil) 3336 return; 3337 3338 free(da->name); 3339 free(da); 3340 } 3341 3342 static void 3343 freedestanchors(DestAnchor* dahead) 3344 { 3345 DestAnchor* da; 3346 DestAnchor* danext; 3347 3348 for(da = dahead; da != nil; da = danext) { 3349 danext = da->next; 3350 freedestanchor(da); 3351 } 3352 } 3353 3354 static void 3355 freearea(Area* a) 3356 { 3357 if(a == nil) 3358 return; 3359 free(a->href); 3360 free(a->coords); 3361 } 3362 3363 static void freekidinfos(Kidinfo* khead); 3364 3365 static void 3366 freekidinfo(Kidinfo* k) 3367 { 3368 if(k->isframeset) { 3369 free(k->rows); 3370 free(k->cols); 3371 freekidinfos(k->kidinfos); 3372 } 3373 else { 3374 free(k->src); 3375 free(k->name); 3376 } 3377 free(k); 3378 } 3379 3380 static void 3381 freekidinfos(Kidinfo* khead) 3382 { 3383 Kidinfo* k; 3384 Kidinfo* knext; 3385 3386 for(k = khead; k != nil; k = knext) { 3387 knext = k->next; 3388 freekidinfo(k); 3389 } 3390 } 3391 3392 static void 3393 freemap(Map* m) 3394 { 3395 Area* a; 3396 Area* anext; 3397 3398 if(m == nil) 3399 return; 3400 3401 free(m->name); 3402 for(a = m->areas; a != nil; a = anext) { 3403 anext = a->next; 3404 freearea(a); 3405 } 3406 free(m); 3407 } 3408 3409 static void 3410 freemaps(Map* mhead) 3411 { 3412 Map* m; 3413 Map* mnext; 3414 3415 for(m = mhead; m != nil; m = mnext) { 3416 mnext = m->next; 3417 freemap(m); 3418 } 3419 } 3420 3421 void 3422 freedocinfo(Docinfo* d) 3423 { 3424 if(d == nil) 3425 return; 3426 free(d->src); 3427 free(d->base); 3428 freeitem((Item*)d->backgrounditem); 3429 free(d->refresh); 3430 freekidinfos(d->kidinfo); 3431 freeanchors(d->anchors); 3432 freedestanchors(d->dests); 3433 freeforms(d->forms); 3434 freemaps(d->maps); 3435 // tables, images, and formfields are freed when 3436 // the items pointing at them are freed 3437 free(d); 3438 } 3439 3440 // Currently, someone else owns all the memory 3441 // pointed to by things in a Pstate. 3442 static void 3443 freepstate(Pstate* p) 3444 { 3445 free(p); 3446 } 3447 3448 static void 3449 freepstatestack(Pstate* pshead) 3450 { 3451 Pstate* p; 3452 Pstate* pnext; 3453 3454 for(p = pshead; p != nil; p = pnext) { 3455 pnext = p->next; 3456 free(p); 3457 } 3458 } 3459 3460 static int 3461 Iconv(Fmt *f) 3462 { 3463 Item* it; 3464 Itext* t; 3465 Irule* r; 3466 Iimage* i; 3467 Ifloat* fl; 3468 int state; 3469 Formfield* ff; 3470 Rune* ty; 3471 Tablecell* c; 3472 Table* tab; 3473 char* p; 3474 int cl; 3475 int hang; 3476 int indent; 3477 int bi; 3478 int nbuf; 3479 char buf[BIGBUFSIZE]; 3480 3481 it = va_arg(f->args, Item*); 3482 bi = 0; 3483 nbuf = sizeof(buf); 3484 state = it->state; 3485 nbuf = nbuf-1; 3486 if(state&IFbrk) { 3487 cl = state&(IFcleft|IFcright); 3488 p = ""; 3489 if(cl) { 3490 if(cl == (IFcleft|IFcright)) 3491 p = " both"; 3492 else if(cl == IFcleft) 3493 p = " left"; 3494 else 3495 p = " right"; 3496 } 3497 bi = snprint(buf, nbuf, "brk(%d%s)", (state&IFbrksp)? 1 : 0, p); 3498 } 3499 if(state&IFnobrk) 3500 bi += snprint(buf+bi, nbuf-bi, " nobrk"); 3501 if(!(state&IFwrap)) 3502 bi += snprint(buf+bi, nbuf-bi, " nowrap"); 3503 if(state&IFrjust) 3504 bi += snprint(buf+bi, nbuf-bi, " rjust"); 3505 if(state&IFcjust) 3506 bi += snprint(buf+bi, nbuf-bi, " cjust"); 3507 if(state&IFsmap) 3508 bi += snprint(buf+bi, nbuf-bi, " smap"); 3509 indent = (state&IFindentmask) >> IFindentshift; 3510 if(indent > 0) 3511 bi += snprint(buf+bi, nbuf-bi, " indent=%d", indent); 3512 hang = state&IFhangmask; 3513 if(hang > 0) 3514 bi += snprint(buf+bi, nbuf-bi, " hang=%d", hang); 3515 3516 switch(it->tag) { 3517 case Itexttag: 3518 t = (Itext*)it; 3519 bi += snprint(buf+bi, nbuf-bi, " Text '%S', fnt=%d, fg=%x", t->s, t->fnt, t->fg); 3520 break; 3521 3522 case Iruletag: 3523 r = (Irule*)it; 3524 bi += snprint(buf+bi, nbuf-bi, "Rule size=%d, al=%S, wspec=", r->size, stringalign(r->align)); 3525 bi += dimprint(buf+bi, nbuf-bi, r->wspec); 3526 break; 3527 3528 case Iimagetag: 3529 i = (Iimage*)it; 3530 bi += snprint(buf+bi, nbuf-bi, 3531 "Image src=%S, alt=%S, al=%S, w=%d, h=%d hsp=%d, vsp=%d, bd=%d, map=%S", 3532 i->imsrc, i->altrep? i->altrep : L"", stringalign(i->align), i->imwidth, i->imheight, 3533 i->hspace, i->vspace, i->border, i->map? i->map->name : L""); 3534 break; 3535 3536 case Iformfieldtag: 3537 ff = ((Iformfield*)it)->formfield; 3538 if(ff->ftype == Ftextarea) 3539 ty = L"textarea"; 3540 else if(ff->ftype == Fselect) 3541 ty = L"select"; 3542 else { 3543 ty = _revlookup(input_tab, NINPUTTAB, ff->ftype); 3544 if(ty == nil) 3545 ty = L"none"; 3546 } 3547 bi += snprint(buf+bi, nbuf-bi, "Formfield %S, fieldid=%d, formid=%d, name=%S, value=%S", 3548 ty, ff->fieldid, ff->form->formid, ff->name? ff->name : L"", 3549 ff->value? ff->value : L""); 3550 break; 3551 3552 case Itabletag: 3553 tab = ((Itable*)it)->table; 3554 bi += snprint(buf+bi, nbuf-bi, "Table tableid=%d, width=", tab->tableid); 3555 bi += dimprint(buf+bi, nbuf-bi, tab->width); 3556 bi += snprint(buf+bi, nbuf-bi, ", nrow=%d, ncol=%d, ncell=%d, totw=%d, toth=%d\n", 3557 tab->nrow, tab->ncol, tab->ncell, tab->totw, tab->toth); 3558 for(c = tab->cells; c != nil; c = c->next) 3559 bi += snprint(buf+bi, nbuf-bi, "Cell %d.%d, at (%d,%d) ", 3560 tab->tableid, c->cellid, c->row, c->col); 3561 bi += snprint(buf+bi, nbuf-bi, "End of Table %d", tab->tableid); 3562 break; 3563 3564 case Ifloattag: 3565 fl = (Ifloat*)it; 3566 bi += snprint(buf+bi, nbuf-bi, "Float, x=%d y=%d, side=%S, it=%I", 3567 fl->x, fl->y, stringalign(fl->side), fl->item); 3568 bi += snprint(buf+bi, nbuf-bi, "\n\t"); 3569 break; 3570 3571 case Ispacertag: 3572 p = ""; 3573 switch(((Ispacer*)it)->spkind) { 3574 case ISPnull: 3575 p = "null"; 3576 break; 3577 case ISPvline: 3578 p = "vline"; 3579 break; 3580 case ISPhspace: 3581 p = "hspace"; 3582 break; 3583 } 3584 bi += snprint(buf+bi, nbuf-bi, "Spacer %s ", p); 3585 break; 3586 } 3587 bi += snprint(buf+bi, nbuf-bi, " w=%d, h=%d, a=%d, anchor=%d\n", 3588 it->width, it->height, it->ascent, it->anchorid); 3589 buf[bi] = 0; 3590 return fmtstrcpy(f, buf); 3591 } 3592 3593 // String version of alignment 'a' 3594 static Rune* 3595 stringalign(int a) 3596 { 3597 Rune* s; 3598 3599 s = _revlookup(align_tab, NALIGNTAB, a); 3600 if(s == nil) 3601 s = L"none"; 3602 return s; 3603 } 3604 3605 // Put at most nbuf chars of representation of d into buf, 3606 // and return number of characters put 3607 static int 3608 dimprint(char* buf, int nbuf, Dimen d) 3609 { 3610 int n; 3611 int k; 3612 3613 n = 0; 3614 n += snprint(buf, nbuf, "%d", dimenspec(d)); 3615 k = dimenkind(d); 3616 if(k == Dpercent) 3617 buf[n++] = '%'; 3618 if(k == Drelative) 3619 buf[n++] = '*'; 3620 return n; 3621 } 3622 3623 void 3624 printitems(Item* items, char* msg) 3625 { 3626 Item* il; 3627 3628 fprint(2, "%s\n", msg); 3629 il = items; 3630 while(il != nil) { 3631 fprint(2, "%I", il); 3632 il = il->next; 3633 } 3634 } 3635 3636 static Genattr* 3637 newgenattr(Rune* id, Rune* class, Rune* style, Rune* title, SEvent* events) 3638 { 3639 Genattr* g; 3640 3641 g = (Genattr*)emalloc(sizeof(Genattr)); 3642 g->id = id; 3643 g->class = class; 3644 g->style = style; 3645 g->title = title; 3646 g->events = events; 3647 return g; 3648 } 3649 3650 static Formfield* 3651 newformfield(int ftype, int fieldid, Form* form, Rune* name, 3652 Rune* value, int size, int maxlength, Formfield* link) 3653 { 3654 Formfield* ff; 3655 3656 ff = (Formfield*)emalloc(sizeof(Formfield)); 3657 ff->ftype = ftype; 3658 ff->fieldid = fieldid; 3659 ff->form = form; 3660 ff->name = name; 3661 ff->value = value; 3662 ff->size = size; 3663 ff->maxlength = maxlength; 3664 ff->ctlid = -1; 3665 ff->next = link; 3666 return ff; 3667 } 3668 3669 // Transfers ownership of value and display to Option. 3670 static Option* 3671 newoption(int selected, Rune* value, Rune* display, Option* link) 3672 { 3673 Option *o; 3674 3675 o = (Option*)emalloc(sizeof(Option)); 3676 o->selected = selected; 3677 o->value = value; 3678 o->display = display; 3679 o->next = link; 3680 return o; 3681 } 3682 3683 static Form* 3684 newform(int formid, Rune* name, Rune* action, int target, int method, Form* link) 3685 { 3686 Form* f; 3687 3688 f = (Form*)emalloc(sizeof(Form)); 3689 f->formid = formid; 3690 f->name = name; 3691 f->action = action; 3692 f->target = target; 3693 f->method = method; 3694 f->nfields = 0; 3695 f->fields = nil; 3696 f->next = link; 3697 return f; 3698 } 3699 3700 static Table* 3701 newtable(int tableid, Align align, Dimen width, int border, 3702 int cellspacing, int cellpadding, Background bg, Token* tok, Table* link) 3703 { 3704 Table* t; 3705 3706 t = (Table*)emalloc(sizeof(Table)); 3707 t->tableid = tableid; 3708 t->align = align; 3709 t->width = width; 3710 t->border = border; 3711 t->cellspacing = cellspacing; 3712 t->cellpadding = cellpadding; 3713 t->background = bg; 3714 t->caption_place = ALbottom; 3715 t->caption_lay = nil; 3716 t->tabletok = tok; 3717 t->tabletok = nil; 3718 t->next = link; 3719 return t; 3720 } 3721 3722 static Tablerow* 3723 newtablerow(Align align, Background bg, int flags, Tablerow* link) 3724 { 3725 Tablerow* tr; 3726 3727 tr = (Tablerow*)emalloc(sizeof(Tablerow)); 3728 tr->align = align; 3729 tr->background = bg; 3730 tr->flags = flags; 3731 tr->next = link; 3732 return tr; 3733 } 3734 3735 static Tablecell* 3736 newtablecell(int cellid, int rowspan, int colspan, Align align, Dimen wspec, int hspec, 3737 Background bg, int flags, Tablecell* link) 3738 { 3739 Tablecell* c; 3740 3741 c = (Tablecell*)emalloc(sizeof(Tablecell)); 3742 c->cellid = cellid; 3743 c->lay = nil; 3744 c->rowspan = rowspan; 3745 c->colspan = colspan; 3746 c->align = align; 3747 c->flags = flags; 3748 c->wspec = wspec; 3749 c->hspec = hspec; 3750 c->background = bg; 3751 c->next = link; 3752 return c; 3753 } 3754 3755 static Anchor* 3756 newanchor(int index, Rune* name, Rune* href, int target, Anchor* link) 3757 { 3758 Anchor* a; 3759 3760 a = (Anchor*)emalloc(sizeof(Anchor)); 3761 a->index = index; 3762 a->name = name; 3763 a->href = href; 3764 a->target = target; 3765 a->next = link; 3766 return a; 3767 } 3768 3769 static DestAnchor* 3770 newdestanchor(int index, Rune* name, Item* item, DestAnchor* link) 3771 { 3772 DestAnchor* d; 3773 3774 d = (DestAnchor*)emalloc(sizeof(DestAnchor)); 3775 d->index = index; 3776 d->name = name; 3777 d->item = item; 3778 d->next = link; 3779 return d; 3780 } 3781 3782 static SEvent* 3783 newscriptevent(int type, Rune* script, SEvent* link) 3784 { 3785 SEvent* ans; 3786 3787 ans = (SEvent*)emalloc(sizeof(SEvent)); 3788 ans->type = type; 3789 ans->script = script; 3790 ans->next = link; 3791 return ans; 3792 } 3793 3794 static void 3795 freescriptevents(SEvent* ehead) 3796 { 3797 SEvent* e; 3798 SEvent* nexte; 3799 3800 e = ehead; 3801 while(e != nil) { 3802 nexte = e->next; 3803 free(e->script); 3804 free(e); 3805 e = nexte; 3806 } 3807 } 3808 3809 static Dimen 3810 makedimen(int kind, int spec) 3811 { 3812 Dimen d; 3813 3814 if(spec&Dkindmask) { 3815 if(warn) 3816 fprint(2, "warning: dimension spec too big: %d\n", spec); 3817 spec = 0; 3818 } 3819 d.kindspec = kind|spec; 3820 return d; 3821 } 3822 3823 int 3824 dimenkind(Dimen d) 3825 { 3826 return (d.kindspec&Dkindmask); 3827 } 3828 3829 int 3830 dimenspec(Dimen d) 3831 { 3832 return (d.kindspec&Dspecmask); 3833 } 3834 3835 static Kidinfo* 3836 newkidinfo(int isframeset, Kidinfo* link) 3837 { 3838 Kidinfo* ki; 3839 3840 ki = (Kidinfo*)emalloc(sizeof(Kidinfo)); 3841 ki->isframeset = isframeset; 3842 if(!isframeset) { 3843 ki->flags = FRhscrollauto|FRvscrollauto; 3844 ki->marginw = FRKIDMARGIN; 3845 ki->marginh = FRKIDMARGIN; 3846 ki->framebd = 1; 3847 } 3848 ki->next = link; 3849 return ki; 3850 } 3851 3852 static Docinfo* 3853 newdocinfo(void) 3854 { 3855 Docinfo* d; 3856 3857 d = (Docinfo*)emalloc(sizeof(Docinfo)); 3858 resetdocinfo(d); 3859 return d; 3860 } 3861 3862 static void 3863 resetdocinfo(Docinfo* d) 3864 { 3865 memset(d, 0, sizeof(Docinfo)); 3866 d->background = makebackground(nil, White); 3867 d->text = Black; 3868 d->link = Blue; 3869 d->vlink = Blue; 3870 d->alink = Blue; 3871 d->target = FTself; 3872 d->chset = ISO_8859_1; 3873 d->scripttype = TextJavascript; 3874 d->frameid = -1; 3875 } 3876 3877 // Use targetmap array to keep track of name <-> targetid mapping. 3878 // Use real malloc(), and never free 3879 static void 3880 targetmapinit(void) 3881 { 3882 int l; 3883 3884 targetmapsize = 10; 3885 l = targetmapsize*sizeof *targetmap; 3886 targetmap = emalloc(l); 3887 memset(targetmap, 0, l); 3888 targetmap[0].key = _Strdup(L"_top"); 3889 targetmap[0].val = FTtop; 3890 targetmap[1].key = _Strdup(L"_self"); 3891 targetmap[1].val = FTself; 3892 targetmap[2].key = _Strdup(L"_parent"); 3893 targetmap[2].val = FTparent; 3894 targetmap[3].key = _Strdup(L"_blank"); 3895 targetmap[3].val = FTblank; 3896 ntargets = 4; 3897 } 3898 3899 int 3900 targetid(Rune* s) 3901 { 3902 int i; 3903 int n; 3904 3905 n = _Strlen(s); 3906 if(n == 0) 3907 return FTself; 3908 for(i = 0; i < ntargets; i++) 3909 if(_Strcmp(s, targetmap[i].key) == 0) 3910 return targetmap[i].val; 3911 if(i == targetmapsize) { 3912 targetmapsize += 10; 3913 targetmap = erealloc(targetmap, targetmapsize*sizeof(StringInt)); 3914 } 3915 targetmap[i].key = _Strdup(s); 3916 targetmap[i].val = i; 3917 ntargets++; 3918 return i; 3919 } 3920 3921 Rune* 3922 targetname(int targid) 3923 { 3924 int i; 3925 3926 for(i = 0; i < ntargets; i++) 3927 if(targetmap[i].val == targid) 3928 return targetmap[i].key; 3929 return L"?"; 3930 } 3931 3932 // Convert HTML color spec to RGB value, returning dflt if can't. 3933 // Argument is supposed to be a valid HTML color, or "". 3934 // Return the RGB value of the color, using dflt if s 3935 // is nil or an invalid color. 3936 static int 3937 color(Rune* s, int dflt) 3938 { 3939 int v; 3940 Rune* rest; 3941 3942 if(s == nil) 3943 return dflt; 3944 if(_lookup(color_tab, NCOLORS, s, _Strlen(s), &v)) 3945 return v; 3946 if(s[0] == '#') 3947 s++; 3948 v = _Strtol(s, &rest, 16); 3949 if(*rest == 0) 3950 return v; 3951 return dflt; 3952 } 3953 3954 // Debugging 3955 3956 #define HUGEPIX 10000 3957 3958 // A "shallow" validitem, that doesn't follow next links 3959 // or descend into tables. 3960 static int 3961 validitem(Item* i) 3962 { 3963 int ok; 3964 Itext* ti; 3965 Irule* ri; 3966 Iimage* ii; 3967 Ifloat* fi; 3968 int a; 3969 3970 ok = (i->tag >= Itexttag && i->tag <= Ispacertag) && 3971 (i->next == nil || validptr(i->next)) && 3972 (i->width >= 0 && i->width < HUGEPIX) && 3973 (i->height >= 0 && i->height < HUGEPIX) && 3974 (i->ascent > -HUGEPIX && i->ascent < HUGEPIX) && 3975 (i->anchorid >= 0) && 3976 (i->genattr == nil || validptr(i->genattr)); 3977 // also, could check state for ridiculous combinations 3978 // also, could check anchorid for within-doc-range 3979 if(ok) 3980 switch(i->tag) { 3981 case Itexttag: 3982 ti = (Itext*)i; 3983 ok = validStr(ti->s) && 3984 (ti->fnt >= 0 && ti->fnt < NumStyle*NumSize) && 3985 (ti->ul == ULnone || ti->ul == ULunder || ti->ul == ULmid); 3986 break; 3987 case Iruletag: 3988 ri = (Irule*)i; 3989 ok = (validvalign(ri->align) || validhalign(ri->align)) && 3990 (ri->size >=0 && ri->size < HUGEPIX); 3991 break; 3992 case Iimagetag: 3993 ii = (Iimage*)i; 3994 ok = (ii->imsrc == nil || validptr(ii->imsrc)) && 3995 (ii->width >= 0 && ii->width < HUGEPIX) && 3996 (ii->height >= 0 && ii->height < HUGEPIX) && 3997 (ii->imwidth >= 0 && ii->imwidth < HUGEPIX) && 3998 (ii->imheight >= 0 && ii->imheight < HUGEPIX) && 3999 (ii->altrep == nil || validStr(ii->altrep)) && 4000 (ii->map == nil || validptr(ii->map)) && 4001 (validvalign(ii->align) || validhalign(ii->align)) && 4002 (ii->nextimage == nil || validptr(ii->nextimage)); 4003 break; 4004 case Iformfieldtag: 4005 ok = validformfield(((Iformfield*)i)->formfield); 4006 break; 4007 case Itabletag: 4008 ok = validptr((Itable*)i); 4009 break; 4010 case Ifloattag: 4011 fi = (Ifloat*)i; 4012 ok = (fi->side == ALleft || fi->side == ALright) && 4013 validitem(fi->item) && 4014 (fi->item->tag == Iimagetag || fi->item->tag == Itabletag); 4015 break; 4016 case Ispacertag: 4017 a = ((Ispacer*)i)->spkind; 4018 ok = a==ISPnull || a==ISPvline || a==ISPhspace || a==ISPgeneral; 4019 break; 4020 default: 4021 ok = 0; 4022 } 4023 return ok; 4024 } 4025 4026 // "deep" validation, that checks whole list of items, 4027 // and descends into tables and floated tables. 4028 // nil is ok for argument. 4029 int 4030 validitems(Item* i) 4031 { 4032 int ok; 4033 Item* ii; 4034 4035 ok = 1; 4036 while(i != nil && ok) { 4037 ok = validitem(i); 4038 if(ok) { 4039 if(i->tag == Itabletag) { 4040 ok = validtable(((Itable*)i)->table); 4041 } 4042 else if(i->tag == Ifloattag) { 4043 ii = ((Ifloat*)i)->item; 4044 if(ii->tag == Itabletag) 4045 ok = validtable(((Itable*)ii)->table); 4046 } 4047 } 4048 if(!ok) { 4049 fprint(2, "invalid item: %I\n", i); 4050 } 4051 i = i->next; 4052 } 4053 return ok; 4054 } 4055 4056 static int 4057 validformfield(Formfield* f) 4058 { 4059 int ok; 4060 4061 ok = (f->next == nil || validptr(f->next)) && 4062 (f->ftype >= 0 && f->ftype <= Ftextarea) && 4063 f->fieldid >= 0 && 4064 (f->form == nil || validptr(f->form)) && 4065 (f->name == nil || validStr(f->name)) && 4066 (f->value == nil || validStr(f->value)) && 4067 (f->options == nil || validptr(f->options)) && 4068 (f->image == nil || validitem(f->image)) && 4069 (f->events == nil || validptr(f->events)); 4070 // when all built, should have f->fieldid < f->form->nfields, 4071 // but this may be called during build... 4072 return ok; 4073 } 4074 4075 // "deep" validation -- checks cell contents too 4076 static int 4077 validtable(Table* t) 4078 { 4079 int ok; 4080 int i, j; 4081 Tablecell* c; 4082 4083 ok = (t->next == nil || validptr(t->next)) && 4084 t->nrow >= 0 && 4085 t->ncol >= 0 && 4086 t->ncell >= 0 && 4087 validalign(t->align) && 4088 validdimen(t->width) && 4089 (t->border >= 0 && t->border < HUGEPIX) && 4090 (t->cellspacing >= 0 && t->cellspacing < HUGEPIX) && 4091 (t->cellpadding >= 0 && t->cellpadding < HUGEPIX) && 4092 validitems(t->caption) && 4093 (t->caption_place == ALtop || t->caption_place == ALbottom) && 4094 (t->totw >= 0 && t->totw < HUGEPIX) && 4095 (t->toth >= 0 && t->toth < HUGEPIX) && 4096 (t->tabletok == nil || validptr(t->tabletok)); 4097 // during parsing, t->rows has list; 4098 // only when parsing is done is t->nrow set > 0 4099 if(ok && t->nrow > 0 && t->ncol > 0) { 4100 // table is "finished" 4101 for(i = 0; i < t->nrow && ok; i++) 4102 ok = validtablerow(t->rows+i); 4103 for(j = 0; j < t->ncol && ok; j++) 4104 ok = validtablecol(t->cols+j); 4105 for(c = t->cells; c != nil && ok; c = c->next) 4106 ok = validtablecell(c); 4107 for(i = 0; i < t->nrow && ok; i++) 4108 for(j = 0; j < t->ncol && ok; j++) 4109 ok = validptr(t->grid[i][j]); 4110 } 4111 return ok; 4112 } 4113 4114 static int 4115 validvalign(int a) 4116 { 4117 return a == ALnone || a == ALmiddle || a == ALbottom || a == ALtop || a == ALbaseline; 4118 } 4119 4120 static int 4121 validhalign(int a) 4122 { 4123 return a == ALnone || a == ALleft || a == ALcenter || a == ALright || 4124 a == ALjustify || a == ALchar; 4125 } 4126 4127 static int 4128 validalign(Align a) 4129 { 4130 return validhalign(a.halign) && validvalign(a.valign); 4131 } 4132 4133 static int 4134 validdimen(Dimen d) 4135 { 4136 int ok; 4137 int s; 4138 4139 ok = 0; 4140 s = d.kindspec&Dspecmask; 4141 switch(d.kindspec&Dkindmask) { 4142 case Dnone: 4143 ok = s==0; 4144 break; 4145 case Dpixels: 4146 ok = s < HUGEPIX; 4147 break; 4148 case Dpercent: 4149 case Drelative: 4150 ok = 1; 4151 break; 4152 } 4153 return ok; 4154 } 4155 4156 static int 4157 validtablerow(Tablerow* r) 4158 { 4159 return (r->cells == nil || validptr(r->cells)) && 4160 (r->height >= 0 && r->height < HUGEPIX) && 4161 (r->ascent > -HUGEPIX && r->ascent < HUGEPIX) && 4162 validalign(r->align); 4163 } 4164 4165 static int 4166 validtablecol(Tablecol* c) 4167 { 4168 return c->width >= 0 && c->width < HUGEPIX 4169 && validalign(c->align); 4170 } 4171 4172 static int 4173 validtablecell(Tablecell* c) 4174 { 4175 int ok; 4176 4177 ok = (c->next == nil || validptr(c->next)) && 4178 (c->nextinrow == nil || validptr(c->nextinrow)) && 4179 (c->content == nil || validptr(c->content)) && 4180 (c->lay == nil || validptr(c->lay)) && 4181 c->rowspan >= 0 && 4182 c->colspan >= 0 && 4183 validalign(c->align) && 4184 validdimen(c->wspec) && 4185 c->row >= 0 && 4186 c->col >= 0; 4187 if(ok) { 4188 if(c->content != nil) 4189 ok = validitems(c->content); 4190 } 4191 return ok; 4192 } 4193 4194 static int 4195 validptr(void* p) 4196 { 4197 // TODO: a better job of this. 4198 // For now, just dereference, which cause a bomb 4199 // if not valid 4200 static char c; 4201 4202 c = *((char*)p); 4203 return 1; 4204 } 4205 4206 static int 4207 validStr(Rune* s) 4208 { 4209 return s != nil && validptr(s); 4210 } 4211