links

lynx-like text mode web browser
git clone anongit@rnpnr.xyz:links.git
Log | Files | Refs | Feed | README | LICENSE

html_r.c (41981B)


      1 /* html_r.c
      2  * (c) 2002 Mikulas Patocka
      3  * This file is a part of the Links program, released under GPL.
      4  */
      5 
      6 #include <limits.h>
      7 
      8 #include "links.h"
      9 
     10 struct f_data *
     11 init_formatted(struct document_options *opt)
     12 {
     13 	struct f_data *scr;
     14 	scr = mem_calloc(sizeof(struct f_data));
     15 	copy_opt(&scr->opt, opt);
     16 	scr->data = NULL;
     17 	scr->nlinks = 0;
     18 	scr->links = NULL;
     19 	init_list(scr->forms);
     20 	init_list(scr->tags);
     21 	init_list(scr->nodes);
     22 	return scr;
     23 }
     24 
     25 void
     26 destroy_fc(struct form_control *fc)
     27 {
     28 	int i;
     29 	free(fc->action);
     30 	free(fc->target);
     31 	free(fc->form_name);
     32 	free(fc->onsubmit);
     33 	free(fc->name);
     34 	free(fc->alt);
     35 	free(fc->default_value);
     36 	for (i = 0; i < fc->nvalues; i++) {
     37 		free(fc->values[i]);
     38 		free(fc->labels[i]);
     39 	}
     40 	free(fc->values);
     41 	free(fc->labels);
     42 	if (fc->menu)
     43 		free_menu(fc->menu);
     44 }
     45 
     46 void
     47 free_frameset_desc(struct frameset_desc *fd)
     48 {
     49 	int i;
     50 	for (i = 0; i < fd->n; i++) {
     51 		if (fd->f[i].subframe)
     52 			free_frameset_desc(fd->f[i].subframe);
     53 		free(fd->f[i].name);
     54 		free(fd->f[i].url);
     55 	}
     56 	free(fd);
     57 }
     58 
     59 struct frameset_desc *
     60 copy_frameset_desc(struct frameset_desc *fd)
     61 {
     62 	int i;
     63 	struct frameset_desc *neww;
     64 	if ((unsigned)fd->n > INT_MAX / sizeof(struct frame_desc))
     65 		overalloc();
     66 	neww = xmalloc(sizeof(struct frameset_desc)
     67 	               + fd->n * sizeof(struct frame_desc));
     68 	memcpy(neww, fd,
     69 	       sizeof(struct frameset_desc)
     70 	           + fd->n * sizeof(struct frame_desc));
     71 	for (i = 0; i < neww->n; i++) {
     72 		if (neww->f[i].subframe)
     73 			neww->f[i].subframe =
     74 			    copy_frameset_desc(neww->f[i].subframe);
     75 		if (neww->f[i].name)
     76 			neww->f[i].name = stracpy(neww->f[i].name);
     77 		if (neww->f[i].url)
     78 			neww->f[i].url = stracpy(neww->f[i].url);
     79 	}
     80 	return neww;
     81 }
     82 
     83 void
     84 free_additional_files(struct additional_files **a)
     85 {
     86 	struct additional_file *af = NULL;
     87 	struct list_head *laf;
     88 	if (!*a)
     89 		return;
     90 	if (--(*a)->refcount) {
     91 		*a = NULL;
     92 		return;
     93 	}
     94 	foreach (struct additional_file, af, laf, (*a)->af)
     95 		release_object(&af->rq);
     96 	free_list(struct additional_file, (*a)->af);
     97 	free(*a);
     98 	*a = NULL;
     99 }
    100 
    101 static void
    102 clear_formatted(struct f_data *scr)
    103 {
    104 	int n;
    105 	int y;
    106 	struct form_control *fc = NULL;
    107 	struct list_head *lfc;
    108 	if (!scr)
    109 		return;
    110 
    111 	free(scr->search_chr);
    112 	free(scr->search_pos);
    113 	free(scr->slines1);
    114 	free(scr->slines2);
    115 
    116 	release_object(&scr->rq);
    117 	free_additional_files(&scr->af);
    118 	free(scr->title);
    119 	if (scr->frame_desc) {
    120 		free_frameset_desc(scr->frame_desc);
    121 	}
    122 	for (n = 0; n < scr->nlinks; n++) {
    123 		struct link *l = &scr->links[n];
    124 		free(l->where);
    125 		free(l->target);
    126 		free(l->where_img);
    127 		free(l->img_alt);
    128 		free(l->pos);
    129 	}
    130 	free(scr->links);
    131 	for (y = 0; y < scr->y; y++)
    132 		free(scr->data[y].d);
    133 	free(scr->data);
    134 	free(scr->lines1);
    135 	free(scr->lines2);
    136 	free(scr->opt.framename);
    137 	foreach (struct form_control, fc, lfc, scr->forms) {
    138 		destroy_fc(fc);
    139 	}
    140 	free_list(struct form_control, scr->forms);
    141 	free_list(struct tag, scr->tags);
    142 	free_list(struct node, scr->nodes);
    143 	free(scr->refresh);
    144 }
    145 
    146 void
    147 destroy_formatted(struct f_data *scr)
    148 {
    149 	if (scr->fd) {
    150 		internal("trying to free locked formatted data");
    151 		return;
    152 	}
    153 	clear_formatted(scr);
    154 	free(scr);
    155 }
    156 
    157 static inline int
    158 color_distance(struct rgb *c1, struct rgb *c2)
    159 {
    160 	return 3 * (c1->r - c2->r) * (c1->r - c2->r)
    161 	       + 4 * (c1->g - c2->g) * (c1->g - c2->g)
    162 	       + 2 * (c1->b - c2->b) * (c1->b - c2->b);
    163 }
    164 
    165 struct rgb palette_16_colors[16] = {
    166 	{0x00,  0x00, 0x00, 0},
    167         { 0x80, 0x00, 0x00, 0},
    168 	{ 0x00, 0x80, 0x00, 0},
    169         { 0xaa, 0x55, 0x00, 0},
    170 	{ 0x00, 0x00, 0x80, 0},
    171         { 0x80, 0x00, 0x80, 0},
    172 	{ 0x00, 0x80, 0x80, 0},
    173         { 0xaa, 0xaa, 0xaa, 0},
    174 	{ 0x55, 0x55, 0x55, 0},
    175         { 0xff, 0x55, 0x55, 0},
    176 	{ 0x55, 0xff, 0x55, 0},
    177         { 0xff, 0xff, 0x55, 0},
    178 	{ 0x55, 0x55, 0xff, 0},
    179         { 0xff, 0x55, 0xff, 0},
    180 	{ 0x55, 0xff, 0xff, 0},
    181         { 0xff, 0xff, 0xff, 0},
    182 };
    183 
    184 struct rgb_cache_entry {
    185 	int color;
    186 	int l;
    187 	struct rgb rgb;
    188 };
    189 
    190 #define RGB_HASH_SIZE 4096
    191 
    192 #define HASH_RGB(r, l)                                                         \
    193 	((((r)->r << 3) + ((r)->g << 2) + (r)->b + (l)) & (RGB_HASH_SIZE - 1))
    194 
    195 int
    196 find_nearest_color(struct rgb *r, int l)
    197 {
    198 	int dist, dst, min, i;
    199 	static struct rgb_cache_entry rgb_cache[RGB_HASH_SIZE];
    200 	static int cache_init = 0;
    201 	int h;
    202 	if ((size_t)l > array_elements(palette_16_colors))
    203 		internal("invalid length %d", l);
    204 	if (!cache_init)
    205 		goto initialize;
    206 back:
    207 	h = HASH_RGB(r, l);
    208 	if (rgb_cache[h].color != -1 && rgb_cache[h].l == l
    209 	    && rgb_cache[h].rgb.r == r->r && rgb_cache[h].rgb.g == r->g
    210 	    && rgb_cache[h].rgb.b == r->b)
    211 		return rgb_cache[h].color;
    212 	dist = 0xffffff;
    213 	min = 0;
    214 	for (i = 0; i < l; i++)
    215 		if ((dst = color_distance(r, &palette_16_colors[i])) < dist) {
    216 			dist = dst;
    217 			min = i;
    218 		}
    219 	rgb_cache[h].color = min;
    220 	rgb_cache[h].l = l;
    221 	rgb_cache[h].rgb.r = r->r;
    222 	rgb_cache[h].rgb.g = r->g;
    223 	rgb_cache[h].rgb.b = r->b;
    224 	return min;
    225 
    226 initialize:
    227 	for (h = 0; h < RGB_HASH_SIZE; h++)
    228 		rgb_cache[h].color = -1;
    229 	cache_init = 1;
    230 	goto back;
    231 }
    232 
    233 int
    234 fg_color(int fg, int bg)
    235 {
    236 	int l = bg < fg ? bg : fg;
    237 	int h = bg < fg ? fg : bg;
    238 	if (l == h || (!l && (h == 4 || h == 8 || h == 12))
    239 	    || (l == 1 && (h == 3 || h == 5 || h == 8 || h == 12))
    240 	    || (l == 2 && h == 6) || (l == 3 && (h == 5 || h == 12))
    241 	    || (l == 4 && (h == 8 || h == 12))
    242 	    || (l == 5 && (h == 8 || h == 12)))
    243 		return (fg == 4 || fg == 12) && (bg == 0 || bg == 8)
    244 		           ? 6
    245 		           : (7 - 7 * (bg == 2 || bg == 6 || bg == 7));
    246 	return fg;
    247 }
    248 
    249 static int nowrap = 0;
    250 
    251 static void
    252 xpand_lines(struct part *p, int y)
    253 {
    254 	if (!p->data)
    255 		return;
    256 	if (y < 0)
    257 		return;
    258 	y = safe_add(y, safe_add(p->yp, 1));
    259 	if (y > p->data->y) {
    260 		int i;
    261 		if ((y ^ p->data->y) > p->data->y) {
    262 			unsigned s;
    263 			for (s = 1; s < (unsigned)y; s = s * 2 + 1) {
    264 				if (s > INT_MAX / sizeof(struct line))
    265 					overalloc();
    266 			}
    267 			p->data->data =
    268 			    xrealloc(p->data->data, s * sizeof(struct line));
    269 		}
    270 		for (i = p->data->y; i < y; i++) {
    271 			p->data->data[i].l = 0;
    272 			p->data->data[i].allocated = 0;
    273 			p->data->data[i].d = NULL;
    274 		}
    275 		p->data->y = y;
    276 	}
    277 }
    278 
    279 static void
    280 xpand_line(struct part *p, int y, int x)
    281 {
    282 	struct line *ln;
    283 	if (!p->data)
    284 		return;
    285 	x = safe_add(x, p->xp);
    286 	y = safe_add(y, p->yp);
    287 	ln = &p->data->data[y];
    288 	if (x >= ln->l) {
    289 		int i;
    290 		if (x >= ln->allocated) {
    291 			if (x >= 0x4000)
    292 				ln->allocated = safe_add(x, x);
    293 			else
    294 				ln->allocated = safe_add(x, 0x10) & ~0xf;
    295 			if ((unsigned)ln->allocated > INT_MAX / sizeof(chr))
    296 				overalloc();
    297 			ln->d = xrealloc(ln->d, ln->allocated * sizeof(chr));
    298 		}
    299 		for (i = ln->l; i <= x; i++) {
    300 			ln->d[i].at = p->attribute;
    301 			ln->d[i].ch = ' ';
    302 		}
    303 		ln->l = i;
    304 	}
    305 }
    306 
    307 static void
    308 r_xpand_spaces(struct part *p, int l)
    309 {
    310 	unsigned char *c;
    311 	if ((unsigned)l >= INT_MAX)
    312 		overalloc();
    313 	c = xrealloc(p->spaces, l + 1);
    314 	memset(c + p->spl, 0, l - p->spl + 1);
    315 	p->spl = l + 1;
    316 	p->spaces = c;
    317 }
    318 
    319 static inline void
    320 xpand_spaces(struct part *p, int l)
    321 {
    322 	if ((unsigned)l >= (unsigned)p->spl)
    323 		r_xpand_spaces(p, l);
    324 }
    325 
    326 #define POS(x, y) (p->data->data[safe_add(p->yp, (y))].d[safe_add(p->xp, (x))])
    327 #define LEN(y)                                                                 \
    328 	(p->data->data[safe_add(p->yp, (y))].l - p->xp < 0                     \
    329 	     ? 0                                                               \
    330 	     : p->data->data[p->yp + (y)].l - p->xp)
    331 #define SLEN(y, x) p->data->data[safe_add(p->yp, (y))].l = safe_add(p->xp, x)
    332 #define X(x)       safe_add(p->xp, (x))
    333 #define Y(y)       safe_add(p->yp, (y))
    334 
    335 static inline void
    336 set_hchar(struct part *p, int x, int y, unsigned ch, unsigned char at)
    337 {
    338 	chr *cc;
    339 	xpand_lines(p, y);
    340 	xpand_line(p, y, x);
    341 	cc = &POS(x, y);
    342 	cc->ch = ch;
    343 	cc->at = at;
    344 }
    345 
    346 static inline void
    347 set_hchars(struct part *p, int x, int y, int xl, unsigned ch, unsigned char at)
    348 {
    349 	chr *cc;
    350 	xpand_lines(p, y);
    351 	xpand_line(p, y, safe_add(x, xl) - 1);
    352 	cc = &POS(x, y);
    353 	for (; xl; xl--) {
    354 		cc->ch = ch;
    355 		cc->at = at;
    356 		cc++;
    357 	}
    358 }
    359 
    360 void
    361 xset_hchar(struct part *p, int x, int y, unsigned ch, unsigned char at)
    362 {
    363 	set_hchar(p, x, y, ch, at);
    364 }
    365 
    366 void
    367 xset_hchars(struct part *p, int x, int y, int xl, unsigned ch, unsigned char at)
    368 {
    369 	set_hchars(p, x, y, xl, ch, at);
    370 }
    371 
    372 void
    373 xxpand_lines(struct part *p, int y)
    374 {
    375 	xpand_lines(p, y);
    376 }
    377 
    378 void
    379 xxpand_line(struct part *p, int y, int x)
    380 {
    381 	xpand_line(p, y, x);
    382 }
    383 
    384 static inline void
    385 set_hline(struct part *p, int x, int y, int xl, unsigned char *d,
    386           unsigned char at)
    387 {
    388 	chr *cc;
    389 	int xp;
    390 	xpand_lines(p, y);
    391 	xpand_line(p, y, safe_add(x, xl) - 1);
    392 	xp = par_format.align != AL_NO;
    393 	if (xp)
    394 		xpand_spaces(p, safe_add(x, xl) - 1);
    395 	cc = NULL;
    396 	if (p->data)
    397 		cc = &POS(x, y);
    398 	for (; xl; xl--, x++, d++) {
    399 		if (xp)
    400 			p->spaces[x] = *d == ' ';
    401 		if (p->data) {
    402 			cc->ch = *d;
    403 			cc->at = at;
    404 			cc++;
    405 		}
    406 	}
    407 }
    408 
    409 static inline void
    410 set_hline_uni(struct part *p, int x, int y, int xl, char_t *d, unsigned char at)
    411 {
    412 	chr *cc;
    413 	int xp;
    414 	xpand_lines(p, y);
    415 	xpand_line(p, y, safe_add(x, xl) - 1);
    416 	xp = par_format.align != AL_NO;
    417 	if (xp)
    418 		xpand_spaces(p, safe_add(x, xl) - 1);
    419 	cc = NULL;
    420 	if (p->data)
    421 		cc = &POS(x, y);
    422 	for (; xl; xl--, x++, d++) {
    423 		if (xp)
    424 			p->spaces[x] = *d == ' ';
    425 		if (p->data) {
    426 			cc->ch = *d;
    427 			cc->at = at;
    428 			cc++;
    429 		}
    430 	}
    431 }
    432 
    433 static int last_link_to_move;
    434 static struct list_head *last_tag_to_move;
    435 static struct list_head *last_tag_for_newline;
    436 
    437 static inline void
    438 move_links(struct part *p, int xf, int yf, int xt, int yt)
    439 {
    440 	int n;
    441 	struct tag *t = NULL;
    442 	struct list_head *lt;
    443 	int w = 0;
    444 	if (!p->data)
    445 		return;
    446 	xpand_lines(p, yt);
    447 	for (n = last_link_to_move; n < p->data->nlinks; n++) {
    448 		int i;
    449 		struct link *link = &p->data->links[n];
    450 		/*printf("ml: %d %d %d
    451 		 * %d",link->pos[0].x,link->pos[0].y,X(xf),Y(yf));fflush(stdout);portable_sleep(1000);*/
    452 		for (i = link->first_point_to_move; i < link->n; i++)
    453 			if (link->pos[i].y >= Y(yf)) {
    454 				w = 1;
    455 				if (link->pos[i].y == Y(yf)
    456 				    && link->pos[i].x >= X(xf)) {
    457 					if (yt >= 0) {
    458 						link->pos[i].y = Y(yt);
    459 						if (xt > xf)
    460 							link->pos[i].x =
    461 							    safe_add(
    462 								link->pos[i].x,
    463 								-xf + xt);
    464 						else
    465 							link->pos[i].x =
    466 							    link->pos[i].x - xf
    467 							    + xt;
    468 					} else {
    469 						memmove(
    470 						    &link->pos[i],
    471 						    &link->pos[i + 1],
    472 						    (link->n - i - 1)
    473 							* sizeof(struct point));
    474 						link->n--;
    475 						i--;
    476 					}
    477 				}
    478 			} else {
    479 				link->first_point_to_move = safe_add(i, 1);
    480 			}
    481 		if (!w)
    482 			last_link_to_move = n;
    483 	}
    484 	w = 0;
    485 	if (yt >= 0)
    486 		foreachfrom (struct tag, t, lt, p->data->tags,
    487 		             last_tag_to_move->next) {
    488 			if (t->y == Y(yf)) {
    489 				w = 1;
    490 				if (t->x >= X(xf)) {
    491 					t->y = Y(yt);
    492 					if (xt > xf)
    493 						t->x = safe_add(t->x, -xf + xt);
    494 					else
    495 						t->x += -xf + xt;
    496 				}
    497 			}
    498 			if (!w)
    499 				last_tag_to_move = &t->list_entry;
    500 		}
    501 }
    502 
    503 static inline void
    504 copy_chars(struct part *p, int x, int y, int xl, chr *d)
    505 {
    506 	if (xl <= 0)
    507 		return;
    508 	xpand_lines(p, y);
    509 	xpand_line(p, y, safe_add(x, xl) - 1);
    510 	for (; xl; xl--, x++, d++)
    511 		POS(x, y) = *d;
    512 }
    513 
    514 static inline void
    515 move_chars(struct part *p, int x, int y, int nx, int ny)
    516 {
    517 	if (LEN(y) - x <= 0)
    518 		return;
    519 	copy_chars(p, nx, ny, LEN(y) - x, &POS(x, y));
    520 	SLEN(y, x);
    521 	move_links(p, x, y, nx, ny);
    522 }
    523 
    524 static inline void
    525 shift_chars(struct part *p, int y, int s)
    526 {
    527 	chr *a;
    528 	int l = LEN(y);
    529 	if ((unsigned)l > INT_MAX / sizeof(chr))
    530 		overalloc();
    531 	a = xmalloc(l * sizeof(chr));
    532 	memcpy(a, &POS(0, y), l * sizeof(chr));
    533 	set_hchars(p, 0, y, s, ' ', p->attribute);
    534 	copy_chars(p, s, y, l, a);
    535 	free(a);
    536 	move_links(p, 0, y, s, y);
    537 }
    538 
    539 static inline void
    540 del_chars(struct part *p, int x, int y)
    541 {
    542 	SLEN(y, x);
    543 	move_links(p, x, y, -1, -1);
    544 }
    545 
    546 #define rm(x)                                                                  \
    547 	((x).width - (x).rightmargin > 0 ? (x).width - (x).rightmargin : 0)
    548 
    549 static void line_break(void *);
    550 
    551 static int
    552 split_line(struct part *p)
    553 {
    554 	int i;
    555 	if (rm(par_format) >= p->z_spaces) {
    556 		for (i = rm(par_format); i >= par_format.leftmargin; i--)
    557 			if (i < p->spl && p->spaces[i])
    558 				goto split;
    559 	}
    560 	i = rm(par_format) + 1;
    561 	if (i < p->z_spaces)
    562 		i = p->z_spaces;
    563 	if (i < par_format.leftmargin)
    564 		i = par_format.leftmargin;
    565 	for (; i < p->cx; i++)
    566 		if (i < p->spl && p->spaces[i])
    567 			goto split;
    568 	p->z_spaces = i;
    569 	if (p->cx >= 0 && safe_add(p->cx, par_format.rightmargin) > p->x)
    570 		p->x = p->cx + par_format.rightmargin;
    571 	return 0;
    572 split:
    573 	if (safe_add(i, par_format.rightmargin) > p->x)
    574 		p->x = i + par_format.rightmargin;
    575 	if (p->data) {
    576 		move_chars(p, safe_add(i, 1), p->cy, par_format.leftmargin,
    577 		           safe_add(p->cy, 1));
    578 		del_chars(p, i, p->cy);
    579 	}
    580 	memmove(p->spaces, p->spaces + safe_add(i, 1), p->spl - i - 1);
    581 	memset(p->spaces + p->spl - i - 1, 0, i + 1);
    582 	memmove(p->spaces + par_format.leftmargin, p->spaces,
    583 	        p->spl - par_format.leftmargin);
    584 	p->z_spaces = 0;
    585 	p->cy = safe_add(p->cy, 1);
    586 	p->cx -= i - par_format.leftmargin + 1;
    587 	if (p->cx == par_format.leftmargin)
    588 		p->cx = -1;
    589 	if (p->y < safe_add(p->cy, (p->cx != -1)))
    590 		p->y = p->cy + (p->cx != -1);
    591 	return 1 + (p->cx == -1);
    592 }
    593 
    594 static void
    595 align_line(struct part *p, int y)
    596 {
    597 	int na;
    598 	if (!p->data)
    599 		return;
    600 	if (!LEN(y) || par_format.align == AL_LEFT || par_format.align == AL_NO
    601 	    || par_format.align == AL_NO_BREAKABLE
    602 	    || par_format.align == AL_BLOCK /* !!! fixme! */)
    603 		return;
    604 	na = rm(par_format) - LEN(y);
    605 	if (par_format.align == AL_CENTER)
    606 		na /= 2;
    607 	if (na > 0)
    608 		shift_chars(p, y, na);
    609 }
    610 
    611 struct link *
    612 new_link(struct f_data *f)
    613 {
    614 	if (!f)
    615 		return NULL;
    616 	if (!(f->nlinks & (ALLOC_GR - 1))) {
    617 		if ((unsigned)f->nlinks
    618 		    > INT_MAX / sizeof(struct link) - ALLOC_GR)
    619 			overalloc();
    620 		f->links = xrealloc(f->links, (f->nlinks + ALLOC_GR)
    621 		                                  * sizeof(struct link));
    622 	}
    623 	memset(&f->links[f->nlinks], 0, sizeof(struct link));
    624 	return &f->links[f->nlinks++];
    625 }
    626 
    627 void
    628 html_tag(struct f_data *f, unsigned char *t, int x, int y)
    629 {
    630 	struct tag *tag;
    631 	size_t sl;
    632 	unsigned char *tt;
    633 	if (!f)
    634 		return;
    635 	tt = NULL;
    636 	add_conv_str(&tt, 0, t, (int)strlen(cast_const_char t), -2);
    637 	sl = strlen(cast_const_char tt);
    638 	if (sl > INT_MAX - sizeof(struct tag))
    639 		overalloc();
    640 	tag = xmalloc(sizeof(struct tag) + sl);
    641 	tag->x = x;
    642 	tag->y = y;
    643 	strcpy(cast_char tag->name, cast_const_char tt);
    644 	add_to_list(f->tags, tag);
    645 	if (last_tag_for_newline == &f->tags)
    646 		last_tag_for_newline = &tag->list_entry;
    647 	free(tt);
    648 }
    649 
    650 unsigned char *last_link = NULL;
    651 unsigned char *last_target = NULL;
    652 unsigned char *last_image = NULL;
    653 struct form_control *last_form = NULL;
    654 
    655 static int nobreak;
    656 
    657 struct conv_table *convert_table;
    658 
    659 static void
    660 put_chars(void *p_, unsigned char *c, int l)
    661 {
    662 	struct part *p = p_;
    663 
    664 	static struct text_attrib_beginning ta_cache = {
    665 		-1, {0,  0, 0, 0},
    666                  { 0, 0, 0, 0},
    667                  0, 0
    668 	};
    669 	static int bg_cache;
    670 	static int fg_cache;
    671 	char_t *uni_c = NULL;
    672 
    673 	int bg, fg;
    674 	int i;
    675 	struct link *link;
    676 	struct point *pt;
    677 	int ll;
    678 
    679 	if (l < 0)
    680 		overalloc();
    681 
    682 	/*printf("%d-", p->cx);for (i=0; i<l; i++) printf("%c", c[i]);
    683 	 * printf("-\n");portable_sleep(1000);*/
    684 	while (p->cx <= par_format.leftmargin && l && *c == ' '
    685 	       && par_format.align != AL_NO
    686 	       && par_format.align != AL_NO_BREAKABLE) {
    687 		c++;
    688 		l--;
    689 	}
    690 	if (!l)
    691 		return;
    692 	if (p->cx < par_format.leftmargin)
    693 		p->cx = par_format.leftmargin;
    694 	if (c[0] != ' ' || (c[1] && c[1] != ' ')) {
    695 		if (p->data)
    696 			last_tag_for_newline = &p->data->tags;
    697 	}
    698 	if (!d_opt->cp && !(format_.attr & AT_GRAPHICS)) {
    699 		int pl;
    700 		unsigned char *cc;
    701 		if (p->utf8_part_len) {
    702 			unsigned char new_part[7];
    703 			unsigned char *q;
    704 next_utf_byte:
    705 			if ((*c & 0xc0) != 0x80)
    706 				goto bad_utf;
    707 			p->utf8_part[p->utf8_part_len++] = *c;
    708 			p->utf8_part[p->utf8_part_len] = 0;
    709 			c++;
    710 			l--;
    711 			q = p->utf8_part;
    712 			if (!get_utf_8(&q)) {
    713 				if (p->utf8_part_len
    714 				    == sizeof(p->utf8_part) - 1)
    715 					goto bad_utf;
    716 				if (l)
    717 					goto next_utf_byte;
    718 				return;
    719 			}
    720 			pl = p->utf8_part_len;
    721 			p->utf8_part_len = 0;
    722 			strcpy(cast_char new_part,
    723 			       cast_const_char p->utf8_part);
    724 			put_chars(p, new_part, pl);
    725 		}
    726 bad_utf:
    727 		p->utf8_part_len = 0;
    728 		if (!l)
    729 			return;
    730 		if ((unsigned)l > (unsigned)INT_MAX / sizeof(char_t))
    731 			overalloc();
    732 		uni_c = xmalloc(l * sizeof(char_t));
    733 		ll = 0;
    734 		cc = c;
    735 next_utf_char:
    736 		pl = utf8chrlen(*cc);
    737 		if (safe_add((int)(cc - c), pl) > l) {
    738 			memcpy(p->utf8_part, cc,
    739 			       p->utf8_part_len = l - (int)(cc - c));
    740 			goto utf_done;
    741 		} else {
    742 			if (!pl) {
    743 				cc++;
    744 			} else {
    745 				unsigned un;
    746 				GET_UTF_8(cc, un);
    747 				if (un != 0xad) {
    748 					uni_c[ll] = un;
    749 					ll = safe_add(ll, 1);
    750 				}
    751 			}
    752 			if (cc < c + l)
    753 				goto next_utf_char;
    754 		}
    755 utf_done:
    756 		if (!ll) {
    757 			free(uni_c);
    758 			return;
    759 		}
    760 	} else {
    761 		ll = l;
    762 	}
    763 	if (last_link || last_image || last_form || format_.link
    764 	    || format_.image || format_.form)
    765 		goto process_link;
    766 no_l:
    767 	/*printf("%d %d\n",p->cx, p->cy);*/
    768 	if (memcmp(&ta_cache, &format_, sizeof(struct text_attrib_beginning)))
    769 		goto format_change;
    770 	bg = bg_cache;
    771 	fg = fg_cache;
    772 end_format_change:
    773 	if (p->y < safe_add(p->cy, 1))
    774 		p->y = p->cy + 1;
    775 	if (nowrap && safe_add(p->cx, ll) > rm(par_format)) {
    776 		free(uni_c);
    777 		return;
    778 	}
    779 	if (!d_opt->cp && !(format_.attr & AT_GRAPHICS)) {
    780 		set_hline_uni(p, p->cx, p->cy, ll, uni_c,
    781 		              ((fg & 0x08) << 3) | (bg << 3) | (fg & 0x07));
    782 	} else
    783 		set_hline(p, p->cx, p->cy, l, c,
    784 		          ((fg & 0x08) << 3) | (bg << 3) | (fg & 0x07));
    785 	p->cx += ll;
    786 	nobreak = 0;
    787 	if (par_format.align != AL_NO)
    788 		while (p->cx > rm(par_format)
    789 		       && p->cx > par_format.leftmargin) {
    790 			int x;
    791 			if (!(x = split_line(p)))
    792 				break;
    793 			align_line(p, p->cy - 1);
    794 			nobreak = x - 1;
    795 		}
    796 	p->xa = safe_add(p->xa, ll);
    797 	if (safe_add(p->xa
    798 	                 - (c[l - 1] == ' ' && par_format.align != AL_NO
    799 	                    && par_format.align != AL_NO_BREAKABLE),
    800 	             safe_add(par_format.leftmargin, par_format.rightmargin))
    801 	    > p->xmax)
    802 		p->xmax = p->xa
    803 		          - (c[l - 1] == ' ' && par_format.align != AL_NO
    804 		             && par_format.align != AL_NO_BREAKABLE)
    805 		          + par_format.leftmargin + par_format.rightmargin;
    806 	free(uni_c);
    807 	return;
    808 
    809 	/* !!! WARNING: THE FOLLOWING CODE IS SHADOWED IN HTML_GR.C */
    810 
    811 process_link:
    812 	if ((last_link || last_image || last_form)
    813 	    && !xstrcmp(format_.link, last_link)
    814 	    && !xstrcmp(format_.target, last_target)
    815 	    && !xstrcmp(format_.image, last_image)
    816 	    && format_.form == last_form) {
    817 		if (!p->data)
    818 			goto x;
    819 		link = &p->data->links[p->data->nlinks - 1];
    820 		if (!p->data->nlinks) {
    821 			internal("no link");
    822 			goto no_l;
    823 		}
    824 		goto set_link;
    825 x:;
    826 	} else {
    827 		free(last_link);
    828 		free(last_target);
    829 		free(last_image);
    830 		last_link = last_target = last_image = NULL;
    831 		last_form = NULL;
    832 		if (!(format_.link || format_.image || format_.form))
    833 			goto no_l;
    834 		if (d_opt->num_links) {
    835 			unsigned char s[64];
    836 			unsigned char *fl = format_.link, *ft = format_.target,
    837 				      *fi = format_.image;
    838 			struct form_control *ff = format_.form;
    839 			format_.link = format_.target = format_.image = NULL;
    840 			format_.form = NULL;
    841 			if (d_opt->num_links) {
    842 				s[0] = '[';
    843 				snzprint(s + 1, 62, p->link_num);
    844 				strcat(cast_char s, "]");
    845 			} else {
    846 				if (ff
    847 				    && (ff->type == FC_TEXT
    848 				        || ff->type == FC_PASSWORD
    849 				        || ff->type == FC_FILE_UPLOAD
    850 				        || ff->type == FC_TEXTAREA)) {
    851 					strcpy(cast_char s, ">");
    852 				} else if (ff
    853 				           && (ff->type == FC_CHECKBOX
    854 				               || ff->type == FC_RADIO
    855 				               || ff->type == FC_SELECT)) {
    856 					strcpy(cast_char s, "");
    857 				} else {
    858 					strcpy(cast_char s, "~");
    859 				}
    860 			}
    861 			put_chars(p, s, (int)strlen(cast_const_char s));
    862 			if (ff && ff->type == FC_TEXTAREA)
    863 				line_break(p);
    864 			if (p->cx < par_format.leftmargin)
    865 				p->cx = par_format.leftmargin;
    866 			format_.link = fl;
    867 			format_.target = ft;
    868 			format_.image = fi;
    869 			format_.form = ff;
    870 		}
    871 		p->link_num++;
    872 		last_link = stracpy(format_.link);
    873 		last_target = stracpy(format_.target);
    874 		last_image = stracpy(format_.image);
    875 		last_form = format_.form;
    876 		if (!p->data)
    877 			goto no_l;
    878 		if (!(link = new_link(p->data)))
    879 			goto no_l;
    880 		link->num = p->link_num - 1;
    881 		link->pos = NULL;
    882 		if (!last_form) {
    883 			link->type = L_LINK;
    884 			link->where = stracpy(last_link);
    885 			link->target = stracpy(last_target);
    886 		} else {
    887 			link->type =
    888 			    last_form->type == FC_TEXT
    889 				    || last_form->type == FC_PASSWORD
    890 				    || last_form->type == FC_FILE_UPLOAD
    891 				? L_FIELD
    892 			    : last_form->type == FC_TEXTAREA ? L_AREA
    893 			    : last_form->type == FC_CHECKBOX
    894 				    || last_form->type == FC_RADIO
    895 				? L_CHECKBOX
    896 			    : last_form->type == FC_SELECT ? L_SELECT
    897 							   : L_BUTTON;
    898 			link->form = last_form;
    899 			link->target = stracpy(last_form->target);
    900 		}
    901 		link->where_img = stracpy(last_image);
    902 		if (link->type != L_FIELD && link->type != L_AREA) {
    903 			bg = find_nearest_color(&format_.clink, 8);
    904 			fg = find_nearest_color(&format_.bg, 8);
    905 			fg = fg_color(fg, bg);
    906 		} else {
    907 			fg = find_nearest_color(&format_.fg, 8);
    908 			bg = find_nearest_color(&format_.bg, 8);
    909 			fg = fg_color(fg, bg);
    910 		}
    911 		link->sel_color = get_attribute(fg, bg);
    912 		link->n = 0;
    913 set_link:
    914 		if ((unsigned)link->n + (unsigned)ll
    915 		    > INT_MAX / sizeof(struct point))
    916 			overalloc();
    917 		pt = xrealloc(link->pos, (link->n + ll) * sizeof(struct point));
    918 		link->pos = pt;
    919 		for (i = 0; i < ll; i++) {
    920 			pt[link->n + i].x = X(p->cx) + i;
    921 			pt[link->n + i].y = Y(p->cy);
    922 		}
    923 		link->n += ll;
    924 	}
    925 	goto no_l;
    926 
    927 format_change:
    928 	bg = find_nearest_color(&format_.bg, 8);
    929 	fg = find_nearest_color(&format_.fg, 16);
    930 	fg = fg_color(fg, bg);
    931 	if (format_.attr & AT_ITALIC)
    932 		fg = fg ^ 0x01;
    933 	if (format_.attr & AT_UNDERLINE)
    934 		fg = (fg ^ 0x04) | 0x08;
    935 	if (format_.attr & AT_BOLD)
    936 		fg = fg | 0x08;
    937 	fg = fg_color(fg, bg);
    938 	if (format_.attr & AT_GRAPHICS)
    939 		bg = bg | 0x10;
    940 	memcpy(&ta_cache, &format_, sizeof(struct text_attrib_beginning));
    941 	fg_cache = fg;
    942 	bg_cache = bg;
    943 	goto end_format_change;
    944 }
    945 
    946 static void
    947 line_break(void *p_)
    948 {
    949 	struct part *p = p_;
    950 	struct tag *t = NULL;
    951 	struct list_head *lt;
    952 	if (p->cx >= 0 && safe_add(p->cx, par_format.rightmargin) > p->x)
    953 		p->x = p->cx + par_format.rightmargin;
    954 	if (nobreak) {
    955 		nobreak = 0;
    956 		p->cx = -1;
    957 		p->xa = 0;
    958 		return;
    959 	}
    960 	if (!p->data)
    961 		goto e;
    962 	xpand_lines(p, safe_add(p->cy, 1));
    963 	if (p->cx > par_format.leftmargin && LEN(p->cy) > p->cx - 1
    964 	    && POS(p->cx - 1, p->cy).ch == ' ') {
    965 		del_chars(p, p->cx - 1, p->cy);
    966 		p->cx--;
    967 	}
    968 	if (p->cx > 0)
    969 		align_line(p, p->cy);
    970 	if (p->data)
    971 		foreachbackfrom (struct tag, t, lt, p->data->tags,
    972 		                 last_tag_for_newline) {
    973 			t->x = X(0);
    974 			t->y = Y(p->cy + 1);
    975 		}
    976 e:
    977 	p->cy++;
    978 	p->cx = -1;
    979 	p->xa = 0;
    980 	if (p->spl > d_opt->xw)
    981 		p->spl = d_opt->xw;
    982 	memset(p->spaces, 0, p->spl);
    983 	p->z_spaces = 0;
    984 }
    985 
    986 int g_ctrl_num;
    987 
    988 /* SHADOWED IN g_html_form_control */
    989 static void
    990 html_form_control(struct part *p, struct form_control *fc)
    991 {
    992 	if (!p->data) {
    993 		add_to_list(p->uf, fc);
    994 		return;
    995 	}
    996 	fc->g_ctrl_num = g_ctrl_num;
    997 	g_ctrl_num = safe_add(g_ctrl_num, 1);
    998 	if (fc->type == FC_TEXT || fc->type == FC_PASSWORD
    999 	    || fc->type == FC_TEXTAREA) {
   1000 		unsigned char *dv = convert_string(
   1001 		    convert_table, fc->default_value,
   1002 		    (int)strlen(cast_const_char fc->default_value), d_opt);
   1003 		if (dv) {
   1004 			free(fc->default_value);
   1005 			fc->default_value = dv;
   1006 		}
   1007 	}
   1008 	if (fc->type == FC_TEXTAREA) {
   1009 		unsigned char *p;
   1010 		for (p = fc->default_value; p[0]; p++)
   1011 			if (p[0] == '\r') {
   1012 				if (p[1] == '\n') {
   1013 					memmove(p, p + 1,
   1014 					        strlen(cast_const_char p));
   1015 					p--;
   1016 				} else
   1017 					p[0] = '\n';
   1018 			}
   1019 	}
   1020 	add_to_list(p->data->forms, fc);
   1021 }
   1022 
   1023 static void
   1024 add_frameset_entry(struct frameset_desc *fsd, struct frameset_desc *subframe,
   1025                    unsigned char *name, unsigned char *url, int marginwidth,
   1026                    int marginheight, unsigned char scrolling)
   1027 {
   1028 	if (fsd->yp >= fsd->y)
   1029 		return;
   1030 	fsd->f[fsd->xp + fsd->yp * fsd->x].subframe = subframe;
   1031 	fsd->f[fsd->xp + fsd->yp * fsd->x].name = stracpy(name);
   1032 	fsd->f[fsd->xp + fsd->yp * fsd->x].url = stracpy(url);
   1033 	fsd->f[fsd->xp + fsd->yp * fsd->x].marginwidth = marginwidth;
   1034 	fsd->f[fsd->xp + fsd->yp * fsd->x].marginheight = marginheight;
   1035 	fsd->f[fsd->xp + fsd->yp * fsd->x].scrolling = scrolling;
   1036 	if (++fsd->xp >= fsd->x) {
   1037 		fsd->xp = 0;
   1038 		fsd->yp++;
   1039 	}
   1040 }
   1041 
   1042 struct frameset_desc *
   1043 create_frameset(struct f_data *fda, struct frameset_param *fp)
   1044 {
   1045 	int i;
   1046 	struct frameset_desc *fd;
   1047 	if (!fp->x || !fp->y) {
   1048 		internal("zero size of frameset");
   1049 		return NULL;
   1050 	}
   1051 	if (fp->x
   1052 	    && (unsigned)fp->x * (unsigned)fp->y / (unsigned)fp->x
   1053 	           != (unsigned)fp->y)
   1054 		overalloc();
   1055 	if ((unsigned)fp->x * (unsigned)fp->y
   1056 	    > (INT_MAX - sizeof(struct frameset_desc))
   1057 	          / sizeof(struct frame_desc))
   1058 		overalloc();
   1059 	fd = mem_calloc(sizeof(struct frameset_desc)
   1060 	                + fp->x * fp->y * sizeof(struct frame_desc));
   1061 	fd->n = fp->x * fp->y;
   1062 	fd->x = fp->x;
   1063 	fd->y = fp->y;
   1064 	for (i = 0; i < fd->n; i++) {
   1065 		fd->f[i].xw = fp->xw[i % fp->x];
   1066 		fd->f[i].yw = fp->yw[i / fp->x];
   1067 	}
   1068 	if (fp->parent)
   1069 		add_frameset_entry(fp->parent, fd, NULL, NULL, -1, -1,
   1070 		                   SCROLLING_AUTO);
   1071 	else if (!fda->frame_desc)
   1072 		fda->frame_desc = fd;
   1073 	else {
   1074 		free(fd);
   1075 		fd = NULL;
   1076 	}
   1077 	return fd;
   1078 }
   1079 
   1080 void
   1081 create_frame(struct frame_param *fp)
   1082 {
   1083 	add_frameset_entry(fp->parent, NULL, fp->name, fp->url, fp->marginwidth,
   1084 	                   fp->marginheight, fp->scrolling);
   1085 }
   1086 
   1087 void
   1088 process_script(struct f_data *f, unsigned char *t)
   1089 {
   1090 }
   1091 
   1092 void
   1093 set_base(struct f_data *f, unsigned char *t)
   1094 {
   1095 }
   1096 
   1097 void
   1098 html_process_refresh(struct f_data *f, unsigned char *url, int time)
   1099 {
   1100 	if (!f)
   1101 		return;
   1102 	if (f->refresh)
   1103 		return;
   1104 	if (!url)
   1105 		f->refresh = stracpy(f->rq->url);
   1106 	else
   1107 		f->refresh = join_urls(f->rq->url, url);
   1108 	f->refresh_seconds = time;
   1109 }
   1110 
   1111 static void *
   1112 html_special(void *p_, int c, ...)
   1113 {
   1114 	struct part *p = p_;
   1115 	va_list l;
   1116 	unsigned char *t;
   1117 	struct form_control *fc;
   1118 	struct frameset_param *fsp;
   1119 	struct frame_param *fp;
   1120 	struct refresh_param *rp;
   1121 	va_start(l, c);
   1122 	switch (c) {
   1123 	case SP_TAG:
   1124 		t = va_arg(l, unsigned char *);
   1125 		va_end(l);
   1126 		html_tag(p->data, t, X(p->cx >= 0 ? p->cx : 0), Y(p->cy));
   1127 		break;
   1128 	case SP_CONTROL:
   1129 		fc = va_arg(l, struct form_control *);
   1130 		va_end(l);
   1131 		html_form_control(p, fc);
   1132 		break;
   1133 	case SP_TABLE:
   1134 		va_end(l);
   1135 		return convert_table;
   1136 	case SP_USED:
   1137 		va_end(l);
   1138 		return (void *)(long)!!p->data;
   1139 	case SP_FRAMESET:
   1140 		fsp = va_arg(l, struct frameset_param *);
   1141 		va_end(l);
   1142 		return create_frameset(p->data, fsp);
   1143 	case SP_FRAME:
   1144 		fp = va_arg(l, struct frame_param *);
   1145 		va_end(l);
   1146 		create_frame(fp);
   1147 		break;
   1148 	case SP_NOWRAP:
   1149 		nowrap = va_arg(l, int);
   1150 		va_end(l);
   1151 		break;
   1152 	case SP_SCRIPT:
   1153 		t = va_arg(l, unsigned char *);
   1154 		va_end(l);
   1155 		if (p->data)
   1156 			process_script(p->data, t);
   1157 		break;
   1158 	case SP_REFRESH:
   1159 		rp = va_arg(l, struct refresh_param *);
   1160 		va_end(l);
   1161 		html_process_refresh(p->data, rp->url, rp->time);
   1162 		break;
   1163 	case SP_SET_BASE:
   1164 		t = va_arg(l, unsigned char *);
   1165 		va_end(l);
   1166 		if (p->data)
   1167 			set_base(p->data, t);
   1168 		break;
   1169 	default:
   1170 		va_end(l);
   1171 		internal("html_special: unknown code %d", c);
   1172 	}
   1173 	return NULL;
   1174 }
   1175 
   1176 static void
   1177 do_format(unsigned char *start, unsigned char *end, struct part *part,
   1178           unsigned char *head)
   1179 {
   1180 	pr(parse_html(start, end, put_chars, line_break, html_special, part,
   1181 	              head);){};
   1182 }
   1183 
   1184 int margin;
   1185 
   1186 struct part *
   1187 format_html_part(unsigned char *start, unsigned char *end, int align, int m,
   1188                  int width, struct f_data *data, int xs, int ys,
   1189                  unsigned char *head, int link_num)
   1190 {
   1191 	struct part *p;
   1192 	struct html_element *e;
   1193 	int llm = last_link_to_move;
   1194 	struct list_head *ltm = last_tag_to_move;
   1195 	int lm = margin;
   1196 	int ef = empty_format;
   1197 	struct form_control *fc = NULL;
   1198 	struct list_head *lfc;
   1199 
   1200 	if (par_format.implicit_pre_wrap) {
   1201 		if (width > d_opt->xw)
   1202 			width = d_opt->xw;
   1203 	}
   1204 
   1205 	if (!data) {
   1206 		p = find_table_cache_entry(start, end, align, m, width, xs,
   1207 		                           link_num);
   1208 		if (p)
   1209 			return p;
   1210 	}
   1211 	if (ys < 0) {
   1212 		internal("format_html_part: ys == %d", ys);
   1213 		return NULL;
   1214 	}
   1215 	if (data) {
   1216 		struct node *n;
   1217 		n = xmalloc(sizeof(struct node));
   1218 		n->x = xs;
   1219 		n->y = ys;
   1220 		n->xw = !table_level ? INT_MAX - 1 : width;
   1221 		add_to_list(data->nodes, n);
   1222 	}
   1223 	last_link_to_move = data ? data->nlinks : 0;
   1224 	last_tag_to_move = data ? &data->tags : NULL;
   1225 	last_tag_for_newline = data ? &data->tags : NULL;
   1226 	margin = m;
   1227 	empty_format = !data;
   1228 	free(last_link);
   1229 	free(last_image);
   1230 	free(last_target);
   1231 	last_link = last_image = last_target = NULL;
   1232 	last_form = NULL;
   1233 	nobreak = align != AL_NO && align != AL_NO_BREAKABLE;
   1234 	p = mem_calloc(sizeof(struct part));
   1235 	/*p->x = p->y = 0;*/
   1236 	p->data = data;
   1237 	p->xp = xs;
   1238 	p->yp = ys;
   1239 	/*p->xmax = p->xa = 0;*/
   1240 	p->attribute =
   1241 	    get_attribute(find_nearest_color(&format_.fg, 16),
   1242 	                  find_nearest_color(&par_format.bgcolor, 8));
   1243 	p->spaces = NULL;
   1244 	/*p->z_spaces = 0;*/
   1245 	/*p->spl = 0;*/
   1246 	p->link_num = link_num;
   1247 	init_list(p->uf);
   1248 	html_stack_dup();
   1249 	e = &html_top;
   1250 	html_top.dontkill = 2;
   1251 	html_top.namelen = 0;
   1252 	par_format.align = align;
   1253 	par_format.leftmargin = m;
   1254 	par_format.rightmargin = m;
   1255 	par_format.width = width;
   1256 	par_format.list_level = 0;
   1257 	par_format.list_number = 0;
   1258 	par_format.dd_margin = 0;
   1259 	if (align == AL_NO || align == AL_NO_BREAKABLE)
   1260 		format_.attr |= AT_FIXED;
   1261 	p->cx = -1;
   1262 	p->cy = 0;
   1263 	do_format(start, end, p, head);
   1264 	if (p->xmax < p->x)
   1265 		p->xmax = p->x;
   1266 	if (align == AL_NO || align == AL_NO_BREAKABLE) {
   1267 		if (p->cy > p->y)
   1268 			p->y = p->cy;
   1269 	}
   1270 	nobreak = 0;
   1271 	line_breax = 1;
   1272 	free(last_link);
   1273 	free(last_image);
   1274 	free(last_target);
   1275 	while (&html_top != e) {
   1276 		kill_html_stack_item(&html_top);
   1277 		if (!&html_top || (void *)&html_top == (void *)&html_stack) {
   1278 			internal("html stack trashed");
   1279 			break;
   1280 		}
   1281 	}
   1282 	html_top.dontkill = 0;
   1283 	kill_html_stack_item(&html_top);
   1284 	free(p->spaces);
   1285 	if (data) {
   1286 		struct node *n = list_struct(data->nodes.next, struct node);
   1287 		n->yw = ys - n->y + p->y;
   1288 	}
   1289 	foreach (struct form_control, fc, lfc, p->uf)
   1290 		destroy_fc(fc);
   1291 	free_list(struct form_control, p->uf);
   1292 	last_link_to_move = llm;
   1293 	last_tag_to_move = ltm;
   1294 	margin = lm;
   1295 	empty_format = ef;
   1296 	last_link = last_image = last_target = NULL;
   1297 	last_form = NULL;
   1298 
   1299 	if (table_level > 1 && !data) {
   1300 		add_table_cache_entry(start, end, align, m, width, xs, link_num,
   1301 		                      p);
   1302 	}
   1303 	return p;
   1304 }
   1305 
   1306 static void
   1307 push_base_format(unsigned char *url, struct document_options *opt, int frame,
   1308                  int implicit_pre_wrap)
   1309 {
   1310 	struct html_element *e;
   1311 	if (!list_empty(html_stack)) {
   1312 		internal("something on html stack");
   1313 		init_list(html_stack);
   1314 	}
   1315 	e = mem_calloc(sizeof(struct html_element));
   1316 	add_to_list(html_stack, e);
   1317 	format_.attr = opt->plain & 1 ? AT_FIXED : 0;
   1318 	format_.fontsize = 3;
   1319 	format_.link = format_.target = format_.image = format_.select = NULL;
   1320 	format_.form = NULL;
   1321 	memcpy(&format_.fg, &opt->default_fg, sizeof(struct rgb));
   1322 	memcpy(&format_.bg, &opt->default_bg, sizeof(struct rgb));
   1323 	memcpy(&format_.clink, &opt->default_link, sizeof(struct rgb));
   1324 	format_.href_base = stracpy(url);
   1325 	format_.target_base = stracpy(opt->framename);
   1326 	par_format.align = !(opt->plain & 1)    ? AL_LEFT
   1327 	                   : !implicit_pre_wrap ? AL_NO
   1328 	                                        : AL_NO_BREAKABLE;
   1329 	par_format.leftmargin = opt->plain & 1 ? 0 : opt->margin;
   1330 	par_format.rightmargin = opt->plain & 1 ? 0 : opt->margin;
   1331 	if (frame && par_format.leftmargin)
   1332 		par_format.leftmargin = 1;
   1333 	if (frame && par_format.rightmargin)
   1334 		par_format.rightmargin = 1;
   1335 	par_format.width = opt->xw;
   1336 	par_format.list_level = par_format.list_number = 0;
   1337 	par_format.dd_margin = opt->margin;
   1338 	par_format.flags = 0;
   1339 	memcpy(&par_format.bgcolor, &opt->default_bg, sizeof(struct rgb));
   1340 	par_format.implicit_pre_wrap = implicit_pre_wrap;
   1341 	html_top.invisible = 0;
   1342 	html_top.name = NULL;
   1343 	html_top.namelen = 0;
   1344 	html_top.options = NULL;
   1345 	html_top.linebreak = 1;
   1346 	html_top.dontkill = 1;
   1347 }
   1348 
   1349 struct conv_table *
   1350 get_convert_table(unsigned char *head, int to, int def, int *frm, int *aa,
   1351                   int hard)
   1352 {
   1353 	int from = -1;
   1354 	unsigned char *a, *b;
   1355 	unsigned char *p = head;
   1356 	while (from == -1
   1357 	       && (a = parse_http_header(p, cast_uchar "Content-Type", &p))) {
   1358 		if ((b = parse_header_param(a, cast_uchar "charset", 0))) {
   1359 			from = 0;
   1360 			free(b);
   1361 		}
   1362 		free(a);
   1363 	}
   1364 	if (aa) {
   1365 		*aa = from == -1;
   1366 		if (hard && !*aa)
   1367 			*aa = 2;
   1368 	}
   1369 	if (hard || from == -1)
   1370 		from = def;
   1371 	if (frm)
   1372 		*frm = from;
   1373 	return get_translation_table(from, to);
   1374 }
   1375 
   1376 struct document_options dd_opt;
   1377 
   1378 struct document_options *d_opt = &dd_opt;
   1379 
   1380 struct f_data *current_f_data = NULL;
   1381 
   1382 void
   1383 really_format_html(struct cache_entry *ce, unsigned char *start,
   1384                    unsigned char *end, struct f_data *screen, int frame)
   1385 {
   1386 	unsigned char *url = ce->url;
   1387 	unsigned char *head, *t;
   1388 	size_t hdl;
   1389 	int i;
   1390 	unsigned char *bg = NULL, *bgcolor = NULL;
   1391 	int implicit_pre_wrap;
   1392 	int bg_col, fg_col;
   1393 	struct part *rp;
   1394 
   1395 	current_f_data = screen;
   1396 	d_opt = &screen->opt;
   1397 	screen->use_tag = ce->count;
   1398 	startf = start;
   1399 	eofff = end;
   1400 	head = NULL;
   1401 	hdl = 0;
   1402 	if (ce->head)
   1403 		hdl = add_to_str(&head, hdl, ce->head);
   1404 	hdl = scan_http_equiv(start, end, &head, hdl, &t,
   1405 	                      d_opt->plain ? NULL : &bg,
   1406 	                      d_opt->plain || d_opt->col < 2 ? NULL : &bgcolor,
   1407 	                      &implicit_pre_wrap);
   1408 	if (d_opt->break_long_lines)
   1409 		implicit_pre_wrap = 1;
   1410 	if (d_opt->plain)
   1411 		*t = 0;
   1412 	if (screen->opt.plain == 2) {
   1413 		screen->cp = 0;
   1414 		screen->ass = -1;
   1415 		convert_table = get_translation_table(0, screen->opt.cp);
   1416 	} else {
   1417 		convert_table = get_convert_table(
   1418 		    head, screen->opt.cp, screen->opt.assume_cp, &screen->cp,
   1419 		    &screen->ass, screen->opt.hard_assume);
   1420 	}
   1421 	screen->opt.real_cp = screen->cp;
   1422 	i = d_opt->plain;
   1423 	d_opt->plain = 0;
   1424 	screen->title = convert_string(convert_table, t,
   1425 	                               (int)strlen(cast_const_char t), d_opt);
   1426 	d_opt->plain = i;
   1427 	free(t);
   1428 	push_base_format(url, &screen->opt, frame, implicit_pre_wrap);
   1429 	table_level = 0;
   1430 	g_ctrl_num = 0;
   1431 	last_form_tag = NULL;
   1432 	last_form_attr = NULL;
   1433 	last_input_tag = NULL;
   1434 	if ((rp = format_html_part(start, end, par_format.align,
   1435 	                           par_format.leftmargin, screen->opt.xw,
   1436 	                           screen, 0, 0, head, 1)))
   1437 		free(rp);
   1438 	free(head);
   1439 	free(bg);
   1440 	free(bgcolor);
   1441 	screen->x = 0;
   1442 	for (i = screen->y - 1; i >= 0; i--) {
   1443 		if (!screen->data[i].l) {
   1444 			free(screen->data[i].d);
   1445 			screen->y--;
   1446 		} else {
   1447 			break;
   1448 		}
   1449 	}
   1450 	for (i = 0; i < screen->y; i++)
   1451 		if (screen->data[i].l > screen->x)
   1452 			screen->x = screen->data[i].l;
   1453 	free(form.action);
   1454 	free(form.target);
   1455 	free(form.form_name);
   1456 	free(form.onsubmit);
   1457 	form.action = NULL;
   1458 	form.target = NULL;
   1459 	form.form_name = NULL;
   1460 	form.onsubmit = NULL;
   1461 	bg_col = find_nearest_color(&format_.bg, 8);
   1462 	fg_col = find_nearest_color(&format_.fg, 16);
   1463 	fg_col = fg_color(fg_col, bg_col);
   1464 	screen->bg = get_attribute(fg_col, bg_col);
   1465 	kill_html_stack_item(&html_top);
   1466 	if (!list_empty(html_stack)) {
   1467 		internal("html stack not empty after operation");
   1468 		init_list(html_stack);
   1469 	}
   1470 	sort_links(screen);
   1471 	current_f_data = NULL;
   1472 	d_opt = &dd_opt;
   1473 }
   1474 
   1475 int
   1476 compare_opt(struct document_options *o1, struct document_options *o2)
   1477 {
   1478 	if (o1->xw == o2->xw && o1->yw == o2->yw && o1->xp == o2->xp
   1479 	    && o1->yp == o2->yp && o1->scrolling == o2->scrolling
   1480 	    && o1->col == o2->col && o1->cp == o2->cp
   1481 	    && o1->assume_cp == o2->assume_cp
   1482 	    && o1->hard_assume == o2->hard_assume && o1->tables == o2->tables
   1483 	    && o1->frames == o2->frames
   1484 	    && o1->break_long_lines == o2->break_long_lines
   1485 	    && o1->images == o2->images && o1->image_names == o2->image_names
   1486 	    && o1->margin == o2->margin && o1->plain == o2->plain
   1487 	    && o1->num_links == o2->num_links
   1488 	    && o1->table_order == o2->table_order
   1489 	    && o1->auto_refresh == o2->auto_refresh
   1490 	    && o1->font_size == o2->font_size
   1491 	    && o1->display_images == o2->display_images
   1492 	    && o1->image_scale == o2->image_scale
   1493 	    && o1->porn_enable == o2->porn_enable
   1494 	    && o1->gamma_stamp == o2->gamma_stamp
   1495 	    && !memcmp(&o1->default_fg, &o2->default_fg, sizeof(struct rgb))
   1496 	    && !memcmp(&o1->default_bg, &o2->default_bg, sizeof(struct rgb))
   1497 	    && !memcmp(&o1->default_link, &o2->default_link, sizeof(struct rgb))
   1498 	    && ((o1->framename && o2->framename
   1499 	         && !casestrcmp(o1->framename, o2->framename))
   1500 	        || (!o1->framename && !o2->framename)))
   1501 		return 0;
   1502 	return 1;
   1503 }
   1504 
   1505 void
   1506 copy_opt(struct document_options *o1, struct document_options *o2)
   1507 {
   1508 	memcpy(o1, o2, sizeof(struct document_options));
   1509 	o1->framename = stracpy(o2->framename);
   1510 }
   1511 
   1512 struct link *
   1513 get_link_at_location(struct f_data *f, int x, int y)
   1514 {
   1515 	struct link *l1, *l2, *l;
   1516 	if (y < 0 || y >= f->y)
   1517 		return NULL;
   1518 	l1 = f->lines1[y];
   1519 	l2 = f->lines2[y];
   1520 	if (!l1 || !l2)
   1521 		return NULL;
   1522 	for (l = l1; l <= l2; l++) {
   1523 		int i;
   1524 		for (i = 0; i < l->n; i++)
   1525 			if (l->pos[i].x == x && l->pos[i].y == y)
   1526 				return l;
   1527 	}
   1528 	return NULL;
   1529 }
   1530 
   1531 static int
   1532 sort_srch(struct f_data *f)
   1533 {
   1534 	int i;
   1535 	int *min, *max;
   1536 	if ((unsigned)f->y > INT_MAX / sizeof(struct search *))
   1537 		overalloc();
   1538 	if ((unsigned)f->y > INT_MAX / sizeof(int))
   1539 		overalloc();
   1540 	f->slines1 = xmalloc(f->y * sizeof(int));
   1541 	f->slines2 = xmalloc(f->y * sizeof(int));
   1542 	min = xmalloc(f->y * sizeof(int));
   1543 	max = xmalloc(f->y * sizeof(int));
   1544 	if (!f->slines1 || !f->slines2 || !min || !max) {
   1545 		free(f->slines1);
   1546 		free(f->slines2);
   1547 		f->slines1 = NULL;
   1548 		f->slines2 = NULL;
   1549 		free(min);
   1550 		free(max);
   1551 		return -1;
   1552 	}
   1553 	for (i = 0; i < f->y; i++)
   1554 		f->slines1[i] = f->slines2[i] = -1;
   1555 	for (i = 0; i < f->y; i++) {
   1556 		min[i] = INT_MAX;
   1557 		max[i] = 0;
   1558 	}
   1559 	for (i = 0; i < f->nsearch_pos; i++) {
   1560 		struct search *s = &f->search_pos[i];
   1561 		int xe;
   1562 		if (s->x < min[s->y]) {
   1563 			min[s->y] = s->x;
   1564 			f->slines1[s->y] = s->idx;
   1565 		}
   1566 		if (s->n == 1)
   1567 			xe = safe_add(s->x, s->co);
   1568 		else
   1569 			xe = safe_add(s->x, s->n);
   1570 		if (xe > max[s->y]) {
   1571 			max[s->y] = xe;
   1572 			f->slines2[s->y] = s->idx + s->co - 1;
   1573 		}
   1574 	}
   1575 	free(min);
   1576 	free(max);
   1577 	return 0;
   1578 }
   1579 
   1580 static inline int
   1581 is_spc(chr *cc)
   1582 {
   1583 	return cc->ch <= ' ' || cc->at & ATTR_FRAME;
   1584 }
   1585 
   1586 static int n_chr, n_pos;
   1587 static char_t srch_last_chr;
   1588 static int srch_last_x, srch_last_y;
   1589 static int srch_cont;
   1590 
   1591 static void
   1592 get_srch_reset(void)
   1593 {
   1594 	n_chr = n_pos = 0;
   1595 	srch_last_chr = ' ';
   1596 	srch_last_x = srch_last_y = -1;
   1597 	srch_cont = 0;
   1598 }
   1599 
   1600 static int
   1601 add_srch_chr(struct f_data *f, unsigned c, int x, int y, int nn)
   1602 {
   1603 	if (c == ' ' && srch_last_chr == ' ')
   1604 		return 0;
   1605 	if (c == '_') {
   1606 		struct link *l = get_link_at_location(f, x, y);
   1607 		if (l
   1608 		    && (l->type == L_SELECT || l->type == L_FIELD
   1609 		        || l->type == L_AREA))
   1610 			return 0;
   1611 	}
   1612 	srch_last_chr = c;
   1613 	if (f->search_chr)
   1614 		f->search_chr[n_chr] = c;
   1615 	if (n_chr == INT_MAX)
   1616 		return -1;
   1617 	n_chr++;
   1618 	if (srch_cont < 0xffff && x == srch_last_x + 1 && y == srch_last_y
   1619 	    && nn == 1) {
   1620 		srch_cont++;
   1621 		if (f->search_pos)
   1622 			f->search_pos[n_pos - 1].co = (unsigned short)srch_cont;
   1623 	} else {
   1624 		if (f->search_pos) {
   1625 			f->search_pos[n_pos].idx = n_chr - 1;
   1626 			f->search_pos[n_pos].x = x;
   1627 			f->search_pos[n_pos].y = y;
   1628 			f->search_pos[n_pos].n = (unsigned short)nn;
   1629 			if (f->search_pos[n_pos].n != nn)
   1630 				f->search_pos[n_pos].n = (unsigned short)~0U;
   1631 			f->search_pos[n_pos].co = 1;
   1632 		}
   1633 		srch_cont = 1;
   1634 		if (n_pos == INT_MAX)
   1635 			return -1;
   1636 		n_pos++;
   1637 	}
   1638 	if (nn == 1) {
   1639 		srch_last_x = x;
   1640 		srch_last_y = y;
   1641 	} else {
   1642 		srch_last_x = -1;
   1643 		srch_last_y = -1;
   1644 	}
   1645 	return 0;
   1646 }
   1647 
   1648 static int
   1649 get_srch(struct f_data *f)
   1650 {
   1651 	struct node *n = NULL;
   1652 	struct list_head *ln;
   1653 	get_srch_reset();
   1654 #define add_srch(c_, x_, y_, n_)                                               \
   1655 	do {                                                                   \
   1656 		if (add_srch_chr(f, c_, x_, y_, n_))                           \
   1657 			return -1;                                             \
   1658 	} while (0)
   1659 	foreachback (struct node, n, ln, f->nodes) {
   1660 		int x, y;
   1661 		int xm = safe_add(n->x, n->xw), ym = safe_add(n->y, n->yw);
   1662 		/*printf("%d %d - %d %d\n", n->x, n->y, xm, ym);
   1663 		fflush(stdout);*/
   1664 		for (y = n->y; y < ym && y < f->y; y++) {
   1665 			int ns = 1;
   1666 			for (x = n->x; x < xm && x < f->data[y].l; x++) {
   1667 				unsigned c = f->data[y].d[x].ch;
   1668 				if (is_spc(&f->data[y].d[x]))
   1669 					c = ' ';
   1670 				if (c == ' ' && ns)
   1671 					continue;
   1672 				c = charset_upcase(c, f->opt.cp);
   1673 				if (ns) {
   1674 					add_srch(c, x, y, 1);
   1675 					ns = 0;
   1676 					continue;
   1677 				}
   1678 				if (c != ' ') {
   1679 					add_srch(c, x, y, 1);
   1680 				} else {
   1681 					int xx;
   1682 					for (xx = safe_add(x, 1);
   1683 					     xx < xm && xx < f->data[y].l; xx++)
   1684 						if (!is_spc(&f->data[y].d[xx]))
   1685 							goto ja_uz_z_toho_programovani_asi_zcvoknu;
   1686 					xx = x;
   1687 ja_uz_z_toho_programovani_asi_zcvoknu:
   1688 					/* uz jsem zcvoknul, trpim poruchou
   1689 					 * osobnosti */
   1690 					add_srch(' ', x, y, xx - x);
   1691 					if (xx == x)
   1692 						goto uz_jsem_zcvoknul__jsem_psychopat__trpim_poruchou_osobnosti;
   1693 					x = xx - 1;
   1694 				}
   1695 			}
   1696 uz_jsem_zcvoknul__jsem_psychopat__trpim_poruchou_osobnosti:
   1697 			add_srch(' ', x, y, 0);
   1698 		}
   1699 	}
   1700 #undef add_srch
   1701 	return 0;
   1702 }
   1703 
   1704 int
   1705 get_search_data(struct f_data *f)
   1706 {
   1707 	if (f->search_pos)
   1708 		return 0;
   1709 	if (get_srch(f))
   1710 		return -1;
   1711 	if ((size_t)n_chr > ((size_t)-1) / sizeof(char_t)
   1712 	    || (size_t)n_pos > ((size_t)-1) / sizeof(struct search))
   1713 		return -1;
   1714 	f->search_chr = xmalloc(n_chr * sizeof(char_t));
   1715 	if (!f->search_chr)
   1716 		return -1;
   1717 	f->search_pos = xmalloc(n_pos * sizeof(struct search));
   1718 	if (!f->search_pos) {
   1719 		free(f->search_chr);
   1720 		f->search_chr = NULL;
   1721 		return -1;
   1722 	}
   1723 	if (get_srch(f))
   1724 		internal(
   1725 		    "get_search_data: get_srch should not fail second time");
   1726 	while (n_chr && f->search_chr[n_chr - 1] == ' ')
   1727 		n_chr--;
   1728 	f->nsearch_chr = n_chr;
   1729 	f->nsearch_pos = n_pos;
   1730 	if (sort_srch(f)) {
   1731 		free(f->search_pos);
   1732 		f->search_pos = NULL;
   1733 		free(f->search_chr);
   1734 		f->search_chr = NULL;
   1735 		f->nsearch_chr = f->nsearch_pos = 0;
   1736 		return -1;
   1737 	}
   1738 	return 0;
   1739 }