"Fossies" - the Fresh Open Source Software Archive

Member "links-1.03/html.c" (30 Sep 2011, 68871 Bytes) of archive /linux/www/links-1.03.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "html.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.8_vs_1.03.

    1 #include "links.h"
    2 
    3 #define format format_
    4 
    5 struct list_head html_stack = {&html_stack, &html_stack};
    6 
    7 static inline int atchr(unsigned char c)
    8 {
    9     return /*isA(c) ||*/ (c > ' ' && c != '=' && c != '<' && c != '>');
   10 }
   11 
   12 int parse_element(unsigned char *e, unsigned char *eof, unsigned char **name, int *namelen, unsigned char **attr, unsigned char **end)
   13 {
   14     if (eof - e < 3 || *(e++) != '<') return -1;
   15     if (name) *name = e;
   16     if (*e == '/') e++;
   17     if (!isA(*e)) return -1;
   18     goto x1;
   19     while (isA(*e) || *e == '=') {
   20         x1:
   21         e++;
   22         if (e >= eof) return -1;
   23     }
   24     /*if ((!WHITECHAR(*e) && *e != '>' && *e != '<' && *e != '/' && *e != ':')) return -1;*/
   25     if (name && namelen) *namelen = e - *name;
   26     while ((WHITECHAR(*e) || *e == '/' || *e == ':')) {
   27         e++;
   28         if (e >= eof) return -1;
   29     }
   30     if ((!atchr(*e) && *e != '>' && *e != '<')) return -1;
   31     if (attr) *attr = e;
   32     nextattr:
   33     while (WHITECHAR(*e)) {
   34         e++;
   35         if (e >= eof) return -1;
   36     }
   37     if ((!atchr(*e) && *e != '>' && *e != '<')) return -1;
   38     if (*e == '>' || *e == '<') goto en;
   39     while (atchr(*e)) {
   40         e++;
   41         if (e >= eof) return -1;
   42     }
   43     while (WHITECHAR(*e)) {
   44         e++;
   45         if (e >= eof) return -1;
   46     }
   47     if (*e != '=') goto endattr;
   48     goto x2;
   49     while (WHITECHAR(*e)) {
   50         x2:
   51         e++;
   52         if (e >= eof) return -1;
   53     }
   54     if (U(*e)) {
   55         unsigned char uu = *e;
   56         /*u:*/
   57         goto x3;
   58         while (e < eof && *e != uu && *e /*(WHITECHAR(*e) || *e > ' ')*/) {
   59             x3:
   60             e++;
   61             if (e >= eof) return -1;
   62         }
   63         if (*e < ' ') return -1;
   64         e++;
   65         if (e >= eof /*|| (!WHITECHAR(*e) && *e != uu && *e != '>' && *e != '<')*/) return -1;
   66         /*if (*e == uu) goto u;*/
   67     } else {
   68         while (!WHITECHAR(*e) && *e != '>' && *e != '<') {
   69             e++;
   70             if (e >= eof) return -1;
   71         }
   72     }
   73     while (WHITECHAR(*e)) {
   74         e++;
   75         if (e >= eof) return -1;
   76     }
   77     endattr:
   78     if (*e != '>' && *e != '<') goto nextattr;
   79     en:
   80     if (end) *end = e + (*e == '>');
   81     return 0;
   82 }
   83 
   84 #define add_chr(s, l, c)                        \
   85 do {                                    \
   86     if (!((l) & (32 - 1))) {                    \
   87         if ((unsigned)(l) > MAXINT - 32) overalloc();       \
   88         (s) = mem_realloc((s), (l) + 32);           \
   89     }                               \
   90     (s)[(l)++] = (c);                       \
   91 } while (0)
   92 
   93 int get_attr_val_nl = 0;
   94 
   95 /* parses html element attributes */
   96 /* e is attr pointer previously get from parse_element, DON'T PASS HERE ANY OTHER VALUE!!! */
   97 /* name is searched attribute */
   98 /* returns allocated string containing the attribute, or NULL on unsuccess */
   99 unsigned char *get_attr_val(unsigned char *e, unsigned char *name)
  100 {
  101     unsigned char *n;
  102     unsigned char *a = DUMMY;
  103     int l = 0;
  104     int f;
  105     aa:
  106     while (WHITECHAR(*e)) e++;
  107     if (*e == '>' || *e == '<') return NULL;
  108     n = name;
  109     while (*n && upcase(*e) == upcase(*n)) e++, n++;
  110     f = *n;
  111     while (atchr(*e)) f = 1, e++;
  112     while (WHITECHAR(*e)) e++;
  113     if (*e != '=') goto ea;
  114     e++;
  115     while (WHITECHAR(*e)) e++;
  116     if (!U(*e)) {
  117         while (!WHITECHAR(*e) && *e != '>' && *e != '<') {
  118             if (!f) add_chr(a, l, *e);
  119             e++;
  120         }
  121     } else {
  122         char uu = *e;
  123         /*a:*/
  124         e++;
  125         while (*e != uu) {
  126             if (!*e) {
  127                 mem_free(a);
  128                 return NULL;
  129             }
  130             if (!f) {
  131                 if (get_attr_val_nl == 2) goto exact;
  132                 if (*e != 13) {
  133                     if (*e != 9 && *e != 10) exact:add_chr(a, l, *e);
  134                     else if (!get_attr_val_nl) add_chr(a, l, ' ');
  135                 }
  136             }
  137             e++;
  138         }
  139         e++;
  140         /*if (*e == uu) {
  141             if (!f) add_chr(a, l, *e);
  142             goto a;
  143         }*/
  144     }
  145     ea:
  146     if (!f) {
  147         unsigned char *b;
  148         add_chr(a, l, 0);
  149         if (strchr(a, '&')) {
  150             unsigned char *aa = a;
  151             int c = d_opt->cp;
  152             d_opt->cp = d_opt->real_cp;
  153             a = convert_string(NULL, aa, strlen(aa));
  154             d_opt->cp = c;
  155             mem_free(aa);
  156         }
  157         while ((b = strchr(a, 1))) *b = ' ';
  158         if (get_attr_val_nl != 2) {
  159             for (b = a; *b == ' '; b++);
  160             if (b != a) memmove(a, b, strlen(b) + 1);
  161             for (b = a + strlen(a) - 1; b >= a && *b == ' '; b--) *b = 0;
  162         }
  163         set_mem_comment(a, name, strlen(name));
  164         return a;
  165     }
  166     goto aa;
  167 }
  168 
  169 int has_attr(unsigned char *e, unsigned char *name)
  170 {
  171     char *a;
  172     if (!(a = get_attr_val(e, name))) return 0;
  173     mem_free(a);
  174     return 1;
  175 }
  176 
  177 /*
  178 unsigned char *get_url_val(unsigned char *e, unsigned char *name)
  179 {
  180     int n = 0;
  181     unsigned char *p, *q, *pp;
  182     if (!(pp = get_attr_val(e, name))) return NULL;
  183     p = pp; q = pp;
  184     while (1) {
  185         if (*p == '#') n = 1;
  186         if ((*p = *q) != ' ' || n) p++;
  187         if (!*q) break;
  188         q++;
  189     }
  190     return pp;
  191 }
  192 */
  193 
  194 unsigned char *get_url_val(unsigned char *e, unsigned char *name)
  195 {
  196     unsigned char *a;
  197     get_attr_val_nl = 1;
  198     a = get_attr_val(e, name);
  199     get_attr_val_nl = 0;
  200     return a;
  201 }
  202 
  203 unsigned char *get_exact_attr_val(unsigned char *e, unsigned char *name)
  204 {
  205     unsigned char *a;
  206     get_attr_val_nl = 2;
  207     a = get_attr_val(e, name);
  208     get_attr_val_nl = 0;
  209     if (a) {
  210         unsigned char *x1, *x2;
  211         for (x1 = x2 = a; *x1; x1++, x2++) {
  212             if (x1[0] == '\r') {
  213                 *x2 = '\n';
  214                 if (x1[1] == '\n') x1++;
  215             } else {
  216                 *x2 = *x1;
  217             }
  218         }
  219         *x2 = 0;
  220     }
  221     return a;
  222 }
  223 
  224 struct {
  225     unsigned short int n;
  226     char *s;
  227 } roman_tbl[] = {
  228     { 1000, "m" },
  229     { 999,  "im" },
  230 /*  { 995,  "vm" },*/
  231     { 990,  "xm" },
  232 /*  { 950,  "lm" },*/
  233     { 900,  "cm" },
  234     { 500,  "d" },
  235     { 499,  "id" },
  236 /*  { 495,  "vd" },*/
  237     { 490,  "xd" },
  238 /*  { 450,  "ld" },*/
  239     { 400,  "cd" },
  240     { 100,  "c" },
  241     { 99,   "ic" },
  242 /*  { 95,   "vc" },*/
  243     { 90,   "xc" },
  244     { 50,   "l" },
  245     { 49,   "il" },
  246 /*  { 45,   "vl" },*/
  247     { 40,   "xl" },
  248     { 10,   "x" },
  249     { 9,    "ix" },
  250     { 5,    "v" },
  251     { 4,    "iv" },
  252     { 1,    "i" },
  253     { 0,    NULL },
  254 };
  255 
  256 void roman(char *p, unsigned n)
  257 {
  258     int i = 0;
  259     if (n >= 4000) {
  260         strcpy(p, "---");
  261         return;
  262     }
  263     if (!n) {
  264         strcpy(p, "o");
  265         return;
  266     }
  267     p[0] = 0;
  268     while (n) {
  269         while (roman_tbl[i].n <= n) {
  270             n -= roman_tbl[i].n;
  271             strcat(p, roman_tbl[i].s);
  272         }
  273         i++;
  274         if (n && !roman_tbl[i].n) {
  275             internal("BUG in roman number convertor");
  276             return;
  277         }
  278     }
  279 }
  280 
  281 struct color_spec {
  282     char *name;
  283     int rgb;
  284 };
  285 
  286 struct color_spec color_specs[] = {
  287     {"aliceblue",       0xF0F8FF},
  288     {"antiquewhite",    0xFAEBD7},
  289     {"aqua",        0x00FFFF},
  290     {"aquamarine",      0x7FFFD4},
  291     {"azure",       0xF0FFFF},
  292     {"beige",       0xF5F5DC},
  293     {"bisque",      0xFFE4C4},
  294     {"black",       0x000000},
  295     {"blanchedalmond",  0xFFEBCD},
  296     {"blue",        0x0000FF},
  297     {"blueviolet",      0x8A2BE2},
  298     {"brown",       0xA52A2A},
  299     {"burlywood",       0xDEB887},
  300     {"cadetblue",       0x5F9EA0},
  301     {"chartreuse",      0x7FFF00},
  302     {"chocolate",       0xD2691E},
  303     {"coral",       0xFF7F50},
  304     {"cornflowerblue",  0x6495ED},
  305     {"cornsilk",        0xFFF8DC},
  306     {"crimson",     0xDC143C},
  307     {"cyan",        0x00FFFF},
  308     {"darkblue",        0x00008B},
  309     {"darkcyan",        0x008B8B},
  310     {"darkgoldenrod",   0xB8860B},
  311     {"darkgray",        0xA9A9A9},
  312     {"darkgreen",       0x006400},
  313     {"darkkhaki",       0xBDB76B},
  314     {"darkmagenta",     0x8B008B},
  315     {"darkolivegreen",  0x556B2F},
  316     {"darkorange",      0xFF8C00},
  317     {"darkorchid",      0x9932CC},
  318     {"darkred",     0x8B0000},
  319     {"darksalmon",      0xE9967A},
  320     {"darkseagreen",    0x8FBC8F},
  321     {"darkslateblue",   0x483D8B},
  322     {"darkslategray",   0x2F4F4F},
  323     {"darkturquoise",   0x00CED1},
  324     {"darkviolet",      0x9400D3},
  325     {"deeppink",        0xFF1493},
  326     {"deepskyblue",     0x00BFFF},
  327     {"dimgray",     0x696969},
  328     {"dodgerblue",      0x1E90FF},
  329     {"firebrick",       0xB22222},
  330     {"floralwhite",     0xFFFAF0},
  331     {"forestgreen",     0x228B22},
  332     {"fuchsia",     0xFF00FF},
  333     {"gainsboro",       0xDCDCDC},
  334     {"ghostwhite",      0xF8F8FF},
  335     {"gold",        0xFFD700},
  336     {"goldenrod",       0xDAA520},
  337     {"gray",        0x808080},
  338     {"green",       0x008000},
  339     {"greenyellow",     0xADFF2F},
  340     {"honeydew",        0xF0FFF0},
  341     {"hotpink",     0xFF69B4},
  342     {"indianred",       0xCD5C5C},
  343     {"indigo",      0x4B0082},
  344     {"ivory",       0xFFFFF0},
  345     {"khaki",       0xF0E68C},
  346     {"lavender",        0xE6E6FA},
  347     {"lavenderblush",   0xFFF0F5},
  348     {"lawngreen",       0x7CFC00},
  349     {"lemonchiffon",    0xFFFACD},
  350     {"lightblue",       0xADD8E6},
  351     {"lightcoral",      0xF08080},
  352     {"lightcyan",       0xE0FFFF},
  353     {"lightgoldenrodyellow",    0xFAFAD2},
  354     {"lightgreen",      0x90EE90},
  355     {"lightgrey",       0xD3D3D3},
  356     {"lightpink",       0xFFB6C1},
  357     {"lightsalmon",     0xFFA07A},
  358     {"lightseagreen",   0x20B2AA},
  359     {"lightskyblue",    0x87CEFA},
  360     {"lightslategray",  0x778899},
  361     {"lightsteelblue",  0xB0C4DE},
  362     {"lightyellow",     0xFFFFE0},
  363     {"lime",        0x00FF00},
  364     {"limegreen",       0x32CD32},
  365     {"linen",       0xFAF0E6},
  366     {"magenta",     0xFF00FF},
  367     {"maroon",      0x800000},
  368     {"mediumaquamarine",    0x66CDAA},
  369     {"mediumblue",      0x0000CD},
  370     {"mediumorchid",    0xBA55D3},
  371     {"mediumpurple",    0x9370DB},
  372     {"mediumseagreen",  0x3CB371},
  373     {"mediumslateblue", 0x7B68EE},
  374     {"mediumspringgreen",   0x00FA9A},
  375     {"mediumturquoise", 0x48D1CC},
  376     {"mediumvioletred", 0xC71585},
  377     {"midnightblue",    0x191970},
  378     {"mintcream",       0xF5FFFA},
  379     {"mistyrose",       0xFFE4E1},
  380     {"moccasin",        0xFFE4B5},
  381     {"navajowhite",     0xFFDEAD},
  382     {"navy",        0x000080},
  383     {"oldlace",     0xFDF5E6},
  384     {"olive",       0x808000},
  385     {"olivedrab",       0x6B8E23},
  386     {"orange",      0xFFA500},
  387     {"orangered",       0xFF4500},
  388     {"orchid",      0xDA70D6},
  389     {"palegoldenrod",   0xEEE8AA},
  390     {"palegreen",       0x98FB98},
  391     {"paleturquoise",   0xAFEEEE},
  392     {"palevioletred",   0xDB7093},
  393     {"papayawhip",      0xFFEFD5},
  394     {"peachpuff",       0xFFDAB9},
  395     {"peru",        0xCD853F},
  396     {"pink",        0xFFC0CB},
  397     {"plum",        0xDDA0DD},
  398     {"powderblue",      0xB0E0E6},
  399     {"purple",      0x800080},
  400     {"red",         0xFF0000},
  401     {"rosybrown",       0xBC8F8F},
  402     {"royalblue",       0x4169E1},
  403     {"saddlebrown",     0x8B4513},
  404     {"salmon",      0xFA8072},
  405     {"sandybrown",      0xF4A460},
  406     {"seagreen",        0x2E8B57},
  407     {"seashell",        0xFFF5EE},
  408     {"sienna",      0xA0522D},
  409     {"silver",      0xC0C0C0},
  410     {"skyblue",     0x87CEEB},
  411     {"slateblue",       0x6A5ACD},
  412     {"slategray",       0x708090},
  413     {"snow",        0xFFFAFA},
  414     {"springgreen",     0x00FF7F},
  415     {"steelblue",       0x4682B4},
  416     {"tan",         0xD2B48C},
  417     {"teal",        0x008080},
  418     {"thistle",     0xD8BFD8},
  419     {"tomato",      0xFF6347},
  420     {"turquoise",       0x40E0D0},
  421     {"violet",      0xEE82EE},
  422     {"wheat",       0xF5DEB3},
  423     {"white",       0xFFFFFF},
  424     {"whitesmoke",      0xF5F5F5},
  425     {"yellow",      0xFFFF00},
  426     {"yellowgreen",     0x9ACD32},
  427 };
  428 
  429 #define endof(T) ((T)+sizeof(T)/sizeof(*(T)))
  430 
  431 int decode_color(unsigned char *str, struct rgb *col)
  432 {
  433     unsigned long ch;
  434     if (*str != '#') {
  435         struct color_spec *cs;
  436         for (cs = color_specs; cs < endof(color_specs); cs++)
  437             if (!strcasecmp(cs->name, str)) {
  438                 ch = cs->rgb;
  439                 goto found;
  440             }
  441         str--;
  442     }
  443     str++;
  444     if (strlen(str) == 6) {
  445         char *end;
  446         ch = strtoul(str, &end, 16);
  447         if (!*end && ch < 0x1000000) {
  448 found:
  449             col->r = ch / 0x10000;
  450             col->g = ch / 0x100 % 0x100;
  451             col->b = ch % 0x100;
  452             return 0;
  453         }
  454     }
  455     return -1;
  456 }
  457 
  458 int get_color(unsigned char *a, unsigned char *c, struct rgb *rgb)
  459 {
  460     char *at;
  461     int r = -1;
  462     if (d_opt->col >= 1) if ((at = get_attr_val(a, c))) {
  463         r = decode_color(at, rgb);
  464         mem_free(at);
  465     }
  466     return r;
  467 }
  468 
  469 int get_bgcolor(unsigned char *a, struct rgb *rgb)
  470 {
  471     if (d_opt->col < 2) return -1;
  472     return get_color(a, "bgcolor", rgb);
  473 }
  474 
  475 unsigned char *get_target(unsigned char *a)
  476 {
  477     unsigned char *v = get_attr_val(a, "target");
  478     if (v) {
  479         if (!strcasecmp(v, "_self")) {
  480             mem_free(v);
  481             v = stracpy(d_opt->framename);
  482         }
  483     }
  484     return v;
  485 }
  486 
  487 void kill_html_stack_item(struct html_element *e)
  488 {
  489     if (e->dontkill == 2) {
  490         internal("trying to kill unkillable element");
  491         return;
  492     }
  493     if (!e || (void *)e == &html_stack) {
  494         internal("trying to free bad html element");
  495         return;
  496     }
  497     if (e->attr.link) mem_free(e->attr.link);
  498     if (e->attr.target) mem_free(e->attr.target);
  499     if (e->attr.image) mem_free(e->attr.image);
  500     if (e->attr.href_base) mem_free(e->attr.href_base);
  501     if (e->attr.target_base) mem_free(e->attr.target_base);
  502     if (e->attr.select) mem_free(e->attr.select);
  503     del_from_list(e);
  504     mem_free(e);
  505     /*if ((void *)(html_stack.next) == &html_stack || !html_stack.next) {
  506         debug("killing last element");
  507     }*/
  508 }
  509 
  510 static inline void kill_elem(char *e)
  511 {
  512     if ((size_t)html_top.namelen == strlen(e) && !casecmp(html_top.name, e, html_top.namelen))
  513         kill_html_stack_item(&html_top);
  514 }
  515 
  516 #ifdef DEBUG
  517 void debug_stack()
  518 {
  519     struct html_element *e;
  520     printf("HTML stack debug: \n");
  521     foreachback(e, html_stack) {
  522         int i;
  523         printf("\"");
  524         for (i = 0; i < e->namelen; i++) printf("%c", e->name[i]);
  525         printf("\"\n");
  526     }
  527     printf("%c", 7);
  528     fflush(stdout);
  529     sleep(1);
  530 }
  531 #endif
  532 
  533 void html_stack_dup()
  534 {
  535     struct html_element *e;
  536     struct html_element *ep;
  537     if ((void *)(ep = html_stack.next) == &html_stack || !html_stack.next) {
  538         internal("html stack empty");
  539         return;
  540     }
  541     e = mem_alloc(sizeof(struct html_element));
  542     memcpy(e, ep, sizeof(struct html_element));
  543     e->attr.link = stracpy(ep->attr.link);
  544     e->attr.target = stracpy(ep->attr.target);
  545     e->attr.image = stracpy(ep->attr.image);
  546     e->attr.href_base = stracpy(ep->attr.href_base);
  547     e->attr.target_base = stracpy(ep->attr.target_base);
  548     e->attr.select = stracpy(ep->attr.select);
  549     /*if (e->name) {
  550         if (e->attr.link) set_mem_comment(e->attr.link, e->name, e->namelen);
  551         if (e->attr.target) set_mem_comment(e->attr.target, e->name, e->namelen);
  552         if (e->attr.image) set_mem_comment(e->attr.image, e->name, e->namelen);
  553         if (e->attr.href_base) set_mem_comment(e->attr.href_base, e->name, e->namelen);
  554         if (e->attr.target_base) set_mem_comment(e->attr.target_base, e->name, e->namelen);
  555         if (e->attr.select) set_mem_comment(e->attr.select, e->name, e->namelen);
  556     }*/
  557     e->name = e->options = NULL;
  558     e->namelen = 0;
  559     e->dontkill = 0;
  560     add_to_list(html_stack, e);
  561 }
  562 
  563 void *ff;
  564 int (*put_chars_f)(void *, unsigned char *, int);
  565 void (*line_break_f)(void *);
  566 void *(*special_f)(void *, int, ...);
  567 
  568 unsigned char *eoff;
  569 unsigned char *eofff;
  570 unsigned char *startf;
  571 
  572 int line_breax;
  573 int pos;
  574 int putsp;
  575 
  576 int was_br;
  577 int table_level;
  578 int empty_format;
  579 
  580 void ln_break(int n, void (*line_break)(void *), void *f)
  581 {
  582     if (!n || html_top.invisible) return;
  583     while (n > line_breax) line_breax++, line_break(f);
  584     pos = 0;
  585     putsp = -1;
  586 }
  587 
  588 void put_chrs(unsigned char *start, int len, int (*put_chars)(void *, unsigned char *, int), void *f)
  589 {
  590     if (par_format.align == AL_NO) putsp = 0;
  591     if (!len || html_top.invisible) return;
  592     if (putsp == 1) pos += put_chars(f, " ", 1), putsp = -1;
  593     if (putsp == -1) {
  594         if (start[0] == ' ') start++, len--;
  595         putsp = 0;
  596     }
  597     if (!len) {
  598         putsp = -1;
  599         if (par_format.align == AL_NO) putsp = 0;
  600         return;
  601     }
  602     if (start[len - 1] == ' ') putsp = -1;
  603     if (par_format.align == AL_NO) putsp = 0;
  604     was_br = 0;
  605     pos += put_chars(f, start, len);
  606     line_breax = 0;
  607 }
  608 
  609 void kill_until(int ls, ...)
  610 {
  611     int l;
  612     struct html_element *e = &html_top;
  613     if (ls) e = e->next;
  614     while ((void *)e != &html_stack) {
  615         int sk = 0;
  616         va_list arg;
  617         va_start(arg, ls);
  618         while (1) {
  619             char *s = va_arg(arg, char *);
  620             if (!s) break;
  621             if (!*s) sk++;
  622             else if ((size_t)e->namelen == strlen(s) && !casecmp(e->name, s, strlen(s))) {
  623                 if (!sk) {
  624                     if (e->dontkill) break;
  625                     va_end(arg);
  626                     goto killll;
  627                 } else if (sk == 1) {
  628                     va_end(arg);
  629                     goto killl;
  630                 } else break;
  631             }
  632         }
  633         va_end(arg);
  634         if (e->dontkill || (e->namelen == 5 && !casecmp(e->name, "TABLE", 5))) break;
  635         if (e->namelen == 2 && upcase(e->name[0]) == 'T' && (upcase(e->name[1]) == 'D' || upcase(e->name[1]) == 'H' || upcase(e->name[1]) == 'R')) break;
  636         e = e->next;
  637     }
  638     return;
  639     killl:
  640     e = e->prev;
  641     killll:
  642     l = 0;
  643     while ((void *)e != &html_stack) {
  644         if (ls && e == html_stack.next) break;
  645         if (e->linebreak > l) l = e->linebreak;
  646         e = e->prev;
  647         kill_html_stack_item(e->next);
  648     }
  649     ln_break(l, line_break_f, ff);
  650 }
  651 
  652 int get_num(unsigned char *a, unsigned char *n)
  653 {
  654     char *al;
  655     if ((al = get_attr_val(a, n))) {
  656         char *end;
  657         unsigned long s = strtoul(al, &end, 10);
  658         if (!*al || *end || s > 10000) s = -1;
  659         mem_free(al);
  660         return s;
  661     }
  662     return -1;
  663 }
  664 
  665 int parse_width(unsigned char *w, int trunc)
  666 {
  667     unsigned char *end;
  668     int p = 0;
  669     long s;
  670     int l;
  671     while (WHITECHAR(*w)) w++;
  672     for (l = 0; w[l] && w[l] != ','; l++) ;
  673     while (l && WHITECHAR(w[l - 1])) l--;
  674     if (!l) return -1;
  675     if (w[l - 1] == '%') l--, p = 1;
  676     while (l && WHITECHAR(w[l - 1])) l--;
  677     if (!l) return -1;
  678     s = strtoul((char *)w, (char **)(void *)&end, 10);
  679     if (end - w < l || s < 0 || s > 10000) return -1;
  680     if (p) {
  681         if (trunc) s = s * (par_format.width - par_format.leftmargin - par_format.rightmargin) / 100;
  682         else return -1;
  683     } else s = (s + (HTML_CHAR_WIDTH - 1) / 2) / HTML_CHAR_WIDTH;
  684     if (trunc && s > par_format.width - par_format.leftmargin - par_format.rightmargin) s = par_format.width - par_format.leftmargin - par_format.rightmargin;
  685     if (s < 0) s = 0;
  686     return s;
  687 }
  688 
  689 int get_width(unsigned char *a, unsigned char *n, int trunc)
  690 {
  691     int r;
  692     unsigned char *w;
  693     if (!(w = get_attr_val(a, n))) return -1;
  694     r = parse_width(w, trunc);
  695     mem_free(w);
  696     return r;
  697 }
  698 
  699 /*int form_num;
  700 struct form form = { 0, NULL, 0 };
  701 int g_ctrl_num;*/
  702 
  703 struct form form = { NULL, NULL, 0, 0 };
  704 
  705 unsigned char *last_form_tag;
  706 unsigned char *last_form_attr;
  707 unsigned char *last_input_tag;
  708 
  709 void put_link_line(unsigned char *prefix, unsigned char *linkname, unsigned char *link, unsigned char *target)
  710 {
  711     html_stack_dup();
  712     ln_break(1, line_break_f, ff);
  713     if (format.link) mem_free(format.link), format.link = NULL;
  714     if (format.target) mem_free(format.target), format.target = NULL;
  715     format.form = NULL;
  716     put_chrs(prefix, strlen(prefix), put_chars_f, ff);
  717     format.link = join_urls(format.href_base, link);
  718     format.target = stracpy(target);
  719     memcpy(&format.fg, &format.clink, sizeof(struct rgb));
  720     put_chrs(linkname, strlen(linkname), put_chars_f, ff);
  721     ln_break(1, line_break_f, ff);
  722     kill_html_stack_item(&html_top);
  723 }
  724 
  725 void html_span(unsigned char *a) { }
  726 void html_bold(unsigned char *a) { format.attr |= AT_BOLD; }
  727 void html_italic(unsigned char *a) { format.attr |= AT_ITALIC; }
  728 void html_underline(unsigned char *a) { format.attr |= AT_UNDERLINE; }
  729 void html_fixed(unsigned char *a) { format.attr |= AT_FIXED; }
  730 
  731 void html_a(unsigned char *a)
  732 {
  733     char *al;
  734     if ((al = get_url_val(a, "href"))) {
  735         char *all = al;
  736         while (all[0] == ' ') all++;
  737         while (all[0] && all[strlen(all) - 1] == ' ') all[strlen(all) - 1] = 0;
  738         if (format.link) mem_free(format.link);
  739         format.link = join_urls(format.href_base, all);
  740         mem_free(al);
  741         if ((al = get_target(a))) {
  742             if (format.target) mem_free(format.target);
  743             format.target = al;
  744         } else {
  745             if (format.target) mem_free(format.target);
  746             format.target = stracpy(format.target_base);
  747         }
  748         /*format.attr ^= AT_BOLD;*/
  749         memcpy(&format.fg, &format.clink, sizeof(struct rgb));
  750     } else kill_html_stack_item(&html_top);
  751     if ((al = get_attr_val(a, "name"))) {
  752         special_f(ff, SP_TAG, al);
  753         mem_free(al);
  754     }
  755 }
  756 
  757 void html_font(unsigned char *a)
  758 {
  759     char *al;
  760     if ((al = get_attr_val(a, "size"))) {
  761         int p = 0;
  762         unsigned long s;
  763         char *nn = al;
  764         char *end;
  765         if (*al == '+') p = 1, nn++;
  766         if (*al == '-') p = -1, nn++;
  767         s = strtoul(nn, &end, 10);
  768         if (*nn && !*end) {
  769             if (s > 7) s = 7;
  770             if (!p) format.fontsize = s;
  771             else format.fontsize += p * s;
  772             if (format.fontsize < 1) format.fontsize = 1;
  773             if (format.fontsize > 7) format.fontsize = 7;
  774         }
  775         mem_free(al);
  776     }
  777     get_color(a, "color", &format.fg);
  778 }
  779 
  780 void html_img(unsigned char *a)
  781 {
  782     unsigned char *al;
  783     int ismap, usemap = 0;
  784     /*put_chrs(" ", 1, put_chars_f, ff);*/
  785     if ((al = get_attr_val(a, "usemap"))) {
  786         unsigned char *u;
  787         usemap = 1;
  788         html_stack_dup();
  789         if (format.link) mem_free(format.link);
  790         if (format.form) format.form = NULL;
  791         u = join_urls(format.href_base, al);
  792         format.link = mem_alloc(strlen(u) + 5);
  793         strcpy(format.link, "MAP@");
  794         strcat(format.link, u);
  795         format.attr |= AT_BOLD;
  796         mem_free(u);
  797         mem_free(al);
  798     }
  799     ismap = format.link && has_attr(a, "ismap") && !usemap;
  800     if ((!(al = get_attr_val(a, "alt")) && !(al = get_attr_val(a, "title"))) || !*al) {
  801         if (al) mem_free(al);
  802         if (!d_opt->images && !format.link) return;
  803         if (usemap) al = stracpy("[USEMAP]");
  804         else if (ismap) al = stracpy("[ISMAP]");
  805         else al = stracpy("[IMG]");
  806     }
  807     if (format.image) mem_free(format.image), format.image = NULL;
  808     if (al) {
  809         unsigned char *s;
  810         if ((s = get_url_val(a, "src")) || (s = get_attr_val(a, "dynsrc"))) {
  811             format.image = join_urls(format.href_base, s);
  812             mem_free(s);
  813         }
  814         if (ismap) {
  815             unsigned char *h;
  816             html_stack_dup();
  817             h = stracpy(format.link);
  818             add_to_strn(&h, "?0,0");
  819             mem_free(format.link);
  820             format.link = h;
  821         }
  822         put_chrs(al, strlen(al), put_chars_f, ff);
  823         if (ismap) kill_html_stack_item(&html_top);
  824     }
  825     if (format.image) mem_free(format.image), format.image = NULL;
  826     mem_free(al);
  827     if (usemap) kill_html_stack_item(&html_top);
  828     /*put_chrs(" ", 1, put_chars_f, ff);*/
  829 }
  830 
  831 void html_body(unsigned char *a)
  832 {
  833     get_color(a, "text", &format.fg);
  834     get_color(a, "link", &format.clink);
  835     get_color(a, "vlink", &format.vlink);
  836     get_bgcolor(a, &format.bg);
  837     get_bgcolor(a, &par_format.bgcolor);
  838 }
  839 
  840 void html_skip(unsigned char *a) { html_top.invisible = html_top.dontkill = 1; }
  841 
  842 void html_title(unsigned char *a) { html_top.invisible = html_top.dontkill = 1; }
  843 
  844 void html_center(unsigned char *a)
  845 {
  846     par_format.align = AL_CENTER;
  847     if (!table_level) par_format.leftmargin = par_format.rightmargin = 0;
  848 }
  849 
  850 void html_linebrk(unsigned char *a)
  851 {
  852     char *al;
  853     if ((al = get_attr_val(a, "align"))) {
  854         if (!strcasecmp(al, "left")) par_format.align = AL_LEFT;
  855         if (!strcasecmp(al, "right")) par_format.align = AL_RIGHT;
  856         if (!strcasecmp(al, "center")) {
  857             par_format.align = AL_CENTER;
  858             if (!table_level) par_format.leftmargin = par_format.rightmargin = 0;
  859         }
  860         if (!strcasecmp(al, "justify")) par_format.align = AL_BLOCK;
  861         mem_free(al);
  862     }
  863 }
  864 
  865 void html_br(unsigned char *a)
  866 {
  867     html_linebrk(a);
  868     if (was_br) ln_break(2, line_break_f, ff);
  869     was_br = 1;
  870 }
  871 
  872 void html_form(unsigned char *a)
  873 {
  874     was_br = 1;
  875 }
  876 
  877 void html_p(unsigned char *a)
  878 {
  879     if (par_format.leftmargin < margin) par_format.leftmargin = margin;
  880     if (par_format.rightmargin < margin) par_format.rightmargin = margin;
  881     /*par_format.align = AL_LEFT;*/
  882     html_linebrk(a);
  883 }
  884 
  885 void html_address(unsigned char *a)
  886 {
  887     par_format.leftmargin += 1;
  888     par_format.align = AL_LEFT;
  889 }
  890 
  891 void html_blockquote(unsigned char *a)
  892 {
  893     par_format.leftmargin += 2;
  894     par_format.align = AL_LEFT;
  895 }
  896 
  897 void html_h(int h, char *a)
  898 {
  899     par_format.align = AL_LEFT;
  900     html_linebrk(a);
  901     switch (par_format.align) {
  902         case AL_LEFT:
  903             par_format.leftmargin = (h - 2) * 2;
  904             par_format.rightmargin = 0;
  905             break;
  906         case AL_RIGHT:
  907             par_format.leftmargin = 0;
  908             par_format.rightmargin = (h - 2) * 2;
  909             break;
  910         case AL_CENTER:
  911             par_format.leftmargin = par_format.rightmargin = 0;
  912             break;
  913         case AL_BLOCK:
  914             par_format.leftmargin = par_format.rightmargin = (h - 2) * 2;
  915             break;
  916     }
  917 }
  918 
  919 void html_h2(unsigned char *a) { html_h(2, a); }
  920 void html_h3(unsigned char *a) { html_h(3, a); }
  921 void html_h4(unsigned char *a) { html_h(4, a); }
  922 void html_h5(unsigned char *a) { html_h(5, a); }
  923 void html_h6(unsigned char *a) { html_h(6, a); }
  924 
  925 void html_pre(unsigned char *a)
  926 {
  927     par_format.align = AL_NO;
  928     par_format.leftmargin = par_format.leftmargin > 1;
  929     par_format.rightmargin = 0;
  930 }
  931 
  932 void html_hr(unsigned char *a)
  933 {
  934     int i/* = par_format.width - 10*/;
  935     unsigned char r = 205;
  936     int q = get_num(a, "size");
  937     if (q >= 0 && q < 2) r = 196;
  938     html_stack_dup();
  939     par_format.align = AL_CENTER;
  940     if (format.link) mem_free(format.link), format.link = NULL;
  941     format.form = NULL;
  942     html_linebrk(a);
  943     if (par_format.align == AL_BLOCK) par_format.align = AL_CENTER;
  944     par_format.leftmargin = margin;
  945     par_format.rightmargin = margin;
  946     if ((i = get_width(a, "width", 1)) == -1) i = par_format.width - 2 * margin - 4;
  947     format.attr = AT_GRAPHICS;
  948     special_f(ff, SP_NOWRAP, 1);
  949     while (i-- > 0) put_chrs(&r, 1, put_chars_f, ff);
  950     special_f(ff, SP_NOWRAP, 0);
  951     ln_break(2, line_break_f, ff);
  952     kill_html_stack_item(&html_top);
  953 }
  954 
  955 void html_table(unsigned char *a)
  956 {
  957     par_format.leftmargin = margin;
  958     par_format.rightmargin = margin;
  959     par_format.align = AL_LEFT;
  960     html_linebrk(a);
  961     format.attr = 0;
  962 }
  963 
  964 void html_tr(unsigned char *a)
  965 {
  966     html_linebrk(a);
  967 }
  968 
  969 void html_th(unsigned char *a)
  970 {
  971     /*html_linebrk(a);*/
  972     kill_until(1, "TD", "TH", "", "TR", "TABLE", NULL);
  973     format.attr |= AT_BOLD;
  974     put_chrs(" ", 1, put_chars_f, ff);
  975 }
  976 
  977 void html_td(unsigned char *a)
  978 {
  979     /*html_linebrk(a);*/
  980     kill_until(1, "TD", "TH", "", "TR", "TABLE", NULL);
  981     format.attr &= ~AT_BOLD;
  982     put_chrs(" ", 1, put_chars_f, ff);
  983 }
  984 
  985 void html_base(unsigned char *a)
  986 {
  987     char *al;
  988     if ((al = get_url_val(a, "href"))) {
  989         if (format.href_base) mem_free(format.href_base);
  990         format.href_base = join_urls(((struct html_element *)html_stack.prev)->attr.href_base, al);
  991         mem_free(al);
  992     }
  993     if ((al = get_target(a))) {
  994         if (format.target_base) mem_free(format.target_base);
  995         format.target_base = al;
  996     }
  997 }
  998 
  999 void html_ul(unsigned char *a)
 1000 {
 1001     char *al;
 1002     /*debug_stack();*/
 1003     par_format.list_level++;
 1004     par_format.list_number = 0;
 1005     par_format.flags = P_STAR;
 1006     if ((al = get_attr_val(a, "type"))) {
 1007         if (!strcasecmp(al, "disc") || !strcasecmp(al, "circle")) par_format.flags = P_O;
 1008         if (!strcasecmp(al, "square")) par_format.flags = P_PLUS;
 1009         mem_free(al);
 1010     }
 1011     if ((par_format.leftmargin += 2 + (par_format.list_level > 1)) > par_format.width * 2 / 3 && !table_level)
 1012         par_format.leftmargin = par_format.width * 2 / 3;
 1013     par_format.align = AL_LEFT;
 1014     html_top.dontkill = 1;
 1015 }
 1016 
 1017 void html_ol(unsigned char *a)
 1018 {
 1019     char *al;
 1020     int st;
 1021     par_format.list_level++;
 1022     st = get_num(a, "start");
 1023     if (st == -1) st = 1;
 1024     par_format.list_number = st;
 1025     par_format.flags = P_NUMBER;
 1026     if ((al = get_attr_val(a, "type"))) {
 1027         if (!strcmp(al, "1")) par_format.flags = P_NUMBER;
 1028         if (!strcmp(al, "a")) par_format.flags = P_alpha;
 1029         if (!strcmp(al, "A")) par_format.flags = P_ALPHA;
 1030         if (!strcmp(al, "r")) par_format.flags = P_roman;
 1031         if (!strcmp(al, "R")) par_format.flags = P_ROMAN;
 1032         if (!strcmp(al, "i")) par_format.flags = P_roman;
 1033         if (!strcmp(al, "I")) par_format.flags = P_ROMAN;
 1034         mem_free(al);
 1035     }
 1036     if ((par_format.leftmargin += (par_format.list_level > 1)) > par_format.width * 2 / 3 && !table_level)
 1037         par_format.leftmargin = par_format.width * 2 / 3;
 1038     par_format.align = AL_LEFT;
 1039     html_top.dontkill = 1;
 1040 }
 1041 
 1042 void html_li(unsigned char *a)
 1043 {
 1044     /*kill_until(0, "", "UL", "OL", NULL);*/
 1045     if (!par_format.list_number) {
 1046         char x[7] = "*&nbsp;";
 1047         if ((par_format.flags & P_LISTMASK) == P_O) x[0] = 'o';
 1048         if ((par_format.flags & P_LISTMASK) == P_PLUS) x[0] = '+';
 1049         put_chrs(x, 7, put_chars_f, ff);
 1050         par_format.leftmargin += 2;
 1051         par_format.align = AL_LEFT;
 1052         putsp = -1;
 1053     } else {
 1054         char c = 0;
 1055         char n[32];
 1056         int t = par_format.flags & P_LISTMASK;
 1057         int s = get_num(a, "value");
 1058         if (s != -1) par_format.list_number = s;
 1059         if ((t != P_roman && t != P_ROMAN && par_format.list_number < 10) || t == P_alpha || t == P_ALPHA) put_chrs("&nbsp;", 6, put_chars_f, ff), c = 1;
 1060         if (t == P_ALPHA || t == P_alpha) {
 1061             n[0] = par_format.list_number ? (par_format.list_number - 1) % 26 + (t == P_ALPHA ? 'A' : 'a') : 0;
 1062             n[1] = 0;
 1063         } else if (t == P_ROMAN || t == P_roman) {
 1064             roman(n, par_format.list_number);
 1065             if (t == P_ROMAN) {
 1066                 char *x;
 1067                 for (x = n; *x; x++) *x = upcase(*x);
 1068             }
 1069         } else sprintf(n, "%d", par_format.list_number);
 1070         put_chrs(n, strlen(n), put_chars_f, ff);
 1071         put_chrs(".&nbsp;", 7, put_chars_f, ff);
 1072         par_format.leftmargin += strlen(n) + c + 2;
 1073         par_format.align = AL_LEFT;
 1074         html_top.next->parattr.list_number = par_format.list_number + 1;
 1075         par_format.list_number = 0;
 1076         putsp = -1;
 1077     }
 1078     line_breax = 2;
 1079 }
 1080 
 1081 void html_dl(unsigned char *a)
 1082 {
 1083     par_format.flags &= ~P_COMPACT;
 1084     if (has_attr(a, "compact")) par_format.flags |= P_COMPACT;
 1085     if (par_format.list_level) par_format.leftmargin += 5;
 1086     par_format.list_level++;
 1087     par_format.list_number = 0;
 1088     par_format.align = AL_LEFT;
 1089     par_format.dd_margin = par_format.leftmargin;
 1090     html_top.dontkill = 1;
 1091     if (!(par_format.flags & P_COMPACT)) {
 1092         ln_break(2, line_break_f, ff);
 1093         html_top.linebreak = 2;
 1094     }
 1095 }
 1096 
 1097 void html_dt(unsigned char *a)
 1098 {
 1099     kill_until(0, "", "DL", NULL);
 1100     par_format.align = AL_LEFT;
 1101     par_format.leftmargin = par_format.dd_margin;
 1102     if (!(par_format.flags & P_COMPACT) && !has_attr(a, "compact"))
 1103         ln_break(2, line_break_f, ff);
 1104 }
 1105 
 1106 void html_dd(unsigned char *a)
 1107 {
 1108     kill_until(0, "", "DL", NULL);
 1109     if ((par_format.leftmargin = par_format.dd_margin + (table_level ? 3 : 8)) > par_format.width * 2 / 3 && !table_level)
 1110         par_format.leftmargin = par_format.width * 2 / 3;
 1111     par_format.align = AL_LEFT;
 1112 }
 1113 
 1114 void get_html_form(unsigned char *a, struct form *form)
 1115 {
 1116     unsigned char *al;
 1117     unsigned char *ch;
 1118     form->method = FM_GET;
 1119     if ((al = get_attr_val(a, "method"))) {
 1120         if (!strcasecmp(al, "post")) {
 1121             char *ax;
 1122             form->method = FM_POST;
 1123             if ((ax = get_attr_val(a, "enctype"))) {
 1124                 if (!strcasecmp(ax, "multipart/form-data"))
 1125                     form->method = FM_POST_MP;
 1126                 mem_free(ax);
 1127             }
 1128         }
 1129         mem_free(al);
 1130     }
 1131     if ((al = get_url_val(a, "action"))) {
 1132         char *all = al;
 1133         while (all[0] == ' ') all++;
 1134         while (all[0] && all[strlen(all) - 1] == ' ') all[strlen(all) - 1] = 0;
 1135         form->action = join_urls(format.href_base, all);
 1136         mem_free(al);
 1137     } else {
 1138         if ((ch = strchr(form->action = stracpy(format.href_base), POST_CHAR))) *ch = 0;
 1139         if (form->method == FM_GET && (ch = strchr(form->action, '?'))) *ch = 0;
 1140     }
 1141     if ((al = get_target(a))) {
 1142         form->target = al;
 1143     } else {
 1144         form->target = stracpy(format.target_base);
 1145     }
 1146     form->num = a - startf;
 1147 }
 1148 
 1149 void find_form_for_input(unsigned char *i, int when_unused)
 1150 {
 1151     unsigned char *s, *ss, *name, *attr, *lf, *la;
 1152     int namelen;
 1153     if (form.action) mem_free(form.action);
 1154     if (form.target) mem_free(form.target);
 1155     memset(&form, 0, sizeof(form));
 1156     if (!when_unused && !special_f(ff, SP_USED, NULL)) return;
 1157     if (last_form_tag && last_input_tag && i <= last_input_tag && i > last_form_tag) {
 1158         get_html_form(last_form_attr, &form);
 1159         return;
 1160     }
 1161     if (last_form_tag && last_input_tag && i > last_input_tag) {
 1162         if (parse_element(last_form_tag, i, &name, &namelen, &la, &s))
 1163             internal("couldn't parse already parsed tag");
 1164         lf = last_form_tag;
 1165         s = last_input_tag;
 1166     } else {
 1167         lf = NULL, la = NULL;
 1168         s = startf;
 1169     }
 1170     se:
 1171     while (s < i && *s != '<') sp:s++;
 1172     if (s >= i) goto end_parse;
 1173     if (s + 2 <= eofff && (s[1] == '!' || s[1] == '?')) {
 1174         s = skip_comment(s, i);
 1175         goto se;
 1176     }
 1177     ss = s;
 1178     if (parse_element(s, i, &name, &namelen, &attr, &s)) goto sp;
 1179     if (namelen != 4 || casecmp(name, "FORM", 4)) goto se;
 1180     lf = ss;
 1181     la = attr;
 1182     goto se;
 1183 
 1184     end_parse:
 1185     if (lf) {
 1186         last_form_tag = lf;
 1187         last_form_attr = la;
 1188         last_input_tag = i;
 1189         get_html_form(la, &form);
 1190     } else {
 1191         memset(&form, 0, sizeof(struct form));
 1192     }
 1193 }
 1194 
 1195 void html_button(unsigned char *a)
 1196 {
 1197     char *al;
 1198     struct form_control *fc;
 1199     find_form_for_input(a, 0);
 1200     fc = mem_alloc(sizeof(struct form_control));
 1201     memset(fc, 0, sizeof(struct form_control));
 1202     if (!(al = get_attr_val(a, "type"))) {
 1203         fc->type = FC_SUBMIT;
 1204         goto xxx;
 1205     }
 1206     if (!strcasecmp(al, "submit")) fc->type = FC_SUBMIT;
 1207     else if (!strcasecmp(al, "reset")) fc->type = FC_RESET;
 1208     else if (!strcasecmp(al, "button")) {
 1209         mem_free(al);
 1210         put_chrs(" [&nbsp;", 8, put_chars_f, ff);
 1211         if ((al = get_attr_val(a, "value"))) {
 1212             put_chrs(al, strlen(al), put_chars_f, ff);
 1213             mem_free(al);
 1214         } else put_chrs("BUTTON", 6, put_chars_f, ff);
 1215         put_chrs("&nbsp;] ", 8, put_chars_f, ff);
 1216         mem_free(fc);
 1217         return;
 1218     } else {
 1219         mem_free(al);
 1220         mem_free(fc);
 1221         return;
 1222     }
 1223     mem_free(al);
 1224     xxx:
 1225     fc->form_num = last_form_tag - startf;
 1226     fc->ctrl_num = a - last_form_tag;
 1227     fc->position = a - startf;
 1228     fc->method = form.method;
 1229     fc->action = stracpy(form.action);
 1230     fc->name = get_attr_val(a, "name");
 1231     fc->default_value = get_exact_attr_val(a, "value");
 1232     fc->ro = has_attr(a, "disabled") ? 2 : has_attr(a, "readonly") ? 1 : 0;
 1233     if (fc->type == FC_IMAGE) fc->alt = get_attr_val(a, "alt");
 1234     if (fc->type == FC_SUBMIT && !fc->default_value) fc->default_value = stracpy("Submit");
 1235     if (fc->type == FC_RESET && !fc->default_value) fc->default_value = stracpy("Reset");
 1236     if (!fc->default_value) fc->default_value = stracpy("");
 1237     special_f(ff, SP_CONTROL, fc);
 1238     format.form = fc;
 1239     format.attr |= AT_BOLD;
 1240     /*put_chrs("[&nbsp;", 7, put_chars_f, ff);
 1241     if (fc->default_value) put_chrs(fc->default_value, strlen(fc->default_value), put_chars_f, ff);
 1242     put_chrs("&nbsp;]", 7, put_chars_f, ff);
 1243     put_chrs(" ", 1, put_chars_f, ff);*/
 1244 }
 1245 
 1246 void html_input(unsigned char *a)
 1247 {
 1248     int i;
 1249     unsigned char *al;
 1250     struct form_control *fc;
 1251     find_form_for_input(a, 0);
 1252     fc = mem_alloc(sizeof(struct form_control));
 1253     memset(fc, 0, sizeof(struct form_control));
 1254     if (!(al = get_attr_val(a, "type"))) {
 1255         fc->type = FC_TEXT;
 1256         goto xxx;
 1257     }
 1258     if (!strcasecmp(al, "text")) fc->type = FC_TEXT;
 1259     else if (!strcasecmp(al, "password")) fc->type = FC_PASSWORD;
 1260     else if (!strcasecmp(al, "checkbox")) fc->type = FC_CHECKBOX;
 1261     else if (!strcasecmp(al, "radio")) fc->type = FC_RADIO;
 1262     else if (!strcasecmp(al, "submit")) fc->type = FC_SUBMIT;
 1263     else if (!strcasecmp(al, "reset")) fc->type = FC_RESET;
 1264     else if (!strcasecmp(al, "file")) fc->type = FC_FILE;
 1265     else if (!strcasecmp(al, "hidden")) fc->type = FC_HIDDEN;
 1266     else if (!strcasecmp(al, "image")) fc->type = FC_IMAGE;
 1267     else if (!strcasecmp(al, "button")) {
 1268         mem_free(al);
 1269         put_chrs(" [&nbsp;", 8, put_chars_f, ff);
 1270         if ((al = get_attr_val(a, "value"))) {
 1271             put_chrs(al, strlen(al), put_chars_f, ff);
 1272             mem_free(al);
 1273         } else put_chrs("BUTTON", 6, put_chars_f, ff);
 1274         put_chrs("&nbsp;] ", 8, put_chars_f, ff);
 1275         mem_free(fc);
 1276         return;
 1277     } else fc->type = FC_TEXT;
 1278     mem_free(al);
 1279     xxx:
 1280     fc->form_num = last_form_tag - startf;
 1281     fc->ctrl_num = a - last_form_tag;
 1282     fc->position = a - startf;
 1283     fc->method = form.method;
 1284     fc->action = stracpy(form.action);
 1285     fc->target = stracpy(form.target);
 1286     fc->name = get_attr_val(a, "name");
 1287     if (fc->type == FC_TEXT || fc->type == FC_PASSWORD) fc->default_value = get_attr_val(a, "value");
 1288     else if (fc->type != FC_FILE) fc->default_value = get_exact_attr_val(a, "value");
 1289     if (fc->type == FC_CHECKBOX && !fc->default_value) fc->default_value = stracpy("on");
 1290     if ((fc->size = get_num(a, "size")) == -1) fc->size = HTML_DEFAULT_INPUT_SIZE;
 1291     fc->size++;
 1292     if (fc->size > d_opt->xw) fc->size = d_opt->xw;
 1293     if ((fc->maxlength = get_num(a, "maxlength")) == -1) fc->maxlength = MAXINT / 4;
 1294     if (fc->type == FC_CHECKBOX || fc->type == FC_RADIO) fc->default_state = has_attr(a, "checked");
 1295     fc->ro = has_attr(a, "disabled") ? 2 : has_attr(a, "readonly") ? 1 : 0;
 1296     if (fc->type == FC_IMAGE) {
 1297         fc->alt = get_attr_val(a, "alt");
 1298         if (!fc->alt) fc->alt = get_attr_val(a, "title");
 1299         if (!fc->alt) fc->alt = get_attr_val(a, "name");
 1300     }
 1301     if (fc->type == FC_SUBMIT && !fc->default_value) fc->default_value = stracpy("Submit");
 1302     if (fc->type == FC_RESET && !fc->default_value) fc->default_value = stracpy("Reset");
 1303     if (!fc->default_value) fc->default_value = stracpy("");
 1304     if (fc->type == FC_HIDDEN) goto hid;
 1305     put_chrs(" ", 1, put_chars_f, ff);
 1306     html_stack_dup();
 1307     format.form = fc;
 1308     switch (fc->type) {
 1309         case FC_TEXT:
 1310         case FC_PASSWORD:
 1311         case FC_FILE:
 1312             format.attr |= AT_BOLD;
 1313             for (i = 0; i < fc->size; i++) put_chrs("_", 1, put_chars_f, ff);
 1314             break;
 1315         case FC_CHECKBOX:
 1316         case FC_RADIO:
 1317             format.attr |= AT_BOLD;
 1318             put_chrs("[&nbsp;]", 8, put_chars_f, ff);
 1319             break;
 1320         case FC_IMAGE:
 1321             if (format.image) mem_free(format.image), format.image = NULL;
 1322             if ((al = get_url_val(a, "src")) || (al = get_url_val(a, "dynsrc"))) {
 1323                 format.image = join_urls(format.href_base, al);
 1324                 mem_free(al);
 1325             }
 1326             format.attr |= AT_BOLD;
 1327             put_chrs("[&nbsp;", 7, put_chars_f, ff);
 1328             if (fc->alt) put_chrs(fc->alt, strlen(fc->alt), put_chars_f, ff);
 1329             else put_chrs("Submit", 6, put_chars_f, ff);
 1330             put_chrs("&nbsp;]", 7, put_chars_f, ff);
 1331             break;
 1332         case FC_SUBMIT:
 1333         case FC_RESET:
 1334             format.attr |= AT_BOLD;
 1335             put_chrs("[&nbsp;", 7, put_chars_f, ff);
 1336             if (fc->default_value) put_chrs(fc->default_value, strlen(fc->default_value), put_chars_f, ff);
 1337             put_chrs("&nbsp;]", 7, put_chars_f, ff);
 1338             break;
 1339         default:
 1340             internal("bad control type");
 1341     }
 1342     kill_html_stack_item(&html_top);
 1343     put_chrs(" ", 1, put_chars_f, ff);
 1344             
 1345     hid:
 1346     special_f(ff, SP_CONTROL, fc);
 1347 }
 1348 
 1349 void html_select(unsigned char *a)
 1350 {
 1351     char *al;
 1352     if (!(al = get_attr_val(a, "name"))) return;
 1353     html_top.dontkill = 1;
 1354     if (format.select) mem_free(format.select);
 1355     format.select = al;
 1356     format.select_disabled = 2 * has_attr(a, "disabled");
 1357 }
 1358 
 1359 void html_option(unsigned char *a)
 1360 {
 1361     struct form_control *fc;
 1362     unsigned char *val;
 1363     find_form_for_input(a, 0);
 1364     if (!format.select) return;
 1365     fc = mem_alloc(sizeof(struct form_control));
 1366     memset(fc, 0, sizeof(struct form_control));
 1367     if (!(val = get_exact_attr_val(a, "value"))) {
 1368         unsigned char *p, *r;
 1369         unsigned char *name;
 1370         int namelen;
 1371         int l = 0;
 1372         for (p = a - 1; *p != '<'; p--) ;
 1373         val = init_str();
 1374         if (parse_element(p, eoff, NULL, NULL, NULL, &p)) {
 1375             internal("parse element failed");
 1376             goto x;
 1377         }
 1378         rrrr:
 1379         while (p < eoff && WHITECHAR(*p)) p++;
 1380         while (p < eoff && !WHITECHAR(*p) && *p != '<') {
 1381             pppp:
 1382             add_chr_to_str(&val, &l, *p), p++;
 1383         }
 1384         r = p;
 1385         while (r < eoff && WHITECHAR(*r)) r++;
 1386         if (r >= eoff) goto x;
 1387         if (r - 2 <= eoff && (r[1] == '!' || r[1] == '?')) {
 1388             p = skip_comment(r, eoff);
 1389             goto rrrr;
 1390         }
 1391         if (parse_element(r, eoff, &name, &namelen, NULL, &p)) goto pppp;
 1392         if (!((namelen == 6 && !casecmp(name, "OPTION", 6)) ||
 1393             (namelen == 7 && !casecmp(name, "/OPTION", 7)) ||
 1394             (namelen == 6 && !casecmp(name, "SELECT", 6)) ||
 1395             (namelen == 7 && !casecmp(name, "/SELECT", 7)) ||
 1396             (namelen == 8 && !casecmp(name, "OPTGROUP", 8)) ||
 1397             (namelen == 9 && !casecmp(name, "/OPTGROUP", 9)))) goto rrrr;
 1398     }
 1399     x:
 1400     fc->form_num = last_form_tag - startf;
 1401     fc->ctrl_num = a - last_form_tag;
 1402     fc->position = a - startf;
 1403     fc->method = form.method;
 1404     fc->action = stracpy(form.action);
 1405     fc->type = FC_CHECKBOX;
 1406     fc->name = stracpy(format.select);
 1407     fc->default_value = val;
 1408     fc->default_state = has_attr(a, "selected");
 1409     fc->ro = format.select_disabled;
 1410     if (has_attr(a, "disabled")) fc->ro = 2;
 1411     put_chrs(" ", 1, put_chars_f, ff);
 1412     html_stack_dup();
 1413     format.form = fc;
 1414     format.attr |= AT_BOLD;
 1415     put_chrs("[ ]", 3, put_chars_f, ff);
 1416     kill_html_stack_item(&html_top);
 1417     put_chrs(" ", 1, put_chars_f, ff);
 1418     special_f(ff, SP_CONTROL, fc);
 1419 }
 1420 
 1421 void clr_spaces(unsigned char *name)
 1422 {
 1423     unsigned char *n1, *n2;
 1424     for (n1 = name; *n1; n1++)
 1425         if (WHITECHAR(*n1) || *n1 == 1) *n1 = ' ';
 1426     /*for (nm = name; *nm; nm++)
 1427         while (nm[0] == ' ' && (nm == name || nm[1] == ' ' || !nm[1]))
 1428             memmove(nm, nm + 1, strlen(nm));*/
 1429     if (!strchr(name, ' ')) return;
 1430     for (n1 = name, n2 = name; *n1; n1++)
 1431         if (!(n1[0] == ' ' && (n2 == name || n1[1] == ' ' || !n1[1])))
 1432             *n2++ = *n1;
 1433     *n2 = 0;
 1434 }
 1435 
 1436 int menu_stack_size;
 1437 struct menu_item **menu_stack;
 1438 
 1439 void new_menu_item(unsigned char *name, int data, int fullname)
 1440     /* name == NULL - up;   data == -1 - down */
 1441 {
 1442     struct menu_item *top, *item, *nmenu = NULL; /* no uninitialized warnings */
 1443     if (name) {
 1444         clr_spaces(name);
 1445         if (!name[0]) mem_free(name), name = stracpy(" ");
 1446         if (name[0] == 1) name[0] = ' ';
 1447     }
 1448     if (name && data == -1) {
 1449         nmenu = mem_alloc(sizeof(struct menu_item));
 1450         memset(nmenu, 0, sizeof(struct menu_item));
 1451         /*nmenu->text = "";*/
 1452     }
 1453     if (menu_stack_size && name) {
 1454         top = item = menu_stack[menu_stack_size - 1];
 1455         while (item->text) item++;
 1456         if ((size_t)((char *)(item + 2) - (char *)top) > MAXINT) overalloc();
 1457         top = mem_realloc(top, (char *)(item + 2) - (char *)top);
 1458         item = item - menu_stack[menu_stack_size - 1] + top;
 1459         menu_stack[menu_stack_size - 1] = top;
 1460         if (menu_stack_size >= 2) {
 1461             struct menu_item *below = menu_stack[menu_stack_size - 2];
 1462             while (below->text) below++;
 1463             below[-1].data = top;
 1464         }
 1465         item->text = name;
 1466         item->rtext = data == -1 ? ">" : "";
 1467         item->hotkey = fullname ? "\000\001" : "\000\000"; /* dirty */
 1468         item->func = data == -1 ? MENU_FUNC do_select_submenu : MENU_FUNC selected_item;
 1469         item->data = data == -1 ? nmenu : (void *)data;
 1470         item->in_m = data == -1 ? 1 : 0;
 1471         item->free_i = 0;
 1472         item++;
 1473         memset(item, 0, sizeof(struct menu_item));
 1474         /*item->text = "";*/
 1475     } else if (name) mem_free(name);
 1476     if (name && data == -1) {
 1477         if ((unsigned)menu_stack_size > MAXINT / sizeof(struct menu_item *) - 1) overalloc();
 1478         menu_stack = mem_realloc(menu_stack, (menu_stack_size + 1) * sizeof(struct menu_item *));
 1479         menu_stack[menu_stack_size++] = nmenu;
 1480     }
 1481     if (!name) menu_stack_size--;
 1482 }
 1483 
 1484 void init_menu()
 1485 {
 1486     menu_stack_size = 0;
 1487     menu_stack = DUMMY;
 1488     new_menu_item(stracpy(""), -1, 0);
 1489 }
 1490 
 1491 void free_menu(struct menu_item *m) /* Grrr. Recursion */
 1492 {
 1493     struct menu_item *mm;
 1494     for (mm = m; mm->text; mm++) {
 1495         mem_free(mm->text);
 1496         if (mm->func == MENU_FUNC do_select_submenu) free_menu(mm->data);
 1497     }
 1498     mem_free(m);
 1499 }
 1500 
 1501 struct menu_item *detach_menu()
 1502 {
 1503     struct menu_item *i = NULL;
 1504     if (menu_stack_size) i = menu_stack[0];
 1505     if (menu_stack) mem_free(menu_stack);
 1506     return i;
 1507 }
 1508 
 1509 void destroy_menu()
 1510 {
 1511     if (menu_stack && menu_stack != DUMMY) free_menu(menu_stack[0]);
 1512     detach_menu();
 1513 }
 1514 
 1515 void menu_labels(struct menu_item *m, unsigned char *base, unsigned char **lbls)
 1516 {
 1517     unsigned char *bs;
 1518     for (; m->text; m++) {
 1519         if (m->func == MENU_FUNC do_select_submenu) {
 1520             if ((bs = stracpy(base))) {
 1521                 add_to_strn(&bs, m->text);
 1522                 add_to_strn(&bs, " ");
 1523                 menu_labels(m->data, bs, lbls);
 1524                 mem_free(bs);
 1525             }
 1526         } else {
 1527             if ((bs = stracpy(m->hotkey[1] ? (unsigned char *)"" : base))) add_to_strn(&bs, m->text);
 1528             lbls[(int)m->data] = bs;
 1529         }
 1530     }
 1531 }
 1532 
 1533 int menu_contains(struct menu_item *m, int f)
 1534 {
 1535     if (m->func != MENU_FUNC do_select_submenu) return (int)m->data == f;
 1536     for (m = m->data; m->text; m++) if (menu_contains(m, f)) return 1;
 1537     return 0;
 1538 }
 1539 
 1540 void do_select_submenu(struct terminal *term, struct menu_item *menu, struct session *ses)
 1541 {
 1542     struct menu_item *m;
 1543     int def = get_current_state(ses);
 1544     int sel = 0;
 1545     if (def < 0) def = 0;
 1546     for (m = menu; m->text; m++, sel++) if (menu_contains(m, def)) goto f;
 1547     sel = 0;
 1548     f:
 1549     do_menu_selected(term, menu, ses, sel);
 1550 }
 1551 
 1552 int do_html_select(unsigned char *attr, unsigned char *html, unsigned char *eof, unsigned char **end, void *f)
 1553 {
 1554     struct form_control *fc;
 1555     unsigned char *t_name, *t_attr, *en;
 1556     int t_namelen;
 1557     unsigned char *lbl;
 1558     int lbl_l;
 1559     unsigned char *vlbl;
 1560     int vlbl_l;
 1561     int nnmi = 0;
 1562     struct conv_table *ct = special_f(f, SP_TABLE, NULL);
 1563     unsigned char **val, **lbls;
 1564     int order, preselect, group;
 1565     int i, mw;
 1566     if (has_attr(attr, "multiple")) return 1;
 1567     find_form_for_input(attr, 0);
 1568     lbl = NULL;
 1569     lbl_l = 0;
 1570     vlbl = NULL;
 1571     vlbl_l = 0;
 1572     val = DUMMY;
 1573     order = 0, group = 0, preselect = -1;
 1574     init_menu();
 1575         se:
 1576         en = html;
 1577         see:
 1578         html = en;
 1579     while (html < eof && *html != '<') html++;
 1580     if (html >= eof) {
 1581         int i;
 1582         abort:
 1583         *end = html;
 1584         if (lbl) mem_free(lbl);
 1585         if (vlbl) mem_free(vlbl);
 1586         for (i = 0; i < order; i++) if (val[i]) mem_free(val[i]);
 1587         mem_free(val);
 1588         destroy_menu();
 1589         *end = en;
 1590         return 0;
 1591     }
 1592     if (lbl) {
 1593         unsigned char *q, *s = en;
 1594         int l = html - en;
 1595         while (l && WHITECHAR(s[0])) s++, l--;
 1596         while (l && WHITECHAR(s[l-1])) l--;
 1597         q = convert_string(ct, s, l);
 1598         if (q) add_to_str(&lbl, &lbl_l, q), mem_free(q);
 1599         add_bytes_to_str(&vlbl, &vlbl_l, s, l);
 1600     }
 1601     if (html + 2 <= eof && (html[1] == '!' || html[1] == '?')) {
 1602         html = skip_comment(html, eof);
 1603         goto se;
 1604     }
 1605     if (parse_element(html, eof, &t_name, &t_namelen, &t_attr, &en)) {
 1606         html++;
 1607         goto se;
 1608     }
 1609     if (t_namelen == 7 && !casecmp(t_name, "/SELECT", 7)) {
 1610         if (lbl) {
 1611             if (!val[order - 1]) val[order - 1] = stracpy(vlbl);
 1612             if (!nnmi) new_menu_item(lbl, order - 1, 1), lbl = NULL;
 1613             else mem_free(lbl), lbl = NULL;
 1614             mem_free(vlbl), vlbl = NULL;
 1615         }
 1616         goto end_parse;
 1617     }
 1618     if (t_namelen == 7 && !casecmp(t_name, "/OPTION", 7)) {
 1619         if (lbl) {
 1620             if (!val[order - 1]) val[order - 1] = stracpy(vlbl);
 1621             if (!nnmi) new_menu_item(lbl, order - 1, 1), lbl = NULL;
 1622             else mem_free(lbl), lbl = NULL;
 1623             mem_free(vlbl), vlbl = NULL;
 1624         }
 1625         goto see;
 1626     }
 1627     if (t_namelen == 6 && !casecmp(t_name, "OPTION", 6)) {
 1628         unsigned char *v, *vx;
 1629         if (lbl) {
 1630             if (!val[order - 1]) val[order - 1] = stracpy(vlbl);
 1631             if (!nnmi) new_menu_item(lbl, order - 1, 1), lbl = NULL;
 1632             else mem_free(lbl), lbl = NULL;
 1633             mem_free(vlbl), vlbl = NULL;
 1634         }
 1635         if (has_attr(t_attr, "disabled")) goto see;
 1636         if (preselect == -1 && has_attr(t_attr, "selected")) preselect = order;
 1637         v = get_exact_attr_val(t_attr, "value");
 1638         if (!(order & (ALLOC_GR - 1))) {
 1639             if ((unsigned)order > MAXINT / sizeof(unsigned char *) - ALLOC_GR) overalloc();
 1640             if ((unsigned)order > MAXINT / sizeof(char *) - ALLOC_GR) overalloc();
 1641             val = mem_realloc(val, (order + ALLOC_GR) * sizeof(unsigned char *));
 1642         }
 1643         val[order++] = v;
 1644         if ((vx = get_attr_val(t_attr, "label"))) {
 1645             new_menu_item(convert_string(ct, vx, strlen(vx)), order - 1, 0);
 1646             mem_free(vx);
 1647         }
 1648         if (!v || !vx) {
 1649             lbl = init_str(), lbl_l = 0;
 1650             vlbl = init_str(), vlbl_l = 0;
 1651             nnmi = !!vx;
 1652         }
 1653         goto see;
 1654     }
 1655     if ((t_namelen == 8 && !casecmp(t_name, "OPTGROUP", 8)) || (t_namelen == 9 && !casecmp(t_name, "/OPTGROUP", 9))) {
 1656         if (lbl) {
 1657             if (!val[order - 1]) val[order - 1] = stracpy(vlbl);
 1658             if (!nnmi) new_menu_item(lbl, order - 1, 1), lbl = NULL;
 1659             else mem_free(lbl), lbl = NULL;
 1660             mem_free(vlbl), vlbl = NULL;
 1661         }
 1662         if (group) new_menu_item(NULL, -1, 0), group = 0;
 1663     }
 1664     if (t_namelen == 8 && !casecmp(t_name, "OPTGROUP", 8)) {
 1665         char *la;
 1666         if (!(la = get_attr_val(t_attr, "label"))) la = stracpy("");
 1667         new_menu_item(convert_string(ct, la, strlen(la)), -1, 0);
 1668         mem_free(la);
 1669         group = 1;
 1670     }
 1671     goto see;
 1672 
 1673     end_parse:
 1674     *end = en;
 1675     if (!order) goto abort;
 1676     fc = mem_alloc(sizeof(struct form_control));
 1677     memset(fc, 0, sizeof(struct form_control));
 1678     lbls = mem_alloc(order * sizeof(char *));
 1679     memset(lbls, 0, order * sizeof(char *));
 1680     fc->form_num = last_form_tag - startf;
 1681     fc->ctrl_num = attr - last_form_tag;
 1682     fc->position = attr - startf;
 1683     fc->method = form.method;
 1684     fc->action = stracpy(form.action);
 1685     fc->name = get_attr_val(attr, "name");
 1686     fc->type = FC_SELECT;
 1687     fc->default_state = preselect < 0 ? 0 : preselect;
 1688     fc->default_value = order ? stracpy(val[fc->default_state]) : stracpy("");
 1689     fc->ro = has_attr(attr, "disabled") ? 2 : has_attr(attr, "readonly") ? 1 : 0;
 1690     fc->nvalues = order;
 1691     fc->values = val;
 1692     fc->menu = detach_menu();
 1693     fc->labels = lbls;
 1694     menu_labels(fc->menu, "", lbls);
 1695     put_chrs("[", 1, put_chars_f, f);
 1696     html_stack_dup();
 1697     format.form = fc;
 1698     format.attr |= AT_BOLD;
 1699     mw = 0;
 1700     for (i = 0; i < order; i++) if (lbls[i] && strlen(lbls[i]) > (size_t)mw) mw = strlen(lbls[i]);
 1701     for (i = 0; i < mw; i++) put_chrs("_", 1, put_chars_f, f);
 1702     kill_html_stack_item(&html_top);
 1703     put_chrs("]", 1, put_chars_f, f);
 1704     special_f(ff, SP_CONTROL, fc);
 1705     return 0;
 1706 }
 1707 
 1708 void html_textarea(unsigned char *a)
 1709 {
 1710     internal("This should be never called");
 1711 }
 1712 
 1713 void do_html_textarea(unsigned char *attr, unsigned char *html, unsigned char *eof, unsigned char **end, void *f)
 1714 {
 1715     struct form_control *fc;
 1716     unsigned char *p, *t_name, *w;
 1717     int t_namelen;
 1718     int cols, rows;
 1719     int i;
 1720     find_form_for_input(attr, 1);
 1721     while (html < eof && (*html == '\n' || *html == '\r')) html++;
 1722     p = html;
 1723     while (p < eof && *p != '<') {
 1724         pp:
 1725         p++;
 1726     }
 1727     if (p >= eof) {
 1728         *end = eof;
 1729         return;
 1730     }
 1731     if (parse_element(p, eof, &t_name, &t_namelen, NULL, end)) goto pp;
 1732     if (t_namelen != 9 || casecmp(t_name, "/TEXTAREA", 9)) goto pp;
 1733     if (!last_form_tag) return;
 1734     fc = mem_alloc(sizeof(struct form_control));
 1735     memset(fc, 0, sizeof(struct form_control));
 1736     fc->form_num = last_form_tag - startf;
 1737     fc->ctrl_num = attr - last_form_tag;
 1738     fc->position = attr - startf;
 1739     fc->method = form.method;
 1740     fc->action = stracpy(form.action);
 1741     fc->name = get_attr_val(attr, "name");
 1742     fc->type = FC_TEXTAREA;;
 1743     fc->ro = has_attr(attr, "disabled") ? 2 : has_attr(attr, "readonly") ? 1 : 0;
 1744     fc->default_value = memacpy(html, p - html);
 1745     if ((cols = get_num(attr, "cols")) <= 5) cols = HTML_DEFAULT_TEXTAREA_WIDTH;
 1746     cols++;
 1747     if ((rows = get_num(attr, "rows")) <= 0) rows = HTML_DEFAULT_TEXTAREA_HEIGHT;
 1748     if (cols > d_opt->xw) cols = d_opt->xw;
 1749     if (rows > d_opt->yw) rows = d_opt->yw;
 1750     fc->cols = cols;
 1751     fc->rows = rows;
 1752     fc->wrap = 1;
 1753     if ((w = get_attr_val(attr, "wrap"))) {
 1754         if (!strcasecmp(w, "hard") || !strcasecmp(w, "physical")) fc->wrap = 2;
 1755         else if (!strcasecmp(w, "off")) fc->wrap = 0;
 1756         mem_free(w);
 1757     }
 1758     if ((fc->maxlength = get_num(attr, "maxlength")) == -1) fc->maxlength = MAXINT / 4;
 1759     if (rows > 1) ln_break(1, line_break_f, f);
 1760     else put_chrs(" ", 1, put_chars_f, f);
 1761     html_stack_dup();
 1762     format.form = fc;
 1763     format.attr |= AT_BOLD;
 1764     for (i = 0; i < rows; i++) {
 1765         int j;
 1766         for (j = 0; j < cols; j++) put_chrs("_", 1, put_chars_f, f);
 1767         if (i < rows - 1) ln_break(1, line_break_f, f);
 1768     }
 1769     kill_html_stack_item(&html_top);
 1770     if (rows > 1) ln_break(1, line_break_f, f);
 1771     else put_chrs(" ", 1, put_chars_f, f);
 1772     special_f(f, SP_CONTROL, fc);
 1773 }
 1774 
 1775 void html_iframe(unsigned char *a)
 1776 {
 1777     unsigned char *name, *url;
 1778     if (!(url = get_url_val(a, "src"))) return;
 1779     if (!(name = get_attr_val(a, "name"))) name = stracpy("");
 1780     if (*name) put_link_line("IFrame: ", name, url, d_opt->framename);
 1781     else put_link_line("", "IFrame", url, d_opt->framename);
 1782     mem_free(name);
 1783     mem_free(url);
 1784 }
 1785 
 1786 void html_noframes(unsigned char *a)
 1787 {
 1788     if (d_opt->frames) html_skip(a);
 1789 }
 1790 
 1791 void html_frame(unsigned char *a)
 1792 {
 1793     unsigned char *name, *u2, *url;
 1794     if (!(u2 = get_url_val(a, "src"))) {
 1795         url = stracpy("");
 1796     } else {
 1797         url = join_urls(format.href_base, u2);
 1798         mem_free(u2);
 1799     }
 1800     if (!url) return;
 1801     name = get_attr_val (a, "name");
 1802     if (!name)
 1803         name = stracpy(url);
 1804     else if (!name[0]) { /* When name doesn't have a value */
 1805         mem_free(name);
 1806         name = stracpy(url);
 1807     }
 1808     if (!d_opt->frames || !html_top.frameset) put_link_line("Frame: ", name, url, "");
 1809     else {
 1810         struct frame_param fp;
 1811         fp.name = name;
 1812         fp.url = url;
 1813         fp.parent = html_top.frameset;
 1814         if (special_f(ff, SP_USED, NULL)) special_f(ff, SP_FRAME, &fp);
 1815     }
 1816     mem_free(name);
 1817     mem_free(url);
 1818 }
 1819 
 1820 void parse_frame_widths(unsigned char *a, int ww, int www, int **op, int *olp)
 1821 {
 1822     unsigned char *aa;
 1823     int q, qq, i, d, nn;
 1824     unsigned long n;
 1825     int *oo, *o;
 1826     int ol;
 1827     ol = 0;
 1828     o = DUMMY;
 1829     new_ch:
 1830     while (WHITECHAR(*a)) a++;
 1831     n = strtoul(a, (char **)(void *)&a, 10);
 1832     if (n > 10000) n = 10000;
 1833     q = n;
 1834     if (*a == '%') q = q * ww / 100;
 1835     else if (*a != '*') q = (q + (www - 1) / 2) / (www ? www : 1);
 1836     else if (!(q = -q)) q = -1;
 1837     if ((unsigned)ol > MAXINT / sizeof(int) - 1) overalloc();
 1838     o = mem_realloc(o, (ol + 1) * sizeof(int));
 1839     o[ol++] = q;
 1840     if ((aa = strchr(a, ','))) {
 1841         a = aa + 1;
 1842         goto new_ch;
 1843     }
 1844     *op = o;
 1845     *olp = ol;
 1846     q = 2 * ol - 1;
 1847     for (i = 0; i < ol; i++) if (o[i] > 0) q += o[i] - 1;
 1848     if (q >= ww) {
 1849         distribute:
 1850         for (i = 0; i < ol; i++) if (o[i] < 1) o[i] = 1;
 1851         q -= ww;
 1852         d = 0;
 1853         for (i = 0; i < ol; i++) d += o[i];
 1854         qq = q;
 1855         for (i = 0; i < ol; i++) {
 1856             q -= o[i] - o[i] * (d - qq) / (d ? d : 1);
 1857             do_not_optimize_here(&d);
 1858                 /* SIGH! gcc 2.7.2.* has an optimizer bug! */
 1859             o[i] = o[i] * (d - qq) / (d ? d : 1);
 1860         }
 1861         while (q) {
 1862             nn = 0;
 1863             for (i = 0; i < ol; i++) {
 1864                 if (q < 0) o[i]++, q++, nn = 1;
 1865                 if (q > 0 && o[i] > 1) o[i]--, q--, nn = 1;
 1866                 if (!q) break;
 1867             }
 1868             if (!nn) break;
 1869         }
 1870     } else {
 1871         int nn = 0;
 1872         for (i = 0; i < ol; i++) if (o[i] < 0) nn = 1;
 1873         if (!nn) goto distribute;
 1874         if ((unsigned)ol > MAXINT / sizeof(int)) overalloc();
 1875         oo = mem_alloc(ol * sizeof(int));
 1876         memcpy(oo, o, ol * sizeof(int));
 1877         for (i = 0; i < ol; i++) if (o[i] < 1) o[i] = 1;
 1878         q = ww - q;
 1879         d = 0;
 1880         for (i = 0; i < ol; i++) if (oo[i] < 0) d += -oo[i];
 1881         qq = q;
 1882         for (i = 0; i < ol; i++) if (oo[i] < 0) {
 1883             o[i] += (-oo[i] * qq / (d ? d : 1));
 1884             q -= (-oo[i] * qq / (d ? d : 1));
 1885         }
 1886         if (q < 0) {
 1887             q = 0;
 1888             /*internal("parse_frame_widths: q < 0"); may happen when page contains too big values */
 1889         }
 1890         for (i = 0; i < ol; i++) if (oo[i] < 0) {
 1891             if (q) o[i]++, q--;
 1892         }
 1893         if (q > 0) {
 1894             q = 0;
 1895             /*internal("parse_frame_widths: q > 0"); may happen when page contains too big values */
 1896         }
 1897         mem_free(oo);
 1898     }
 1899     for (i = 0; i < ol; i++) if (!o[i]) {
 1900         int j;
 1901         int m = 0;
 1902         int mj = 0;
 1903         for (j = 0; j < ol; j++) if (o[j] > m) m = o[j], mj = j;
 1904         if (m) o[i] = 1, o[mj]--;
 1905     }
 1906 }
 1907 
 1908 void html_frameset(unsigned char *a)
 1909 {
 1910     int x, y;
 1911     struct frameset_param fp;
 1912     unsigned char *c, *d;
 1913     if (!d_opt->frames || !special_f(ff, SP_USED, NULL)) return;
 1914     if (!(c = get_attr_val(a, "cols"))) c = stracpy("100%");
 1915     if (!(d = get_attr_val(a, "rows"))) d = stracpy("100%");
 1916     if (!html_top.frameset) {
 1917         x = d_opt->xw;
 1918         y = d_opt->yw;
 1919     } else {
 1920         struct frameset_desc *f = html_top.frameset;
 1921         if (f->yp >= f->y) goto free_cd;
 1922         x = f->f[f->xp + f->yp * f->x].xw;
 1923         y = f->f[f->xp + f->yp * f->x].yw;
 1924     }
 1925     parse_frame_widths(c, x, HTML_FRAME_CHAR_WIDTH, &fp.xw, &fp.x);
 1926     parse_frame_widths(d, y, HTML_FRAME_CHAR_HEIGHT, &fp.yw, &fp.y);
 1927     fp.parent = html_top.frameset;
 1928     if (fp.x && fp.y) html_top.frameset = special_f(ff, SP_FRAMESET, &fp);
 1929     mem_free(fp.xw);
 1930     mem_free(fp.yw);
 1931     free_cd:
 1932     mem_free(c);
 1933     mem_free(d);
 1934 }
 1935 
 1936 /*void html_frameset(unsigned char *a)
 1937 {
 1938     int w;
 1939     int horiz = 0;
 1940     struct frameset_param *fp;
 1941     unsigned char *c, *d;
 1942     if (!d_opt->frames || !special_f(ff, SP_USED, NULL)) return;
 1943     if (!(c = get_attr_val(a, "cols"))) {
 1944         horiz = 1;
 1945         if (!(c = get_attr_val(a, "rows"))) return;
 1946     }
 1947     fp = mem_alloc(sizeof(struct frameset_param));
 1948     fp->n = 0;
 1949     fp->horiz = horiz;
 1950     par_format.leftmargin = par_format.rightmargin = 0;
 1951     d = c;
 1952     while (1) {
 1953         while (WHITECHAR(*d)) d++;
 1954         if (!*d) break;
 1955         if (*d == ',') {
 1956             d++;
 1957             continue;
 1958         }
 1959         if ((w = parse_width(d, 1)) != -1) {
 1960             if ((unsigned)fp->n > (MAXINT - sizeof(struct frameset_param)) / sizeof(int) - 1) overalloc();
 1961             fp = mem_realloc(fp, sizeof(struct frameset_param) + (fp->n + 1) * sizeof(int));
 1962             fp->width[fp->n++] = w;
 1963         }
 1964         if (!(d = strchr(d, ','))) break;
 1965         d++;
 1966     }
 1967     fp->parent = html_top.frameset;
 1968     if (fp->n) html_top.frameset = special_f(ff, SP_FRAMESET, fp);
 1969     mem_free(fp);
 1970     f:
 1971     mem_free(c);
 1972 }*/
 1973 
 1974 void html_link(unsigned char *a)
 1975 {
 1976     unsigned char *name, *url, *title;
 1977     if ((name = get_attr_val(a, "type"))) {
 1978         if (strcasecmp(name, "text/html")) {
 1979             mem_free(name);
 1980             return;
 1981         }
 1982         mem_free(name);
 1983     }
 1984     if (!(url = get_url_val(a, "href"))) return;
 1985     if (!(name = get_attr_val(a, "rel")))
 1986         if (!(name = get_attr_val(a, "rev")))
 1987             if (!(name = get_attr_val(a, "ref")))
 1988                 name = stracpy(url);
 1989     if (!strcasecmp(name, "stylesheet") ||
 1990         !strcasecmp(name, "alternate stylesheet") ||
 1991         !strcasecmp(name, "made") ||
 1992         !strcasecmp(name, "icon") ||
 1993         !strcasecmp(name, "shortcut icon") ||
 1994         !strcasecmp(name, "apple-touch-icon") ||
 1995         !strcasecmp(name, "meta") ||
 1996         !strcasecmp(name, "pingback") ||
 1997         !strcasecmp(name, "File-List") ||
 1998         !casecmp(name, "schema", 6)) goto skip;
 1999     if ((title = get_attr_val(a, "title"))) {
 2000         add_to_strn(&name, ": ");
 2001         add_to_strn(&name, title);
 2002         mem_free(title);
 2003     }
 2004     put_link_line("Link: ", name, url, format.target_base);
 2005     skip:
 2006     mem_free(name);
 2007     mem_free(url);
 2008 }
 2009 
 2010 struct element_info {
 2011     char *name;
 2012     void (*func)(unsigned char *);
 2013     int linebreak;
 2014     int nopair;
 2015 };
 2016 
 2017 struct element_info elements[] = {
 2018     {"SPAN",    html_span,  0, 0},
 2019     {"B",       html_bold,  0, 0},
 2020     {"STRONG",  html_bold,  0, 0},
 2021     {"DFN",     html_bold,  0, 0},
 2022     {"I",       html_italic,    0, 0},
 2023     {"Q",       html_italic,    0, 0},
 2024     {"CITE",    html_italic,    0, 0},
 2025     {"EM",      html_italic,    0, 0},
 2026     {"ABBR",    html_italic,    0, 0},
 2027     {"U",       html_underline, 0, 0},
 2028     {"S",       html_underline, 0, 0},
 2029     {"STRIKE",  html_underline, 0, 0},
 2030     {"FIXED",   html_fixed, 0, 0},
 2031     {"CODE",    html_fixed, 0, 0},
 2032     {"FONT",    html_font,  0, 0},
 2033     {"A",       html_a,     0, 2},
 2034     {"IMG",     html_img,   0, 1},
 2035 
 2036     {"BASE",    html_base,  0, 1},
 2037     {"BASEFONT",    html_font,  0, 1},
 2038 
 2039     {"BODY",    html_body,  0, 0},
 2040 
 2041 /*  {"HEAD",    html_skip,  0, 0},*/
 2042     {"TITLE",   html_title, 0, 0},
 2043     {"SCRIPT",  html_skip,  0, 0},
 2044     {"STYLE",   html_skip,  0, 0},
 2045 
 2046     {"BR",      html_br,    1, 1},
 2047     {"DIV",     html_linebrk,   1, 0},
 2048     {"CENTER",  html_center,    1, 0},
 2049     {"CAPTION", html_center,    1, 0},
 2050     {"P",       html_p,     2, 2},
 2051     {"HR",      html_hr,    2, 1},
 2052     {"H1",      html_center,    2, 2},
 2053     {"H2",      html_h2,    2, 2},
 2054     {"H3",      html_h3,    2, 2},
 2055     {"H4",      html_h4,    2, 2},
 2056     {"H5",      html_h5,    2, 2},
 2057     {"H6",      html_h6,    2, 2},
 2058     {"BLOCKQUOTE",  html_blockquote,2, 0},
 2059     {"ADDRESS", html_address,   2, 0},
 2060     {"PRE",     html_pre,   2, 0},
 2061     {"LISTING", html_pre,   2, 0},
 2062 
 2063     {"UL",      html_ul,    1, 0},
 2064     {"DIR",     html_ul,    1, 0},
 2065     {"MENU",    html_ul,    1, 0},
 2066     {"OL",      html_ol,    1, 0},
 2067     {"LI",      html_li,    1, 3},
 2068     {"DL",      html_dl,    1, 0},
 2069     {"DT",      html_dt,    1, 1},
 2070     {"DD",      html_dd,    1, 1},
 2071 
 2072     {"TABLE",   html_table, 2, 0},
 2073     {"TR",      html_tr,    1, 0},
 2074     {"TD",      html_td,    0, 0},
 2075     {"TH",      html_th,    0, 0},
 2076 
 2077     {"FORM",    html_form,  1, 0},
 2078     {"INPUT",   html_input, 0, 1},
 2079     {"TEXTAREA",    html_textarea,  0, 1},
 2080     {"SELECT",  html_select,    0, 0},
 2081     {"OPTION",  html_option,    1, 1},
 2082     {"BUTTON",  html_button,    0, 0},
 2083 
 2084     {"LINK",    html_link,  1, 1},
 2085     {"IFRAME",  html_iframe,    1, 1},
 2086     {"FRAME",   html_frame, 1, 1},
 2087     {"FRAMESET",    html_frameset,  1, 0},
 2088     {"NOFRAMES",    html_noframes,  0, 0},
 2089 
 2090     {NULL,      NULL, 0, 0},
 2091 };
 2092 
 2093 unsigned char *skip_comment(unsigned char *html, unsigned char *eof)
 2094 {
 2095     int comm = html + 4 <= eof && html[2] == '-' && html[3] == '-';
 2096     html += comm ? 4 : 2;
 2097     while (html < eof) {
 2098         if (!comm && html[0] == '>') return html + 1;
 2099         if (comm && html + 2 <= eof && html[0] == '-' && html[1] == '-') {
 2100             html += 2;
 2101             while (html < eof && *html == '-') html++;
 2102             while (html < eof && WHITECHAR(*html)) html++;
 2103             if (html >= eof) return eof;
 2104             if (*html == '>') return html + 1;
 2105             continue;
 2106         }
 2107         html++;
 2108     }
 2109     return eof;
 2110 }
 2111 
 2112 void process_head(unsigned char *head)
 2113 {
 2114     unsigned char *r, *p;
 2115     if ((r = parse_http_header(head, "Refresh", NULL))) {
 2116         if ((p = parse_header_param(r, "URL")) || (p = parse_header_param(r, ""))) {
 2117             put_link_line("Refresh: ", p, p, d_opt->framename);
 2118             mem_free(p);
 2119         }
 2120         mem_free(r);
 2121     }
 2122 }
 2123 
 2124 int qd(unsigned char *html, unsigned char *eof, int *len)
 2125 {
 2126     int l;
 2127     *len = 1;
 2128     if (html >= eof) {
 2129         internal("qd: out of data, html == %p, eof == %p", html, eof);
 2130         return -1;
 2131     }
 2132     if (html[0] != '&' || d_opt->plain) return html[0];
 2133     if (html + 1 >= eof) return -1;
 2134     if (html[1] != '#') return -1;
 2135     for (l = 2; l < 10 && html + l < eof; l++) if (html[l] == ';') {
 2136         int n = get_entity_number(html + 2, l - 2);
 2137         if (n >= 0) {
 2138             *len = l + 1;
 2139             return n;
 2140         }
 2141         break;
 2142     }
 2143     return -1;
 2144 }
 2145 
 2146 void parse_html(unsigned char *html, unsigned char *eof, int (*put_chars)(void *, unsigned char *, int), void (*line_break)(void *), void *(*special)(void *, int, ...), void *f, unsigned char *head)
 2147 {
 2148     /*unsigned char *start = html;*/
 2149     unsigned char *lt;
 2150     putsp = -1;
 2151     line_breax = table_level ? 2 : 1;
 2152     pos = 0;
 2153     was_br = 0;
 2154     put_chars_f = put_chars;
 2155     line_break_f = line_break;
 2156     special_f = special;
 2157     ff = f;
 2158     eoff = eof;
 2159     if (head) process_head(head);
 2160     set_lt:
 2161     put_chars_f = put_chars;
 2162     line_break_f = line_break;
 2163     special_f = special;
 2164     ff = f;
 2165     eoff = eof;
 2166     lt = html;
 2167     while (html < eof) {
 2168         unsigned char *name, *attr, *end;
 2169         int namelen;
 2170         struct element_info *ei;
 2171         int inv;
 2172         if (WHITECHAR(*html) && par_format.align != AL_NO) {
 2173             unsigned char *h = html;
 2174             /*if (putsp == -1) {
 2175                 while (html < eof && WHITECHAR(*html)) html++;
 2176                 goto set_lt;
 2177             }
 2178             putsp = 0;*/
 2179             while (h < eof && WHITECHAR(*h)) h++;
 2180             if (h + 1 < eof && h[0] == '<' && h[1] == '/') {
 2181                 if (!parse_element(h, eof, &name, &namelen, &attr, &end)) {
 2182                     put_chrs(lt, html - lt, put_chars, f);
 2183                     lt = html = h;
 2184                     if (!html_top.invisible) putsp = 1;
 2185                     goto element;
 2186                 }
 2187             }
 2188             html++;
 2189             if (!(pos + (html-lt-1))) goto skip_w; /* ??? */
 2190             if (*(html - 1) == ' ') {
 2191                 if (html < eof && !WHITECHAR(*html)) continue;  /* BIG performance win; not sure if it doesn't cause any bug */
 2192                 put_chrs(lt, html - lt, put_chars, f);
 2193             } else {
 2194                 put_chrs(lt, html - 1 - lt, put_chars, f);
 2195                 put_chrs(" ", 1, put_chars, f);
 2196             }
 2197             skip_w:
 2198             while (html < eof && WHITECHAR(*html)) html++;
 2199             /*putsp = -1;*/
 2200             goto set_lt;
 2201             put_sp:
 2202             put_chrs(" ", 1, put_chars, f);
 2203             /*putsp = -1;*/
 2204         }
 2205         if (par_format.align == AL_NO && (*html < 32 || *html == '&')) {
 2206             int l;
 2207             int q = qd(html, eof, &l);
 2208             putsp = 0;
 2209             if (q == 9) {
 2210                 put_chrs(lt, html - lt, put_chars, f);
 2211                 put_chrs("        ", 8 - pos % 8, put_chars, f);
 2212                 html += l;
 2213                 goto set_lt;
 2214             } else if (q == 13 || q == 10) {
 2215                 put_chrs(lt, html - lt, put_chars, f);
 2216                 next_break:
 2217                 html += l;
 2218                 if (q == 13 && html < eof - 1 && qd(html, eof, &l) == 10) html += l;
 2219                 ln_break(1, line_break, f);
 2220                 if (html >= eof) goto set_lt;
 2221                 q = qd(html, eof, &l);
 2222                 if (q == 13 || q == 10) {
 2223                     line_breax = 0;
 2224                     goto next_break;
 2225                 }
 2226                 goto set_lt;
 2227             }
 2228         }
 2229         if (*html < ' ') {
 2230             /*if (putsp == 1) goto put_sp;
 2231             putsp = 0;*/
 2232             put_chrs(lt, html - lt, put_chars, f);
 2233             put_chrs(".", 1, put_chars, f);
 2234             html++;
 2235             goto set_lt;
 2236         }
 2237         if (html + 2 <= eof && html[0] == '<' && (html[1] == '!' || html[1] == '?') && !d_opt->plain) {
 2238             /*if (putsp == 1) goto put_sp;
 2239             putsp = 0;*/
 2240             put_chrs(lt, html - lt, put_chars, f);
 2241             html = skip_comment(html, eof);
 2242             goto set_lt;
 2243         }
 2244         if (*html != '<' || d_opt->plain || parse_element(html, eof, &name, &namelen, &attr, &end)) {
 2245             /*if (putsp == 1) goto put_sp;
 2246             putsp = 0;*/
 2247             html++;
 2248             continue;
 2249         }
 2250         element:
 2251         inv = *name == '/'; name += inv; namelen -= inv;
 2252         if (!inv && putsp == 1 && !html_top.invisible) goto put_sp;
 2253         put_chrs(lt, html - lt, put_chars, f);
 2254         if (par_format.align != AL_NO) if (!inv && !putsp) {
 2255             unsigned char *ee = end;
 2256             unsigned char *nm;
 2257             while (!parse_element(ee, eof, &nm, NULL, NULL, &ee))
 2258                 if (*nm == '/') goto ng;
 2259             if (ee < eof && WHITECHAR(*ee)) {
 2260                 /*putsp = -1;*/
 2261                 put_chrs(" ", 1, put_chars, f);
 2262             }
 2263             ng:;
 2264         }
 2265         html = end;
 2266         for (ei = elements; ei->name; ei++) {
 2267             if (ei->name &&
 2268                (strlen(ei->name) != (size_t)namelen || casecmp(ei->name, name, namelen)))
 2269                 continue;
 2270             if (!inv) {
 2271                 char *a;
 2272                 ln_break(ei->linebreak, line_break, f);
 2273                 if (ei->name && ((a = get_attr_val(attr, "id")))) {
 2274                     special(f, SP_TAG, a);
 2275                     mem_free(a);
 2276                 }
 2277                 if (!html_top.invisible) {
 2278                     int a = par_format.align == AL_NO;
 2279                     struct par_attrib pa = par_format;
 2280                     if (ei->func == html_table && d_opt->tables && table_level < HTML_MAX_TABLE_LEVEL) {
 2281                         format_table(attr, html, eof, &html, f);
 2282                         ln_break(2, line_break, f);
 2283                         goto set_lt;
 2284                     }
 2285                     if (ei->func == html_select) {
 2286                         if (!do_html_select(attr, html, eof, &html, f))
 2287                             goto set_lt;
 2288                     }
 2289                     if (ei->func == html_textarea) {
 2290                         do_html_textarea(attr, html, eof, &html, f);
 2291                         goto set_lt;
 2292                     }
 2293                     if (ei->nopair == 2 || ei->nopair == 3) {
 2294                         struct html_element *e;
 2295                         if (ei->nopair == 2) {
 2296                             foreach(e, html_stack) {
 2297                                 if (e->dontkill) break;
 2298                                 if (e->linebreak || !ei->linebreak) break;
 2299                             }
 2300                         } else foreach(e, html_stack) {
 2301                             if (e->linebreak && !ei->linebreak) break;
 2302                             if (e->dontkill) break;
 2303                             if (e->namelen == namelen && !casecmp(e->name, name, e->namelen)) break;
 2304                         }
 2305                         if (e->namelen == namelen && !casecmp(e->name, name, e->namelen)) {
 2306                             while (e->prev != (void *)&html_stack) kill_html_stack_item(e->prev);
 2307                             if (e->dontkill != 2) kill_html_stack_item(e);
 2308                         }
 2309                     }
 2310                     if (ei->nopair != 1) {
 2311                         html_stack_dup();
 2312                         html_top.name = name;
 2313                         html_top.namelen = namelen;
 2314                         html_top.options = attr;
 2315                         html_top.linebreak = ei->linebreak;
 2316                     }
 2317                     if (ei->func) ei->func(attr);
 2318                     if (ei->func != html_br) was_br = 0;
 2319                     if (a) par_format = pa;
 2320                 }
 2321             } else {
 2322                 struct html_element *e, *ff;
 2323                 int lnb = 0;
 2324                 int xxx = 0;
 2325                 was_br = 0;
 2326                 if (ei->nopair == 1 || ei->nopair == 3) break;
 2327                 /*debug_stack();*/
 2328                 foreach(e, html_stack) {
 2329                     if (e->linebreak && !ei->linebreak && ei->name) xxx = 1;
 2330                     if (e->namelen != namelen || casecmp(e->name, name, e->namelen)) {
 2331                         if (e->dontkill) break;
 2332                         else continue;
 2333                     }
 2334                     if (xxx) {
 2335                         kill_html_stack_item(e);
 2336                         break;
 2337                     }
 2338                     for (ff = e; ff != (void *)&html_stack; ff = ff->prev)
 2339                         if (ff->linebreak > lnb) lnb = ff->linebreak;
 2340                     ln_break(lnb, line_break, f);
 2341                     while (e->prev != (void *)&html_stack) kill_html_stack_item(e->prev);
 2342                     kill_html_stack_item(e);
 2343                     break;
 2344                 }
 2345                 /*debug_stack();*/
 2346             }
 2347             goto set_lt;
 2348         }
 2349         goto set_lt; /* unreachable */
 2350     }
 2351     put_chrs(lt, html - lt, put_chars, f);
 2352     ln_break(1, line_break, f);
 2353     putsp = -1;
 2354     pos = 0;
 2355     /*line_breax = 1;*/
 2356     was_br = 0;
 2357 }
 2358 
 2359 int get_image_map(unsigned char *head, unsigned char *s, unsigned char *eof, unsigned char *tag, struct menu_item **menu, struct memory_list **ml, unsigned char *href_base, unsigned char *target_base, int to, int def, int hdef)
 2360 {
 2361     unsigned char *name, *attr, *al, *label, *href, *target;
 2362     int namelen, lblen;
 2363     struct link_def *ld;
 2364     struct menu_item *nm;
 2365     int nmenu = 0;
 2366     int i;
 2367     unsigned char *hd = init_str();
 2368     int hdl = 0;
 2369     struct conv_table *ct;
 2370     if (head) add_to_str(&hd, &hdl, head);
 2371     scan_http_equiv(s, eof, &hd, &hdl, NULL);
 2372     ct = get_convert_table(hd, to, def, NULL, NULL, hdef);
 2373     mem_free(hd);
 2374     *menu = mem_alloc(sizeof(struct menu_item));
 2375     memset(*menu, 0, sizeof(struct menu_item));
 2376     /*(*menu)->text = NULL;*/
 2377     se:
 2378     while (s < eof && *s != '<') {
 2379         sp:
 2380         s++;
 2381     }
 2382     if (s >= eof) {
 2383         mem_free(*menu);
 2384         return -1;
 2385     }
 2386     if (s + 2 <= eof && (s[1] == '!' || s[1] == '?')) {
 2387         s = skip_comment(s, eof);
 2388         goto se;
 2389     }
 2390     if (parse_element(s, eof, &name, &namelen, &attr, &s)) goto sp;
 2391     if (namelen != 3 || casecmp(name, "MAP", 3)) goto se;
 2392     if (tag && *tag) {
 2393         if (!(al = get_attr_val(attr, "name"))) goto se;
 2394         if (strcasecmp(al, tag)) {
 2395             mem_free(al);
 2396             goto se;
 2397         }
 2398         mem_free(al);
 2399     }
 2400     *ml = getml(NULL);
 2401     se2:
 2402     while (s < eof && *s != '<') {
 2403         sp2:
 2404         s++;
 2405     }
 2406     if (s >= eof) {
 2407         freeml(*ml);
 2408         mem_free(*menu);
 2409         return -1;
 2410     }
 2411     if (s + 2 <= eof && (s[1] == '!' || s[1] == '?')) {
 2412         s = skip_comment(s, eof);
 2413         goto se2;
 2414     }
 2415     if (parse_element(s, eof, &name, &namelen, &attr, &s)) goto sp2;
 2416     if (namelen == 1 && !casecmp(name, "A", 1)) {
 2417         unsigned char *ss;
 2418         label = init_str();
 2419         lblen = 0;
 2420         se3:
 2421         ss = s;
 2422         while (ss < eof && *ss != '<') ss++;
 2423         if (ss >= eof) {
 2424             mem_free(label);
 2425             freeml(*ml);
 2426             mem_free(*menu);
 2427             return -1;
 2428         }
 2429         add_bytes_to_str(&label, &lblen, s, ss - s);
 2430         s = ss;
 2431         if (s + 2 <= eof && (s[1] == '!' || s[1] == '?')) {
 2432             s = skip_comment(s, eof);
 2433             goto se3;
 2434         }
 2435         if (parse_element(s, eof, NULL, NULL, NULL, &ss)) goto se3;
 2436         if (!((namelen == 1 && !casecmp(name, "A", 1)) ||
 2437               (namelen == 2 && !casecmp(name, "/A", 2)) ||
 2438               (namelen == 3 && !casecmp(name, "MAP", 3)) ||
 2439               (namelen == 4 && !casecmp(name, "/MAP", 4)) ||
 2440               (namelen == 4 && !casecmp(name, "AREA", 4)) ||
 2441               (namelen == 5 && !casecmp(name, "/AREA", 5)))) {
 2442                 s = ss;
 2443                 goto se3;
 2444         }
 2445     } else if (namelen == 4 && !casecmp(name, "AREA", 4)) {
 2446         unsigned char *l = get_attr_val(attr, "alt");
 2447         if (l) label = convert_string(ct, l, strlen(l)), mem_free(l);
 2448         else label = NULL;
 2449     } else if (namelen == 4 && !casecmp(name, "/MAP", 4)) goto done;
 2450     else goto se2;
 2451     if (!(href = get_url_val(attr, "href"))) {
 2452         if (label) mem_free(label);
 2453         goto se2;
 2454     }
 2455     if (!(target = get_target(attr)) && !(target = stracpy(target_base)))
 2456         target = stracpy("");
 2457     ld = mem_alloc(sizeof(struct link_def));
 2458     if (!(ld->link = join_urls(href_base, href))) {
 2459         mem_free(href);
 2460         mem_free(target);
 2461         f:
 2462         mem_free(ld);
 2463         if (label) mem_free(label);
 2464         goto se2;
 2465     }
 2466     mem_free(href);
 2467     ld->target = target;
 2468     for (i = 0; i < nmenu; i++) {
 2469         struct link_def *ll = (*menu)[i].data;
 2470         if (!strcmp(ll->link, ld->link) && !strcmp(ll->target, ld->target)) {
 2471             mem_free(ld->link);
 2472             mem_free(ld->target);
 2473             goto f;
 2474         }
 2475     }
 2476     if (label) clr_spaces(label);
 2477     if (label && !*label) mem_free(label), label = NULL;
 2478     if (!label) label = stracpy(ld->link);
 2479     if ((unsigned)nmenu > MAXINT / sizeof(struct menu_item) - 2) overalloc();
 2480     nm = mem_realloc(*menu, (nmenu + 2) * sizeof(struct menu_item));
 2481     *menu = nm;
 2482     memset(&nm[nmenu], 0, 2 * sizeof(struct menu_item));
 2483     nm[nmenu].text = label;
 2484     nm[nmenu].rtext = "";
 2485     nm[nmenu].hotkey = "";
 2486     nm[nmenu].func = MENU_FUNC map_selected;
 2487     nm[nmenu].data = ld;
 2488     nm[++nmenu].text = NULL;
 2489     add_to_ml(ml, ld, ld->link, ld->target, label, NULL);
 2490     goto se2;
 2491     done:
 2492     add_to_ml(ml, *menu, NULL);
 2493     return 0;
 2494 }
 2495 
 2496 void scan_http_equiv(unsigned char *s, unsigned char *eof, unsigned char **head, int *hdl, unsigned char **title)
 2497 {
 2498     unsigned char *name, *attr, *he, *c;
 2499     int namelen;
 2500     int tlen = 0;
 2501     if (title) *title = init_str();
 2502     add_chr_to_str(head, hdl, '\n');
 2503     se:
 2504     while (s < eof && *s != '<') sp:s++;
 2505     if (s >= eof) return;
 2506     if (s + 2 <= eof && (s[1] == '!' || s[1] == '?')) {
 2507         s = skip_comment(s, eof);
 2508         goto se;
 2509     }
 2510     if (parse_element(s, eof, &name, &namelen, &attr, &s)) goto sp;
 2511     ps:
 2512     if (namelen == 5 && !casecmp(name, "/HEAD", 5)) return;
 2513     if (title && !tlen && namelen == 5 && !casecmp(name, "TITLE", 5)) {
 2514         unsigned char *s1;
 2515         xse:
 2516         s1 = s;
 2517         while (s < eof && *s != '<') xsp:s++;
 2518         add_bytes_to_str(title, &tlen, s1, s - s1);
 2519         if (s >= eof) goto se;
 2520         if (s + 2 <= eof && (s[1] == '!' || s[1] == '?')) {
 2521             s = skip_comment(s, eof);
 2522             goto xse;
 2523         }
 2524         if (parse_element(s, eof, &name, &namelen, &attr, &s)) {
 2525             s1 = s;
 2526             goto xsp;
 2527         }
 2528         clr_spaces(*title);
 2529         goto ps;
 2530     }
 2531     if (namelen != 4 || casecmp(name, "META", 4)) goto se;
 2532     if ((he = get_attr_val(attr, "charset"))) {
 2533         add_to_str(head, hdl, "Charset: ");
 2534         add_to_str(head, hdl, he);
 2535         mem_free(he);
 2536     }
 2537     if (!(he = get_attr_val(attr, "http-equiv"))) goto se;
 2538     c = get_attr_val(attr, "content");
 2539     add_to_str(head, hdl, he);
 2540     if (c) add_to_str(head, hdl, ": "), add_to_str(head, hdl, c), mem_free(c);
 2541     mem_free(he);
 2542     add_to_str(head, hdl, "\r\n");
 2543     goto se;
 2544 }
 2545