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