"Fossies" - the Fresh Open Source Software Archive

Member "links-1.03/html_r.c" (16 Nov 2011, 41919 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_r.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 static inline int color_distance(struct rgb *c1, struct rgb *c2)
    6 {
    7     return
    8         3 * (c1->r - c2->r) * (c1->r - c2->r) +
    9         4 * (c1->g - c2->g) * (c1->g - c2->g) +
   10         2 * (c1->b - c2->b) * (c1->b - c2->b);
   11 }
   12 
   13 struct rgb palette[] = {
   14 /*  {0x00, 0x00, 0x00, 0},
   15     {0x80, 0x00, 0x00, 0},
   16     {0x00, 0x80, 0x00, 0},
   17     {0x80, 0x80, 0x00, 0},
   18     {0x00, 0x00, 0x80, 0},
   19     {0x80, 0x00, 0x80, 0},
   20     {0x00, 0x80, 0x80, 0},
   21     {0xC0, 0xC0, 0xC0, 0},
   22     {0x80, 0x80, 0x80, 0},
   23     {0xff, 0x00, 0x00, 0},
   24     {0x00, 0xff, 0x00, 0},
   25     {0xff, 0xff, 0x00, 0},
   26     {0x00, 0x00, 0xff, 0},
   27     {0xff, 0x00, 0xff, 0},
   28     {0x00, 0xff, 0xff, 0},
   29     {0xff, 0xff, 0xff, 0},*/
   30     /*{0x00, 0x00, 0x00, 0},
   31     {0xaa, 0x00, 0x00, 0},
   32     {0x00, 0xaa, 0x00, 0},
   33     {0xaa, 0x55, 0x00, 0},
   34     {0x00, 0x00, 0xaa, 0},
   35     {0xaa, 0x00, 0xaa, 0},
   36     {0x00, 0xaa, 0xaa, 0},
   37     {0xaa, 0xaa, 0xaa, 0},
   38     {0x55, 0x55, 0x55, 0},
   39     {0xff, 0x55, 0x55, 0},
   40     {0x55, 0xff, 0x55, 0},
   41     {0xff, 0xff, 0x55, 0},
   42     {0x55, 0x55, 0xff, 0},
   43     {0xff, 0x55, 0xff, 0},
   44     {0x55, 0xff, 0xff, 0},
   45     {0xff, 0xff, 0xff, 0},*/
   46     {0x00, 0x00, 0x00, 0},
   47     {0x80, 0x00, 0x00, 0},
   48     {0x00, 0x80, 0x00, 0},
   49     {0xaa, 0x55, 0x00, 0},
   50     {0x00, 0x00, 0x80, 0},
   51     {0x80, 0x00, 0x80, 0},
   52     {0x00, 0x80, 0x80, 0},
   53     {0xaa, 0xaa, 0xaa, 0},
   54     {0x55, 0x55, 0x55, 0},
   55     {0xff, 0x55, 0x55, 0},
   56     {0x55, 0xff, 0x55, 0},
   57     {0xff, 0xff, 0x55, 0},
   58     {0x55, 0x55, 0xff, 0},
   59     {0xff, 0x55, 0xff, 0},
   60     {0x55, 0xff, 0xff, 0},
   61     {0xff, 0xff, 0xff, 0},
   62     {-1, -1, -1, 0},
   63 };
   64 
   65 /*struct rgb rgbcache = {0, 0, 0};
   66 int rgbcache_c = 0;
   67 
   68 static inline int find_nearest_color(struct rgb *r, int l)
   69 {
   70     int dist, dst, min, i;
   71     if (r->r == rgbcache.r && r->g == rgbcache.g && r->b == rgbcache.b) return rgbcache_c;
   72     dist = 0xffffff;
   73     min = 0;
   74     for (i = 0; i < l; i++) if ((dst = color_distance(r, &palette[i])) < dist)
   75         dist = dst, min = i;
   76     return min;
   77 }*/
   78 
   79 struct rgb_cache_entry {
   80     int color;
   81     int l;
   82     struct rgb rgb;
   83 };
   84 
   85 #define RGB_HASH_SIZE 4096
   86 
   87 #define HASH_RGB(r, l) ((((r)->r << 3) + ((r)->g << 2) + (r)->b + (l)) & (RGB_HASH_SIZE - 1))
   88 
   89 static inline int find_nearest_color(struct rgb *r, int l)
   90 {
   91     int dist, dst, min, i;
   92     static struct rgb_cache_entry rgb_cache[RGB_HASH_SIZE];
   93     static int cache_init = 0;
   94     int h;
   95     if (!cache_init) goto initialize;
   96     back:
   97     h = HASH_RGB(r, l);
   98     if (rgb_cache[h].color != -1 && rgb_cache[h].l == l && rgb_cache[h].rgb.r == r->r && rgb_cache[h].rgb.g == r->g && rgb_cache[h].rgb.b == r->b) return rgb_cache[h].color;
   99     dist = 0xffffff;
  100     min = 0;
  101     for (i = 0; i < l; i++) if ((dst = color_distance(r, &palette[i])) < dist)
  102         dist = dst, min = i;
  103     rgb_cache[h].color = min;
  104     rgb_cache[h].l = l;
  105     rgb_cache[h].rgb.r = r->r;
  106     rgb_cache[h].rgb.g = r->g;
  107     rgb_cache[h].rgb.b = r->b;
  108     return min;
  109 
  110     initialize:
  111     for (h = 0; h < RGB_HASH_SIZE; h++) rgb_cache[h].color = -1;
  112     cache_init = 1;
  113     goto back;
  114 }
  115 
  116 static inline int fg_color(int fg, int bg)
  117 {
  118     int l = bg < fg ? bg : fg;
  119     int h = bg < fg ? fg : bg;
  120     if (l == h || (!l && (h == 4 || h == 8 || h == 12)) ||
  121        (l == 1 && (h == 3 || h == 5 || h == 8 || h == 12)) ||
  122        (l == 2 && h == 6) || (l == 3 && (h == 5 || h == 12)) ||
  123        (l == 4 && (h == 8 || h == 12)) || (l == 5 && (h == 8 || h == 12)))
  124         return (fg == 4 || fg == 12) && (bg == 0 || bg == 8) ? 6 : (7 - 7 * (bg == 2 || bg == 6 || bg == 7));
  125     return fg;
  126 }
  127 
  128 #define XALIGN(x) (((x)+0x7f)&~0x7f)
  129 
  130 int nowrap = 0;
  131 
  132 static inline void xpand_lines(struct part *p, int y)
  133 {
  134     /*if (y >= p->y) p->y = y + 1;*/
  135     if (!p->data) return;
  136     if (XALIGN((unsigned)y + (unsigned)p->yp) > MAXINT) overalloc();
  137     y += p->yp;
  138     if (y >= p->data->y) {          /* !!! FIXME: out of inline */
  139         int i;
  140         if (XALIGN(y + 1) > XALIGN(p->data->y)) {
  141             if (XALIGN((unsigned)y + 1) > MAXINT / sizeof(struct line)) overalloc();
  142             p->data->data = mem_realloc(p->data->data, XALIGN(y+1)*sizeof(struct line));
  143         }
  144         for (i = p->data->y; i <= y; i++) {
  145             p->data->data[i].l = 0;
  146             p->data->data[i].c = p->bgcolor;
  147             p->data->data[i].d = DUMMY;
  148         }
  149         p->data->y = i;
  150     }
  151 }
  152 
  153 static inline void xpand_line(struct part *p, int y, int x)
  154 {
  155     if (!p->data) return; /* !!! FIXME: p->x (?) */
  156     if (XALIGN((unsigned)x + (unsigned)p->xp) > MAXINT) overalloc();
  157     x += p->xp;
  158     y += p->yp;
  159 #ifdef DEBUG
  160     if (y >= p->data->y) {
  161         internal("line does not exist");
  162         return;
  163     }
  164 #endif
  165     if (x >= p->data->data[y].l) {      /* !!! FIXME: out of inline */
  166         int i;
  167         if (XALIGN(x+1) > XALIGN(p->data->data[y].l)) {
  168             if (XALIGN((unsigned)x + 1) > MAXINT / sizeof(chr)) overalloc();
  169             p->data->data[y].d = mem_realloc(p->data->data[y].d, XALIGN(x+1)*sizeof(chr));
  170         }
  171         for (i = p->data->data[y].l; i <= x; i++)
  172             p->data->data[y].d[i] = (p->data->data[y].c << 11) | ' ';
  173         p->data->data[y].c = p->bgcolor;
  174         p->data->data[y].l = i;
  175     }
  176 }
  177 
  178 void r_xpand_spaces(struct part *p, int l)
  179 {
  180     unsigned char *c;
  181     if ((unsigned)l >= MAXINT) overalloc();
  182     c = mem_realloc(p->spaces, l + 1);
  183     memset(c + p->spl, 0, l - p->spl + 1);
  184     p->spl = l + 1;
  185     p->spaces = c;
  186 }
  187 
  188 static inline void xpand_spaces(struct part *p, int l)
  189 {
  190     if ((unsigned)l >= (unsigned)p->spl) r_xpand_spaces(p, l);
  191 }
  192 
  193 #define POS(x, y) (p->data->data[p->yp + (y)].d[p->xp + (x)])
  194 #define LEN(y) (p->data->data[p->yp + (y)].l - p->xp < 0 ? 0 : p->data->data[p->yp + (y)].l - p->xp)
  195 #define SLEN(y, x) p->data->data[p->yp + (y)].l = p->xp + x;
  196 #define X(x) (p->xp + (x))
  197 #define Y(y) (p->yp + (y))
  198 
  199 static inline void set_hchar(struct part *p, int x, int y, unsigned c)
  200 {
  201     xpand_lines(p, y);
  202     xpand_line(p, y, x);
  203     POS(x, y) = c;
  204 }
  205 
  206 static inline void set_hchars(struct part *p, int x, int y, int xl, unsigned c)
  207 {
  208     xpand_lines(p, y);
  209     xpand_line(p, y, x+xl-1);
  210     for (; xl; xl--, x++) POS(x, y) = c;
  211 }
  212 
  213 void xset_hchar(struct part *p, int x, int y, unsigned c)
  214 {
  215     set_hchar(p, x, y, c);
  216 }
  217 
  218 void xset_hchars(struct part *p, int x, int y, int xl, unsigned c)
  219 {
  220     set_hchars(p, x, y, xl, c);
  221 }
  222 
  223 void xxpand_lines(struct part *p, int y)
  224 {
  225     xpand_lines(p, y);
  226 }
  227 
  228 void xxpand_line(struct part *p, int y, int x)
  229 {
  230     xpand_line(p, y, x);
  231 }
  232 
  233 static inline void set_hline(struct part *p, int x, int y, int xl, unsigned char *d, unsigned c, int spc)
  234 {
  235     xpand_lines(p, y);
  236     xpand_line(p, y, x+xl-1);
  237     if (spc) xpand_spaces(p, x+xl-1);
  238     for (; xl; xl--, x++, d++) {
  239         if (spc) p->spaces[x] = *d == ' ';
  240         if (p->data) POS(x, y) = *d | c;
  241     }
  242 }
  243 
  244 int last_link_to_move;
  245 struct tag *last_tag_to_move;
  246 struct tag *last_tag_for_newline;
  247 
  248 static inline void move_links(struct part *p, int xf, int yf, int xt, int yt)
  249 {
  250     int n;
  251     struct tag *t;
  252     int w = 0;
  253     if (!p->data) return;
  254     xpand_lines(p, yt);
  255     for (n = last_link_to_move; n < p->data->nlinks; n++) {
  256         int i;
  257         struct link *link = &p->data->links[n];
  258             /*printf("ml: %d %d %d %d",link->pos[0].x,link->pos[0].y,X(xf),Y(yf));fflush(stdout);sleep(1);*/
  259         /*for (i = 0; i < link->n; i++) fprintf(stderr, "%d.%d -> %d.%d: %d.%d : %d %d\n", X(xf), Y(yf), X(xt), yt != -1 ? Y(yt) : -1, n, i, link->pos[i].x, link->pos[i].y);*/
  260         for (i = 0; i < link->n; i++) if (link->pos[i].y >= Y(yf)) {
  261             w = 1;
  262             if (link->pos[i].y == Y(yf) && link->pos[i].x >= X(xf)) {
  263                 if (yt >= 0) link->pos[i].y = Y(yt), link->pos[i].x += -xf + xt;
  264                 else memmove(&link->pos[i], &link->pos[i+1], (link->n-i-1) * sizeof(struct point)), link->n--, i--;
  265             }
  266         }
  267         /*if (!link->n) {
  268             if (link->where) mem_free(link->where);
  269             if (link->target) mem_free(link->target);
  270             if (link->where_img) mem_free(link->where_img);
  271             if (link->pos) mem_free(link->pos);
  272             memmove(link, link + 1, (p->data->nlinks - n - 1) * sizeof(struct link));
  273             p->data->nlinks --;
  274             n--;
  275         }*/
  276         if (!w /*&& n >= 0*/) last_link_to_move = n;
  277     }
  278     w = 0;
  279     if (yt >= 0) for (t = last_tag_to_move->next; (void *)t != &p->data->tags; t = t->next) {
  280         if (t->y == Y(yf)) {
  281             w = 1;
  282             if (t->x >= X(xf)) {
  283                 t->y = Y(yt), t->x += -xf + xt;
  284             }
  285         }
  286         if (!w) last_tag_to_move = t;
  287     }
  288 }
  289 
  290 static inline void copy_chars(struct part *p, int x, int y, int xl, chr *d)
  291 {
  292     if (xl <= 0) return;
  293     xpand_lines(p, y);
  294     xpand_line(p, y, x+xl-1);
  295     for (; xl; xl--, x++, d++) POS(x, y) = *d;
  296 }
  297 
  298 static inline void move_chars(struct part *p, int x, int y, int nx, int ny)
  299 {
  300     if (LEN(y) - x <= 0) return;
  301     copy_chars(p, nx, ny, LEN(y) - x, &POS(x, y));
  302     SLEN(y, x);
  303     move_links(p, x, y, nx, ny);
  304 }
  305 
  306 static inline void shift_chars(struct part *p, int y, int s)
  307 {
  308     chr *a;
  309     int l = LEN(y);
  310     if ((unsigned)l > MAXINT / sizeof(chr)) overalloc();
  311     a = mem_alloc(l * sizeof(chr));
  312     memcpy(a, &POS(0, y), l * sizeof(chr));
  313     set_hchars(p, 0, y, s, (p->data->data[y].c << 11) | ' ');
  314     copy_chars(p, s, y, l, a);
  315     mem_free(a);
  316     move_links(p, 0, y, s, y);
  317 }
  318 
  319 static inline void del_chars(struct part *p, int x, int y)
  320 {
  321     SLEN(y, x);
  322     move_links(p, x, y, -1, -1);
  323 }
  324 
  325 #define rm(x) ((x).width - (x).rightmargin > 0 ? (x).width - (x).rightmargin : 0)
  326 
  327 void line_break(struct part *);
  328 
  329 int split_line(struct part *p)
  330 {
  331     int i;
  332     /*if (!p->data) goto r;*/
  333     /*printf("split: %d,%d   , %d,%d,%d\n",p->cx,p->cy,par_format.rightmargin,par_format.leftmargin,p->cx);*/
  334     for (i = rm(par_format); i >= par_format.leftmargin; i--)
  335         if (i < p->spl && p->spaces[i]) goto split;
  336     /*for (i = p->cx - 1; i > rm(par_format) && i > par_format.leftmargin; i--)*/
  337     for (i = par_format.leftmargin; i < p->cx ; i++)
  338         if (i < p->spl && p->spaces[i]) goto split;
  339     /*for (i = rm(par_format); i >= par_format.leftmargin; i--)
  340         if ((POS(i, p->cy) & 0xff) == ' ') goto split;
  341     for (i = p->cx - 1; i > rm(par_format) && i > par_format.leftmargin; i--)
  342         if ((POS(i, p->cy) & 0xff) == ' ') goto split;*/
  343     if (p->cx + par_format.rightmargin > p->x) p->x = p->cx + par_format.rightmargin;
  344     /*if (p->y < p->cy + 1) p->y = p->cy + 1;
  345     p->cy++; p->cx = -1;
  346     memset(p->spaces, 0, p->spl);
  347     if (p->data) xpand_lines(p, p->cy + 1);*/
  348     /*line_break(p);*/
  349     return 0;
  350     split:
  351     if (i + par_format.rightmargin > p->x) p->x = i + par_format.rightmargin;
  352     if (p->data) {
  353 #ifdef DEBUG
  354         if ((POS(i, p->cy) & 0xff) != ' ') internal("bad split: %c", (char)POS(i, p->cy));
  355 #endif
  356         move_chars(p, i+1, p->cy, par_format.leftmargin, p->cy+1);
  357         del_chars(p, i, p->cy);
  358     }
  359     memmove(p->spaces, p->spaces + i + 1, p->spl - i - 1);
  360     memset(p->spaces + p->spl - i - 1, 0, i + 1);
  361     memmove(p->spaces + par_format.leftmargin, p->spaces, p->spl - par_format.leftmargin);
  362     p->cy++; p->cx -= i - par_format.leftmargin + 1;
  363     /*return 1 + (p->cx == par_format.leftmargin);*/
  364     if (p->cx == par_format.leftmargin) p->cx = -1;
  365     if (p->y < p->cy + (p->cx != -1)) p->y = p->cy + (p->cx != -1);
  366     return 1 + (p->cx == -1);
  367 }
  368 
  369 void align_line(struct part *p, int y)
  370 {
  371     int na;
  372     if (!p->data) return;
  373     if (!LEN(y) || par_format.align == AL_LEFT || par_format.align == AL_NO || par_format.align == AL_BLOCK /* !!! fixme! */) return;
  374     na = rm(par_format) - LEN(y);
  375     if (par_format.align == AL_CENTER) na /= 2;
  376     if (na > 0) shift_chars(p, y, na);
  377 }
  378 
  379 struct link *new_link(struct f_data *f)
  380 {
  381     if (!f) return NULL;
  382     if (!(f->nlinks & (ALLOC_GR - 1))) {
  383         if ((unsigned)f->nlinks > MAXINT / sizeof(struct link) - ALLOC_GR) overalloc();
  384         f->links = mem_realloc(f->links, (f->nlinks + ALLOC_GR) * sizeof(struct link));
  385     }
  386     memset(&f->links[f->nlinks], 0, sizeof(struct link));
  387     return &f->links[f->nlinks++];
  388 }
  389 
  390 void html_tag(struct f_data *f, unsigned char *t, int x, int y)
  391 {
  392     struct tag *tag;
  393     unsigned char *tt;
  394     int ll;
  395     if (!f) return;
  396     tt = init_str();
  397     ll = 0;
  398     add_conv_str(&tt, &ll, t, strlen(t), -2);
  399     tag = mem_alloc(sizeof(struct tag) + strlen(tt) + 1);
  400     tag->x = x;
  401     tag->y = y;
  402     strcpy(tag->name, tt);
  403     add_to_list(f->tags, tag);
  404     if ((void *)last_tag_for_newline == &f->tags) last_tag_for_newline = tag;
  405     mem_free(tt);
  406 }
  407 
  408 unsigned char *last_link;
  409 unsigned char *last_target;
  410 unsigned char *last_image;
  411 struct form_control *last_form;
  412 
  413 int nobreak;
  414 
  415 struct conv_table *convert_table;
  416 
  417 void put_chars(struct part *, unsigned char *, int);
  418 
  419 #define CH_BUF  256
  420 
  421 int put_chars_conv(struct part *p, unsigned char *c, int l)
  422 {
  423     static char buffer[CH_BUF];
  424     int bp = 0;
  425     int pp = 0;
  426     int total = 0;
  427     if (format.attr & AT_GRAPHICS) {
  428         put_chars(p, c, l);
  429         return l;
  430     }
  431     if (!l) put_chars(p, NULL, 0);
  432     while (pp < l) {
  433         unsigned char *e;
  434         if (c[pp] < 128 && c[pp] != '&') {
  435             put_c:
  436             buffer[bp++] = c[pp++];
  437             if (bp < CH_BUF) continue;
  438             goto flush;
  439         }
  440         if (c[pp] != '&') {
  441             struct conv_table *t;
  442             int i;
  443             if (!convert_table) goto put_c;
  444             t = convert_table;
  445             i = pp;
  446             decode:
  447             if (!t[c[i]].t) {
  448                 e = t[c[i]].u.str;
  449             } else {
  450                 t = t[c[i++]].u.tbl;
  451                 if (i >= l) goto put_c;
  452                 goto decode;
  453             }
  454             pp = i + 1;
  455         } else {
  456             int i = pp + 1;
  457             if (d_opt->plain) goto put_c;
  458             while (i < l && c[i] != ';' && c[i] != '&' && c[i] > ' ') i++;
  459             if (!(e = get_entity_string(&c[pp + 1], i - pp - 1, d_opt->cp))) goto put_c;
  460             pp = i + (i < l && c[i] == ';');
  461         }
  462         if (!e[0]) continue;
  463         if (!e[1]) {
  464             buffer[bp++] = e[0];
  465             if (bp < CH_BUF) continue;
  466             flush:
  467             e = "";
  468             goto flush1;
  469         }
  470         while (*e) {
  471             buffer[bp++] = *(e++);
  472             if (bp < CH_BUF) continue;
  473             flush1:
  474             put_chars(p, buffer, bp);
  475             total += bp;
  476             bp = 0;
  477         }
  478     }
  479     if (bp) put_chars(p, buffer, bp);
  480     total += bp;
  481     return total;
  482 }
  483 
  484 void put_chars(struct part *p, unsigned char *c, int l)
  485 {
  486     static struct text_attrib_beginning ta_cache = { -1, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } };
  487     static int bg_cache;
  488     static int fg_cache;
  489 
  490     int bg, fg;
  491     int i;
  492     struct link *link;
  493     struct point *pt;
  494     if (l < 0) overalloc();
  495     /*printf("%d-", p->cx);for (i=0; i<l; i++) printf("%c", c[i]); printf("-\n");sleep(1);*/
  496     while (par_format.align != AL_NO && p->cx == -1 && l && *c == ' ') c++, l--;
  497     if (!l) return;
  498     if (c[0] != ' ' || (c[1] && c[1] != ' ')) {
  499         last_tag_for_newline = (void *)&p->data->tags;
  500     }
  501     if (p->cx < par_format.leftmargin) p->cx = par_format.leftmargin;
  502     if (last_link || last_image || last_form || format.link || format.image || format.form) goto process_link;
  503     no_l:
  504     /*printf("%d %d\n",p->cx, p->cy);*/
  505     if (memcmp(&ta_cache, &format, sizeof(struct text_attrib_beginning))) goto format_change;
  506     bg = bg_cache, fg = fg_cache;
  507     end_format_change:
  508     if (p->cx == par_format.leftmargin && *c == ' ' && par_format.align != AL_NO) c++, l--;
  509     if (p->y < p->cy + 1) p->y = p->cy + 1;
  510     if (nowrap && p->cx + l > rm(par_format)) return;
  511     set_hline(p, p->cx, p->cy, l, c, (((fg&0x08)<<3)|(bg<<3)|(fg&0x07))<<8, 1);
  512     p->cx += l;
  513     nobreak = 0;
  514     if (par_format.align != AL_NO)
  515         while (p->cx > rm(par_format) && p->cx > par_format.leftmargin) {
  516             int x;
  517             /*if (p->cx > p->x) {
  518                 p->x = p->cx + par_format.rightmargin;
  519                 if (c[l - 1] == ' ') p->x--;
  520             }*/
  521             if (!(x = split_line(p))) break;
  522             /*if (LEN(p->cy-1) > p->x) p->x = LEN(p->cy-1);*/
  523             align_line(p, p->cy - 1);
  524             nobreak = x - 1;
  525         }
  526     if ((p->xa += l) - (c[l-1] == ' ' && par_format.align != AL_NO) + par_format.leftmargin + par_format.rightmargin > p->xmax) p->xmax = p->xa - (c[l-1] == ' ' && par_format.align != AL_NO) + par_format.leftmargin + par_format.rightmargin;
  527     return;
  528     process_link:
  529     if ((last_link /*|| last_target*/ || last_image || last_form) &&
  530         !xstrcmp(format.link, last_link) && !xstrcmp(format.target, last_target) &&
  531         !xstrcmp(format.image, last_image) && format.form == last_form) {
  532         if (!p->data) goto x;
  533         link = &p->data->links[p->data->nlinks - 1];
  534         if (!p->data->nlinks) {
  535             internal("no link");
  536             goto no_l;
  537         }
  538         goto set_link;
  539         x:;
  540     } else {
  541         if (last_link) mem_free(last_link); /* !!! FIXME: optimize */
  542         if (last_target) mem_free(last_target);
  543         if (last_image) mem_free(last_image);
  544         last_link = last_target = last_image = NULL;
  545         last_form = NULL;
  546         if (!(format.link || format.image || format.form)) goto no_l;
  547         if (d_opt->num_links) {
  548             unsigned char s[64];
  549             unsigned char *fl = format.link, *ft = format.target, *fi = format.image;
  550             struct form_control *ff = format.form;
  551             format.link = format.target = format.image = NULL;
  552             format.form = NULL;
  553             s[0] = '[';
  554             snzprint(s + 1, 62, p->link_num);
  555             strcat(s, "]");
  556             put_chars(p, s, strlen(s));
  557             if (ff && ff->type == FC_TEXTAREA) line_break(p);
  558             if (p->cx == -1) p->cx = par_format.leftmargin;
  559             format.link = fl, format.target = ft, format.image = fi;
  560             format.form = ff;
  561         }
  562         p->link_num++;
  563         last_link = stracpy(format.link);
  564         last_target = stracpy(format.target);
  565         last_image = stracpy(format.image);
  566         last_form = format.form;
  567         if (!p->data) goto no_l;
  568         if (!(link = new_link(p->data))) goto no_l;
  569         link->num = p->link_num - 1;
  570         link->pos = DUMMY;
  571         if (!last_form) {
  572             link->type = L_LINK;
  573             link->where = stracpy(last_link);
  574             link->target = stracpy(last_target);
  575         } else {
  576             link->type = last_form->type == FC_TEXT || last_form->type == FC_PASSWORD || last_form->type == FC_FILE ? L_FIELD : last_form->type == FC_TEXTAREA ? L_AREA : last_form->type == FC_CHECKBOX || last_form->type == FC_RADIO ? L_CHECKBOX : last_form->type == FC_SELECT ? L_SELECT : L_BUTTON;
  577             link->form = last_form;
  578             link->target = stracpy(last_form->target);
  579         }
  580         link->where_img = stracpy(last_image);
  581         if (link->type != L_FIELD && link->type != L_AREA) {
  582             bg = find_nearest_color(&format.clink, 8);
  583             fg = find_nearest_color(&format.bg, 8);
  584             fg = fg_color(fg, bg);
  585         } else {
  586             fg = find_nearest_color(&format.fg, 8);
  587             bg = find_nearest_color(&format.bg, 8);
  588             fg = fg_color(fg, bg);
  589         }
  590         link->sel_color = ((fg & 8) << 3) | (fg & 7) | (bg << 3);
  591         link->n = 0;
  592         set_link:
  593         if ((unsigned)link->n + (unsigned)l > MAXINT / sizeof(struct point)) overalloc();
  594         pt = mem_realloc(link->pos, (link->n + l) * sizeof(struct point));
  595         link->pos = pt;
  596         for (i = 0; i < l; i++) pt[link->n + i].x = X(p->cx) + i,
  597                     pt[link->n + i].y = Y(p->cy);
  598         link->n += l;
  599     }
  600     goto no_l;
  601 
  602         format_change:
  603         bg = find_nearest_color(&format.bg, 8);
  604         fg = find_nearest_color(&format.fg, 16);
  605         fg = fg_color(fg, bg);
  606         if (format.attr & AT_ITALIC) fg = fg ^ 0x01;
  607         if (format.attr & AT_UNDERLINE) fg = (fg ^ 0x04) | 0x08;
  608         if (format.attr & AT_BOLD) fg = fg | 0x08;
  609         fg = fg_color(fg, bg);
  610         if (format.attr & AT_GRAPHICS) bg = bg | 0x10;
  611         memcpy(&ta_cache, &format, sizeof(struct text_attrib_beginning));
  612         fg_cache = fg; bg_cache = bg;
  613         goto end_format_change;
  614 }
  615 
  616 void line_break(struct part *p)
  617 {
  618     struct tag *t;
  619     /*printf("-break-\n");*/
  620     if (p->cx + par_format.rightmargin > p->x) p->x = p->cx + par_format.rightmargin;
  621     if (nobreak) {
  622         /*if (p->y < p->cy) p->y = p->cy;*/
  623         nobreak = 0;
  624         p->cx = -1;
  625         p->xa = 0;
  626         return;
  627     }
  628     if (!p->data) goto e;
  629     /*move_links(p, p->cx, p->cy, 0, p->cy + 1);*/
  630     xpand_lines(p, p->cy + 1);
  631     if (p->cx > par_format.leftmargin && LEN(p->cy) > p->cx - 1 && (POS(p->cx-1, p->cy) & 0xff) == ' ') del_chars(p, p->cx-1, p->cy), p->cx--;
  632     /*if (LEN(p->cy) > p->x) p->x = LEN(p->cy);*/
  633     if (p->cx > 0) align_line(p, p->cy);
  634     if (p->data) for (t = last_tag_for_newline; t && (void *)t != &p->data->tags; t = t->prev) {
  635         t->x = X(0);
  636         t->y = Y(p->cy + 1);
  637     }
  638     e:
  639     p->cy++; p->cx = -1; p->xa = 0; /*if (p->y < p->cy) p->y = p->cy;*/
  640     memset(p->spaces, 0, p->spl);
  641 }
  642 
  643 int g_ctrl_num;
  644 
  645 void html_form_control(struct part *p, struct form_control *fc)
  646 {
  647     if (!p->data) {
  648         /*destroy_fc(fc);
  649         mem_free(fc);*/
  650         add_to_list(p->uf, fc);
  651         return;
  652     }
  653     fc->g_ctrl_num = g_ctrl_num++;
  654     if (fc->type == FC_TEXT || fc->type == FC_PASSWORD || fc->type == FC_TEXTAREA) {
  655         unsigned char *dv = convert_string(convert_table, fc->default_value, strlen(fc->default_value));
  656         if (dv) {
  657             mem_free(fc->default_value);
  658             fc->default_value = dv;
  659         }
  660         /*
  661         for (i = 0; i < fc->nvalues; i++) if ((dv = convert_string(convert_table, fc->values[i], strlen(fc->values[i])))) {
  662             mem_free(fc->values[i]);
  663             fc->values[i] = dv;
  664         }
  665         */
  666     }
  667     if (fc->type == FC_TEXTAREA) {
  668         unsigned char *p;
  669         for (p = fc->default_value; p[0]; p++) if (p[0] == '\r') {
  670             if (p[1] == '\n') memmove(p, p + 1, strlen(p)), p--;
  671             else p[0] = '\n';
  672         }
  673     }
  674     add_to_list(p->data->forms, fc);
  675 }
  676 
  677 void add_frameset_entry(struct frameset_desc *fsd, struct frameset_desc *subframe, unsigned char *name, unsigned char *url)
  678 {
  679     if (fsd->yp >= fsd->y) return;
  680     fsd->f[fsd->xp + fsd->yp * fsd->x].subframe = subframe;
  681     fsd->f[fsd->xp + fsd->yp * fsd->x].name = stracpy(name);
  682     fsd->f[fsd->xp + fsd->yp * fsd->x].url = stracpy(url);
  683     if (++fsd->xp >= fsd->x) fsd->xp = 0, fsd->yp++;
  684 }
  685 
  686 struct frameset_desc *create_frameset(struct f_data *fda, struct frameset_param *fp)
  687 {
  688     int i;
  689     struct frameset_desc *fd;
  690     if (!fp->x || !fp->y) {
  691         internal("zero size of frameset");
  692         return NULL;
  693     }
  694     if (fp->x && (unsigned)fp->x * (unsigned)fp->y / (unsigned)fp->x != (unsigned)fp->y) overalloc();
  695     if ((unsigned)fp->x * (unsigned)fp->y > (MAXINT - sizeof(struct frameset_desc)) / sizeof(struct frame_desc)) overalloc();
  696     fd = mem_alloc(sizeof(struct frameset_desc) + fp->x * fp->y * sizeof(struct frame_desc));
  697     memset(fd, 0, sizeof(struct frameset_desc) + fp->x * fp->y * sizeof(struct frame_desc));
  698     fd->n = fp->x * fp->y;
  699     fd->x = fp->x;
  700     fd->y = fp->y;
  701     for (i = 0; i < fd->n; i++) {
  702         fd->f[i].xw = fp->xw[i % fp->x];
  703         fd->f[i].yw = fp->yw[i / fp->x];
  704     }
  705     if (fp->parent) add_frameset_entry(fp->parent, fd, NULL, NULL);
  706     else if (!fda->frame_desc) fda->frame_desc = fd;
  707          else mem_free(fd), fd = NULL;
  708     return fd;
  709 }
  710 
  711 void create_frame(struct frame_param *fp)
  712 {
  713     add_frameset_entry(fp->parent, NULL, fp->name, fp->url);
  714 }
  715 
  716 void *html_special(struct part *p, int c, ...)
  717 {
  718     va_list l;
  719     unsigned char *t;
  720     struct form_control *fc;
  721     struct frameset_param *fsp;
  722     struct frame_param *fp;
  723     va_start(l, c);
  724     switch (c) {
  725         case SP_TAG:
  726             t = va_arg(l, unsigned char *);
  727             va_end(l);
  728             html_tag(p->data, t, X(p->cx), Y(p->cy));
  729             break;
  730         case SP_CONTROL:
  731             fc = va_arg(l, struct form_control *);
  732             va_end(l);
  733             html_form_control(p, fc);
  734             break;
  735         case SP_TABLE:
  736             va_end(l);
  737             return convert_table;
  738         case SP_USED:
  739             va_end(l);
  740             return (void *)!!p->data;
  741         case SP_FRAMESET:
  742             fsp = va_arg(l, struct frameset_param *);
  743             va_end(l);
  744             return create_frameset(p->data, fsp);
  745         case SP_FRAME:
  746             fp = va_arg(l, struct frame_param *);
  747             va_end(l);
  748             create_frame(fp);
  749             break;
  750         case SP_NOWRAP:
  751             nowrap = va_arg(l, int);
  752             va_end(l);
  753             break;
  754         default:
  755             internal("html_special: unknown code %d", c);
  756             va_end(l);
  757     }
  758     return NULL;
  759 }
  760 
  761 void do_format(char *start, char *end, struct part *part, unsigned char *head)
  762 {
  763     parse_html(start, end, (int (*)(void *, unsigned char *, int)) put_chars_conv, (void (*)(void *)) line_break, (void *(*)(void *, int, ...)) html_special, part, head);
  764     /*if ((part->y -= line_breax) < 0) part->y = 0;*/
  765 }
  766 
  767 int margin;
  768 
  769 struct table_cache_entry {
  770     struct table_cache_entry *next;
  771     struct table_cache_entry *prev;
  772     struct table_cache_entry *hash_next;
  773     unsigned char *start;
  774     unsigned char *end;
  775     int align;
  776     int m;
  777     int width;
  778     int xs;
  779     int link_num;
  780     struct part p;
  781 };
  782 
  783 struct list_head table_cache = { &table_cache, &table_cache };
  784 
  785 #define TC_HASH_SIZE    4096
  786 
  787 struct table_cache_entry *table_cache_hash[TC_HASH_SIZE];
  788 
  789 void free_table_cache()
  790 {
  791     struct table_cache_entry *tce;
  792     foreach(tce, table_cache) {
  793         int hash = ((int)(unsigned long)tce->start + tce->xs) & (TC_HASH_SIZE - 1);
  794         table_cache_hash[hash] = NULL;
  795     }
  796     free_list(table_cache);
  797 }
  798 
  799 struct part *format_html_part(unsigned char *start, unsigned char *end, int align, int m, int width, struct f_data *data, int xs, int ys, unsigned char *head, int link_num)
  800 {
  801     struct part *p;
  802     struct html_element *e;
  803     int llm = last_link_to_move;
  804     struct tag *ltm = last_tag_to_move;
  805     /*struct tag *ltn = last_tag_for_newline;*/
  806     int lm = margin;
  807     int ef = empty_format;
  808     struct form_control *fc;
  809     struct table_cache_entry *tce;
  810     if (!data) {
  811         tce = table_cache_hash[((int)(unsigned long)start + xs) & (TC_HASH_SIZE - 1)];
  812         while (tce) {
  813             if (tce->start == start && tce->end == end && tce->align == align && tce->m == m && tce->width == width && tce->xs == xs && tce->link_num == link_num) {
  814                 p = mem_alloc(sizeof(struct part));
  815                 memcpy(p, &tce->p, sizeof(struct part));
  816                 return p;
  817             }
  818             tce = tce->hash_next;
  819         }
  820     }
  821     if (ys < 0) {
  822         internal("format_html_part: ys == %d", ys);
  823         return NULL;
  824     }
  825     if (data) {
  826         struct node *n;
  827         n = mem_alloc(sizeof(struct node));
  828         n->x = xs;
  829         n->y = ys;
  830         n->xw = !table_level ? MAXINT : width;
  831         add_to_list(data->nodes, n);
  832         /*sdbg(data);*/
  833     }
  834     last_link_to_move = data ? data->nlinks : 0;
  835     last_tag_to_move = data ? (void *)&data->tags : NULL;
  836     last_tag_for_newline = data ? (void *)&data->tags: NULL;
  837     margin = m;
  838     empty_format = !data;
  839     if (last_link) mem_free(last_link);
  840     if (last_image) mem_free(last_image);
  841     if (last_target) mem_free(last_target);
  842     last_link = last_image = last_target = NULL;
  843     last_form = NULL;
  844     nobreak = 1;
  845     p = mem_alloc(sizeof(struct part));
  846     p->x = p->y = 0;
  847     p->data = data;
  848     p->xp = xs; p->yp = ys;
  849     p->xmax = p->xa = 0;
  850     p->bgcolor = find_nearest_color(&par_format.bgcolor, 8);
  851     p->spaces = DUMMY;
  852     p->spl = 0;
  853     p->link_num = link_num;
  854     init_list(p->uf);
  855     html_stack_dup();
  856     e = &html_top;
  857     html_top.dontkill = 2;
  858     html_top.namelen = 0;
  859     par_format.align = align;
  860     par_format.leftmargin = m;
  861     par_format.rightmargin = m;
  862     par_format.width = width;
  863     par_format.list_level = 0;
  864     par_format.list_number = 0;
  865     par_format.dd_margin = 0;
  866     p->cx = -1;
  867     p->cy = 0;
  868     do_format(start, end, p, head);
  869     if (p->xmax < p->x) p->xmax = p->x;
  870     nobreak = 0;
  871     line_breax = 1;
  872     if (last_link) mem_free(last_link);
  873     if (last_image) mem_free(last_image);
  874     if (last_target) mem_free(last_target);
  875     while (&html_top != e) {
  876         kill_html_stack_item(&html_top);
  877         if (!&html_top || (void *)&html_top == (void *)&html_stack) {
  878             internal("html stack trashed");
  879             break;
  880         }
  881     }
  882     html_top.dontkill = 0;
  883     kill_html_stack_item(&html_top);
  884     mem_free(p->spaces);
  885     if (data) {
  886         struct node *n = data->nodes.next;
  887         n->yw = ys - n->y + p->y;
  888     }
  889     foreach(fc, p->uf) destroy_fc(fc);
  890     free_list(p->uf);
  891     last_link_to_move = llm;
  892     last_tag_to_move = ltm;
  893     /*last_tag_for_newline = ltn;*/
  894     margin = lm;
  895     empty_format = ef;
  896     last_link = last_image = last_target = NULL;
  897     last_form = NULL;
  898     if (table_level > 1 && !data) {
  899         int hash;
  900         tce = mem_alloc(sizeof(struct table_cache_entry));
  901         tce->start = start;
  902         tce->end = end;
  903         tce->align = align;
  904         tce->m = m;
  905         tce->width = width;
  906         tce->xs = xs;
  907         tce->link_num = link_num;
  908         memcpy(&tce->p, p, sizeof(struct part));
  909         add_to_list(table_cache, tce);
  910         hash = ((int)(unsigned long)start + xs) & (TC_HASH_SIZE - 1);
  911         tce->hash_next = table_cache_hash[hash];
  912         table_cache_hash[hash] = tce;
  913     }
  914     return p;
  915 }
  916 
  917 void push_base_format(unsigned char *url, struct document_options *opt)
  918 {
  919     struct html_element *e;
  920     if (html_stack.next != &html_stack) {
  921         internal("something on html stack");
  922         init_list(html_stack);
  923     }
  924     e = mem_alloc(sizeof(struct html_element));
  925     memset(e, 0, sizeof(struct html_element));
  926     add_to_list(html_stack, e);
  927     format.attr = 0;
  928     format.fontsize = 3;
  929     format.link = format.target = format.image = format.select = NULL;
  930     format.form = NULL;
  931     memcpy(&format.fg, &opt->default_fg, sizeof(struct rgb));
  932     memcpy(&format.bg, &opt->default_bg, sizeof(struct rgb));
  933     memcpy(&format.clink, &opt->default_link, sizeof(struct rgb));
  934     memcpy(&format.vlink, &opt->default_vlink, sizeof(struct rgb));
  935     format.href_base = stracpy(url);
  936     format.target_base = stracpy(opt->framename);
  937     par_format.align = opt->plain ? AL_NO : AL_LEFT;
  938     par_format.leftmargin = opt->plain ? 0 : opt->margin;
  939     par_format.rightmargin = opt->plain ? 0 : opt->margin;
  940     par_format.width = opt->xw;
  941     par_format.list_level = par_format.list_number = 0;
  942     par_format.dd_margin = opt->margin;
  943     par_format.flags = 0;
  944     memcpy(&par_format.bgcolor, &opt->default_bg, sizeof(struct rgb));
  945     html_top.invisible = 0;
  946     html_top.name = NULL; html_top.namelen = 0; html_top.options = NULL;
  947     html_top.linebreak = 1;
  948     html_top.dontkill = 1;
  949 }
  950 
  951 struct conv_table *get_convert_table(unsigned char *head, int to, int def, int *frm, int *aa, int hard)
  952 {
  953     int from = -1;
  954     unsigned char *a, *b;
  955     unsigned char *p = head;
  956     while (from == -1 && p && (a = parse_http_header(p, "Content-Type", &p))) {
  957         if ((b = parse_header_param(a, "charset"))) {
  958             from = get_cp_index(b);
  959             mem_free(b);
  960         }
  961         mem_free(a);
  962     }
  963     if (from == -1 && head && (a = parse_http_header(head, "Content-Charset", NULL))) {
  964         from = get_cp_index(a);
  965         mem_free(a);
  966     }
  967     if (from == -1 && head && (a = parse_http_header(head, "Charset", NULL))) {
  968         from = get_cp_index(a);
  969         mem_free(a);
  970     }
  971     if (aa) {
  972         *aa = from == -1;
  973         if (hard && !*aa) *aa = 2;
  974     }
  975     if (hard || from == -1) from = def;
  976     if (frm) *frm = from;
  977     return get_translation_table(from, to);
  978 }
  979 
  980 struct document_options *d_opt;
  981 
  982 void format_html(struct cache_entry *ce, struct f_data *screen)
  983 {
  984     unsigned char *url = ce->url;
  985     struct fragment *fr;
  986     struct part *rp;
  987     unsigned char *start, *end;
  988     unsigned char *head, *t;
  989     int hdl;
  990     int i;
  991     memset(table_cache_hash, 0, sizeof(table_cache_hash));
  992     d_opt = &screen->opt;
  993     screen->use_tag = ce->count;
  994     defrag_entry(ce);
  995     fr = ce->frag.next;
  996     if ((void *)fr == &ce->frag || fr->offset || !fr->length) start = NULL, end = NULL;
  997     else start = fr->data, end = fr->data + fr->length;
  998     startf = start;
  999     eofff = end;
 1000     head = init_str(), hdl = 0;
 1001     if (ce->head) add_to_str(&head, &hdl, ce->head);
 1002     scan_http_equiv(start, end, &head, &hdl, &t);
 1003     convert_table = get_convert_table(head, screen->opt.cp, screen->opt.assume_cp, &screen->cp, &screen->ass, screen->opt.hard_assume);
 1004     screen->opt.real_cp = screen->cp;
 1005     i = d_opt->plain; d_opt->plain = 0;
 1006     screen->title = convert_string(convert_table, t, strlen(t));
 1007     d_opt->plain = i;
 1008     mem_free(t);
 1009     push_base_format(url, &screen->opt);
 1010     table_level = 0;
 1011     g_ctrl_num = 0;
 1012     last_form_tag = NULL;
 1013     last_form_attr = NULL;
 1014     last_input_tag = NULL;
 1015     if ((rp = format_html_part(start, end, par_format.align, par_format.leftmargin, screen->opt.xw, screen, 0, 0, head, 1))) mem_free(rp);
 1016     mem_free(head);
 1017     screen->x = 0;
 1018     for (i = screen->y - 1; i >= 0; i--) {
 1019         if (!screen->data[i].l) mem_free(screen->data[i].d), screen->y--;
 1020         else break;
 1021     }
 1022     for (i = 0; i < screen->y; i++) if (screen->data[i].l > screen->x) screen->x = screen->data[i].l;
 1023     if (form.action) mem_free(form.action), form.action = NULL;
 1024     if (form.target) mem_free(form.target), form.target = NULL;
 1025     kill_html_stack_item(html_stack.next);
 1026     if (html_stack.next != &html_stack) {
 1027         internal("html stack not empty after operation");
 1028         init_list(html_stack);
 1029     }
 1030     screen->bg = 007 << 8; /* !!! FIXME */
 1031     sort_links(screen);
 1032     if (screen->frame_desc) screen->frame = 1;
 1033     /*{
 1034         FILE *f = fopen("forms", "a");
 1035         struct form_control *form;
 1036         unsigned char *qq;
 1037         fprintf(f,"FORM:\n");
 1038         foreach(form, screen->forms) fprintf(f, "g=%d f=%d c=%d t:%d\n", form->g_ctrl_num, form->form_num, form->ctrl_num, form->type);
 1039         fprintf(f,"fragment: \n");
 1040         for (qq = start; qq < end; qq++) fprintf(f, "%c", *qq);
 1041         fprintf(f,"----------\n\n");
 1042         fclose(f);
 1043     }*/
 1044 }
 1045 
 1046 static inline int compare_opt(struct document_options *o1, struct document_options *o2)
 1047 {
 1048     if (o1->xw == o2->xw &&
 1049         o1->yw == o2->yw &&
 1050         o1->xp == o2->xp &&
 1051         o1->yp == o2->yp &&
 1052         o1->col == o2->col &&
 1053         o1->cp == o2->cp &&
 1054         o1->assume_cp == o2->assume_cp &&
 1055         o1->hard_assume == o2->hard_assume &&
 1056         o1->tables == o2->tables &&
 1057         o1->frames == o2->frames &&
 1058         o1->images == o2->images &&
 1059         o1->margin == o2->margin &&
 1060         o1->plain == o2->plain &&
 1061         !memcmp(&o1->default_fg, &o2->default_fg, sizeof(struct rgb)) &&
 1062         !memcmp(&o1->default_bg, &o2->default_bg, sizeof(struct rgb)) &&
 1063         !memcmp(&o1->default_link, &o2->default_link, sizeof(struct rgb)) &&
 1064         !memcmp(&o1->default_vlink, &o2->default_vlink, sizeof(struct rgb)) &&
 1065         o1->num_links == o2->num_links &&
 1066         o1->table_order == o2->table_order &&
 1067         !strcasecmp(o1->framename, o2->framename)) return 0;
 1068     return 1;
 1069 }
 1070 
 1071 static inline void copy_opt(struct document_options *o1, struct document_options *o2)
 1072 {
 1073     memcpy(o1, o2, sizeof(struct document_options));
 1074     o1->framename = stracpy(o2->framename);
 1075 }
 1076 
 1077 struct list_head format_cache = {&format_cache, &format_cache};
 1078 int format_cache_entries = 0;
 1079 
 1080 void shrink_format_cache(int u)
 1081 {
 1082     struct f_data *ce;
 1083     delete_unused_format_cache_entries();
 1084     if (format_cache_entries < 0) {
 1085         internal("format_cache_entries underflow");
 1086         format_cache_entries = 0;
 1087     }
 1088     ce = format_cache.prev;
 1089     while ((u || format_cache_entries > max_format_cache_entries) && (void *)ce != &format_cache) {
 1090         if (ce->refcount) {
 1091             ce = ce->prev;
 1092             continue;
 1093         }
 1094         ce = ce->prev;
 1095         destroy_formatted(ce->next);
 1096         format_cache_entries--;
 1097     }
 1098 }
 1099 
 1100 void count_format_cache()
 1101 {
 1102     struct f_data *ce;
 1103     format_cache_entries = 0;
 1104     foreach(ce, format_cache) if (!ce->refcount) format_cache_entries++;
 1105 }
 1106 
 1107 void delete_unused_format_cache_entries()
 1108 {
 1109     struct f_data *ce;
 1110     foreach(ce, format_cache) {
 1111         struct cache_entry *cee;
 1112         if (find_in_cache(ce->url, &cee))
 1113             internal("file %s disappeared from cache", ce->url);
 1114         cee->refcount--;
 1115         if (!ce->refcount && cee->count != ce->use_tag) {
 1116             ce = ce->prev;
 1117             destroy_formatted(ce->next);
 1118             format_cache_entries--;
 1119         }
 1120     }
 1121 }
 1122 
 1123 void format_cache_reactivate(struct f_data *ce)
 1124 {
 1125     del_from_list(ce);
 1126     add_to_list(format_cache, ce);
 1127 }
 1128 
 1129 void cached_format_html(struct view_state *vs, struct f_data_c *screen, struct document_options *opt)
 1130 {
 1131     unsigned char *n;
 1132     struct f_data *ce;
 1133     struct cache_entry *cee;
 1134     if (!vs) return;
 1135     n = screen->name; screen->name = NULL;
 1136     detach_formatted(screen);
 1137     screen->name = n;
 1138     screen->link_bg = NULL;
 1139     screen->link_bg_n = 0;
 1140     screen->vs = vs;
 1141     screen->xl = screen->yl = -1;
 1142     screen->f_data = NULL;
 1143     foreach(ce, format_cache) if (!strcmp(ce->url, vs->url) && !compare_opt(&ce->opt, opt)) {
 1144         cee = NULL;
 1145         if (find_in_cache(ce->url, &cee))
 1146             internal("file %s disappeared from cache", ce->url);
 1147         cee->refcount--;
 1148         if (cee->count != ce->use_tag) {
 1149             if (!ce->refcount) {
 1150                 ce = ce->prev;
 1151                 destroy_formatted(ce->next);
 1152                 format_cache_entries--;
 1153             }
 1154             continue;
 1155         }
 1156         format_cache_reactivate(ce);
 1157         if (!ce->refcount++) format_cache_entries--;
 1158         screen->f_data = ce;
 1159         goto sx;
 1160     }
 1161     if (find_in_cache(vs->url, &cee)) {
 1162         internal("document to format not found");
 1163         return;
 1164     }
 1165     /*cee->refcount++;*/
 1166     shrink_memory(0);
 1167     ce = mem_alloc(SIZEOF_F_DATA);
 1168     init_formatted(ce);
 1169     ce->refcount = 1;
 1170     ce->url = stracpy(vs->url);
 1171     copy_opt(&ce->opt, opt);
 1172     add_to_list(format_cache, ce);
 1173     screen->f_data = ce;
 1174     ce->time_to_get = -get_time();
 1175     format_html(cee, ce);
 1176     ce->time_to_get += get_time();
 1177     sx:
 1178     screen->xw = ce->opt.xw;
 1179     screen->yw = ce->opt.yw;
 1180     screen->xp = ce->opt.xp;
 1181     screen->yp = ce->opt.yp;
 1182 }
 1183 
 1184 long formatted_info(int type)
 1185 {
 1186     int i = 0;
 1187     struct f_data *ce;
 1188     switch (type) {
 1189         case CI_FILES:
 1190             foreach(ce, format_cache) i++;
 1191             return i;
 1192         case CI_LOCKED:
 1193             foreach(ce, format_cache) i += !!ce->refcount;
 1194             return i;
 1195         default:
 1196             internal("formatted_info: bad request");
 1197     }
 1198     return 0;
 1199 }
 1200 
 1201 void add_frame_to_list(struct session *ses, struct f_data_c *fd)
 1202 {
 1203     struct f_data_c *f, *fp;
 1204     foreach(f, ses->scrn_frames) {
 1205         if (f->yp > fd->yp || (f->yp == fd->yp && f->xp > fd->xp)) {
 1206             add_at_pos(f->prev, fd);
 1207             return;
 1208         }
 1209     }
 1210     fp = (struct f_data_c *)ses->scrn_frames.prev;
 1211     add_at_pos(fp, fd);
 1212 }
 1213 
 1214 struct f_data_c *find_fd(struct session *ses, unsigned char *name, int depth, int x, int y)
 1215 {
 1216     struct f_data_c *fd;
 1217     foreachback(fd, ses->scrn_frames) if (!strcasecmp(fd->name, name) && !fd->used) {
 1218         fd->used = 1;
 1219         fd->depth = depth;
 1220         return fd;
 1221     }
 1222     fd = mem_alloc(sizeof(struct f_data_c));
 1223     memset(fd, 0, sizeof(struct f_data_c));
 1224     fd->used = 1;
 1225     fd->name = stracpy(name);
 1226     fd->depth = depth;
 1227     fd->xp = x, fd->yp = y;
 1228     fd->search_word = &ses->search_word;
 1229     /*add_to_list(ses->scrn_frames, fd);*/
 1230     add_frame_to_list(ses, fd);
 1231     return fd;
 1232 }
 1233 
 1234 struct f_data_c *format_frame(struct session *ses, unsigned char *name, unsigned char *url, struct document_options *o, int depth)
 1235 {
 1236     struct cache_entry *ce;
 1237     struct view_state *vs;
 1238     struct f_data_c *fd;
 1239     struct frame *fr;
 1240     repeat:
 1241     if (!(fr = ses_find_frame(ses, name))) return NULL;
 1242     vs = &fr->vs;
 1243     if (find_in_cache(vs->url, &ce)) return NULL;
 1244     ce->refcount--;
 1245     if (ce->redirect && fr->redirect_cnt < MAX_REDIRECTS) {
 1246         unsigned char *u;
 1247         if ((u = join_urls(vs->url, ce->redirect))) {
 1248             fr->redirect_cnt++;
 1249             ses_change_frame_url(ses, name, u);
 1250             mem_free(u);
 1251             goto repeat;
 1252         }
 1253     }
 1254     if (!(fd = find_fd(ses, name, depth, o->xp, o->yp))) return NULL;
 1255     cached_format_html(vs, fd, o);
 1256     return fd;
 1257 }
 1258 
 1259 void format_frames(struct session *ses, struct frameset_desc *fsd, struct document_options *op, int depth)
 1260 {
 1261     int i, j, n;
 1262     struct document_options o;
 1263     if (depth > HTML_MAX_FRAME_DEPTH) return;
 1264     memcpy(&o, op, sizeof(struct document_options));
 1265     if (o.margin) o.margin = 1;
 1266     n = 0;
 1267     for (j = 0; j < fsd->y; j++) {
 1268         o.xp = op->xp;
 1269         for (i = 0; i < fsd->x; i++) {
 1270             struct f_data_c *fdc;
 1271             o.xw = fsd->f[n].xw;
 1272             o.yw = fsd->f[n].yw;
 1273             o.framename = fsd->f[n].name;
 1274             if (fsd->f[n].subframe) format_frames(ses, fsd->f[n].subframe, &o, depth + 1);
 1275             else if (fsd->f[n].name) {
 1276                 fdc = format_frame(ses, fsd->f[n].name, fsd->f[n].url, &o, depth);
 1277                 if (fdc && fdc->f_data && fdc->f_data->frame) format_frames(ses, fdc->f_data->frame_desc, &o, depth + 1);
 1278             }
 1279             o.xp += o.xw + 1;
 1280             n++;
 1281         }
 1282         o.yp += o.yw + 1;
 1283     }
 1284             
 1285     /*for (i = 0; i < fsd->n; i++) {
 1286         if (!fsd->horiz) o.xw = fsd->f[i].width;
 1287         else o.yw = fsd->f[i].width;
 1288         o.framename = fsd->f[i].name;
 1289         if (fsd->f[i].subframe) format_frames(ses, fsd->f[i].subframe, &o);
 1290         else format_frame(ses, fsd->f[i].name, fsd->f[i].url, &o);
 1291         if (!fsd->horiz) o.xp += fsd->f[i].width + 1;
 1292         else o.yp += fsd->f[i].width + 1;
 1293     }*/
 1294 }
 1295 
 1296 void html_interpret(struct session *ses)
 1297 {
 1298     struct view_state *l;
 1299     struct document_options o;
 1300     struct f_data_c *fd, *cf = NULL;
 1301     /*debug("I");*/
 1302     memset(&o, 0, sizeof(struct document_options));
 1303     if (!ses->screen) {
 1304         ses->screen = mem_alloc(sizeof(struct f_data_c));
 1305         memset(ses->screen, 0, sizeof(struct f_data_c));
 1306         ses->screen->search_word = &ses->search_word;
 1307     }
 1308     if (!list_empty(ses->history)) l = &cur_loc(ses)->vs;
 1309     else l = NULL;
 1310     o.xp = 0;
 1311     o.yp = 1;
 1312     o.xw = ses->term->x;
 1313     if (ses->term->y < 2) o.yw = 0;
 1314     else o.yw = ses->term->y - 2;
 1315     o.col = ses->term->spec->col;
 1316     o.cp = ses->term->spec->charset;
 1317     ds2do(&ses->ds, &o);
 1318     if ((o.plain = l ? l->plain : 1) == -1) o.plain = 0;
 1319     if (l) l->plain = o.plain;
 1320     memcpy(&o.default_fg, &default_fg, sizeof(struct rgb));
 1321     memcpy(&o.default_bg, &default_bg, sizeof(struct rgb));
 1322     memcpy(&o.default_link, &default_link, sizeof(struct rgb));
 1323     memcpy(&o.default_vlink, &default_vlink, sizeof(struct rgb));
 1324     o.framename = "";
 1325     foreach(fd, ses->scrn_frames) fd->used = 0;
 1326     cached_format_html(l, ses->screen, &o);
 1327     if (ses->screen->f_data && ses->screen->f_data->frame) {
 1328         cf = current_frame(ses);
 1329         format_frames(ses, ses->screen->f_data->frame_desc, &o, 0);
 1330     }
 1331     foreach(fd, ses->scrn_frames) if (!fd->used) {
 1332         struct f_data_c *fdp = fd->prev;
 1333         detach_formatted(fd);
 1334         del_from_list(fd);
 1335         mem_free(fd);
 1336         fd = fdp;
 1337     }
 1338     if (cf) {
 1339         int n = 0;
 1340         foreach(fd, ses->scrn_frames) {
 1341             if (fd->f_data && fd->f_data->frame) continue;
 1342             if (fd == cf) {
 1343                 cur_loc(ses)->vs.current_link = n;
 1344                 break;
 1345             }
 1346             n++;
 1347         }
 1348     }
 1349 }
 1350 
 1351 void add_srch_chr(struct f_data *f, unsigned char c, int x, int y, int nn)
 1352 {
 1353     int n = f->nsearch;
 1354     if (c == ' ' && (!n || f->search[n - 1].c == ' ')) return;
 1355     f->search[n].c = c;
 1356     f->search[n].x = x;
 1357     f->search[n].y = y;
 1358     f->search[n].n = nn;
 1359     f->nsearch++;
 1360 }
 1361 
 1362 /*void sdbg(struct f_data *f)
 1363 {
 1364     struct node *n;
 1365     foreachback(n, f->nodes) {
 1366         int xm = n->x + n->xw, ym = n->y + n->yw;
 1367         printf("%d %d - %d %d\n", n->x, n->y, xm, ym);
 1368         fflush(stdout);
 1369     }
 1370     debug("!");
 1371 }*/
 1372 
 1373 void sort_srch(struct f_data *f)
 1374 {
 1375     int i;
 1376     int *min, *max;
 1377     if ((unsigned)f->y > MAXINT / sizeof(struct search *)) overalloc();
 1378     if ((unsigned)f->y > MAXINT / sizeof(int)) overalloc();
 1379     f->slines1 = mem_alloc(f->y * sizeof(struct search *));
 1380     f->slines2 = mem_alloc(f->y * sizeof(struct search *));
 1381     min = mem_alloc(f->y * sizeof(int));
 1382     max = mem_alloc(f->y * sizeof(int));
 1383     memset(f->slines1, 0, f->y * sizeof(struct search *));
 1384     memset(f->slines2, 0, f->y * sizeof(struct search *));
 1385     for (i = 0; i < f->y; i++) min[i] = MAXINT, max[i] = 0;
 1386     for (i = 0; i < f->nsearch; i++) {
 1387         struct search *s = &f->search[i];
 1388         if (s->x < min[s->y]) min[s->y] = s->x, f->slines1[s->y] = s;
 1389         if (s->x + s->n > max[s->y]) max[s->y] = s->x + s->n, f->slines2[s->y] = s;
 1390     }
 1391     mem_free(min);
 1392     mem_free(max);
 1393 }
 1394 
 1395 static inline int is_spc(chr c)
 1396 {
 1397     return (unsigned char)c <= ' ' || c & ATTR_FRAME;
 1398 }
 1399 
 1400 int get_srch(struct f_data *f)
 1401 {
 1402     struct node *n;
 1403     int cnt = 0;
 1404     int cc = !f->search;
 1405     foreachback(n, f->nodes) {
 1406         int x, y;
 1407         int xm = n->x + n->xw, ym = n->y + n->yw;
 1408         /*printf("%d %d - %d %d\n", n->x, n->y, xm, ym);
 1409         fflush(stdout);*/
 1410         for (y = n->y; y < ym && y < f->y; y++) {
 1411             int ns = 1;
 1412             for (x = n->x; x < xm && x < f->data[y].l; x++) {
 1413                 unsigned char c = f->data[y].d[x];
 1414                 if (is_spc(f->data[y].d[x])) c = ' ';
 1415                 if (c == ' ' && ns) continue;
 1416                 c = charset_upcase(c, f->opt.cp);
 1417                 if (ns) {
 1418                     if (!cc) add_srch_chr(f, c, x, y, 1);
 1419                     else cnt++;
 1420                     ns = 0;
 1421                     continue;
 1422                 }
 1423                 if (c != ' ')  if (!cc) add_srch_chr(f, c, x, y, 1);
 1424                            else cnt++;
 1425                 else {
 1426                     int xx;
 1427                     for (xx = x + 1; xx < xm && xx < f->data[y].l; xx++) if (!is_spc(f->data[y].d[xx])) goto ja_uz_z_toho_programovani_asi_zcvoknu;
 1428                     xx = x;
 1429                     ja_uz_z_toho_programovani_asi_zcvoknu:
 1430                 /* uz jsem zcvoknul, trpim poruchou osobnosti */
 1431                     if (!cc) add_srch_chr(f, ' ', x, y, xx - x);
 1432                     else cnt++;
 1433                     if (xx == x) break;
 1434                     x = xx - 1;
 1435                 }
 1436             }
 1437             if (!cc) add_srch_chr(f, ' ', x, y, 0);
 1438             else cnt++;
 1439         }
 1440     }
 1441     return cnt;
 1442     
 1443 }
 1444 
 1445 void get_search_data(struct f_data *f)
 1446 {
 1447     int n;
 1448     if (f->search) return;
 1449     n = get_srch(f);
 1450     f->nsearch = 0;
 1451     if ((unsigned)n > MAXINT / sizeof(struct search)) overalloc();
 1452     f->search = mem_alloc(n * sizeof(struct search));
 1453     get_srch(f);
 1454     while (f->nsearch && f->search[f->nsearch - 1].c == ' ') f->nsearch--;
 1455     /*debug("!");*/
 1456     sort_srch(f);
 1457 }