html.c (96124B)
1 /* html.c 2 * (c) 2002 Mikulas Patocka 3 * This file is a part of the Links program, released under GPL. 4 */ 5 6 #include <limits.h> 7 #include <string.h> 8 9 #include "links.h" 10 11 struct list_head html_stack = { &html_stack, &html_stack }; 12 13 int html_format_changed = 0; 14 15 static inline int 16 isA(unsigned char c) 17 { 18 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); 19 } 20 21 static inline int 22 atchr(unsigned char c) 23 { 24 return /*isA(c) ||*/ (c > ' ' && c != '=' && c != '<' && c != '>'); 25 } 26 27 /* accepts one html element */ 28 /* e is pointer to the begining of the element (*e must be '<') */ 29 /* eof is pointer to the end of scanned area */ 30 /* parsed element name is stored in name, it's length is namelen */ 31 /* first attribute is stored in attr */ 32 /* end points to first character behind the html element */ 33 /* returns: -1 fail (returned values in pointers are invalid) */ 34 /* 0 success */ 35 int 36 parse_element(unsigned char *e, unsigned char *eof, unsigned char **name, 37 int *namelen, unsigned char **attr, unsigned char **end) 38 { 39 if (eof - e < 3 || *(e++) != '<') 40 return -1; 41 if (name) 42 *name = e; 43 if (*e == '/') { 44 e++; 45 if (*e == '>' || *e == '<') 46 goto xx; 47 } else if (!isA(*e)) { 48 return -1; 49 } 50 while (isA(*e) || (*e >= '0' && *e <= '9') || *e == '_' || *e == '-' 51 || *e == '=') { 52 e++; 53 if (e >= eof) 54 return -1; 55 } 56 xx: 57 if (name && namelen) 58 *namelen = (int)(e - *name); 59 while ((WHITECHAR(*e) || *e == '/' || *e == ':')) { 60 e++; 61 if (e >= eof) 62 return -1; 63 } 64 if ((!atchr(*e) && *e != '>' && *e != '<')) 65 return -1; 66 if (attr) 67 *attr = e; 68 nextattr: 69 while (WHITECHAR(*e)) { 70 e++; 71 if (e >= eof) 72 return -1; 73 } 74 if ((!atchr(*e) && *e != '>' && *e != '<')) 75 return -1; 76 if (*e == '>' || *e == '<') 77 goto en; 78 while (atchr(*e)) { 79 e++; 80 if (e >= eof) 81 return -1; 82 } 83 while (WHITECHAR(*e)) { 84 e++; 85 if (e >= eof) 86 return -1; 87 } 88 if (*e != '=') 89 goto endattr; 90 if (1) 91 goto x2; 92 while (WHITECHAR(*e)) { 93 x2: 94 e++; 95 if (e >= eof) 96 return -1; 97 } 98 if (U(*e)) { 99 unsigned char uu = *e; 100 /*u:*/ 101 if (1) 102 goto x3; 103 while (e < eof && *e != uu 104 && *e /*(WHITECHAR(*e) || *e > ' ')*/) { 105 x3: 106 e++; 107 if (e >= eof) 108 return -1; 109 } 110 if (*e < ' ') 111 return -1; 112 e++; 113 if (e >= eof /*|| (!WHITECHAR(*e) && *e != uu && *e != '>' && *e != '<')*/) 114 return -1; 115 /*if (*e == uu) goto u;*/ 116 } else { 117 while (!WHITECHAR(*e) && *e != '>' && *e != '<') { 118 e++; 119 if (e >= eof) 120 return -1; 121 } 122 } 123 while (WHITECHAR(*e)) { 124 e++; 125 if (e >= eof) 126 return -1; 127 } 128 endattr: 129 if (*e != '>' && *e != '<') 130 goto nextattr; 131 en: 132 if (e[-1] == '\\') 133 return -1; 134 if (end) 135 *end = e + (*e == '>'); 136 return 0; 137 } 138 139 #define add_chr(s, l, c) \ 140 do { \ 141 if (!((l) & (32 - 1))) { \ 142 if ((unsigned)(l) > INT_MAX - 32) \ 143 overalloc(); \ 144 (s) = xrealloc((s), (l) + 32); \ 145 } \ 146 (s)[(l)++] = (c); \ 147 } while (0) 148 149 int get_attr_val_nl = 0; 150 151 /* parses html element attributes */ 152 /* e is attr pointer previously got from parse_element, DON'T PASS HERE ANY 153 * OTHER VALUE!!! */ 154 /* name is searched attribute */ 155 /* returns allocated string containing the attribute, or NULL on unsuccess */ 156 unsigned char * 157 get_attr_val(unsigned char *e, unsigned char *name) 158 { 159 unsigned char *n; 160 unsigned char *a = NULL; 161 int l = 0; 162 int f; 163 aa: 164 while (WHITECHAR(*e)) 165 e++; 166 if (*e == '>' || *e == '<') 167 return NULL; 168 n = name; 169 while (*n && upcase(*e) == upcase(*n)) { 170 e++; 171 n++; 172 } 173 f = *n; 174 while (atchr(*e)) { 175 f = 1; 176 e++; 177 } 178 while (WHITECHAR(*e)) 179 e++; 180 if (*e != '=') 181 goto ea; 182 e++; 183 while (WHITECHAR(*e)) 184 e++; 185 if (!U(*e)) { 186 while (!WHITECHAR(*e) && *e != '>' && *e != '<') { 187 if (!f) 188 add_chr(a, l, *e); 189 e++; 190 } 191 } else { 192 unsigned char uu = *e; 193 e++; 194 while (*e != uu) { 195 if (!*e) { 196 free(a); 197 return NULL; 198 } 199 if (!f) { 200 if (get_attr_val_nl == 2) 201 goto exact; 202 if (*e != 13) { 203 if (*e != 9 && *e != 10) 204 exact: 205 add_chr(a, l, *e); 206 else if (!get_attr_val_nl) 207 add_chr(a, l, ' '); 208 } 209 } 210 e++; 211 } 212 e++; 213 } 214 ea: 215 if (!f) { 216 unsigned char *b; 217 add_chr(a, l, 0); 218 if (strchr((char *)a, '&')) { 219 unsigned char *aa = a; 220 int c = d_opt->cp; 221 d_opt->cp = d_opt->real_cp; 222 a = convert_string(NULL, aa, strlen((char *)aa), d_opt); 223 d_opt->cp = c; 224 free(aa); 225 } 226 while ((b = (unsigned char *)strchr((char *)a, 1))) 227 *b = ' '; 228 if (get_attr_val_nl != 2) { 229 for (b = a; *b == ' '; b++) 230 ; 231 if (b != a) 232 memmove(a, b, strlen((char *)b) + 1); 233 for (b = a + strlen((char *)a) - 1; b >= a && *b == ' '; 234 b--) 235 *b = 0; 236 } 237 return a; 238 } 239 goto aa; 240 } 241 242 int 243 has_attr(unsigned char *e, unsigned char *name) 244 { 245 unsigned char *a; 246 if (!(a = get_attr_val(e, name))) 247 return 0; 248 free(a); 249 return 1; 250 } 251 252 static unsigned char * 253 get_url_val(unsigned char *e, unsigned char *name) 254 { 255 unsigned char *a, *p, *c; 256 int l; 257 get_attr_val_nl = 1; 258 a = get_attr_val(e, name); 259 get_attr_val_nl = 0; 260 if (!a) 261 return NULL; 262 if (d_opt->real_cp) { 263 if (url_non_ascii(a)) 264 goto need_convert; 265 } 266 return a; 267 268 need_convert: 269 c = NULL; 270 l = 0; 271 for (p = a; *p; p++) 272 l = add_to_str(&c, l, encode_utf_8(*p)); 273 free(a); 274 return c; 275 } 276 277 static unsigned char * 278 get_exact_attr_val(unsigned char *e, unsigned char *name) 279 { 280 unsigned char *a; 281 get_attr_val_nl = 2; 282 a = get_attr_val(e, name); 283 get_attr_val_nl = 0; 284 if (a) { 285 unsigned char *x1, *x2; 286 for (x1 = x2 = a; *x1; x1++, x2++) 287 if (x1[0] == '\r') { 288 *x2 = '\n'; 289 if (x1[1] == '\n') 290 x1++; 291 } else 292 *x2 = *x1; 293 *x2 = 0; 294 } 295 return a; 296 } 297 298 static struct { 299 const unsigned short int n; 300 const char *s; 301 } roman_tbl[] = { 302 {1000, "m" }, 303 { 999, "im"}, 304 { 990, "xm"}, 305 { 900, "cm"}, 306 { 500, "d" }, 307 { 499, "id"}, 308 { 490, "xd"}, 309 { 400, "cd"}, 310 { 100, "c" }, 311 { 99, "ic"}, 312 { 90, "xc"}, 313 { 50, "l" }, 314 { 49, "il"}, 315 { 40, "xl"}, 316 { 10, "x" }, 317 { 9, "ix"}, 318 { 5, "v" }, 319 { 4, "iv"}, 320 { 1, "i" }, 321 { 0, NULL} 322 }; 323 324 static void 325 roman(char *p, unsigned int n, const size_t psz) 326 { 327 int i = 0; 328 if (!n) { 329 if (strlcpy(p, "o", psz) >= psz) 330 die("strlcpy(): dstsize too small\n"); 331 return; 332 } else if (n >= 4000) { 333 if (strlcpy(p, "---", psz) >= psz) 334 die("strlcpy(): dstsize too small\n"); 335 return; 336 } 337 p[0] = 0; 338 while (n) { 339 while (roman_tbl[i].n <= n) { 340 n -= roman_tbl[i].n; 341 if (strlcat(p, roman_tbl[i].s, psz) >= psz) 342 die("strlcat(): dstsize too small\n"); 343 } 344 i++; 345 if (n && !roman_tbl[i].n) { 346 internal("BUG in roman number convertor"); 347 return; 348 } 349 } 350 } 351 352 struct color_spec { 353 const char *name; 354 const int rgb; 355 }; 356 357 static const struct color_spec color_specs[] = { 358 {"aliceblue", 0xF0F8FF}, 359 { "antiquewhite", 0xFAEBD7}, 360 { "aqua", 0x00FFFF}, 361 { "aquamarine", 0x7FFFD4}, 362 { "azure", 0xF0FFFF}, 363 { "beige", 0xF5F5DC}, 364 { "bisque", 0xFFE4C4}, 365 { "black", 0x000000}, 366 { "blanchedalmond", 0xFFEBCD}, 367 { "blue", 0x0000FF}, 368 { "blueviolet", 0x8A2BE2}, 369 { "brown", 0xA52A2A}, 370 { "burlywood", 0xDEB887}, 371 { "cadetblue", 0x5F9EA0}, 372 { "chartreuse", 0x7FFF00}, 373 { "chocolate", 0xD2691E}, 374 { "coral", 0xFF7F50}, 375 { "cornflowerblue", 0x6495ED}, 376 { "cornsilk", 0xFFF8DC}, 377 { "crimson", 0xDC143C}, 378 { "cyan", 0x00FFFF}, 379 { "darkblue", 0x00008B}, 380 { "darkcyan", 0x008B8B}, 381 { "darkgoldenrod", 0xB8860B}, 382 { "darkgray", 0xA9A9A9}, 383 { "darkgreen", 0x006400}, 384 { "darkkhaki", 0xBDB76B}, 385 { "darkmagenta", 0x8B008B}, 386 { "darkolivegreen", 0x556B2F}, 387 { "darkorange", 0xFF8C00}, 388 { "darkorchid", 0x9932CC}, 389 { "darkred", 0x8B0000}, 390 { "darksalmon", 0xE9967A}, 391 { "darkseagreen", 0x8FBC8F}, 392 { "darkslateblue", 0x483D8B}, 393 { "darkslategray", 0x2F4F4F}, 394 { "darkturquoise", 0x00CED1}, 395 { "darkviolet", 0x9400D3}, 396 { "deeppink", 0xFF1493}, 397 { "deepskyblue", 0x00BFFF}, 398 { "dimgray", 0x696969}, 399 { "dodgerblue", 0x1E90FF}, 400 { "firebrick", 0xB22222}, 401 { "floralwhite", 0xFFFAF0}, 402 { "forestgreen", 0x228B22}, 403 { "fuchsia", 0xFF00FF}, 404 { "gainsboro", 0xDCDCDC}, 405 { "ghostwhite", 0xF8F8FF}, 406 { "gold", 0xFFD700}, 407 { "goldenrod", 0xDAA520}, 408 { "gray", 0x808080}, 409 { "green", 0x008000}, 410 { "greenyellow", 0xADFF2F}, 411 { "honeydew", 0xF0FFF0}, 412 { "hotpink", 0xFF69B4}, 413 { "indianred", 0xCD5C5C}, 414 { "indigo", 0x4B0082}, 415 { "ivory", 0xFFFFF0}, 416 { "khaki", 0xF0E68C}, 417 { "lavender", 0xE6E6FA}, 418 { "lavenderblush", 0xFFF0F5}, 419 { "lawngreen", 0x7CFC00}, 420 { "lemonchiffon", 0xFFFACD}, 421 { "lightblue", 0xADD8E6}, 422 { "lightcoral", 0xF08080}, 423 { "lightcyan", 0xE0FFFF}, 424 { "lightgoldenrodyellow", 0xFAFAD2}, 425 { "lightgreen", 0x90EE90}, 426 { "lightgrey", 0xD3D3D3}, 427 { "lightpink", 0xFFB6C1}, 428 { "lightsalmon", 0xFFA07A}, 429 { "lightseagreen", 0x20B2AA}, 430 { "lightskyblue", 0x87CEFA}, 431 { "lightslategray", 0x778899}, 432 { "lightsteelblue", 0xB0C4DE}, 433 { "lightyellow", 0xFFFFE0}, 434 { "lime", 0x00FF00}, 435 { "limegreen", 0x32CD32}, 436 { "linen", 0xFAF0E6}, 437 { "magenta", 0xFF00FF}, 438 { "maroon", 0x800000}, 439 { "mediumaquamarine", 0x66CDAA}, 440 { "mediumblue", 0x0000CD}, 441 { "mediumorchid", 0xBA55D3}, 442 { "mediumpurple", 0x9370DB}, 443 { "mediumseagreen", 0x3CB371}, 444 { "mediumslateblue", 0x7B68EE}, 445 { "mediumspringgreen", 0x00FA9A}, 446 { "mediumturquoise", 0x48D1CC}, 447 { "mediumvioletred", 0xC71585}, 448 { "midnightblue", 0x191970}, 449 { "mintcream", 0xF5FFFA}, 450 { "mistyrose", 0xFFE4E1}, 451 { "moccasin", 0xFFE4B5}, 452 { "navajowhite", 0xFFDEAD}, 453 { "navy", 0x000080}, 454 { "oldlace", 0xFDF5E6}, 455 { "olive", 0x808000}, 456 { "olivedrab", 0x6B8E23}, 457 { "orange", 0xFFA500}, 458 { "orangered", 0xFF4500}, 459 { "orchid", 0xDA70D6}, 460 { "palegoldenrod", 0xEEE8AA}, 461 { "palegreen", 0x98FB98}, 462 { "paleturquoise", 0xAFEEEE}, 463 { "palevioletred", 0xDB7093}, 464 { "papayawhip", 0xFFEFD5}, 465 { "peachpuff", 0xFFDAB9}, 466 { "peru", 0xCD853F}, 467 { "pink", 0xFFC0CB}, 468 { "plum", 0xDDA0DD}, 469 { "powderblue", 0xB0E0E6}, 470 { "purple", 0x800080}, 471 { "red", 0xFF0000}, 472 { "rosybrown", 0xBC8F8F}, 473 { "royalblue", 0x4169E1}, 474 { "saddlebrown", 0x8B4513}, 475 { "salmon", 0xFA8072}, 476 { "sandybrown", 0xF4A460}, 477 { "seagreen", 0x2E8B57}, 478 { "seashell", 0xFFF5EE}, 479 { "sienna", 0xA0522D}, 480 { "silver", 0xC0C0C0}, 481 { "skyblue", 0x87CEEB}, 482 { "slateblue", 0x6A5ACD}, 483 { "slategray", 0x708090}, 484 { "snow", 0xFFFAFA}, 485 { "springgreen", 0x00FF7F}, 486 { "steelblue", 0x4682B4}, 487 { "tan", 0xD2B48C}, 488 { "teal", 0x008080}, 489 { "thistle", 0xD8BFD8}, 490 { "tomato", 0xFF6347}, 491 { "turquoise", 0x40E0D0}, 492 { "violet", 0xEE82EE}, 493 { "wheat", 0xF5DEB3}, 494 { "white", 0xFFFFFF}, 495 { "whitesmoke", 0xF5F5F5}, 496 { "yellow", 0xFFFF00}, 497 { "yellowgreen", 0x9ACD32}, 498 }; 499 500 #define endof(T) ((T) + array_elements(T)) 501 502 int 503 decode_color(unsigned char *str, struct rgb *col) 504 { 505 unsigned long ch; 506 char *end; 507 if (*str != '#') { 508 const struct color_spec *cs; 509 for (cs = color_specs; cs < endof(color_specs); cs++) 510 if (!casestrcmp(cast_uchar cs->name, str)) { 511 ch = cs->rgb; 512 goto found; 513 } 514 } else { 515 str++; 516 } 517 if (strlen(cast_const_char str) == 6) { 518 ch = strtoul(cast_const_char str, &end, 16); 519 if (!*end && ch < 0x1000000) { 520 found: 521 memset(col, 0, sizeof(struct rgb)); 522 col->r = (unsigned)ch / 0x10000; 523 col->g = (unsigned)ch / 0x100 % 0x100; 524 col->b = (unsigned)ch % 0x100; 525 return 0; 526 } 527 } 528 if (strlen(cast_const_char str) == 3) { 529 ch = strtoul(cast_const_char str, &end, 16); 530 if (!*end && ch < 0x1000) { 531 memset(col, 0, sizeof(struct rgb)); 532 col->r = ((unsigned)ch / 0x100) * 0x11; 533 col->g = ((unsigned)ch / 0x10 % 0x10) * 0x11; 534 col->b = ((unsigned)ch % 0x10) * 0x11; 535 return 0; 536 } 537 } 538 return -1; 539 } 540 541 int 542 get_color(unsigned char *a, unsigned char *c, struct rgb *rgb) 543 { 544 unsigned char *at; 545 int r = -1; 546 if (d_opt->col >= 1) 547 if ((at = get_attr_val(a, c))) { 548 r = decode_color(at, rgb); 549 free(at); 550 } 551 return r; 552 } 553 554 int 555 get_bgcolor(unsigned char *a, struct rgb *rgb) 556 { 557 if (d_opt->col < 2) 558 return -1; 559 return get_color(a, cast_uchar "bgcolor", rgb); 560 } 561 562 static unsigned char * 563 get_target(unsigned char *a) 564 { 565 return get_attr_val(a, cast_uchar "target"); 566 } 567 568 void 569 kill_html_stack_item(struct html_element *e) 570 { 571 if (!e || (void *)e == &html_stack) { 572 internal("trying to free bad html element"); 573 return; 574 } 575 if (e->dontkill == 2) { 576 internal("trying to kill unkillable element"); 577 return; 578 } 579 html_format_changed = 1; 580 free(e->attr.fontface); 581 free(e->attr.link); 582 free(e->attr.target); 583 free(e->attr.image); 584 free(e->attr.href_base); 585 free(e->attr.target_base); 586 free(e->attr.select); 587 del_from_list(e); 588 free(e); 589 } 590 591 void 592 html_stack_dup(void) 593 { 594 struct html_element *e; 595 struct html_element *ep; 596 html_format_changed = 1; 597 ep = &html_top; 598 e = xmalloc(sizeof(struct html_element)); 599 memcpy(e, ep, sizeof(struct html_element)); 600 e->attr.fontface = stracpy(ep->attr.fontface); 601 e->attr.link = stracpy(ep->attr.link); 602 e->attr.target = stracpy(ep->attr.target); 603 e->attr.image = stracpy(ep->attr.image); 604 e->attr.href_base = stracpy(ep->attr.href_base); 605 e->attr.target_base = stracpy(ep->attr.target_base); 606 e->attr.select = stracpy(ep->attr.select); 607 e->name = e->options = NULL; 608 e->namelen = 0; 609 e->dontkill = 0; 610 add_to_list(html_stack, e); 611 } 612 613 void *ff; 614 void (*put_chars_f)(void *, unsigned char *, int); 615 void (*line_break_f)(void *); 616 void *(*special_f)(void *, int, ...); 617 618 static unsigned char *eoff; 619 unsigned char *eofff; 620 unsigned char *startf; 621 622 int line_breax; 623 static int pos; 624 static int putsp; 625 626 static int was_br; 627 int table_level; 628 int empty_format; 629 630 static void 631 ln_break(const int n) 632 { 633 if (!n || html_top.invisible) 634 return; 635 while (n > line_breax) { 636 line_breax++; 637 line_break_f(ff); 638 } 639 pos = 0; 640 putsp = -1; 641 } 642 643 #define CH_BUF 256 644 #define BUF_RESERVE 6 645 646 static int 647 put_chars_conv(unsigned char *c, int l) 648 { 649 static unsigned char buffer[CH_BUF]; 650 int bp = 0; 651 int pp = 0; 652 int total = 0; 653 if (format_.attr & AT_GRAPHICS) { 654 put_chars_f(ff, c, l); 655 return l; 656 } 657 if (!l) 658 put_chars_f(ff, NULL, 0); 659 while (pp < l) { 660 int sl; 661 unsigned char *e = NULL; /* against warning */ 662 if (c[pp] < 128 && c[pp] != '&') { 663 put_c: 664 if (bp > CH_BUF - BUF_RESERVE && c[pp] >= 0xc0) 665 goto flush; 666 if (!(buffer[bp++] = c[pp++])) 667 buffer[bp - 1] = ' '; 668 if ((buffer[bp - 1] != ' ' || par_format.align == AL_NO 669 || par_format.align == AL_NO_BREAKABLE) 670 && bp < CH_BUF) 671 continue; 672 goto flush; 673 } 674 if (c[pp] != '&') { 675 struct conv_table *t; 676 int i; 677 if (l - pp >= 3 && c[pp] == 0xef && c[pp + 1] == 0xbb 678 && c[pp + 2] == 0xbf && !d_opt->real_cp) { 679 pp += 3; 680 continue; 681 } 682 if ((d_opt->real_cp == d_opt->cp && !d_opt->real_cp) 683 || !convert_table) 684 goto put_c; 685 t = convert_table; 686 i = pp; 687 decode: 688 if (!t[c[i]].t) { 689 e = t[c[i]].u.str; 690 } else { 691 t = t[c[i++]].u.tbl; 692 if (i >= l) 693 goto put_c; 694 goto decode; 695 } 696 pp = i + 1; 697 } else { 698 int i = pp + 1; 699 if (d_opt->plain & 1) 700 goto put_c; 701 while (i < l && !is_entity_terminator(c[i])) 702 i++; 703 if (!(e = get_entity_string(&c[pp + 1], i - pp - 1))) 704 goto put_c; 705 pp = i + (i < l && c[i] == ';'); 706 } 707 if (!e[0]) 708 continue; 709 if (!e[1]) { 710 buffer[bp++] = e[0]; 711 if (bp < CH_BUF) 712 continue; 713 flush: 714 e = cast_uchar ""; 715 goto flush1; 716 } 717 sl = (int)strlen(cast_const_char e); 718 if (sl > BUF_RESERVE) { 719 e = cast_uchar ""; 720 sl = 0; 721 } 722 if (bp + sl > CH_BUF) { 723 flush1: 724 put_chars_f(ff, buffer, bp); 725 if (!d_opt->cp) { 726 while (bp) 727 if ((buffer[--bp] & 0xc0) != 0x80) 728 total++; 729 } else { 730 total += bp; 731 bp = 0; 732 } 733 } 734 while (*e) { 735 buffer[bp++] = *(e++); 736 } 737 if (bp == CH_BUF) 738 goto flush; 739 } 740 if (bp) 741 put_chars_f(ff, buffer, bp); 742 if (!d_opt->cp) { 743 while (bp) 744 if ((buffer[--bp] & 0xc0) != 0x80) 745 total++; 746 } else { 747 total += bp; 748 } 749 return total; 750 } 751 752 static void 753 put_chrs(unsigned char *start, int len) 754 { 755 if (par_format.align == AL_NO || par_format.align == AL_NO_BREAKABLE) 756 putsp = 0; 757 if (!len || html_top.invisible) 758 return; 759 if (putsp == 1) { 760 pos += put_chars_conv(cast_uchar " ", 1); 761 putsp = -1; 762 } 763 if (putsp == -1) { 764 if (start[0] == ' ') { 765 start++; 766 len--; 767 } 768 putsp = 0; 769 } 770 if (!len) { 771 putsp = -1; 772 if (par_format.align == AL_NO 773 || par_format.align == AL_NO_BREAKABLE) 774 putsp = 0; 775 return; 776 } 777 if (start[len - 1] == ' ') 778 putsp = -1; 779 if (par_format.align == AL_NO || par_format.align == AL_NO_BREAKABLE) 780 putsp = 0; 781 was_br = 0; 782 pos += put_chars_conv(start, len); 783 line_breax = 0; 784 } 785 786 static void 787 kill_until(int ls, ...) 788 { 789 int l; 790 struct list_head *e = &html_top.list_entry; 791 if (ls) 792 e = e->next; 793 while (e != &html_stack) { 794 struct html_element *he = list_struct(e, struct html_element); 795 int sk = 0; 796 va_list arg; 797 va_start(arg, ls); 798 while (1) { 799 unsigned char *s = va_arg(arg, unsigned char *); 800 if (!s) 801 break; 802 if (!*s) 803 sk++; 804 else if ((size_t)he->namelen 805 == strlen(cast_const_char s) 806 && !casecmp(he->name, s, 807 strlen(cast_const_char s))) { 808 if (!sk) { 809 if (he->dontkill) 810 break; 811 va_end(arg); 812 goto killll; 813 } else if (sk == 1) { 814 va_end(arg); 815 goto killl; 816 } else 817 break; 818 } 819 } 820 va_end(arg); 821 if (he->dontkill 822 || (he->namelen == 5 823 && !casecmp(he->name, cast_uchar "TABLE", 5))) 824 break; 825 if (he->namelen == 2 && upcase(he->name[0]) == 'T' 826 && (upcase(he->name[1]) == 'D' || upcase(he->name[1]) == 'H' 827 || upcase(he->name[1]) == 'R')) 828 break; 829 e = e->next; 830 } 831 return; 832 killl: 833 e = e->prev; 834 killll: 835 l = 0; 836 while (e != &html_stack) { 837 struct html_element *he = list_struct(e, struct html_element); 838 if (ls && e == html_stack.next) 839 break; 840 if (he->linebreak > l) 841 l = he->linebreak; 842 e = e->prev; 843 kill_html_stack_item(he); 844 } 845 ln_break(l); 846 } 847 848 static inline unsigned char * 849 top_href_base(void) 850 { 851 return list_struct(html_stack.prev, struct html_element) 852 ->attr.href_base; 853 } 854 855 int 856 get_num(unsigned char *a, unsigned char *n) 857 { 858 unsigned char *al; 859 if ((al = get_attr_val(a, n))) { 860 char *end; 861 unsigned long s = strtoul(cast_const_char al, &end, 10); 862 if (!*al || *end || s > 10000) 863 s = -1; 864 free(al); 865 return (int)s; 866 } 867 return -1; 868 } 869 870 /* trunc somehow clips the maximum values. Use 0 to disable truncastion. */ 871 static int 872 parse_width(const char *w, const int trunc) 873 { 874 char *end; 875 int p = 0; 876 long s; 877 int l; 878 int limit = 879 par_format.width - par_format.leftmargin + par_format.rightmargin; 880 while (WHITECHAR(*w)) 881 w++; 882 for (l = 0; w[l] && w[l] != ','; l++) 883 ; 884 while (l && WHITECHAR(w[l - 1])) 885 l--; 886 if (!l) 887 return -1; 888 if (w[l - 1] == '%') { 889 l--; 890 p = 1; 891 } 892 while (l && WHITECHAR(w[l - 1])) 893 l--; 894 if (!l) 895 return -1; 896 s = strtol(w, &end, 10); 897 if (end - w < l || s < 0 || s > 10000) 898 return -1; 899 if (p) { 900 if (trunc) { 901 s = s * limit / 100; 902 } else 903 return -1; 904 } else { 905 s = (s + (HTML_CHAR_WIDTH - 1) / 2) / HTML_CHAR_WIDTH; 906 } 907 if (trunc == 1 && s > limit) 908 s = limit; 909 if (s < 0) 910 s = 0; 911 return (int)s; 912 } 913 914 /* trunc somehow clips the maximum values. Use 0 to disable truncastion. */ 915 int 916 get_width(unsigned char *a, unsigned char *n, int trunc) 917 { 918 int r; 919 char *w; 920 if (!(w = (char *)get_attr_val(a, n))) 921 return -1; 922 r = parse_width(w, trunc); 923 free(w); 924 return r; 925 } 926 927 static unsigned char * 928 find_element_end(unsigned char *a) 929 { 930 unsigned char *p; 931 for (p = a - 1; *p != '<'; p--) 932 ; 933 if (parse_element(p, eoff, NULL, NULL, NULL, &p)) { 934 internal("parse element failed"); 935 return a; 936 } 937 return p; 938 } 939 940 struct form form = { NULL, NULL, NULL, NULL, 0, 0 }; 941 942 unsigned char *last_form_tag; 943 unsigned char *last_form_attr; 944 unsigned char *last_input_tag; 945 946 static inline void 947 set_link_attr(void) 948 { 949 memcpy(!(format_.attr & AT_INVERT) ? &format_.fg : &format_.bg, 950 &format_.clink, sizeof(struct rgb)); 951 } 952 953 static void 954 put_link_line(unsigned char *prefix, unsigned char *linkname, 955 unsigned char *link, unsigned char *target) 956 { 957 if (!casecmp(link, cast_uchar "android-app:", 12)) 958 return; 959 html_stack_dup(); 960 ln_break(1); 961 free(format_.link); 962 format_.link = NULL; 963 free(format_.target); 964 format_.target = NULL; 965 format_.form = NULL; 966 put_chrs(prefix, (int)strlen(cast_const_char prefix)); 967 html_format_changed = 1; 968 format_.link = join_urls(format_.href_base, link); 969 format_.target = stracpy(target); 970 set_link_attr(); 971 put_chrs(linkname, (int)strlen(cast_const_char linkname)); 972 ln_break(1); 973 kill_html_stack_item(&html_top); 974 } 975 976 static void 977 html_span(unsigned char *a) 978 { 979 unsigned char *al; 980 if ((al = get_attr_val(a, cast_uchar "class"))) { 981 if (!strcmp(cast_const_char al, "line-number")) 982 ln_break(1); 983 if (!strcmp(cast_const_char al, 984 "blob-code-inner")) { /* github hack */ 985 ln_break(1); 986 format_.attr |= AT_FIXED; 987 par_format.align = AL_NO; 988 } 989 990 free(al); 991 } 992 } 993 994 static void 995 html_bold(unsigned char *a) 996 { 997 format_.attr |= AT_BOLD; 998 } 999 1000 static void 1001 html_italic(unsigned char *a) 1002 { 1003 format_.attr |= AT_ITALIC; 1004 } 1005 1006 static void 1007 html_underline(unsigned char *a) 1008 { 1009 format_.attr |= AT_UNDERLINE; 1010 } 1011 1012 static void 1013 html_fixed(unsigned char *a) 1014 { 1015 format_.attr |= AT_FIXED; 1016 } 1017 1018 static void 1019 html_invert(unsigned char *a) 1020 { 1021 struct rgb rgb; 1022 memcpy(&rgb, &format_.fg, sizeof(struct rgb)); 1023 memcpy(&format_.fg, &format_.bg, sizeof(struct rgb)); 1024 memcpy(&format_.bg, &rgb, sizeof(struct rgb)); 1025 format_.attr ^= AT_INVERT; 1026 } 1027 1028 static void 1029 html_a(unsigned char *a) 1030 { 1031 unsigned char *al; 1032 1033 if ((al = get_url_val(a, cast_uchar "href"))) { 1034 unsigned char *all = al; 1035 while (all[0] == ' ') 1036 all++; 1037 while (all[0] && all[strlen(cast_const_char all) - 1] == ' ') 1038 all[strlen(cast_const_char all) - 1] = 0; 1039 free(format_.link); 1040 format_.link = join_urls(format_.href_base, all); 1041 free(al); 1042 if ((al = get_target(a))) { 1043 free(format_.target); 1044 format_.target = al; 1045 } else { 1046 free(format_.target); 1047 format_.target = stracpy(format_.target_base); 1048 } 1049 /*format_.attr ^= AT_BOLD;*/ 1050 set_link_attr(); 1051 } else 1052 kill_html_stack_item(&html_top); 1053 if ((al = get_attr_val(a, cast_uchar "name"))) { 1054 special_f(ff, SP_TAG, al); 1055 free(al); 1056 } 1057 } 1058 1059 static void 1060 html_a_special(unsigned char *a, unsigned char *next, unsigned char *eof) 1061 { 1062 unsigned char *t; 1063 if (!format_.link) 1064 return; 1065 while (next < eof && WHITECHAR(*next)) 1066 next++; 1067 if (eof - next >= 4 && next[0] == '<' && next[1] == '/' 1068 && upcase(next[2]) == 'A' && next[3] == '>') 1069 goto ok; 1070 if (strstr(cast_const_char format_.link, "/raw/")) /* gitlab hack */ 1071 goto ok; 1072 return; 1073 1074 ok: 1075 if (!has_attr(a, cast_uchar "href")) 1076 return; 1077 t = get_attr_val(a, cast_uchar "title"); 1078 if (!t) 1079 return; 1080 put_chrs(t, (int)strlen(cast_const_char t)); 1081 free(t); 1082 } 1083 1084 static void 1085 html_sub(unsigned char *a) 1086 { 1087 put_chrs(cast_uchar "_", 1); 1088 format_.fontsize = 1; 1089 format_.baseline = -1; 1090 } 1091 1092 static void 1093 html_sup(unsigned char *a) 1094 { 1095 put_chrs(cast_uchar "^", 1); 1096 format_.fontsize = 1; 1097 if (format_.baseline <= 0) 1098 format_.baseline = format_.fontsize; 1099 } 1100 1101 static void 1102 html_font(unsigned char *a) 1103 { 1104 unsigned char *al; 1105 if ((al = get_attr_val(a, cast_uchar "size"))) { 1106 int p = 0; 1107 unsigned long s; 1108 unsigned char *nn = al; 1109 char *end; 1110 if (*al == '+') { 1111 p = 1; 1112 nn++; 1113 } 1114 if (*al == '-') { 1115 p = -1; 1116 nn++; 1117 } 1118 s = strtoul(cast_const_char nn, &end, 10); 1119 if (*nn && !*end) { 1120 if (s > 7) 1121 s = 7; 1122 if (!p) 1123 format_.fontsize = (int)s; 1124 else 1125 format_.fontsize += p * (int)s; 1126 if (format_.fontsize < 1) 1127 format_.fontsize = 1; 1128 if (format_.fontsize > 7) 1129 format_.fontsize = 7; 1130 } 1131 free(al); 1132 } 1133 get_color(a, cast_uchar "color", &format_.fg); 1134 } 1135 1136 static unsigned char * 1137 get_url_val_img(unsigned char *a, unsigned char *name) 1138 { 1139 unsigned char *v = get_url_val(a, name); 1140 if (v && !v[strcspn(cast_const_char v, "./")]) { 1141 free(v); 1142 v = NULL; 1143 } 1144 return v; 1145 } 1146 1147 static void 1148 html_img(unsigned char *a) 1149 { 1150 unsigned char *al; 1151 unsigned char *s; 1152 unsigned char *orig_link = NULL; 1153 int ismap, usemap = 0; 1154 if ((al = get_url_val(a, cast_uchar "usemap"))) { 1155 unsigned char *u; 1156 usemap = 1; 1157 html_stack_dup(); 1158 free(format_.link); 1159 format_.form = NULL; 1160 u = join_urls(*al == '#' ? top_href_base() : format_.href_base, 1161 al); 1162 format_.link = stracpy(cast_uchar "MAP@"); 1163 add_to_strn(&format_.link, u); 1164 format_.attr |= AT_BOLD; 1165 free(u); 1166 free(al); 1167 } 1168 ismap = format_.link && !has_attr(a, cast_uchar "usemap") 1169 && has_attr(a, cast_uchar "ismap"); 1170 free(format_.image); 1171 format_.image = NULL; 1172 if ((s = get_url_val_img(a, cast_uchar "data-defer-src")) 1173 || (s = get_url_val_img(a, cast_uchar "data-delay-url")) 1174 || (s = get_url_val_img(a, cast_uchar "data-full")) 1175 || (s = get_url_val_img(a, cast_uchar "data-lazy")) 1176 || (s = get_url_val_img(a, cast_uchar "data-lazy-src")) 1177 || (s = get_url_val_img(a, cast_uchar "data-li-src")) 1178 || (s = get_url_val_img(a, cast_uchar "data-normal")) 1179 || (s = get_url_val_img(a, cast_uchar "data-original")) 1180 || (s = get_url_val_img(a, cast_uchar "data-small")) 1181 || (s = get_url_val_img(a, cast_uchar "data-source")) 1182 || (s = get_url_val_img(a, cast_uchar "data-src")) 1183 || (s = get_url_val_img(a, cast_uchar "data-thumb")) 1184 || (s = get_url_val_img(a, cast_uchar "src")) 1185 || (s = get_url_val_img(a, cast_uchar "dynsrc")) 1186 || (s = get_url_val_img(a, cast_uchar "data")) 1187 || (s = get_url_val_img(a, cast_uchar "content")) 1188 || (s = get_url_val(a, cast_uchar "src"))) { 1189 if (!s[0]) 1190 goto skip_img; 1191 format_.image = join_urls(format_.href_base, s); 1192 skip_img: 1193 orig_link = s; 1194 } 1195 if ((!(al = get_attr_val(a, cast_uchar "alt")) 1196 && !(al = get_attr_val(a, cast_uchar "title"))) 1197 || !*al) { 1198 free(al); 1199 if (!d_opt->images && !format_.link) 1200 goto ret; 1201 if (d_opt->image_names && s) { 1202 unsigned char *ss; 1203 al = stracpy(cast_uchar "["); 1204 if (!(ss = cast_uchar strrchr(cast_const_char s, '/'))) 1205 ss = s; 1206 else 1207 ss++; 1208 add_to_strn(&al, ss); 1209 if ((ss = cast_uchar strchr(cast_const_char al, '?'))) 1210 *ss = 0; 1211 if ((ss = cast_uchar strchr(cast_const_char al, '&'))) 1212 *ss = 0; 1213 add_to_strn(&al, cast_uchar "]"); 1214 } else if (usemap) { 1215 al = stracpy(cast_uchar "[USEMAP]"); 1216 } else if (ismap) { 1217 al = stracpy(cast_uchar "[ISMAP]"); 1218 } else { 1219 al = stracpy(cast_uchar "[IMG]"); 1220 } 1221 } 1222 if (al) { 1223 if (ismap) { 1224 unsigned char *h; 1225 html_stack_dup(); 1226 h = stracpy(format_.link); 1227 add_to_strn(&h, cast_uchar "?0,0"); 1228 free(format_.link); 1229 format_.link = h; 1230 } 1231 html_format_changed = 1; 1232 put_chrs(al, (int)strlen(cast_const_char al)); 1233 if (ismap) 1234 kill_html_stack_item(&html_top); 1235 } 1236 free(al); 1237 ret: 1238 free(format_.image); 1239 format_.image = NULL; 1240 html_format_changed = 1; 1241 if (usemap) 1242 kill_html_stack_item(&html_top); 1243 free(orig_link); 1244 } 1245 1246 static void 1247 html_obj(unsigned char *a, int obj) 1248 { 1249 unsigned char *old_base = format_.href_base; 1250 unsigned char *url; 1251 unsigned char *type = get_attr_val(a, cast_uchar "type"); 1252 unsigned char *base; 1253 if ((base = get_url_val(a, cast_uchar "codebase"))) 1254 format_.href_base = join_urls(format_.href_base, base); 1255 if (!type) { 1256 url = get_url_val(a, cast_uchar "src"); 1257 if (!url) 1258 url = get_url_val(a, cast_uchar "data"); 1259 if (url) { 1260 unsigned char *ju = join_urls(format_.href_base, url); 1261 type = get_content_type(NULL, ju); 1262 free(url); 1263 free(ju); 1264 } 1265 } 1266 url = get_url_val(a, cast_uchar "src"); 1267 if (!url) 1268 url = get_url_val(a, cast_uchar "data"); 1269 if (url) { 1270 put_link_line(cast_uchar "", 1271 !obj ? cast_uchar "[EMBED]" : cast_uchar "[OBJ]", 1272 url, cast_uchar ""); 1273 free(url); 1274 } 1275 if (base) { 1276 free(format_.href_base); 1277 format_.href_base = old_base; 1278 free(base); 1279 } 1280 free(type); 1281 } 1282 1283 static void 1284 html_embed(unsigned char *a) 1285 { 1286 html_obj(a, 0); 1287 } 1288 1289 static void 1290 html_object(unsigned char *a) 1291 { 1292 html_obj(a, 1); 1293 } 1294 1295 static void 1296 html_body(unsigned char *a) 1297 { 1298 get_color(a, cast_uchar "text", &format_.fg); 1299 get_color(a, cast_uchar "link", &format_.clink); 1300 if (has_attr(a, cast_uchar "onload")) 1301 special_f(ff, SP_SCRIPT, NULL); 1302 } 1303 1304 static void 1305 html_skip(unsigned char *a) 1306 { 1307 html_top.dontkill = 1; 1308 html_top.invisible = INVISIBLE; 1309 } 1310 1311 static void 1312 html_title(unsigned char *a) 1313 { 1314 if (a[0] == '>' && a[-1] == '/') 1315 return; 1316 html_top.dontkill = 1; 1317 html_top.invisible = INVISIBLE; 1318 } 1319 1320 int 1321 should_skip_script(unsigned char *a) 1322 { 1323 return !has_attr(a, cast_uchar "/"); 1324 } 1325 1326 static void 1327 html_script(unsigned char *a) 1328 { 1329 unsigned char *s; 1330 s = get_url_val(a, cast_uchar "src"); 1331 special_f(ff, SP_SCRIPT, s); 1332 free(s); 1333 if (should_skip_script(a)) { 1334 html_top.dontkill = 1; 1335 html_top.invisible = INVISIBLE_SCRIPT; 1336 } 1337 } 1338 1339 static void 1340 html_style(unsigned char *a) 1341 { 1342 html_top.dontkill = 1; 1343 html_top.invisible = INVISIBLE_STYLE; 1344 } 1345 1346 static void 1347 html_center(unsigned char *a) 1348 { 1349 par_format.align = AL_CENTER; 1350 if (!table_level) 1351 par_format.leftmargin = par_format.rightmargin = 0; 1352 } 1353 1354 static void 1355 html_linebrk(unsigned char *a) 1356 { 1357 unsigned char *al; 1358 if ((al = get_attr_val(a, cast_uchar "align"))) { 1359 if (!casestrcmp(al, cast_uchar "left")) 1360 par_format.align = AL_LEFT; 1361 if (!casestrcmp(al, cast_uchar "right")) 1362 par_format.align = AL_RIGHT; 1363 if (!casestrcmp(al, cast_uchar "center")) { 1364 par_format.align = AL_CENTER; 1365 if (!table_level) 1366 par_format.leftmargin = par_format.rightmargin = 1367 0; 1368 } 1369 if (!casestrcmp(al, cast_uchar "justify")) 1370 par_format.align = AL_BLOCK; 1371 free(al); 1372 } 1373 } 1374 1375 static void 1376 html_br(unsigned char *a) 1377 { 1378 html_linebrk(a); 1379 if (par_format.align != AL_NO && par_format.align != AL_NO_BREAKABLE) { 1380 if (was_br) 1381 ln_break(2); 1382 was_br = 1; 1383 } 1384 } 1385 1386 static void 1387 html_form(unsigned char *a) 1388 { 1389 was_br = 1; 1390 } 1391 1392 static void 1393 html_p(unsigned char *a) 1394 { 1395 if (par_format.leftmargin < margin) 1396 par_format.leftmargin = margin; 1397 if (par_format.rightmargin < margin) 1398 par_format.rightmargin = margin; 1399 /*par_format.align = AL_LEFT;*/ 1400 html_linebrk(a); 1401 } 1402 1403 static void 1404 html_address(unsigned char *a) 1405 { 1406 par_format.leftmargin += 1; 1407 par_format.align = AL_LEFT; 1408 } 1409 1410 static void 1411 html_blockquote(unsigned char *a) 1412 { 1413 par_format.leftmargin += 2; 1414 par_format.align = AL_LEFT; 1415 } 1416 1417 static void 1418 html_h(int h, unsigned char *a) 1419 { 1420 par_format.align = AL_LEFT; 1421 if (h == 1) 1422 return; 1423 html_linebrk(a); 1424 switch (par_format.align) { 1425 case AL_LEFT: 1426 break; 1427 case AL_RIGHT: 1428 par_format.leftmargin = 0; 1429 par_format.rightmargin = (h - 2) * 2; 1430 break; 1431 case AL_CENTER: 1432 par_format.leftmargin = par_format.rightmargin = 0; 1433 break; 1434 case AL_BLOCK: 1435 par_format.leftmargin = par_format.rightmargin = (h - 2) * 2; 1436 break; 1437 } 1438 } 1439 1440 static void 1441 html_h1(unsigned char *a) 1442 { 1443 html_h(1, a); 1444 } 1445 static void 1446 html_h2(unsigned char *a) 1447 { 1448 html_h(2, a); 1449 } 1450 static void 1451 html_h3(unsigned char *a) 1452 { 1453 html_h(3, a); 1454 } 1455 static void 1456 html_h4(unsigned char *a) 1457 { 1458 html_h(4, a); 1459 } 1460 static void 1461 html_h5(unsigned char *a) 1462 { 1463 html_h(5, a); 1464 } 1465 static void 1466 html_h6(unsigned char *a) 1467 { 1468 html_h(6, a); 1469 } 1470 1471 static void 1472 html_pre(unsigned char *a) 1473 { 1474 unsigned char *cl; 1475 format_.attr |= AT_FIXED; 1476 par_format.align = 1477 !par_format.implicit_pre_wrap ? AL_NO : AL_NO_BREAKABLE; 1478 par_format.leftmargin = par_format.leftmargin > 1; 1479 par_format.rightmargin = par_format.leftmargin; 1480 if ((cl = get_attr_val(a, cast_uchar "class"))) { 1481 if (strstr(cast_const_char cl, "bz_comment")) /* hack */ 1482 par_format.align = AL_NO_BREAKABLE; 1483 free(cl); 1484 } 1485 } 1486 1487 static void 1488 html_div(unsigned char *a) 1489 { 1490 unsigned char *al; 1491 if ((al = get_attr_val(a, cast_uchar "class"))) { 1492 if (!strcmp(cast_const_char al, "commit-msg") 1493 || !strcmp(cast_const_char al, "pre") /* sourceware hack */ 1494 || (!strncmp(cast_const_char al, "diff", 4) 1495 && casestrcmp(al, cast_uchar "diff-view") 1496 && strncmp(cast_const_char al, "diffbar", 1497 7)) /* gitweb hack, github counter-hacks */ 1498 || 0) { 1499 format_.attr |= AT_FIXED; 1500 par_format.align = AL_NO; 1501 } else if (strstr(cast_const_char al, 1502 "plain-text-white-space")) { 1503 format_.attr |= AT_FIXED; 1504 par_format.align = AL_NO_BREAKABLE; 1505 } 1506 free(al); 1507 } 1508 html_linebrk(a); 1509 } 1510 1511 static void 1512 html_hr(unsigned char *a) 1513 { 1514 int i; 1515 int q = get_num(a, cast_uchar "size"); 1516 unsigned char r = 205; 1517 1518 html_stack_dup(); 1519 par_format.align = AL_CENTER; 1520 free(format_.link); 1521 format_.link = NULL; 1522 format_.form = NULL; 1523 html_linebrk(a); 1524 if (par_format.align == AL_BLOCK) 1525 par_format.align = AL_CENTER; 1526 par_format.leftmargin = margin; 1527 par_format.rightmargin = margin; 1528 i = get_width(a, cast_uchar "width", 1); 1529 if (q >= 0 && q < 2) 1530 r = 196; 1531 if (i < 0) 1532 i = par_format.width - 2 * margin - 4; 1533 format_.attr = AT_GRAPHICS; 1534 special_f(ff, SP_NOWRAP, 1); 1535 while (i-- > 0) 1536 put_chrs(&r, 1); 1537 special_f(ff, SP_NOWRAP, 0); 1538 ln_break(2); 1539 kill_html_stack_item(&html_top); 1540 } 1541 1542 static void 1543 html_table(unsigned char *a) 1544 { 1545 par_format.leftmargin = margin; 1546 par_format.rightmargin = margin; 1547 par_format.align = AL_LEFT; 1548 html_linebrk(a); 1549 format_.attr = 0; 1550 } 1551 1552 static void 1553 html_tr(unsigned char *a) 1554 { 1555 html_linebrk(a); 1556 } 1557 1558 static void 1559 html_th(unsigned char *a) 1560 { 1561 /*html_linebrk(a);*/ 1562 kill_until(1, cast_uchar "TD", cast_uchar "TH", cast_uchar "", 1563 cast_uchar "TR", cast_uchar "TABLE", NULL); 1564 format_.attr |= AT_BOLD; 1565 put_chrs(cast_uchar " ", 1); 1566 } 1567 1568 static void 1569 html_td(unsigned char *a) 1570 { 1571 /*html_linebrk(a);*/ 1572 kill_until(1, cast_uchar "TD", cast_uchar "TH", cast_uchar "", 1573 cast_uchar "TR", cast_uchar "TABLE", NULL); 1574 format_.attr &= ~AT_BOLD; 1575 put_chrs(cast_uchar " ", 1); 1576 } 1577 1578 static void 1579 html_base(unsigned char *a) 1580 { 1581 unsigned char *al; 1582 if ((al = get_url_val(a, cast_uchar "href"))) { 1583 free(format_.href_base); 1584 format_.href_base = join_urls(top_href_base(), al); 1585 special_f(ff, SP_SET_BASE, format_.href_base); 1586 free(al); 1587 } 1588 if ((al = get_target(a))) { 1589 free(format_.target_base); 1590 format_.target_base = al; 1591 } 1592 } 1593 1594 static void 1595 html_ul(unsigned char *a) 1596 { 1597 unsigned char *al; 1598 /*debug_stack();*/ 1599 par_format.list_level++; 1600 par_format.list_number = 0; 1601 par_format.flags = P_STAR; 1602 if ((al = get_attr_val(a, cast_uchar "type"))) { 1603 if (!casestrcmp(al, cast_uchar "disc") 1604 || !casestrcmp(al, cast_uchar "circle")) 1605 par_format.flags = P_O; 1606 if (!casestrcmp(al, cast_uchar "square")) 1607 par_format.flags = P_PLUS; 1608 free(al); 1609 } 1610 if ((par_format.leftmargin += 2 + (par_format.list_level > 1)) 1611 > par_format.width * 2 / 3 1612 && !table_level) 1613 par_format.leftmargin = par_format.width * 2 / 3; 1614 par_format.align = AL_LEFT; 1615 html_top.dontkill = 1; 1616 } 1617 1618 static void 1619 html_ol(unsigned char *a) 1620 { 1621 unsigned char *al; 1622 int st; 1623 par_format.list_level++; 1624 st = get_num(a, cast_uchar "start"); 1625 if (st == -1) 1626 st = 1; 1627 par_format.list_number = st; 1628 par_format.flags = P_NUMBER; 1629 if ((al = get_attr_val(a, cast_uchar "type"))) { 1630 if (!strcmp(cast_const_char al, "1")) 1631 par_format.flags = P_NUMBER; 1632 if (!strcmp(cast_const_char al, "a")) 1633 par_format.flags = P_alpha; 1634 if (!strcmp(cast_const_char al, "A")) 1635 par_format.flags = P_ALPHA; 1636 if (!strcmp(cast_const_char al, "r")) 1637 par_format.flags = P_roman; 1638 if (!strcmp(cast_const_char al, "R")) 1639 par_format.flags = P_ROMAN; 1640 if (!strcmp(cast_const_char al, "i")) 1641 par_format.flags = P_roman; 1642 if (!strcmp(cast_const_char al, "I")) 1643 par_format.flags = P_ROMAN; 1644 free(al); 1645 } 1646 if ((par_format.leftmargin += (par_format.list_level > 1)) 1647 > par_format.width * 2 / 3 1648 && !table_level) 1649 par_format.leftmargin = par_format.width * 2 / 3; 1650 par_format.align = AL_LEFT; 1651 html_top.dontkill = 1; 1652 } 1653 1654 static void 1655 html_li(unsigned char *a) 1656 { 1657 /*kill_until(0, cast_uchar "", cast_uchar "UL", cast_uchar "OL", 1658 * NULL);*/ 1659 if (!par_format.list_number) { 1660 unsigned char x[8] = "* "; 1661 if ((par_format.flags & P_LISTMASK) == P_O) 1662 x[0] = 'o'; 1663 if ((par_format.flags & P_LISTMASK) == P_PLUS) 1664 x[0] = '+'; 1665 put_chrs(x, 7); 1666 par_format.leftmargin += 2; 1667 par_format.align = AL_LEFT; 1668 putsp = -1; 1669 } else { 1670 unsigned char c = 0; 1671 char n[32]; 1672 int t = par_format.flags & P_LISTMASK; 1673 int s = get_num(a, cast_uchar "value"); 1674 if (s != -1) 1675 par_format.list_number = s; 1676 if ((t != P_roman && t != P_ROMAN 1677 && par_format.list_number < 10) 1678 || t == P_alpha || t == P_ALPHA) { 1679 put_chrs(cast_uchar " ", 6); 1680 c = 1; 1681 } 1682 if (t == P_ALPHA || t == P_alpha) { 1683 n[0] = par_format.list_number 1684 ? (par_format.list_number - 1) % 26 1685 + (t == P_ALPHA ? 'A' : 'a') 1686 : 0; 1687 n[1] = 0; 1688 } else if (t == P_ROMAN || t == P_roman) { 1689 roman(n, par_format.list_number, sizeof(n)); 1690 if (t == P_ROMAN) { 1691 char *x; 1692 for (x = n; *x; x++) 1693 *x = upcase(*x); 1694 } 1695 } else 1696 sprintf(cast_char n, "%d", par_format.list_number); 1697 put_chrs((unsigned char *)n, strlen(n)); 1698 put_chrs(cast_uchar ". ", 7); 1699 par_format.leftmargin += (int)strlen(cast_const_char n) + c + 2; 1700 par_format.align = AL_LEFT; 1701 list_struct(html_top.list_entry.next, struct html_element) 1702 ->parattr.list_number = par_format.list_number + 1; 1703 par_format.list_number = 0; 1704 putsp = -1; 1705 } 1706 line_breax = 2; 1707 } 1708 1709 static void 1710 html_dl(unsigned char *a) 1711 { 1712 par_format.flags &= ~P_COMPACT; 1713 if (has_attr(a, cast_uchar "compact")) 1714 par_format.flags |= P_COMPACT; 1715 if (par_format.list_level) 1716 par_format.leftmargin += 5; 1717 par_format.list_level++; 1718 par_format.list_number = 0; 1719 par_format.align = AL_LEFT; 1720 par_format.dd_margin = par_format.leftmargin; 1721 html_top.dontkill = 1; 1722 if (!(par_format.flags & P_COMPACT)) { 1723 ln_break(2); 1724 html_top.linebreak = 2; 1725 } 1726 } 1727 1728 static void 1729 html_dt(unsigned char *a) 1730 { 1731 kill_until(0, cast_uchar "", cast_uchar "DL", NULL); 1732 par_format.align = AL_LEFT; 1733 par_format.leftmargin = par_format.dd_margin; 1734 if (!(par_format.flags & P_COMPACT) 1735 && !has_attr(a, cast_uchar "compact")) 1736 ln_break(2); 1737 } 1738 1739 static void 1740 html_dd(unsigned char *a) 1741 { 1742 kill_until(0, cast_uchar "", cast_uchar "DL", NULL); 1743 if ((par_format.leftmargin = 1744 par_format.dd_margin + (table_level ? 3 : 8)) 1745 > par_format.width * 2 / 3 1746 && !table_level) 1747 par_format.leftmargin = par_format.width * 2 / 3; 1748 par_format.align = AL_LEFT; 1749 } 1750 1751 static void 1752 get_html_form(unsigned char *a, struct form *form) 1753 { 1754 unsigned char *al; 1755 unsigned char *ch; 1756 form->method = FM_GET; 1757 if ((al = get_attr_val(a, cast_uchar "method"))) { 1758 if (!casestrcmp(al, cast_uchar "post")) { 1759 unsigned char *ax; 1760 form->method = FM_POST; 1761 if ((ax = get_attr_val(a, cast_uchar "enctype"))) { 1762 if (!casestrcmp(ax, cast_uchar 1763 "multipart/form-data")) 1764 form->method = FM_POST_MP; 1765 free(ax); 1766 } 1767 } 1768 free(al); 1769 } 1770 if ((al = get_url_val(a, cast_uchar "action"))) { 1771 unsigned char *all = al; 1772 while (all[0] == ' ') 1773 all++; 1774 while (all[0] && all[strlen(cast_const_char all) - 1] == ' ') 1775 all[strlen(cast_const_char all) - 1] = 0; 1776 form->action = join_urls(format_.href_base, all); 1777 free(al); 1778 } else { 1779 if ((ch = cast_uchar strchr( 1780 cast_const_char(form->action = 1781 stracpy(format_.href_base)), 1782 POST_CHAR))) 1783 *ch = 0; 1784 if (form->method == FM_GET 1785 && (ch = cast_uchar strchr(cast_const_char form->action, 1786 '?'))) 1787 *ch = 0; 1788 } 1789 if ((al = get_target(a))) { 1790 form->target = al; 1791 } else 1792 form->target = stracpy(format_.target_base); 1793 if ((al = get_attr_val(a, cast_uchar "name"))) 1794 form->form_name = al; 1795 if ((al = get_attr_val(a, cast_uchar "onsubmit"))) 1796 form->onsubmit = al; 1797 form->num = (int)(a - startf); 1798 } 1799 1800 static void 1801 find_form_for_input(unsigned char *i) 1802 { 1803 unsigned char *s, *ss, *name, *attr, *lf, *la; 1804 int namelen; 1805 free(form.action); 1806 free(form.target); 1807 free(form.form_name); 1808 free(form.onsubmit); 1809 memset(&form, 0, sizeof(struct form)); 1810 if (!special_f(ff, SP_USED, NULL)) 1811 return; 1812 if (last_form_tag && last_input_tag && i <= last_input_tag 1813 && i > last_form_tag) { 1814 get_html_form(last_form_attr, &form); 1815 return; 1816 } 1817 if (last_form_tag && last_input_tag && i > last_input_tag) { 1818 if (parse_element(last_form_tag, i, &name, &namelen, &la, &s)) 1819 internal("couldn't parse already parsed tag"); 1820 lf = last_form_tag; 1821 s = last_input_tag; 1822 } else { 1823 lf = NULL; 1824 la = NULL; 1825 s = startf; 1826 } 1827 se: 1828 while (s < i && *s != '<') 1829 sp: 1830 s++; 1831 if (s >= i) 1832 goto end_parse; 1833 if (eofff - s >= 2 && (s[1] == '!' || s[1] == '?')) { 1834 s = skip_comment(s, i); 1835 goto se; 1836 } 1837 ss = s; 1838 if (parse_element(s, i, &name, &namelen, &attr, &s)) 1839 goto sp; 1840 if (namelen != 4 || casecmp(name, cast_uchar "FORM", 4)) 1841 goto se; 1842 lf = ss; 1843 la = attr; 1844 goto se; 1845 1846 end_parse: 1847 if (lf) { 1848 last_form_tag = lf; 1849 last_form_attr = la; 1850 last_input_tag = i; 1851 get_html_form(la, &form); 1852 } else { 1853 last_form_tag = NULL; 1854 } 1855 } 1856 1857 static void 1858 html_button(unsigned char *a) 1859 { 1860 unsigned char *al; 1861 struct form_control *fc; 1862 find_form_for_input(a); 1863 fc = mem_calloc(sizeof(struct form_control)); 1864 if (!(al = get_attr_val(a, cast_uchar "type"))) { 1865 fc->type = FC_SUBMIT; 1866 goto xxx; 1867 } 1868 if (!casestrcmp(al, cast_uchar "submit")) 1869 fc->type = FC_SUBMIT; 1870 else if (!casestrcmp(al, cast_uchar "reset")) 1871 fc->type = FC_RESET; 1872 else if (!casestrcmp(al, cast_uchar "button")) 1873 fc->type = FC_BUTTON; 1874 else { 1875 free(al); 1876 free(fc); 1877 return; 1878 } 1879 free(al); 1880 xxx: 1881 fc->form_num = last_form_tag ? (int)(last_form_tag - startf) : 0; 1882 fc->ctrl_num = 1883 last_form_tag ? (int)(a - last_form_tag) : (int)(a - startf); 1884 fc->position = (int)(a - startf); 1885 fc->method = form.method; 1886 fc->action = stracpy(form.action); 1887 fc->form_name = stracpy(form.form_name); 1888 fc->onsubmit = stracpy(form.onsubmit); 1889 fc->name = get_attr_val(a, cast_uchar "name"); 1890 fc->default_value = get_exact_attr_val(a, cast_uchar "value"); 1891 fc->ro = has_attr(a, cast_uchar "disabled") ? 2 1892 : has_attr(a, cast_uchar "readonly") ? 1 1893 : 0; 1894 if (fc->type == FC_SUBMIT && !fc->default_value) 1895 fc->default_value = stracpy(cast_uchar "Submit"); 1896 if (fc->type == FC_RESET && !fc->default_value) 1897 fc->default_value = stracpy(cast_uchar "Reset"); 1898 if (fc->type == FC_BUTTON && !fc->default_value) 1899 fc->default_value = stracpy(cast_uchar "BUTTON"); 1900 if (!fc->default_value) 1901 fc->default_value = stracpy(cast_uchar ""); 1902 special_f(ff, SP_CONTROL, fc); 1903 format_.form = fc; 1904 format_.attr |= AT_BOLD | AT_FIXED; 1905 1906 if (fc->type != FC_BUTTON) { 1907 unsigned char *p, *name; 1908 int namelen; 1909 p = find_element_end(a); 1910 p1: 1911 while (p < eoff && WHITECHAR(*p)) 1912 p2: 1913 p++; 1914 if (p == eoff) 1915 goto put_text; 1916 if (*p != '<') 1917 return; 1918 if (parse_element(p, eoff, &name, &namelen, NULL, &p)) 1919 goto p2; 1920 if (namelen == 6 && !casecmp(name, cast_uchar "BUTTON", 6)) 1921 goto put_text; 1922 if (namelen == 7 && !casecmp(name, cast_uchar "/BUTTON", 7)) 1923 goto put_text; 1924 if (namelen == 3 && !casecmp(name, cast_uchar "IMG", 3)) 1925 return; 1926 goto p1; 1927 1928 put_text: 1929 put_chrs(cast_uchar "[ ", 7); 1930 put_chrs(fc->default_value, 1931 (int)strlen(cast_const_char fc->default_value)); 1932 put_chrs(cast_uchar " ]", 7); 1933 putsp = -1; 1934 } 1935 } 1936 1937 static void 1938 set_max_textarea_width(int *w) 1939 { 1940 int limit; 1941 if (!table_level) 1942 limit = par_format.width - par_format.leftmargin 1943 + par_format.rightmargin; 1944 else 1945 limit = d_opt->xw - 2; 1946 if (*w > limit) { 1947 *w = limit; 1948 if (*w < HTML_MINIMAL_TEXTAREA_WIDTH) 1949 *w = HTML_MINIMAL_TEXTAREA_WIDTH; 1950 } 1951 } 1952 1953 static void 1954 html_input(unsigned char *a) 1955 { 1956 int i; 1957 int size; 1958 unsigned char *al; 1959 struct form_control *fc; 1960 find_form_for_input(a); 1961 fc = mem_calloc(sizeof(struct form_control)); 1962 if (!(al = get_attr_val(a, cast_uchar "type"))) { 1963 if (has_attr(a, cast_uchar "onclick")) 1964 fc->type = FC_BUTTON; 1965 else 1966 fc->type = FC_TEXT; 1967 goto xxx; 1968 } 1969 if (!casestrcmp(al, cast_uchar "text")) 1970 fc->type = FC_TEXT; 1971 else if (!casestrcmp(al, cast_uchar "password")) 1972 fc->type = FC_PASSWORD; 1973 else if (!casestrcmp(al, cast_uchar "checkbox")) 1974 fc->type = FC_CHECKBOX; 1975 else if (!casestrcmp(al, cast_uchar "radio")) 1976 fc->type = FC_RADIO; 1977 else if (!casestrcmp(al, cast_uchar "submit")) 1978 fc->type = FC_SUBMIT; 1979 else if (!casestrcmp(al, cast_uchar "reset")) 1980 fc->type = FC_RESET; 1981 else if (!casestrcmp(al, cast_uchar "file")) 1982 fc->type = FC_FILE_UPLOAD; 1983 else if (!casestrcmp(al, cast_uchar "hidden")) 1984 fc->type = FC_HIDDEN; 1985 else if (!casestrcmp(al, cast_uchar "image")) 1986 fc->type = FC_IMAGE; 1987 else if (!casestrcmp(al, cast_uchar "button")) 1988 fc->type = FC_BUTTON; 1989 else 1990 fc->type = FC_TEXT; 1991 free(al); 1992 xxx: 1993 fc->form_num = last_form_tag ? (int)(last_form_tag - startf) : 0; 1994 fc->ctrl_num = 1995 last_form_tag ? (int)(a - last_form_tag) : (int)(a - startf); 1996 fc->position = (int)(a - startf); 1997 fc->method = form.method; 1998 fc->action = stracpy(form.action); 1999 fc->form_name = stracpy(form.form_name); 2000 fc->onsubmit = stracpy(form.onsubmit); 2001 fc->target = stracpy(form.target); 2002 fc->name = get_attr_val(a, cast_uchar "name"); 2003 if (fc->type == FC_TEXT || fc->type == FC_PASSWORD) 2004 fc->default_value = get_attr_val(a, cast_uchar "value"); 2005 else if (fc->type != FC_FILE_UPLOAD) 2006 fc->default_value = get_exact_attr_val(a, cast_uchar "value"); 2007 if (fc->type == FC_CHECKBOX && !fc->default_value) 2008 fc->default_value = stracpy(cast_uchar "on"); 2009 if ((size = get_num(a, cast_uchar "size")) <= 1) 2010 size = HTML_DEFAULT_INPUT_SIZE; 2011 size++; 2012 if (size > HTML_MINIMAL_TEXTAREA_WIDTH) { 2013 set_max_textarea_width(&size); 2014 } 2015 fc->size = size; 2016 if ((fc->maxlength = get_num(a, cast_uchar "maxlength")) == -1) 2017 fc->maxlength = INT_MAX / 4; 2018 if (fc->type == FC_CHECKBOX || fc->type == FC_RADIO) 2019 fc->default_state = has_attr(a, cast_uchar "checked"); 2020 fc->ro = has_attr(a, cast_uchar "disabled") ? 2 2021 : has_attr(a, cast_uchar "readonly") ? 1 2022 : 0; 2023 if (fc->type == FC_IMAGE) { 2024 fc->alt = get_attr_val(a, cast_uchar "alt"); 2025 if (!fc->alt) 2026 fc->alt = get_attr_val(a, cast_uchar "title"); 2027 if (!fc->alt) 2028 fc->alt = get_attr_val(a, cast_uchar "name"); 2029 } 2030 if (fc->type == FC_SUBMIT && !fc->default_value) 2031 fc->default_value = stracpy(cast_uchar "Submit"); 2032 if (fc->type == FC_RESET && !fc->default_value) 2033 fc->default_value = stracpy(cast_uchar "Reset"); 2034 if (!fc->default_value) 2035 fc->default_value = stracpy(cast_uchar ""); 2036 if (fc->type == FC_HIDDEN) 2037 goto hid; 2038 put_chrs(cast_uchar " ", 1); 2039 html_stack_dup(); 2040 format_.form = fc; 2041 switch (fc->type) { 2042 case FC_TEXT: 2043 case FC_PASSWORD: 2044 case FC_FILE_UPLOAD: 2045 format_.attr |= AT_BOLD | AT_FIXED; 2046 format_.fontsize = 3; 2047 for (i = 0; i < fc->size; i++) 2048 put_chrs(cast_uchar "_", 1); 2049 break; 2050 case FC_CHECKBOX: 2051 format_.attr |= AT_BOLD | AT_FIXED; 2052 format_.fontsize = 3; 2053 put_chrs(cast_uchar "[ ]", 8); 2054 break; 2055 case FC_RADIO: 2056 format_.attr |= AT_BOLD | AT_FIXED; 2057 format_.fontsize = 3; 2058 put_chrs(cast_uchar "[ ]", 8); 2059 break; 2060 case FC_IMAGE: 2061 free(format_.image); 2062 format_.image = NULL; 2063 if ((al = get_url_val(a, cast_uchar "src")) 2064 || (al = get_url_val(a, cast_uchar "dynsrc"))) { 2065 format_.image = join_urls(format_.href_base, al); 2066 free(al); 2067 } 2068 format_.attr |= AT_BOLD | AT_FIXED; 2069 put_chrs(cast_uchar "[ ", 7); 2070 if (fc->alt) 2071 put_chrs(fc->alt, (int)strlen(cast_const_char fc->alt)); 2072 else 2073 put_chrs(cast_uchar "Submit", 6); 2074 put_chrs(cast_uchar " ]", 7); 2075 break; 2076 case FC_SUBMIT: 2077 case FC_RESET: 2078 format_.attr |= AT_BOLD | AT_FIXED; 2079 format_.fontsize = 3; 2080 put_chrs(cast_uchar "[ ", 7); 2081 if (fc->default_value) 2082 put_chrs( 2083 fc->default_value, 2084 (int)strlen(cast_const_char fc->default_value)); 2085 put_chrs(cast_uchar " ]", 7); 2086 break; 2087 case FC_BUTTON: 2088 format_.attr |= AT_BOLD | AT_FIXED; 2089 format_.fontsize = 3; 2090 put_chrs(cast_uchar "[ ", 7); 2091 if (fc->default_value) 2092 put_chrs( 2093 fc->default_value, 2094 (int)strlen(cast_const_char fc->default_value)); 2095 else 2096 put_chrs(cast_uchar "BUTTON", 6); 2097 put_chrs(cast_uchar " ]", 7); 2098 break; 2099 default: 2100 internal("bad control type"); 2101 } 2102 kill_html_stack_item(&html_top); 2103 put_chrs(cast_uchar " ", 1); 2104 2105 hid: 2106 special_f(ff, SP_CONTROL, fc); 2107 } 2108 2109 static void 2110 html_select(unsigned char *a) 2111 { 2112 unsigned char *al; 2113 if (!(al = get_attr_val(a, cast_uchar "name"))) 2114 return; 2115 html_top.dontkill = 1; 2116 free(format_.select); 2117 format_.select = al; 2118 format_.select_disabled = 2 * has_attr(a, cast_uchar "disabled"); 2119 } 2120 2121 static void 2122 html_option(unsigned char *a) 2123 { 2124 struct form_control *fc; 2125 unsigned char *val; 2126 find_form_for_input(a); 2127 if (!format_.select) 2128 return; 2129 fc = mem_calloc(sizeof(struct form_control)); 2130 if (!(val = get_exact_attr_val(a, cast_uchar "value"))) { 2131 unsigned char *p, *r; 2132 unsigned char *name; 2133 int namelen; 2134 int l = 0; 2135 val = NULL; 2136 p = find_element_end(a); 2137 rrrr: 2138 while (p < eoff && WHITECHAR(*p)) 2139 p++; 2140 while (p < eoff && !WHITECHAR(*p) && *p != '<') { 2141 pppp: 2142 l = add_chr_to_str(&val, l, *p); 2143 p++; 2144 } 2145 r = p; 2146 while (r < eoff && WHITECHAR(*r)) 2147 r++; 2148 if (r >= eoff) 2149 goto x; 2150 if (eoff - r >= 2 && (r[1] == '!' || r[1] == '?')) { 2151 p = skip_comment(r, eoff); 2152 goto rrrr; 2153 } 2154 if (parse_element(r, eoff, &name, &namelen, NULL, &p)) 2155 goto pppp; 2156 if (!((namelen == 6 && !casecmp(name, cast_uchar "OPTION", 6)) 2157 || (namelen == 7 2158 && !casecmp(name, cast_uchar "/OPTION", 7)) 2159 || (namelen == 6 2160 && !casecmp(name, cast_uchar "SELECT", 6)) 2161 || (namelen == 7 2162 && !casecmp(name, cast_uchar "/SELECT", 7)) 2163 || (namelen == 8 2164 && !casecmp(name, cast_uchar "OPTGROUP", 8)) 2165 || (namelen == 9 2166 && !casecmp(name, cast_uchar "/OPTGROUP", 9)))) 2167 goto rrrr; 2168 } 2169 x: 2170 fc->form_num = last_form_tag ? (int)(last_form_tag - startf) : 0; 2171 fc->ctrl_num = 2172 last_form_tag ? (int)(a - last_form_tag) : (int)(a - startf); 2173 fc->position = (int)(a - startf); 2174 fc->method = form.method; 2175 fc->action = stracpy(form.action); 2176 fc->form_name = stracpy(form.form_name); 2177 fc->onsubmit = stracpy(form.onsubmit); 2178 fc->type = FC_CHECKBOX; 2179 fc->name = stracpy(format_.select); 2180 fc->default_value = val; 2181 fc->default_state = has_attr(a, cast_uchar "selected"); 2182 fc->ro = format_.select_disabled; 2183 if (has_attr(a, cast_uchar "disabled")) 2184 fc->ro = 2; 2185 put_chrs(cast_uchar " ", 1); 2186 html_stack_dup(); 2187 format_.form = fc; 2188 format_.attr |= AT_BOLD | AT_FIXED; 2189 format_.fontsize = 3; 2190 put_chrs(cast_uchar "[ ]", 3); 2191 kill_html_stack_item(&html_top); 2192 put_chrs(cast_uchar " ", 1); 2193 special_f(ff, SP_CONTROL, fc); 2194 } 2195 2196 void 2197 clr_white(unsigned char *name) 2198 { 2199 unsigned char *nm; 2200 for (nm = name; *nm; nm++) 2201 if (WHITECHAR(*nm) || *nm == 1) 2202 *nm = ' '; 2203 } 2204 2205 void 2206 clr_spaces(unsigned char *name, int firstlast) 2207 { 2208 unsigned char *n1, *n2; 2209 clr_white(name); 2210 if (!strchr(cast_const_char name, ' ')) 2211 return; 2212 for (n1 = name, n2 = name; *n1; n1++) 2213 if (!(n1[0] == ' ' 2214 && ((firstlast && n2 == name) || n1[1] == ' ' 2215 || (firstlast && !n1[1])))) 2216 *n2++ = *n1; 2217 *n2 = 0; 2218 } 2219 2220 static int menu_stack_size; 2221 static struct menu_item **menu_stack; 2222 2223 static void 2224 new_menu_item(unsigned char *name, long data, int fullname) 2225 /* name == NULL - up; data == -1 - down */ 2226 { 2227 struct menu_item *top, *item, 2228 *nmenu = NULL; /* no uninitialized warnings */ 2229 if (name) { 2230 clr_spaces(name, 1); 2231 if (!name[0]) { 2232 free(name); 2233 name = stracpy(cast_uchar " "); 2234 } 2235 if (name[0] == 1) 2236 name[0] = ' '; 2237 } 2238 if (name && data == -1) { 2239 nmenu = mem_calloc(sizeof(struct menu_item)); 2240 /*nmenu->text = cast_uchar "";*/ 2241 } 2242 if (menu_stack_size && name) { 2243 top = item = menu_stack[menu_stack_size - 1]; 2244 while (item->text) 2245 item++; 2246 if ((size_t)((unsigned char *)(item + 2) - (unsigned char *)top) 2247 > INT_MAX) 2248 overalloc(); 2249 top = xrealloc(top, (unsigned char *)(item + 2) 2250 - (unsigned char *)top); 2251 item = item - menu_stack[menu_stack_size - 1] + top; 2252 menu_stack[menu_stack_size - 1] = top; 2253 if (menu_stack_size >= 2) { 2254 struct menu_item *below = 2255 menu_stack[menu_stack_size - 2]; 2256 while (below->text) 2257 below++; 2258 below[-1].data = top; 2259 } 2260 item->text = name; 2261 item->rtext = data == -1 ? cast_uchar ">" : cast_uchar ""; 2262 item->hotkey = fullname ? cast_uchar "\000\001" 2263 : cast_uchar "\000\000"; /* dirty */ 2264 item->func = data == -1 ? do_select_submenu : selected_item; 2265 item->data = data == -1 ? nmenu : (void *)data; 2266 item->in_m = data == -1 ? 1 : 0; 2267 item->free_i = 0; 2268 item++; 2269 memset(item, 0, sizeof(struct menu_item)); 2270 /*item->text = cast_uchar "";*/ 2271 } else 2272 free(name); 2273 if (name && data == -1) { 2274 if ((unsigned)menu_stack_size 2275 > INT_MAX / sizeof(struct menu_item *) - 1) 2276 overalloc(); 2277 menu_stack = 2278 xrealloc(menu_stack, (menu_stack_size + 1) 2279 * sizeof(struct menu_item *)); 2280 menu_stack[menu_stack_size++] = nmenu; 2281 } 2282 if (!name) 2283 menu_stack_size--; 2284 } 2285 2286 static void 2287 init_menu(void) 2288 { 2289 menu_stack_size = 0; 2290 menu_stack = NULL; 2291 new_menu_item(stracpy(cast_uchar ""), -1, 0); 2292 } 2293 2294 void 2295 free_menu(struct menu_item *m) /* Grrr. Recursion */ 2296 { 2297 struct menu_item *mm; 2298 for (mm = m; mm->text; mm++) { 2299 free(mm->text); 2300 if (mm->func == do_select_submenu) 2301 free_menu(mm->data); 2302 } 2303 free(m); 2304 } 2305 2306 static struct menu_item * 2307 detach_menu(void) 2308 { 2309 struct menu_item *i = NULL; 2310 if (menu_stack) { 2311 if (menu_stack_size) 2312 i = menu_stack[0]; 2313 free(menu_stack); 2314 } 2315 return i; 2316 } 2317 2318 static void 2319 destroy_menu(void) 2320 { 2321 if (menu_stack) 2322 free_menu(menu_stack[0]); 2323 detach_menu(); 2324 } 2325 2326 static void 2327 menu_labels(struct menu_item *m, unsigned char *base, unsigned char **lbls) 2328 { 2329 unsigned char *bs; 2330 for (; m->text; m++) { 2331 if (m->func == do_select_submenu) { 2332 if ((bs = stracpy(base))) { 2333 add_to_strn(&bs, m->text); 2334 add_to_strn(&bs, cast_uchar " "); 2335 menu_labels(m->data, bs, lbls); 2336 free(bs); 2337 } 2338 } else { 2339 if ((bs = stracpy(m->hotkey[1] ? (unsigned char *)"" 2340 : base))) 2341 add_to_strn(&bs, m->text); 2342 lbls[(int)(long)m->data] = bs; 2343 } 2344 } 2345 } 2346 2347 static int 2348 menu_contains(struct menu_item *m, int f) 2349 { 2350 if (m->func != do_select_submenu) 2351 return (int)(long)m->data == f; 2352 for (m = m->data; m->text; m++) 2353 if (menu_contains(m, f)) 2354 return 1; 2355 return 0; 2356 } 2357 2358 void 2359 do_select_submenu(struct terminal *term, void *menu_, void *ses_) 2360 { 2361 struct menu_item *menu = (struct menu_item *)menu_; 2362 struct session *ses = (struct session *)ses_; 2363 struct menu_item *m; 2364 int def = get_current_state(ses); 2365 int sel = 0; 2366 if (def < 0) 2367 def = 0; 2368 for (m = menu; m->text; m++, sel++) 2369 if (menu_contains(m, def)) 2370 goto f; 2371 sel = 0; 2372 f: 2373 do_menu_selected(term, menu, ses, sel, NULL, NULL); 2374 } 2375 2376 static int 2377 do_html_select(unsigned char *attr, unsigned char *html, unsigned char *eof, 2378 unsigned char **end) 2379 { 2380 struct form_control *fc; 2381 unsigned char *t_name, *t_attr, *en; 2382 int t_namelen; 2383 unsigned char *lbl; 2384 int lbl_l; 2385 unsigned char *vlbl; 2386 int vlbl_l; 2387 int nnmi = 0; 2388 struct conv_table *ct = special_f(ff, SP_TABLE, NULL); 2389 unsigned char **val, **lbls; 2390 int order, preselect, group; 2391 int i, mw; 2392 if (has_attr(attr, cast_uchar "multiple") || dmp) 2393 return 1; 2394 find_form_for_input(attr); 2395 lbl = NULL; 2396 lbl_l = 0; 2397 vlbl = NULL; 2398 vlbl_l = 0; 2399 val = NULL; 2400 order = 0; 2401 group = 0; 2402 preselect = -1; 2403 init_menu(); 2404 se: 2405 en = html; 2406 see: 2407 html = en; 2408 while (html < eof && *html != '<') 2409 html++; 2410 if (html >= eof) { 2411 int i; 2412 abort: 2413 *end = html; 2414 free(lbl); 2415 free(vlbl); 2416 for (i = 0; i < order; i++) 2417 free(val[i]); 2418 free(val); 2419 destroy_menu(); 2420 *end = en; 2421 return 0; 2422 } 2423 if (lbl) { 2424 unsigned char *q, *s = en; 2425 int l = (int)(html - en); 2426 while (l && WHITECHAR(s[0])) { 2427 s++; 2428 l--; 2429 } 2430 while (l && WHITECHAR(s[l - 1])) 2431 l--; 2432 q = convert_string(ct, s, l, d_opt); 2433 if (q) { 2434 lbl_l = add_to_str(&lbl, lbl_l, q); 2435 free(q); 2436 } 2437 vlbl_l = add_bytes_to_str(&vlbl, vlbl_l, s, l); 2438 } 2439 if (eof - html >= 2 && (html[1] == '!' || html[1] == '?')) { 2440 html = skip_comment(html, eof); 2441 goto se; 2442 } 2443 if (parse_element(html, eof, &t_name, &t_namelen, &t_attr, &en)) { 2444 html++; 2445 goto se; 2446 } 2447 if (t_namelen == 7 && !casecmp(t_name, cast_uchar "/SELECT", 7)) { 2448 if (lbl) { 2449 if (!val[order - 1]) 2450 val[order - 1] = stracpy(vlbl); 2451 if (!nnmi) { 2452 new_menu_item(lbl, order - 1, 1); 2453 lbl = NULL; 2454 } else { 2455 free(lbl); 2456 lbl = NULL; 2457 } 2458 free(vlbl); 2459 vlbl = NULL; 2460 } 2461 goto end_parse; 2462 } 2463 if (t_namelen == 7 && !casecmp(t_name, cast_uchar "/OPTION", 7)) { 2464 if (lbl) { 2465 if (!val[order - 1]) 2466 val[order - 1] = stracpy(vlbl); 2467 if (!nnmi) { 2468 new_menu_item(lbl, order - 1, 1); 2469 lbl = NULL; 2470 } else { 2471 free(lbl); 2472 lbl = NULL; 2473 } 2474 free(vlbl); 2475 vlbl = NULL; 2476 } 2477 goto see; 2478 } 2479 if (t_namelen == 6 && !casecmp(t_name, cast_uchar "OPTION", 6)) { 2480 unsigned char *v, *vx; 2481 if (lbl) { 2482 if (!val[order - 1]) 2483 val[order - 1] = stracpy(vlbl); 2484 if (!nnmi) { 2485 new_menu_item(lbl, order - 1, 1); 2486 lbl = NULL; 2487 } else { 2488 free(lbl); 2489 lbl = NULL; 2490 } 2491 free(vlbl); 2492 vlbl = NULL; 2493 } 2494 if (has_attr(t_attr, cast_uchar "disabled")) 2495 goto see; 2496 if (preselect == -1 && has_attr(t_attr, cast_uchar "selected")) 2497 preselect = order; 2498 v = get_exact_attr_val(t_attr, cast_uchar "value"); 2499 if (!(order & (ALLOC_GR - 1))) { 2500 if ((unsigned)order 2501 > INT_MAX / sizeof(unsigned char *) - ALLOC_GR) 2502 overalloc(); 2503 val = xrealloc(val, (order + ALLOC_GR) 2504 * sizeof(unsigned char *)); 2505 } 2506 val[order++] = v; 2507 if ((vx = get_attr_val(t_attr, cast_uchar "label"))) { 2508 new_menu_item( 2509 convert_string( 2510 ct, vx, (int)strlen(cast_const_char vx), d_opt), 2511 order - 1, 0); 2512 free(vx); 2513 } 2514 if (!v || !vx) { 2515 lbl = NULL; 2516 lbl_l = 0; 2517 vlbl = NULL; 2518 vlbl_l = 0; 2519 nnmi = !!vx; 2520 } 2521 goto see; 2522 } 2523 if ((t_namelen == 8 && !casecmp(t_name, cast_uchar "OPTGROUP", 8)) 2524 || (t_namelen == 9 2525 && !casecmp(t_name, cast_uchar "/OPTGROUP", 9))) { 2526 if (lbl) { 2527 if (!val[order - 1]) 2528 val[order - 1] = stracpy(vlbl); 2529 if (!nnmi) { 2530 new_menu_item(lbl, order - 1, 1); 2531 lbl = NULL; 2532 } else { 2533 free(lbl); 2534 lbl = NULL; 2535 } 2536 free(vlbl); 2537 vlbl = NULL; 2538 } 2539 if (group) { 2540 new_menu_item(NULL, -1, 0); 2541 group = 0; 2542 } 2543 } 2544 if (t_namelen == 8 && !casecmp(t_name, cast_uchar "OPTGROUP", 8)) { 2545 unsigned char *la; 2546 if (!(la = get_attr_val(t_attr, cast_uchar "label"))) 2547 la = stracpy(cast_uchar ""); 2548 new_menu_item(convert_string(ct, la, 2549 (int)strlen(cast_const_char la), 2550 d_opt), 2551 -1, 0); 2552 free(la); 2553 group = 1; 2554 } 2555 goto see; 2556 2557 end_parse: 2558 *end = en; 2559 if (!order) 2560 goto abort; 2561 fc = mem_calloc(sizeof(struct form_control)); 2562 if ((unsigned)order > (unsigned)INT_MAX / sizeof(unsigned char *)) 2563 overalloc(); 2564 lbls = mem_calloc(order * sizeof(unsigned char *)); 2565 fc->form_num = last_form_tag ? (int)(last_form_tag - startf) : 0; 2566 fc->ctrl_num = 2567 last_form_tag ? (int)(attr - last_form_tag) : (int)(attr - startf); 2568 fc->position = (int)(attr - startf); 2569 fc->method = form.method; 2570 fc->action = stracpy(form.action); 2571 fc->form_name = stracpy(form.form_name); 2572 fc->onsubmit = stracpy(form.onsubmit); 2573 fc->name = get_attr_val(attr, cast_uchar "name"); 2574 fc->type = FC_SELECT; 2575 fc->default_state = preselect < 0 ? 0 : preselect; 2576 fc->default_value = 2577 order ? stracpy(val[fc->default_state]) : stracpy(cast_uchar ""); 2578 fc->ro = has_attr(attr, cast_uchar "disabled") ? 2 2579 : has_attr(attr, cast_uchar "readonly") ? 1 2580 : 0; 2581 fc->nvalues = order; 2582 fc->values = val; 2583 fc->menu = detach_menu(); 2584 fc->labels = lbls; 2585 menu_labels(fc->menu, cast_uchar "", lbls); 2586 html_stack_dup(); 2587 format_.attr |= AT_FIXED; 2588 format_.fontsize = 3; 2589 put_chrs(cast_uchar "[", 1); 2590 html_stack_dup(); 2591 format_.form = fc; 2592 format_.attr |= AT_BOLD | AT_FIXED; 2593 format_.fontsize = 3; 2594 mw = 0; 2595 for (i = 0; i < order; i++) 2596 if (lbls[i] && strlen((char *)lbls[i]) > mw) 2597 mw = strlen((char *)lbls[i]); 2598 for (i = 0; i < mw; i++) 2599 put_chrs(cast_uchar "_", 1); 2600 kill_html_stack_item(&html_top); 2601 put_chrs(cast_uchar "]", 1); 2602 kill_html_stack_item(&html_top); 2603 special_f(ff, SP_CONTROL, fc); 2604 return 0; 2605 } 2606 2607 static void 2608 html_textarea(unsigned char *a) 2609 { 2610 internal("This should be never called"); 2611 } 2612 2613 static void 2614 do_html_textarea(unsigned char *attr, unsigned char *html, unsigned char *eof, 2615 unsigned char **end) 2616 { 2617 struct form_control *fc; 2618 unsigned char *p, *t_name, *w; 2619 int t_namelen; 2620 int cols, rows; 2621 int i; 2622 find_form_for_input(attr); 2623 while (html < eof && (*html == '\n' || *html == '\r')) 2624 html++; 2625 p = html; 2626 while (p < eof && *p != '<') { 2627 pp: 2628 p++; 2629 } 2630 if (p >= eof) { 2631 *end = eof; 2632 return; 2633 } 2634 if (parse_element(p, eof, &t_name, &t_namelen, NULL, end)) 2635 goto pp; 2636 if (t_namelen != 9 || casecmp(t_name, cast_uchar "/TEXTAREA", 9)) 2637 goto pp; 2638 fc = mem_calloc(sizeof(struct form_control)); 2639 fc->form_num = last_form_tag ? (int)(last_form_tag - startf) : 0; 2640 fc->ctrl_num = 2641 last_form_tag ? (int)(attr - last_form_tag) : (int)(attr - startf); 2642 fc->position = (int)(attr - startf); 2643 fc->method = form.method; 2644 fc->action = stracpy(form.action); 2645 fc->form_name = stracpy(form.form_name); 2646 fc->onsubmit = stracpy(form.onsubmit); 2647 fc->name = get_attr_val(attr, cast_uchar "name"); 2648 fc->type = FC_TEXTAREA; 2649 ; 2650 fc->ro = has_attr(attr, cast_uchar "disabled") ? 2 2651 : has_attr(attr, cast_uchar "readonly") ? 1 2652 : 0; 2653 fc->default_value = memacpy(html, p - html); 2654 if ((cols = get_num(attr, cast_uchar "cols")) 2655 < HTML_MINIMAL_TEXTAREA_WIDTH) 2656 cols = HTML_DEFAULT_TEXTAREA_WIDTH; 2657 cols++; 2658 set_max_textarea_width(&cols); 2659 if ((rows = get_num(attr, cast_uchar "rows")) <= 0) 2660 rows = HTML_DEFAULT_TEXTAREA_HEIGHT; 2661 if (rows > d_opt->yw) { 2662 rows = d_opt->yw; 2663 if (rows <= 0) 2664 rows = 1; 2665 } 2666 fc->cols = cols; 2667 fc->rows = rows; 2668 fc->wrap = 1; 2669 if ((w = get_attr_val(attr, cast_uchar "wrap"))) { 2670 if (!casestrcmp(w, cast_uchar "hard") 2671 || !casestrcmp(w, cast_uchar "physical")) 2672 fc->wrap = 2; 2673 else if (!casestrcmp(w, cast_uchar "off")) 2674 fc->wrap = 0; 2675 free(w); 2676 } 2677 if ((fc->maxlength = get_num(attr, cast_uchar "maxlength")) == -1) 2678 fc->maxlength = INT_MAX / 4; 2679 if (rows > 1) 2680 ln_break(1); 2681 else 2682 put_chrs(cast_uchar " ", 1); 2683 html_stack_dup(); 2684 format_.form = fc; 2685 format_.attr = AT_BOLD | AT_FIXED; 2686 format_.fontsize = 3; 2687 for (i = 0; i < rows; i++) { 2688 int j; 2689 for (j = 0; j < cols; j++) 2690 put_chrs(cast_uchar "_", 1); 2691 if (i < rows - 1) 2692 ln_break(1); 2693 } 2694 kill_html_stack_item(&html_top); 2695 if (rows > 1) 2696 ln_break(1); 2697 else 2698 put_chrs(cast_uchar " ", 1); 2699 special_f(ff, SP_CONTROL, fc); 2700 } 2701 2702 static void 2703 html_iframe(unsigned char *a) 2704 { 2705 unsigned char *name, *url; 2706 if (!(url = get_url_val(a, cast_uchar "src"))) 2707 return; 2708 if (!*url) 2709 goto free_url_ret; 2710 if (!(name = get_attr_val(a, cast_uchar "name"))) 2711 name = stracpy(cast_uchar ""); 2712 if (*name) 2713 put_link_line(cast_uchar "IFrame: ", name, url, 2714 d_opt->framename); 2715 else 2716 put_link_line(cast_uchar "", cast_uchar "IFrame", url, 2717 d_opt->framename); 2718 free(name); 2719 free_url_ret: 2720 free(url); 2721 } 2722 2723 static void 2724 html_noframes(unsigned char *a) 2725 { 2726 if (d_opt->frames) 2727 html_skip(a); 2728 } 2729 2730 static void 2731 html_frame(unsigned char *a) 2732 { 2733 unsigned char *name, *u2, *url; 2734 if (!(u2 = get_url_val(a, cast_uchar "src"))) { 2735 url = stracpy(cast_uchar ""); 2736 } else { 2737 url = join_urls(format_.href_base, u2); 2738 free(u2); 2739 } 2740 name = get_attr_val(a, cast_uchar "name"); 2741 if (!name[0]) { 2742 free(name); 2743 name = NULL; 2744 } 2745 if (!name) { 2746 name = get_attr_val(a, cast_uchar "src"); 2747 if (!name) 2748 name = stracpy(cast_uchar "Frame"); 2749 } 2750 if (!d_opt->frames || !html_top.frameset) 2751 put_link_line(cast_uchar "Frame: ", name, url, cast_uchar ""); 2752 else { 2753 struct frame_param fp; 2754 unsigned char *scroll = get_attr_val(a, cast_uchar "scrolling"); 2755 fp.name = name; 2756 fp.url = url; 2757 fp.parent = html_top.frameset; 2758 fp.marginwidth = get_num(a, cast_uchar "marginwidth"); 2759 fp.marginheight = get_num(a, cast_uchar "marginheight"); 2760 fp.scrolling = SCROLLING_AUTO; 2761 if (scroll) { 2762 if (!casestrcmp(scroll, cast_uchar "no")) 2763 fp.scrolling = SCROLLING_NO; 2764 else if (!casestrcmp(scroll, cast_uchar "yes")) 2765 fp.scrolling = SCROLLING_YES; 2766 free(scroll); 2767 } 2768 if (special_f(ff, SP_USED, NULL)) 2769 special_f(ff, SP_FRAME, &fp); 2770 } 2771 free(name); 2772 free(url); 2773 } 2774 2775 static void 2776 parse_frame_widths(unsigned char *a, int ww, int www, int **op, int *olp) 2777 { 2778 unsigned char *aa; 2779 char *end; 2780 int q, qq, i, d, nn; 2781 unsigned long n; 2782 int *oo, *o; 2783 int ol; 2784 ol = 0; 2785 o = NULL; 2786 new_ch: 2787 while (WHITECHAR(*a)) 2788 a++; 2789 n = strtoul(cast_const_char a, &end, 10); 2790 a = cast_uchar end; 2791 if (n > 10000) 2792 n = 10000; 2793 q = (int)n; 2794 if (*a == '%') 2795 q = q * ww / 100; 2796 else if (*a != '*') 2797 q = (q + (www - 1) / 2) / (www ? www : 1); 2798 else if (!(q = -q)) 2799 q = -1; 2800 if ((unsigned)ol > INT_MAX / sizeof(int) - 1) 2801 overalloc(); 2802 o = xrealloc(o, (ol + 1) * sizeof(int)); 2803 o[ol++] = q; 2804 if ((aa = cast_uchar strchr(cast_const_char a, ','))) { 2805 a = aa + 1; 2806 goto new_ch; 2807 } 2808 *op = o; 2809 *olp = ol; 2810 q = 2 * ol - 1; 2811 for (i = 0; i < ol; i++) 2812 if (o[i] > 0) 2813 q += o[i] - 1; 2814 if (q >= ww) { 2815 distribute: 2816 for (i = 0; i < ol; i++) 2817 if (o[i] < 1) 2818 o[i] = 1; 2819 q -= ww; 2820 d = 0; 2821 for (i = 0; i < ol; i++) 2822 d += o[i]; 2823 qq = q; 2824 for (i = 0; i < ol; i++) { 2825 q -= o[i] - o[i] * (d - qq) / (d ? d : 1); 2826 o[i] = o[i] * (d - qq) / (d ? d : 1); 2827 } 2828 while (q) { 2829 nn = 0; 2830 for (i = 0; i < ol; i++) { 2831 if (q < 0) { 2832 o[i]++; 2833 q++; 2834 nn = 1; 2835 } 2836 if (q > 0 && o[i] > 1) { 2837 o[i]--; 2838 q--; 2839 nn = 1; 2840 } 2841 if (!q) 2842 break; 2843 } 2844 if (!nn) 2845 break; 2846 } 2847 } else { 2848 int nn = 0; 2849 for (i = 0; i < ol; i++) 2850 if (o[i] < 0) 2851 nn = 1; 2852 if (!nn) 2853 goto distribute; 2854 if ((unsigned)ol > INT_MAX / sizeof(int)) 2855 overalloc(); 2856 oo = xmalloc(ol * sizeof(int)); 2857 memcpy(oo, o, ol * sizeof(int)); 2858 for (i = 0; i < ol; i++) 2859 if (o[i] < 1) 2860 o[i] = 1; 2861 q = ww - q; 2862 d = 0; 2863 for (i = 0; i < ol; i++) 2864 if (oo[i] < 0) 2865 d += -oo[i]; 2866 qq = q; 2867 for (i = 0; i < ol; i++) 2868 if (oo[i] < 0) { 2869 o[i] += (-oo[i] * qq / (d ? d : 1)); 2870 q -= (-oo[i] * qq / (d ? d : 1)); 2871 } 2872 if (q < 0) { 2873 q = 0; 2874 /*internal("parse_frame_widths: q < 0"); may happen when 2875 * page contains too big values */ 2876 } 2877 for (i = 0; i < ol; i++) 2878 if (oo[i] < 0) { 2879 if (q) { 2880 o[i]++; 2881 q--; 2882 } 2883 } 2884 free(oo); 2885 } 2886 for (i = 0; i < ol; i++) 2887 if (!o[i]) { 2888 int j; 2889 int m = 0; 2890 int mj = 0; 2891 for (j = 0; j < ol; j++) 2892 if (o[j] > m) { 2893 m = o[j]; 2894 mj = j; 2895 } 2896 if (m) { 2897 o[i] = 1; 2898 o[mj]--; 2899 } 2900 } 2901 } 2902 2903 static void 2904 html_frameset(unsigned char *a) 2905 { 2906 int x = 0, y = 0; /* against warning */ 2907 struct frameset_param fp; 2908 unsigned char *c, *d; 2909 if (!d_opt->frames || !special_f(ff, SP_USED, NULL)) 2910 return; 2911 if (!(c = get_attr_val(a, cast_uchar "cols"))) 2912 c = stracpy(cast_uchar "100%"); 2913 if (!(d = get_attr_val(a, cast_uchar "rows"))) 2914 d = stracpy(cast_uchar "100%"); 2915 if (!html_top.frameset) { 2916 x = d_opt->xw; 2917 y = d_opt->yw; 2918 } else { 2919 struct frameset_desc *f = html_top.frameset; 2920 if (f->yp >= f->y) 2921 goto free_cd; 2922 x = f->f[f->xp + f->yp * f->x].xw; 2923 y = f->f[f->xp + f->yp * f->x].yw; 2924 } 2925 parse_frame_widths(c, x, HTML_FRAME_CHAR_WIDTH, &fp.xw, &fp.x); 2926 parse_frame_widths(d, y, HTML_FRAME_CHAR_HEIGHT, &fp.yw, &fp.y); 2927 fp.parent = html_top.frameset; 2928 if (fp.x && fp.y) { 2929 html_top.frameset = special_f(ff, SP_FRAMESET, &fp); 2930 } 2931 free(fp.xw); 2932 free(fp.yw); 2933 free_cd: 2934 free(c); 2935 free(d); 2936 } 2937 2938 static void 2939 html_meta(unsigned char *a) 2940 { 2941 unsigned char *prop; 2942 if ((prop = get_attr_val(a, cast_uchar "property"))) { 2943 if (!strcmp(cast_const_char prop, "og:image")) { 2944 unsigned char *host = get_host_name(format_.href_base); 2945 if (host) { 2946 if (strstr(cast_const_char host, "facebook.") 2947 || strstr(cast_const_char host, "flickr.") 2948 || strstr(cast_const_char host, "imgur.") 2949 || strstr(cast_const_char host, 2950 "instagram.") 2951 || strstr(cast_const_char host, "mastadon.") 2952 || strstr(cast_const_char host, 2953 "pinterest.") 2954 || strstr(cast_const_char host, "twitter.")) 2955 html_img(a); 2956 free(host); 2957 } 2958 } 2959 free(prop); 2960 } 2961 } 2962 2963 static void 2964 html_link(unsigned char *a) 2965 { 2966 unsigned char *name, *url, *title; 2967 if ((name = get_attr_val(a, cast_uchar "type"))) { 2968 if (casestrcmp(name, cast_uchar "text/html")) { 2969 free(name); 2970 return; 2971 } 2972 free(name); 2973 } 2974 if (!(url = get_url_val(a, cast_uchar "href"))) 2975 return; 2976 if (!(name = get_attr_val(a, cast_uchar "rel"))) 2977 if (!(name = get_attr_val(a, cast_uchar "rev"))) 2978 name = get_attr_val(a, cast_uchar "ref"); 2979 if (name) { 2980 unsigned char *lang; 2981 if ((lang = get_attr_val(a, cast_uchar "hreflang"))) { 2982 add_to_strn(&name, cast_uchar " "); 2983 add_to_strn(&name, lang); 2984 free(lang); 2985 } 2986 } 2987 if (!name) { 2988 if (has_attr(a, cast_uchar "title")) 2989 name = stracpy(cast_uchar ""); 2990 else { 2991 name = get_attr_val(a, cast_uchar "href"); 2992 if (!name) 2993 name = stracpy(cast_uchar "Link"); 2994 } 2995 } 2996 if (!casecmp(name, cast_uchar "schema", 6) 2997 || !casecmp(name, cast_uchar "mw-", 3) 2998 || !casestrcmp(name, cast_uchar "Edit-Time-Data") 2999 || !casestrcmp(name, cast_uchar "File-List") 3000 || !casestrcmp(name, cast_uchar "alternate stylesheet") 3001 || !casestrcmp(name, cast_uchar "generator-home") 3002 || !casestrcmp(name, cast_uchar "https://api.w.org/") 3003 || !casestrcmp(name, cast_uchar "https://github.com/WP-API/WP-API") 3004 || !casestrcmp(name, cast_uchar "made") 3005 || !casestrcmp(name, cast_uchar "manifest") 3006 || !casestrcmp(name, cast_uchar "meta") 3007 || !casestrcmp(name, cast_uchar "pingback") 3008 || !casestrcmp(name, cast_uchar "preconnect") 3009 || !casestrcmp(name, cast_uchar "stylesheet") 3010 || casestrstr(name, cast_uchar "icon") || 0) 3011 goto skip; 3012 if (!casestrcmp(name, cast_uchar "prefetch") 3013 || !casestrcmp(name, cast_uchar "prerender") 3014 || !casestrcmp(name, cast_uchar "preload")) { 3015 unsigned char *pre_url = join_urls(format_.href_base, url); 3016 if (!dmp) 3017 load_url(pre_url, format_.href_base, NULL, PRI_PRELOAD, 3018 NC_ALWAYS_CACHE, 0, 0, 0); 3019 free(pre_url); 3020 goto skip; 3021 } 3022 if (!casestrcmp(name, cast_uchar "dns-prefetch")) { 3023 unsigned char *pre_url, *host; 3024 if (dmp || *proxies.socks_proxy || proxies.only_proxies) 3025 goto skip; 3026 pre_url = join_urls(format_.href_base, url); 3027 if (get_proxy_string(pre_url) || is_noproxy_url(pre_url)) { 3028 free(pre_url); 3029 goto skip; 3030 } 3031 host = get_host_name(pre_url); 3032 free(pre_url); 3033 free(host); 3034 goto skip; 3035 } 3036 if ((title = get_attr_val(a, cast_uchar "title"))) { 3037 if (*name) 3038 add_to_strn(&name, cast_uchar ": "); 3039 add_to_strn(&name, title); 3040 free(title); 3041 } 3042 put_link_line(cast_uchar "Link: ", name, url, format_.target_base); 3043 skip: 3044 free(name); 3045 free(url); 3046 } 3047 3048 struct element_info { 3049 char *name; 3050 void (*func)(unsigned char *); 3051 int linebreak; 3052 int nopair; /* Somehow relates to paired elements */ 3053 }; 3054 3055 static struct element_info elements[] = { 3056 {"SPAN", html_span, 0, 0}, 3057 { "B", html_bold, 0, 0}, 3058 { "STRONG", html_bold, 0, 0}, 3059 { "DFN", html_bold, 0, 0}, 3060 { "I", html_italic, 0, 0}, 3061 { "Q", html_italic, 0, 0}, 3062 { "CITE", html_italic, 0, 0}, 3063 { "EM", html_italic, 0, 0}, 3064 { "ABBR", html_italic, 0, 0}, 3065 { "U", html_underline, 0, 0}, 3066 { "S", html_underline, 0, 0}, 3067 { "STRIKE", html_underline, 0, 0}, 3068 { "FIXED", html_fixed, 0, 0}, 3069 { "CODE", html_fixed, 0, 0}, 3070 { "TT", html_fixed, 0, 0}, 3071 { "SAMP", html_fixed, 0, 0}, 3072 { "SUB", html_sub, 0, 0}, 3073 { "SUP", html_sup, 0, 0}, 3074 { "FONT", html_font, 0, 0}, 3075 { "INVERT", html_invert, 0, 0}, 3076 { "A", html_a, 0, 2}, 3077 { "IMG", html_img, 0, 1}, 3078 { "IMAGE", html_img, 0, 1}, 3079 { "OBJECT", html_object, 0, 0}, 3080 { "EMBED", html_embed, 0, 1}, 3081 3082 { "BASE", html_base, 0, 1}, 3083 { "BASEFONT", html_font, 0, 1}, 3084 3085 { "BODY", html_body, 0, 0}, 3086 3087 /* {"HEAD", html_skip, 0, 0},*/ 3088 { "TITLE", html_title, 0, 0}, 3089 { "SCRIPT", html_script, 0, 0}, 3090 { "STYLE", html_style, 0, 0}, 3091 { "NOEMBED", html_skip, 0, 0}, 3092 3093 { "BR", html_br, 1, 1}, 3094 { "DIV", html_div, 1, 0}, 3095 { "CENTER", html_center, 1, 0}, 3096 { "CAPTION", html_center, 1, 0}, 3097 { "P", html_p, 2, 2}, 3098 { "HR", html_hr, 2, 1}, 3099 { "H1", html_h1, 2, 2}, 3100 { "H2", html_h2, 2, 2}, 3101 { "H3", html_h3, 2, 2}, 3102 { "H4", html_h4, 2, 2}, 3103 { "H5", html_h5, 2, 2}, 3104 { "H6", html_h6, 2, 2}, 3105 { "BLOCKQUOTE", html_blockquote, 2, 0}, 3106 { "ADDRESS", html_address, 2, 0}, 3107 { "PRE", html_pre, 2, 0}, 3108 { "LISTING", html_pre, 2, 0}, 3109 3110 { "UL", html_ul, 1, 0}, 3111 { "DIR", html_ul, 1, 0}, 3112 { "MENU", html_ul, 1, 0}, 3113 { "OL", html_ol, 1, 0}, 3114 { "LI", html_li, 1, 3}, 3115 { "DL", html_dl, 1, 0}, 3116 { "DT", html_dt, 1, 1}, 3117 { "DD", html_dd, 1, 1}, 3118 3119 { "TABLE", html_table, 2, 0}, 3120 { "TR", html_tr, 1, 0}, 3121 { "TD", html_td, 0, 0}, 3122 { "TH", html_th, 0, 0}, 3123 3124 { "FORM", html_form, 1, 0}, 3125 { "INPUT", html_input, 0, 1}, 3126 { "TEXTAREA", html_textarea, 0, 1}, 3127 { "SELECT", html_select, 0, 0}, 3128 { "OPTION", html_option, 1, 1}, 3129 { "BUTTON", html_button, 0, 0}, 3130 3131 { "META", html_meta, 0, 1}, 3132 { "LINK", html_link, 0, 1}, 3133 { "IFRAME", html_iframe, 1, 1}, 3134 { "FRAME", html_frame, 1, 1}, 3135 { "FRAMESET", html_frameset, 1, 0}, 3136 { "NOFRAMES", html_noframes, 0, 0}, 3137 }; 3138 3139 unsigned char * 3140 skip_comment(unsigned char *html, unsigned char *eof) 3141 { 3142 int comm = eof - html >= 4 && html[2] == '-' && html[3] == '-'; 3143 html += comm ? 4 : 2; 3144 while (html < eof) { 3145 if (!comm && html[0] == '>') 3146 return html + 1; 3147 if (comm && eof - html >= 2 && html[0] == '-' 3148 && html[1] == '-') { 3149 html += 2; 3150 while (html < eof && (*html == '-' || *html == '!')) 3151 html++; 3152 while (html < eof && WHITECHAR(*html)) 3153 html++; 3154 if (html >= eof) 3155 return eof; 3156 if (*html == '>') 3157 return html + 1; 3158 continue; 3159 } 3160 html++; 3161 } 3162 return eof; 3163 } 3164 3165 static void 3166 process_head(unsigned char *head) 3167 { 3168 unsigned char *r, *p; 3169 struct refresh_param rp; 3170 if ((r = parse_http_header(head, cast_uchar "Refresh", NULL))) { 3171 if (!d_opt->auto_refresh) { 3172 if ((p = parse_header_param(r, cast_uchar "URL", 0)) 3173 || (p = parse_header_param(r, cast_uchar "", 0))) { 3174 put_link_line(cast_uchar "Refresh: ", p, p, 3175 d_opt->framename); 3176 free(p); 3177 } 3178 } else { 3179 rp.url = parse_header_param(r, cast_uchar "URL", 0); 3180 if (!rp.url) 3181 rp.url = 3182 parse_header_param(r, cast_uchar "", 0); 3183 rp.time = atoi(cast_const_char r); 3184 if (rp.time < 1) 3185 rp.time = 1; 3186 special_f(ff, SP_REFRESH, &rp); 3187 free(rp.url); 3188 } 3189 free(r); 3190 } 3191 } 3192 3193 static int 3194 qd(unsigned char *html, unsigned char *eof, int *len) 3195 { 3196 int l; 3197 *len = 1; 3198 if (html >= eof) { 3199 internal("qd: out of data, html == %p, eof == %p", html, eof); 3200 return -1; 3201 } 3202 if (html[0] != '&' || d_opt->plain & 1) 3203 return html[0]; 3204 if (eof - html >= 5 && !memcmp(html + 1, "Tab;", 4)) { 3205 *len = 5; 3206 return 9; 3207 } 3208 if (eof - html <= 1) 3209 return -1; 3210 if (html[1] != '#') 3211 return -1; 3212 for (l = 2; l < 10 && eof - html > l; l++) 3213 if (html[l] == ';') { 3214 int n = get_entity_number(html + 2, l - 2); 3215 if (n >= 0) { 3216 *len = l + 1; 3217 return n; 3218 } 3219 break; 3220 } 3221 return -1; 3222 } 3223 3224 void 3225 parse_html(unsigned char *html, unsigned char *eof, 3226 void (*put_chars)(void *, unsigned char *, int), 3227 void (*line_break)(void *), void *(*special)(void *, int, ...), 3228 void *f, unsigned char *head) 3229 { 3230 unsigned char *lt; 3231 3232 html_format_changed = 1; 3233 putsp = -1; 3234 line_breax = table_level ? 2 : 1; 3235 pos = 0; 3236 was_br = 0; 3237 3238 #define set_globals \ 3239 do { \ 3240 put_chars_f = put_chars; \ 3241 line_break_f = line_break; \ 3242 special_f = special; \ 3243 ff = f; \ 3244 eoff = eof; \ 3245 } while (0) 3246 3247 set_globals; 3248 3249 if (head) 3250 process_head(head); 3251 3252 set_lt: 3253 3254 /*set_globals;*/ 3255 3256 lt = html; 3257 while (html < eof) { 3258 unsigned char *name, *attr, *end; 3259 unsigned char *a; 3260 int namelen; 3261 struct element_info *ei; 3262 int inv; 3263 if (par_format.align != AL_NO 3264 && par_format.align != AL_NO_BREAKABLE 3265 && WHITECHAR(*html)) { 3266 unsigned char *h = html; 3267 /*if (putsp == -1) { 3268 while (html < eof && WHITECHAR(*html)) html++; 3269 goto set_lt; 3270 } 3271 putsp = 0;*/ 3272 while (h < eof && WHITECHAR(*h)) 3273 h++; 3274 if (eof - h > 1 && h[0] == '<' && h[1] == '/') { 3275 if (!parse_element(h, eof, &name, &namelen, 3276 &attr, &end)) { 3277 put_chrs(lt, (int)(html - lt)); 3278 lt = html = h; 3279 if (!html_top.invisible) 3280 putsp = 1; 3281 goto element; 3282 } 3283 } 3284 html++; 3285 if (!(pos + (html - lt - 1))) 3286 goto skip_w; /* ??? */ 3287 if (*(html - 1) == ' ') { 3288 if (html < eof && !WHITECHAR(*html)) 3289 continue; /* BIG performance win; not 3290 sure if it doesn't cause 3291 any bug */ 3292 put_chrs(lt, (int)(html - lt)); 3293 } else { 3294 put_chrs(lt, (int)(html - 1 - lt)); 3295 put_chrs(cast_uchar " ", 1); 3296 } 3297 skip_w: 3298 while (html < eof && WHITECHAR(*html)) 3299 html++; 3300 /*putsp = -1;*/ 3301 goto set_lt; 3302 } 3303 if (0) { 3304 put_sp: 3305 put_chrs(cast_uchar " ", 1); 3306 /*putsp = -1;*/ 3307 } 3308 if ((par_format.align == AL_NO 3309 || par_format.align == AL_NO_BREAKABLE) 3310 && (*html < 32 || *html == '&')) { 3311 int l; 3312 int q = qd(html, eof, &l); 3313 putsp = 0; 3314 if (q == 9) { 3315 put_chrs(lt, (int)(html - lt)); 3316 put_chrs(cast_uchar " ", 8 - pos % 8); 3317 html += l; 3318 goto set_lt; 3319 } else if (q == 13 || q == 10) { 3320 put_chrs(lt, (int)(html - lt)); 3321 next_break: 3322 html += l; 3323 if (q == 13 && eof - html > 1 3324 && qd(html, eof, &l) == 10) 3325 html += l; 3326 ln_break(1); 3327 if (html >= eof) 3328 goto set_lt; 3329 q = qd(html, eof, &l); 3330 if (q == 13 || q == 10) { 3331 line_breax = 0; 3332 goto next_break; 3333 } 3334 goto set_lt; 3335 } 3336 } 3337 if (*html < ' ') { 3338 int xl; 3339 put_chrs(lt, (int)(html - lt)); 3340 xl = 1; 3341 while (xl < 240 && eof - html > xl + 1 3342 && html[xl + 1] < ' ' && html[xl + 1] != 9 3343 && html[xl + 1] != 10 && html[xl + 1] != 13) 3344 xl++; 3345 put_chrs( 3346 cast_uchar 3347 ".................................................." 3348 ".................................................." 3349 ".................................................." 3350 ".................................................." 3351 "........................................", 3352 xl); 3353 html += xl; 3354 goto set_lt; 3355 } 3356 if (eof - html >= 2 && html[0] == '<' 3357 && (html[1] == '!' || html[1] == '?') && !(d_opt->plain & 1) 3358 && html_top.invisible != INVISIBLE_STYLE) { 3359 /*if (putsp == 1) goto put_sp; 3360 putsp = 0;*/ 3361 put_chrs(lt, (int)(html - lt)); 3362 html = skip_comment(html, eof); 3363 goto set_lt; 3364 } 3365 if (*html != '<' || d_opt->plain & 1 3366 || parse_element(html, eof, &name, &namelen, &attr, &end)) { 3367 /*if (putsp == 1) goto put_sp; 3368 putsp = 0;*/ 3369 html++; 3370 continue; 3371 } 3372 element: 3373 html_format_changed = 1; 3374 inv = *name == '/'; 3375 name += inv; 3376 namelen -= inv; 3377 if (html_top.invisible == INVISIBLE_SCRIPT 3378 && !(inv && namelen == 6 3379 && !casecmp(name, cast_uchar "SCRIPT", 6))) { 3380 html++; 3381 continue; 3382 } 3383 if (html_top.invisible == INVISIBLE_STYLE 3384 && !(inv && namelen == 5 3385 && !casecmp(name, cast_uchar "STYLE", 5))) { 3386 html++; 3387 continue; 3388 } 3389 if (!inv && putsp == 1 && !html_top.invisible) 3390 goto put_sp; 3391 put_chrs(lt, (int)(html - lt)); 3392 if (par_format.align != AL_NO 3393 && par_format.align != AL_NO_BREAKABLE) 3394 if (!inv && !putsp) { 3395 unsigned char *ee = end; 3396 unsigned char *nm; 3397 while (!parse_element(ee, eof, &nm, NULL, NULL, 3398 &ee)) 3399 if (*nm == '/') 3400 goto ng; 3401 if (ee < eof && WHITECHAR(*ee)) { 3402 /*putsp = -1;*/ 3403 put_chrs(cast_uchar " ", 1); 3404 } 3405 ng:; 3406 } 3407 html = end; 3408 for (ei = elements; ei != endof(elements); ei++) { 3409 if (strlen(cast_const_char ei->name) != (size_t)namelen 3410 || casecmp(cast_uchar ei->name, name, namelen)) 3411 continue; 3412 if (ei - elements > 4) { 3413 struct element_info e = *ei; 3414 memmove(elements + 1, elements, 3415 (ei - elements) 3416 * sizeof(struct element_info)); 3417 elements[0] = e; 3418 ei = &elements[0]; 3419 } 3420 if (!inv) { 3421 int display_none = 0; 3422 int noskip = 0; 3423 /* treat <br> literally in <pre> (fixes source 3424 * code viewer on github) */ 3425 if ((par_format.align == AL_NO 3426 || par_format.align == AL_NO_BREAKABLE) 3427 && !casestrcmp(cast_uchar ei->name, 3428 cast_uchar "BR")) 3429 line_breax = 0; 3430 ln_break(ei->linebreak); 3431 if ((a = get_attr_val(attr, cast_uchar "id"))) { 3432 special(f, SP_TAG, a); 3433 free(a); 3434 } 3435 if ((a = get_attr_val(attr, 3436 cast_uchar "style"))) { 3437 unsigned char *d, *s; 3438 3439 if (!casestrcmp(cast_uchar ei->name, 3440 cast_uchar "INPUT")) { 3441 unsigned char *aa = 3442 get_attr_val(attr, 3443 cast_uchar 3444 "type"); 3445 if (aa) { 3446 if (!casestrcmp( 3447 aa, cast_uchar 3448 "hidden")) 3449 noskip = 1; 3450 free(aa); 3451 } 3452 } 3453 3454 for (d = s = a; *s; s++) 3455 if (*s > ' ') 3456 *d++ = *s; 3457 *d = 0; 3458 display_none |= 3459 !casecmp(a, 3460 cast_uchar "display:none", 3461 12) 3462 && !noskip; 3463 free(a); 3464 } 3465 if (display_none) { 3466 if (ei->nopair == 1) 3467 goto set_lt; 3468 html_stack_dup(); 3469 html_top.name = name; 3470 html_top.namelen = namelen; 3471 html_top.options = attr; 3472 html_top.linebreak = 0; 3473 html_top.invisible = INVISIBLE; 3474 goto set_lt; 3475 } 3476 if (!html_top.invisible) { 3477 int a = par_format.align == AL_NO 3478 || par_format.align 3479 == AL_NO_BREAKABLE; 3480 struct par_attrib pa = par_format; 3481 if (ei->func == html_table 3482 && d_opt->tables 3483 && table_level 3484 < HTML_MAX_TABLE_LEVEL) { 3485 format_table(attr, html, eof, 3486 &html, f); 3487 set_globals; 3488 ln_break(2); 3489 goto set_lt; 3490 } 3491 if (ei->func == html_select) { 3492 if (!do_html_select(attr, html, 3493 eof, &html)) 3494 goto set_lt; 3495 } 3496 if (ei->func == html_textarea) { 3497 do_html_textarea(attr, html, 3498 eof, &html); 3499 goto set_lt; 3500 } 3501 if (ei->nopair == 2 3502 || ei->nopair == 3) { 3503 struct html_element *e = 3504 NULL; /* against warning */ 3505 struct list_head *le; 3506 if (ei->nopair == 2) { 3507 foreach ( 3508 struct html_element, 3509 e, le, html_stack) { 3510 if (e->dontkill) 3511 break; 3512 if (e->linebreak 3513 || !ei->linebreak) 3514 break; 3515 } 3516 } else 3517 foreach ( 3518 struct html_element, 3519 e, le, html_stack) { 3520 if (e->linebreak 3521 && !ei->linebreak) 3522 break; 3523 if (e->dontkill) 3524 break; 3525 if (e->namelen 3526 == namelen 3527 && !casecmp( 3528 e->name, 3529 name, 3530 e->namelen)) 3531 break; 3532 } 3533 if (e->namelen == namelen 3534 && !casecmp(e->name, name, 3535 e->namelen)) { 3536 while ( 3537 e->list_entry.prev 3538 != &html_stack) 3539 kill_html_stack_item( 3540 list_struct( 3541 e->list_entry 3542 .prev, 3543 struct 3544 html_element)); 3545 if (e->dontkill != 2) 3546 kill_html_stack_item( 3547 e); 3548 } 3549 } 3550 if (ei->nopair != 1) { 3551 html_stack_dup(); 3552 html_top.name = name; 3553 html_top.namelen = namelen; 3554 html_top.options = attr; 3555 html_top.linebreak = 3556 ei->linebreak; 3557 } 3558 if (ei->func) 3559 ei->func(attr); 3560 if (ei->func == html_a) 3561 html_a_special(attr, html, eof); 3562 if (ei->func != html_br) 3563 was_br = 0; 3564 if (a) 3565 par_format = pa; 3566 } else { 3567 if (!ei->nopair) { 3568 html_stack_dup(); 3569 html_top.name = name; 3570 html_top.namelen = namelen; 3571 html_top.options = attr; 3572 html_top.linebreak = 0; 3573 html_top.invisible = INVISIBLE; 3574 } 3575 } 3576 } else { 3577 struct html_element *e = NULL, *fx = NULL; 3578 struct list_head *le, *lfx; 3579 int lnb = 0; 3580 int xxx = 0; 3581 was_br = 0; 3582 if (ei->nopair == 1 || ei->nopair == 3) 3583 break; 3584 /*debug_stack();*/ 3585 foreach (struct html_element, e, le, 3586 html_stack) { 3587 if (e->linebreak && !ei->linebreak) 3588 xxx = 1; 3589 if (e->namelen != namelen 3590 || casecmp(e->name, name, 3591 e->namelen)) { 3592 if (e->dontkill) 3593 break; 3594 else 3595 continue; 3596 } 3597 if (xxx) { 3598 kill_html_stack_item(e); 3599 break; 3600 } 3601 foreachbackfrom (struct html_element, 3602 fx, lfx, html_stack, 3603 le) 3604 if (fx->linebreak > lnb) 3605 lnb = fx->linebreak; 3606 format_.fontsize = 3607 list_struct(e->list_entry.next, 3608 struct html_element) 3609 ->attr.fontsize; 3610 ln_break(lnb); 3611 while (e->list_entry.prev 3612 != &html_stack) 3613 kill_html_stack_item( 3614 list_struct( 3615 e->list_entry.prev, 3616 struct html_element)); 3617 kill_html_stack_item(e); 3618 break; 3619 } 3620 /*debug_stack();*/ 3621 } 3622 goto set_lt; 3623 } 3624 if (!inv) { 3625 if ((a = get_attr_val(attr, cast_uchar "id"))) { 3626 special(f, SP_TAG, a); 3627 free(a); 3628 } 3629 } 3630 goto set_lt; 3631 } 3632 put_chrs(lt, (int)(html - lt)); 3633 ln_break(1); 3634 putsp = -1; 3635 pos = 0; 3636 /*line_breax = 1;*/ 3637 was_br = 0; 3638 #undef set_globals 3639 } 3640 3641 static void 3642 scan_area_tag(unsigned char *attr, unsigned char *name, unsigned char **ptr, 3643 struct memory_list **ml) 3644 { 3645 unsigned char *v; 3646 if ((v = get_attr_val(attr, name))) { 3647 *ptr = v; 3648 add_to_ml(ml, v, NULL); 3649 } 3650 } 3651 3652 int 3653 get_image_map(unsigned char *head, unsigned char *s, unsigned char *eof, 3654 unsigned char *tag, struct menu_item **menu, 3655 struct memory_list **ml, unsigned char *href_base, 3656 unsigned char *target_base, int to, int def, int hdef, int gfx) 3657 { 3658 unsigned char *name, *attr, *al, *label, *href, *target; 3659 int namelen, lblen; 3660 struct link_def *ld; 3661 struct menu_item *nm; 3662 int nmenu = 0; 3663 int i; 3664 unsigned char *hd = NULL; 3665 size_t hdl = 0; 3666 struct conv_table *ct; 3667 if (head) 3668 hdl = add_to_str(&hd, hdl, head); 3669 hdl = scan_http_equiv(s, eof, &hd, hdl, NULL, NULL, NULL, NULL); 3670 if (!gfx) 3671 ct = get_convert_table(hd, to, def, NULL, NULL, hdef); 3672 else 3673 ct = convert_table; 3674 free(hd); 3675 *menu = mem_calloc(sizeof(struct menu_item)); 3676 se: 3677 while (s < eof && *s != '<') { 3678 sp: 3679 s++; 3680 } 3681 if (s >= eof) { 3682 free(*menu); 3683 return -1; 3684 } 3685 if (eof - s >= 2 && (s[1] == '!' || s[1] == '?')) { 3686 s = skip_comment(s, eof); 3687 goto se; 3688 } 3689 if (parse_element(s, eof, &name, &namelen, &attr, &s)) 3690 goto sp; 3691 if (namelen != 3 || casecmp(name, cast_uchar "MAP", 3)) 3692 goto se; 3693 if (tag && *tag) { 3694 if (!(al = get_attr_val(attr, cast_uchar "name"))) 3695 goto se; 3696 if (casestrcmp(al, tag)) { 3697 free(al); 3698 goto se; 3699 } 3700 free(al); 3701 } 3702 *ml = getml(NULL); 3703 se2: 3704 while (s < eof && *s != '<') { 3705 sp2: 3706 s++; 3707 } 3708 if (s >= eof) { 3709 freeml(*ml); 3710 free(*menu); 3711 return -1; 3712 } 3713 if (eof - s >= 2 && (s[1] == '!' || s[1] == '?')) { 3714 s = skip_comment(s, eof); 3715 goto se2; 3716 } 3717 if (parse_element(s, eof, &name, &namelen, &attr, &s)) 3718 goto sp2; 3719 if (namelen == 1 && !casecmp(name, cast_uchar "A", 1)) { 3720 unsigned char *ss; 3721 label = NULL; 3722 lblen = 0; 3723 se3: 3724 ss = s; 3725 se4: 3726 while (ss < eof && *ss != '<') 3727 ss++; 3728 if (ss >= eof) { 3729 free(label); 3730 freeml(*ml); 3731 free(*menu); 3732 return -1; 3733 } 3734 lblen = add_bytes_to_str(&label, lblen, s, ss - s); 3735 s = ss; 3736 if (eof - s >= 2 && (s[1] == '!' || s[1] == '?')) { 3737 s = skip_comment(s, eof); 3738 goto se3; 3739 } 3740 if (parse_element(s, eof, NULL, NULL, NULL, &ss)) { 3741 ss = s + 1; 3742 goto se4; 3743 } 3744 if (!((namelen == 1 && !casecmp(name, cast_uchar "A", 1)) 3745 || (namelen == 2 && !casecmp(name, cast_uchar "/A", 2)) 3746 || (namelen == 3 && !casecmp(name, cast_uchar "MAP", 3)) 3747 || (namelen == 4 && !casecmp(name, cast_uchar "/MAP", 4)) 3748 || (namelen == 4 && !casecmp(name, cast_uchar "AREA", 4)) 3749 || (namelen == 5 3750 && !casecmp(name, cast_uchar "/AREA", 5)))) { 3751 s = ss; 3752 goto se3; 3753 } 3754 } else if (namelen == 4 && !casecmp(name, cast_uchar "AREA", 4)) { 3755 unsigned char *l = get_attr_val(attr, cast_uchar "alt"); 3756 if (l) { 3757 label = 3758 !gfx ? convert_string( 3759 ct, l, (int)strlen(cast_const_char l), d_opt) 3760 : stracpy(l); 3761 free(l); 3762 } else 3763 label = NULL; 3764 } else if (namelen == 4 && !casecmp(name, cast_uchar "/MAP", 4)) 3765 goto done; 3766 else 3767 goto se2; 3768 href = get_url_val(attr, cast_uchar "href"); 3769 if (!(target = get_target(attr)) && !(target = stracpy(target_base))) 3770 target = stracpy(cast_uchar ""); 3771 ld = mem_calloc(sizeof(struct link_def)); 3772 if (href) { 3773 ld->link = join_urls(href_base, href); 3774 free(href); 3775 } 3776 ld->target = target; 3777 3778 add_to_ml(ml, ld, ld->target, NULL); 3779 if (ld->link) 3780 add_to_ml(ml, ld->link, NULL); 3781 scan_area_tag(attr, cast_uchar "shape", &ld->shape, ml); 3782 scan_area_tag(attr, cast_uchar "coords", &ld->coords, ml); 3783 scan_area_tag(attr, cast_uchar "onclick", &ld->onclick, ml); 3784 scan_area_tag(attr, cast_uchar "ondblclick", &ld->ondblclick, ml); 3785 scan_area_tag(attr, cast_uchar "onmousedown", &ld->onmousedown, ml); 3786 scan_area_tag(attr, cast_uchar "onmouseup", &ld->onmouseup, ml); 3787 scan_area_tag(attr, cast_uchar "onmouseover", &ld->onmouseover, ml); 3788 scan_area_tag(attr, cast_uchar "onmouseout", &ld->onmouseout, ml); 3789 scan_area_tag(attr, cast_uchar "onmousemove", &ld->onmousemove, ml); 3790 3791 if (label) 3792 clr_spaces(label, 1); 3793 if (!*label) { 3794 free(label); 3795 label = NULL; 3796 } 3797 ld->label = label; 3798 if (!label) 3799 label = stracpy(ld->link); 3800 if (!*label) { 3801 free(label); 3802 label = NULL; 3803 } 3804 if (!label) 3805 label = stracpy(ld->onclick); 3806 if (!*label) { 3807 free(label); 3808 label = NULL; 3809 } 3810 if (!label && !gfx) 3811 goto se2; 3812 if (!label) 3813 label = stracpy(ld->onmousedown); 3814 if (!*label) { 3815 free(label); 3816 label = NULL; 3817 } 3818 if (!label) 3819 label = stracpy(ld->onmouseup); 3820 if (!*label) { 3821 free(label); 3822 label = NULL; 3823 } 3824 if (!label) 3825 label = stracpy(ld->ondblclick); 3826 if (!*label) { 3827 free(label); 3828 label = NULL; 3829 } 3830 if (!label) 3831 label = stracpy(ld->onmouseover); 3832 if (!*label) { 3833 free(label); 3834 label = NULL; 3835 } 3836 if (!label) 3837 label = stracpy(ld->onmouseout); 3838 if (!*label) { 3839 free(label); 3840 label = NULL; 3841 } 3842 if (!label) 3843 label = stracpy(ld->onmousemove); 3844 if (!*label) { 3845 free(label); 3846 label = NULL; 3847 } 3848 if (!label) 3849 goto se2; 3850 add_to_ml(ml, label, NULL); 3851 3852 if (!gfx) 3853 for (i = 0; i < nmenu; i++) { 3854 struct link_def *ll = (*menu)[i].data; 3855 if (!xstrcmp(ll->link, ld->link) 3856 && !xstrcmp(ll->target, ld->target) 3857 && !xstrcmp(ll->onclick, ld->onclick)) 3858 goto se2; 3859 } 3860 if ((unsigned)nmenu > INT_MAX / sizeof(struct menu_item) - 2) 3861 overalloc(); 3862 nm = xrealloc(*menu, (nmenu + 2) * sizeof(struct menu_item)); 3863 *menu = nm; 3864 memset(&nm[nmenu], 0, 2 * sizeof(struct menu_item)); 3865 nm[nmenu].text = label; 3866 nm[nmenu].rtext = cast_uchar ""; 3867 nm[nmenu].hotkey = cast_uchar ""; 3868 nm[nmenu].func = map_selected; 3869 nm[nmenu].data = ld; 3870 nm[++nmenu].text = NULL; 3871 goto se2; 3872 done: 3873 add_to_ml(ml, *menu, NULL); 3874 return 0; 3875 } 3876 3877 size_t 3878 scan_http_equiv(unsigned char *s, unsigned char *eof, unsigned char **head, 3879 size_t hdl, unsigned char **title, unsigned char **background, 3880 unsigned char **bgcolor, int *pre_wrap) 3881 { 3882 unsigned char *name, *attr, *he, *c; 3883 int namelen; 3884 int tlen = 0; 3885 if (background) 3886 *background = NULL; 3887 if (bgcolor) 3888 *bgcolor = NULL; 3889 if (pre_wrap) 3890 *pre_wrap = 0; 3891 if (title) 3892 *title = NULL; 3893 hdl = add_chr_to_str(head, hdl, '\n'); 3894 se: 3895 while (s < eof && *s != '<') 3896 sp: 3897 s++; 3898 if (s >= eof) 3899 return hdl; 3900 if (eof - s >= 2 && (s[1] == '!' || s[1] == '?')) { 3901 s = skip_comment(s, eof); 3902 goto se; 3903 } 3904 if (parse_element(s, eof, &name, &namelen, &attr, &s)) 3905 goto sp; 3906 ps: 3907 if (namelen == 6 && !casecmp(name, cast_uchar "SCRIPT", 6)) { 3908 if (should_skip_script(attr)) { 3909 s = skip_element(s, eof, cast_uchar "SCRIPT", 0); 3910 goto se; 3911 } 3912 } 3913 if (namelen == 4 && !casecmp(name, cast_uchar "BODY", 4)) { 3914 if (background) { 3915 *background = 3916 get_attr_val(attr, cast_uchar "background"); 3917 background = NULL; 3918 } 3919 if (bgcolor) { 3920 *bgcolor = get_attr_val(attr, cast_uchar "bgcolor"); 3921 bgcolor = NULL; 3922 } 3923 /*return;*/ 3924 } 3925 if (title && !tlen && namelen == 5 3926 && !casecmp(name, cast_uchar "TITLE", 5)) { 3927 unsigned char *s1; 3928 xse: 3929 s1 = s; 3930 while (s < eof && *s != '<') 3931 xsp: 3932 s++; 3933 tlen = add_bytes_to_str(title, tlen, s1, s - s1); 3934 if (s >= eof) 3935 goto se; 3936 if (eof - s >= 2 && (s[1] == '!' || s[1] == '?')) { 3937 s = skip_comment(s, eof); 3938 goto xse; 3939 } 3940 if (parse_element(s, eof, &name, &namelen, &attr, &s)) { 3941 s1 = s; 3942 goto xsp; 3943 } 3944 clr_spaces(*title, 1); 3945 goto ps; 3946 } 3947 if (namelen == 5 && !casecmp(name, cast_uchar "STYLE", 5)) { 3948 while (s < eof && *s != '<') { 3949 if (*s == 'p' && eof - s >= 8 3950 && !strncmp(cast_const_char s, "pre-wrap", 8)) { 3951 if (pre_wrap) 3952 *pre_wrap = 1; 3953 } 3954 s++; 3955 } 3956 goto se; 3957 } 3958 if (namelen != 4 || casecmp(name, cast_uchar "META", 4)) 3959 goto se; 3960 if ((he = get_attr_val(attr, cast_uchar "charset"))) { 3961 hdl = add_to_str(head, hdl, cast_uchar "Charset: "); 3962 hdl = add_to_str(head, hdl, he); 3963 hdl = add_to_str(head, hdl, cast_uchar "\r\n"); 3964 free(he); 3965 } 3966 if (!(he = get_attr_val(attr, cast_uchar "http-equiv"))) 3967 goto se; 3968 c = get_attr_val(attr, cast_uchar "content"); 3969 hdl = add_to_str(head, hdl, he); 3970 if (c) { 3971 hdl = add_to_str(head, hdl, cast_uchar ": "); 3972 hdl = add_to_str(head, hdl, c); 3973 free(c); 3974 } 3975 free(he); 3976 hdl = add_to_str(head, hdl, cast_uchar "\r\n"); 3977 goto se; 3978 }