links

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

view.c (121672B)


      1 /* view.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 static void init_ctrl(struct form_control *, struct form_state *);
     11 
     12 static int c_in_view(struct f_data_c *);
     13 
     14 static void set_pos_x(struct f_data_c *, struct link *);
     15 static void set_pos_y(struct f_data_c *, struct link *);
     16 static void find_link(struct f_data_c *, int, int);
     17 
     18 static int is_active_frame(struct session *ses, struct f_data_c *f);
     19 
     20 static void send_open_in_new_xterm(struct terminal *term, void *open_window_,
     21                                    void *ses_);
     22 static void (*const send_open_in_new_xterm_ptr)(
     23     struct terminal *, void *fn_, void *ses_) = send_open_in_new_xterm;
     24 
     25 /* FIXME: remove */
     26 static void
     27 free_format_text_cache_entry(struct form_state *fs)
     28 {
     29 	struct format_text_cache_entry *ftce = fs->ftce;
     30 	if (!ftce)
     31 		return;
     32 	fs->ftce = NULL;
     33 	free(ftce);
     34 }
     35 
     36 struct view_state *
     37 create_vs(void)
     38 {
     39 	struct view_state *vs;
     40 	vs = mem_calloc(sizeof(struct view_state));
     41 	vs->refcount = 1;
     42 	vs->current_link = -1;
     43 	vs->orig_link = -1;
     44 	vs->frame_pos = -1;
     45 	vs->plain = -1;
     46 	vs->form_info = NULL;
     47 	vs->form_info_len = 0;
     48 	return vs;
     49 }
     50 
     51 void
     52 destroy_vs(struct view_state *vs)
     53 {
     54 	int i;
     55 	if (--vs->refcount) {
     56 		if (vs->refcount < 0)
     57 			internal("destroy_vs: view_state refcount underflow");
     58 		return;
     59 	}
     60 	for (i = 0; i < vs->form_info_len; i++) {
     61 		free(vs->form_info[i].string);
     62 		free(vs->form_info[i].ftce);
     63 		vs->form_info[i].ftce = NULL;
     64 	}
     65 	free(vs->form_info);
     66 	free(vs);
     67 }
     68 
     69 void
     70 check_vs(struct f_data_c *f)
     71 {
     72 	struct view_state *vs = f->vs;
     73 	int ovx, ovy, ol, obx, oby;
     74 	if (f->f_data->frame_desc) {
     75 		int n = (int)list_size(&f->subframes);
     76 		if (vs->frame_pos < 0)
     77 			vs->frame_pos = 0;
     78 		if (vs->frame_pos >= n)
     79 			vs->frame_pos = n - 1;
     80 		return;
     81 	}
     82 	ovx = f->vs->orig_view_posx;
     83 	ovy = f->vs->orig_view_pos;
     84 	ol = f->vs->orig_link;
     85 	obx = f->vs->orig_brl_x;
     86 	oby = f->vs->orig_brl_y;
     87 	if (vs->current_link >= f->f_data->nlinks)
     88 		vs->current_link = f->f_data->nlinks - 1;
     89 	if (vs->current_link != -1 && !c_in_view(f)) {
     90 		set_pos_x(f, &f->f_data->links[f->vs->current_link]);
     91 		set_pos_y(f, &f->f_data->links[f->vs->current_link]);
     92 	}
     93 	if (vs->current_link == -1)
     94 		find_link(f, 1, 0);
     95 	f->vs->orig_view_posx = ovx;
     96 	f->vs->orig_view_pos = ovy;
     97 	f->vs->orig_link = ol;
     98 	f->vs->orig_brl_x = obx;
     99 	f->vs->orig_brl_y = oby;
    100 }
    101 
    102 static void
    103 set_link(struct f_data_c *f)
    104 {
    105 	if (c_in_view(f))
    106 		return;
    107 	find_link(f, 1, 0);
    108 }
    109 
    110 static int
    111 find_tag(struct f_data *f, unsigned char *name)
    112 {
    113 	struct tag *tag = NULL;
    114 	struct list_head *ltag;
    115 	unsigned char *tt;
    116 	tt = NULL;
    117 	add_conv_str(&tt, 0, name, (int)strlen(cast_const_char name), -2);
    118 	foreachback (struct tag, tag, ltag, f->tags)
    119 		if (!casestrcmp(tag->name, tt)
    120 		    || (tag->name[0] == '#'
    121 		        && !casestrcmp(tag->name + 1, tt))) {
    122 			free(tt);
    123 			return tag->y;
    124 		}
    125 	free(tt);
    126 	return -1;
    127 }
    128 
    129 static int
    130 comp_links(const void *l1_, const void *l2_)
    131 {
    132 	const struct link *l1 = (const struct link *)l1_;
    133 	const struct link *l2 = (const struct link *)l2_;
    134 	return l1->num - l2->num;
    135 }
    136 
    137 void
    138 sort_links(struct f_data *f)
    139 {
    140 	int i;
    141 	if (f->nlinks)
    142 		qsort(f->links, f->nlinks, sizeof(struct link), comp_links);
    143 	if ((unsigned)f->y > INT_MAX / sizeof(struct link *))
    144 		overalloc();
    145 	f->lines1 = mem_calloc(f->y * sizeof(struct link *));
    146 	f->lines2 = mem_calloc(f->y * sizeof(struct link *));
    147 	for (i = 0; i < f->nlinks; i++) {
    148 		int p, q, j;
    149 		struct link *link = &f->links[i];
    150 		if (!link->n) {
    151 			if (d_opt->num_links)
    152 				continue;
    153 			free(link->where);
    154 			free(link->target);
    155 			free(link->where_img);
    156 			free(link->img_alt);
    157 			free(link->pos);
    158 			memmove(link, link + 1,
    159 			        (f->nlinks - i - 1) * sizeof(struct link));
    160 			f->nlinks--;
    161 			i--;
    162 			continue;
    163 		}
    164 		p = f->y - 1;
    165 		q = 0;
    166 		for (j = 0; j < link->n; j++) {
    167 			if (link->pos[j].y < p)
    168 				p = link->pos[j].y;
    169 			if (link->pos[j].y > q)
    170 				q = link->pos[j].y;
    171 		}
    172 		if (p > q) {
    173 			j = p;
    174 			p = q;
    175 			q = j;
    176 		}
    177 		for (j = p; j <= q; j++) {
    178 			if (j >= f->y) {
    179 				internal("link out of screen");
    180 				continue;
    181 			}
    182 			f->lines2[j] = &f->links[i];
    183 			if (!f->lines1[j])
    184 				f->lines1[j] = &f->links[i];
    185 		}
    186 	}
    187 }
    188 
    189 unsigned char *
    190 textptr_add(unsigned char *t, int i, int cp)
    191 {
    192 	if (cp) {
    193 		if (i)
    194 			t += strnlen(cast_const_char t, i);
    195 		return t;
    196 	} else {
    197 		while (i-- && *t)
    198 			FWD_UTF_8(t);
    199 		return t;
    200 	}
    201 }
    202 
    203 int
    204 textptr_diff(unsigned char *t2, unsigned char *t1, int cp)
    205 {
    206 	if (cp)
    207 		return (int)(t2 - t1);
    208 	else {
    209 		int i = 0;
    210 		while (t2 > t1) {
    211 			FWD_UTF_8(t1);
    212 			i++;
    213 		}
    214 		return i;
    215 	}
    216 }
    217 
    218 static struct format_text_cache_entry *
    219 format_text_uncached(unsigned char *text, int width, int wrap, int cp)
    220 {
    221 	unsigned char *text_start = text;
    222 	struct format_text_cache_entry *ftce;
    223 	int lnn_allocated = ALLOC_GR;
    224 	int lnn = 0;
    225 	unsigned char *b = text;
    226 	int sk, ps = 0;
    227 	int xpos = 0;
    228 	unsigned char *last_space = NULL;
    229 	int last_space_xpos = 0;
    230 
    231 	ftce = xmalloc(sizeof(struct format_text_cache_entry)
    232 	               - sizeof(struct line_info)
    233 	               + lnn_allocated * sizeof(struct line_info));
    234 
    235 	ftce->width = width;
    236 	ftce->wrap = wrap;
    237 	ftce->cp = cp;
    238 	ftce->last_state = -1;
    239 
    240 	while (*text) {
    241 		if (*text == '\n') {
    242 			sk = 1;
    243 put:
    244 			if (lnn == lnn_allocated) {
    245 				if ((unsigned)lnn_allocated
    246 				    > INT_MAX / sizeof(struct line_info)
    247 				          - ALLOC_GR)
    248 					overalloc();
    249 				ftce = xrealloc(
    250 				    ftce, sizeof(struct format_text_cache_entry)
    251 					      - sizeof(struct line_info)
    252 					      + lnn_allocated
    253 						    * sizeof(struct line_info));
    254 			}
    255 			ftce->ln[lnn].st_offs = (int)(b - text_start);
    256 			ftce->ln[lnn].en_offs = (int)(text - text_start);
    257 			ftce->ln[lnn++].chars = xpos;
    258 			b = text += sk;
    259 			xpos = 0;
    260 			last_space = NULL;
    261 			continue;
    262 		}
    263 		if (*text == ' ') {
    264 			last_space = text;
    265 			last_space_xpos = xpos;
    266 		}
    267 		if (!wrap || xpos < width) {
    268 			if (cp)
    269 				text++;
    270 			else
    271 				FWD_UTF_8(text);
    272 			xpos++;
    273 			continue;
    274 		}
    275 		if (last_space) {
    276 			text = last_space;
    277 			xpos = last_space_xpos;
    278 			if (wrap == 2) {
    279 				unsigned char *s = last_space;
    280 				*s = '\n';
    281 				for (s++; *s; s++)
    282 					if (*s == '\n') {
    283 						if (s[1] != '\n')
    284 							*s = ' ';
    285 						break;
    286 					}
    287 			}
    288 			sk = 1;
    289 			goto put;
    290 		}
    291 		sk = 0;
    292 		goto put;
    293 	}
    294 	if (ps < 1) {
    295 		ps++;
    296 		sk = 0;
    297 		goto put;
    298 	}
    299 	ftce->n_lines = lnn;
    300 	return ftce;
    301 }
    302 
    303 struct format_text_cache_entry *
    304 format_text(struct f_data_c *fd, struct form_control *fc, struct form_state *fs)
    305 {
    306 	int width = fc->cols;
    307 	int wrap = fc->wrap;
    308 	int cp = fd->f_data->opt.cp;
    309 	struct format_text_cache_entry *ftce = fs->ftce;
    310 
    311 	if (ftce && ftce->width == width && ftce->wrap == wrap
    312 	    && ftce->cp == cp)
    313 		return fs->ftce;
    314 
    315 	free_format_text_cache_entry(fs);
    316 
    317 	ftce = format_text_uncached(fs->string, width, wrap, cp);
    318 	fs->ftce = ftce;
    319 	return ftce;
    320 }
    321 
    322 static int
    323 find_cursor_line(struct format_text_cache_entry *ftce, int state)
    324 {
    325 	int res;
    326 #define LINE_EQ(x, key)                                                        \
    327 	(key >= ftce->ln[x].st_offs                                            \
    328 	 && (x >= ftce->n_lines - 1 || key < ftce->ln[x + 1].st_offs))
    329 #define LINE_ABOVE(x, key) (key < ftce->ln[x].st_offs)
    330 	BIN_SEARCH(ftce->n_lines, LINE_EQ, LINE_ABOVE, state, res);
    331 #undef LINE_EQ
    332 #undef LINE_ABOVE
    333 	return res;
    334 }
    335 
    336 int
    337 area_cursor(struct f_data_c *f, struct form_control *fc, struct form_state *fs)
    338 {
    339 	struct format_text_cache_entry *ftce;
    340 	int q = 0;
    341 	int x, y;
    342 	ftce = format_text(f, fc, fs);
    343 	if (ftce->last_state == fs->state && ftce->last_vpos == fs->vpos
    344 	    && ftce->last_vypos == fs->vypos)
    345 		return fs->ftce->last_cursor;
    346 	y = find_cursor_line(ftce, fs->state);
    347 	if (y >= 0) {
    348 		x = textptr_diff(fs->string + fs->state,
    349 		                 fs->string + ftce->ln[y].st_offs,
    350 		                 f->f_data->opt.cp);
    351 		if (fc->wrap && x == fc->cols)
    352 			x--;
    353 
    354 		if (x >= fc->cols + fs->vpos)
    355 			fs->vpos = x - fc->cols + 1;
    356 		if (x < fs->vpos)
    357 			fs->vpos = x;
    358 
    359 		if (fs->vypos > ftce->n_lines - fc->rows) {
    360 			fs->vypos = ftce->n_lines - fc->rows;
    361 			if (fs->vypos < 0)
    362 				fs->vypos = 0;
    363 		}
    364 
    365 		if (y < fs->vypos)
    366 			fs->vypos = y;
    367 		x -= fs->vpos;
    368 		y -= fs->vypos;
    369 		q = y * fc->cols + x;
    370 	}
    371 	ftce->last_state = fs->state;
    372 	ftce->last_vpos = fs->vpos;
    373 	ftce->last_vypos = fs->vypos;
    374 	ftce->last_cursor = q;
    375 	return q;
    376 }
    377 
    378 static void
    379 draw_link(struct terminal *t, struct f_data_c *scr, int l)
    380 {
    381 	struct link *link = &scr->f_data->links[l];
    382 	int xp = scr->xp;
    383 	int yp = scr->yp;
    384 	int xw = scr->xw;
    385 	int yw = scr->yw;
    386 	int vx, vy;
    387 	struct view_state *vs = scr->vs;
    388 	int f = 0;
    389 	vx = vs->view_posx;
    390 	vy = vs->view_pos;
    391 	if (scr->link_bg) {
    392 		internal("link background not empty");
    393 		free(scr->link_bg);
    394 	}
    395 	if (l == -1)
    396 		return;
    397 	switch (link->type) {
    398 		int i;
    399 		int q;
    400 	case L_LINK:
    401 	case L_CHECKBOX:
    402 	case L_BUTTON:
    403 	case L_SELECT:
    404 	case L_FIELD:
    405 	case L_AREA:
    406 		q = 0;
    407 		if (link->type == L_FIELD) {
    408 			struct form_state *fs =
    409 			    find_form_state(scr, link->form);
    410 			q = textptr_diff(fs->string + fs->state,
    411 			                 fs->string + fs->vpos,
    412 			                 scr->f_data->opt.cp);
    413 		} else if (link->type == L_AREA) {
    414 			struct form_state *fs =
    415 			    find_form_state(scr, link->form);
    416 			q = area_cursor(scr, link->form, fs);
    417 		}
    418 		if ((unsigned)link->n > INT_MAX / sizeof(struct link_bg))
    419 			overalloc();
    420 		scr->link_bg = xmalloc(link->n * sizeof(struct link_bg));
    421 		scr->link_bg_n = link->n;
    422 		for (i = 0; i < link->n; i++) {
    423 			int x = link->pos[i].x + xp - vx;
    424 			int y = link->pos[i].y + yp - vy;
    425 			if (x >= xp && y >= yp && x < xp + xw && y < yp + yw) {
    426 				const chr *co;
    427 				co = get_char(t, x, y);
    428 				scr->link_bg[i].x = x;
    429 				scr->link_bg[i].y = y;
    430 				scr->link_bg[i].c = co->at;
    431 				if (!f || (link->type == L_CHECKBOX && i == 1)
    432 				    || (link->type == L_BUTTON && i == 2)
    433 				    || ((link->type == L_FIELD
    434 				         || link->type == L_AREA)
    435 				        && i == q)) {
    436 					int xx = x, yy = y;
    437 					if (link->type != L_FIELD
    438 					    && link->type != L_AREA) {
    439 						if ((unsigned)(co->at & 0x38)
    440 						    != (link->sel_color
    441 						        & 0x38)) {
    442 							xx = xp + xw - 1;
    443 							yy = yp + yw - 1;
    444 						}
    445 					}
    446 					set_cursor(t, x, y, xx, yy);
    447 					set_window_ptr(scr->ses->win, x, y);
    448 					f = 1;
    449 				}
    450 				set_color(t, x, y, link->sel_color);
    451 			} else {
    452 				scr->link_bg[i].x = scr->link_bg[i].y = -1;
    453 				scr->link_bg[i].c = 0;
    454 			}
    455 		}
    456 		break;
    457 	default:
    458 		internal("bad link type");
    459 	}
    460 }
    461 
    462 static void
    463 free_link(struct f_data_c *scr)
    464 {
    465 	free(scr->link_bg);
    466 	scr->link_bg = NULL;
    467 	scr->link_bg_n = 0;
    468 }
    469 
    470 static void
    471 clear_link(struct terminal *t, struct f_data_c *scr)
    472 {
    473 	if (scr->link_bg) {
    474 		int i;
    475 		for (i = scr->link_bg_n - 1; i >= 0; i--)
    476 			set_color(t, scr->link_bg[i].x, scr->link_bg[i].y,
    477 			          scr->link_bg[i].c);
    478 		free_link(scr);
    479 	}
    480 }
    481 
    482 static struct search *
    483 search_lookup(struct f_data *f, int idx)
    484 {
    485 	static struct search sr;
    486 	int result;
    487 #define S_EQUAL(i, id)                                                         \
    488 	(f->search_pos[i].idx <= id                                            \
    489 	 && f->search_pos[i].idx + f->search_pos[i].co > id)
    490 #define S_ABOVE(i, id) (f->search_pos[i].idx > id)
    491 	BIN_SEARCH(f->nsearch_pos, S_EQUAL, S_ABOVE, idx, result)
    492 	if (result == -1)
    493 		internal("search_lookup: invalid index: %d, %d", idx,
    494 		         f->nsearch_chr);
    495 	if (idx == f->search_pos[result].idx)
    496 		return &f->search_pos[result];
    497 	memcpy(&sr, &f->search_pos[result], sizeof(struct search));
    498 	sr.x += idx - f->search_pos[result].idx;
    499 	return &sr;
    500 }
    501 
    502 static int
    503 get_range(struct f_data *f, int y, int yw, int l, int *s1, int *s2)
    504 {
    505 	int i;
    506 	*s1 = *s2 = -1;
    507 	for (i = y < 0 ? 0 : y; i < y + yw && i < f->y; i++) {
    508 		if (f->slines1[i] >= 0 && (*s1 < 0 || f->slines1[i] < *s1))
    509 			*s1 = f->slines1[i];
    510 		if (f->slines2[i] >= 0 && (*s2 < 0 || f->slines2[i] > *s2))
    511 			*s2 = f->slines2[i];
    512 	}
    513 
    514 	if (l > f->nsearch_chr)
    515 		*s1 = *s2 = -1;
    516 	if (*s1 < 0 || *s2 < 0)
    517 		return -1;
    518 
    519 	if (*s1 < l)
    520 		*s1 = 0;
    521 	else
    522 		*s1 -= l;
    523 
    524 	if (f->nsearch_chr - *s2 < l)
    525 		*s2 = f->nsearch_chr - l;
    526 
    527 	if (*s1 > *s2)
    528 		*s1 = *s2 = -1;
    529 	if (*s1 < 0 || *s2 < 0)
    530 		return -1;
    531 
    532 	return 0;
    533 }
    534 
    535 static int
    536 is_in_range(struct f_data *f, int y, int yw, unsigned char *txt, int *min,
    537             int *max)
    538 {
    539 	int utf8 = f->opt.cp == 0;
    540 	int found = 0;
    541 	int l;
    542 	int s1, s2;
    543 	*min = INT_MAX;
    544 	*max = 0;
    545 
    546 	l = strlen((char *)txt);
    547 
    548 	if (get_range(f, y, yw, l, &s1, &s2))
    549 		return 0;
    550 	for (; s1 <= s2; s1++) {
    551 		int i;
    552 		if (!utf8) {
    553 			if (f->search_chr[s1] != txt[0])
    554 				continue;
    555 			for (i = 1; i < l; i++)
    556 				if (f->search_chr[s1 + i] != txt[i])
    557 					goto cont;
    558 		} else {
    559 			unsigned char *tt = txt;
    560 			for (i = 0; i < l; i++) {
    561 				unsigned cc;
    562 				GET_UTF_8(tt, cc);
    563 				if (f->search_chr[s1 + i] != cc)
    564 					goto cont;
    565 			}
    566 		}
    567 		for (i = 0; i < l; i++) {
    568 			struct search *sr = search_lookup(f, s1 + i);
    569 			if (sr->y >= y && sr->y < y + yw && sr->n)
    570 				goto in_view;
    571 		}
    572 		continue;
    573 in_view:
    574 		found = 1;
    575 		for (i = 0; i < l; i++) {
    576 			struct search *sr = search_lookup(f, s1 + i);
    577 			if (sr->n) {
    578 				if (sr->x < *min)
    579 					*min = sr->x;
    580 				if (sr->x + sr->n > *max)
    581 					*max = sr->x + sr->n;
    582 			}
    583 		}
    584 cont:;
    585 	}
    586 	return found;
    587 }
    588 
    589 static int
    590 get_searched(struct f_data_c *scr, struct point **pt, int *pl)
    591 {
    592 	struct f_data *f = scr->f_data;
    593 	int xp = scr->xp;
    594 	int yp = scr->yp;
    595 	int xw = scr->xw;
    596 	int yw = scr->yw;
    597 	int vx = scr->vs->view_posx;
    598 	int vy = scr->vs->view_pos;
    599 	int s1, s2;
    600 	int l;
    601 	unsigned c;
    602 	struct point *points = NULL;
    603 	int len = 0;
    604 	unsigned char *ww;
    605 	unsigned char *w = scr->ses->search_word;
    606 	if (!w || !*w)
    607 		return -1;
    608 	if (get_search_data(f) < 0) {
    609 		free(scr->ses->search_word);
    610 		scr->ses->search_word = NULL;
    611 		return -1;
    612 	}
    613 	l = strlen((char *)w);
    614 	ww = w;
    615 	GET_UTF_8(ww, c);
    616 	if (get_range(f, scr->vs->view_pos, scr->yw, l, &s1, &s2))
    617 		goto ret;
    618 	for (; s1 <= s2; s1++) {
    619 		int i, j;
    620 		if (f->search_chr[s1] != c) {
    621 c:
    622 			continue;
    623 		}
    624 		ww = w;
    625 		for (i = 0; i < l; i++) {
    626 			unsigned cc;
    627 			GET_UTF_8(ww, cc);
    628 			if (f->search_chr[s1 + i] != cc)
    629 				goto c;
    630 		}
    631 		for (i = 0; i < l; i++) {
    632 			struct search *sr = search_lookup(f, s1 + i);
    633 			for (j = 0; j < sr->n; j++) {
    634 				int x = sr->x + j + xp - vx;
    635 				int y = sr->y + yp - vy;
    636 				if (x >= xp && y >= yp && x < xp + xw
    637 				    && y < yp + yw) {
    638 					if (!(len & (ALLOC_GR - 1))) {
    639 						struct point *points2;
    640 						if ((unsigned)len
    641 						    > INT_MAX
    642 						              / sizeof(
    643 								  struct point)
    644 						          - ALLOC_GR)
    645 							goto ret;
    646 						points2 = xrealloc(
    647 						    points,
    648 						    sizeof(struct point)
    649 							* (len + ALLOC_GR));
    650 						if (!points2)
    651 							goto ret;
    652 						points = points2;
    653 					}
    654 					points[len].x = sr->x + j;
    655 					points[len++].y = sr->y;
    656 				}
    657 			}
    658 		}
    659 	}
    660 ret:
    661 	*pt = points;
    662 	*pl = len;
    663 	return 0;
    664 }
    665 
    666 static void
    667 draw_searched(struct terminal *t, struct f_data_c *scr)
    668 {
    669 	int xp = scr->xp;
    670 	int yp = scr->yp;
    671 	int vx = scr->vs->view_posx;
    672 	int vy = scr->vs->view_pos;
    673 	struct point *pt;
    674 	int len, i;
    675 	if (get_searched(scr, &pt, &len) < 0)
    676 		return;
    677 	for (i = 0; i < len; i++) {
    678 		int x = pt[i].x + xp - vx, y = pt[i].y + yp - vy;
    679 		const chr *co;
    680 		unsigned char nco;
    681 		co = get_char(t, x, y);
    682 		nco = ((co->at >> 3) & 0x07) | ((co->at << 3) & 0x38);
    683 		set_color(t, x, y, nco);
    684 	}
    685 	free(pt);
    686 }
    687 
    688 static void
    689 draw_current_link(struct terminal *t, struct f_data_c *scr)
    690 {
    691 	draw_link(t, scr, scr->vs->current_link);
    692 	draw_searched(t, scr);
    693 }
    694 
    695 static struct link *
    696 get_first_link(struct f_data_c *f)
    697 {
    698 	int i;
    699 	struct link *l = f->f_data->links + f->f_data->nlinks;
    700 	for (i = f->vs->view_pos; i < f->vs->view_pos + f->yw; i++)
    701 		if (i >= 0 && i < f->f_data->y && f->f_data->lines1[i]
    702 		    && f->f_data->lines1[i] < l)
    703 			l = f->f_data->lines1[i];
    704 	if (l == f->f_data->links + f->f_data->nlinks)
    705 		l = NULL;
    706 	return l;
    707 }
    708 
    709 static struct link *
    710 get_last_link(struct f_data_c *f)
    711 {
    712 	int i;
    713 	struct link *l = NULL;
    714 	for (i = f->vs->view_pos; i < f->vs->view_pos + f->yw; i++)
    715 		if (i >= 0 && i < f->f_data->y && f->f_data->lines2[i]
    716 		    && (!l || f->f_data->lines2[i] > l))
    717 			l = f->f_data->lines2[i];
    718 	return l;
    719 }
    720 
    721 void
    722 fixup_select_state(struct form_control *fc, struct form_state *fs)
    723 {
    724 	int inited = 0;
    725 	int i;
    726 retry:
    727 	if (fs->state >= 0 && fs->state < fc->nvalues
    728 	    && !strcmp(cast_const_char fc->values[fs->state],
    729 	               cast_const_char fs->string))
    730 		return;
    731 	for (i = 0; i < fc->nvalues; i++) {
    732 		if (!strcmp(cast_const_char fc->values[i],
    733 		            cast_const_char fs->string)) {
    734 			fs->state = i;
    735 			return;
    736 		}
    737 	}
    738 	if (!inited) {
    739 		init_ctrl(fc, fs);
    740 		inited = 1;
    741 		goto retry;
    742 	}
    743 	fs->state = 0;
    744 	free(fs->string);
    745 	if (fc->nvalues)
    746 		fs->string = stracpy(fc->values[0]);
    747 	else
    748 		fs->string = stracpy(cast_uchar "");
    749 }
    750 
    751 static void
    752 init_ctrl(struct form_control *form, struct form_state *fs)
    753 {
    754 	free(fs->string);
    755 	fs->string = NULL;
    756 	switch (form->type) {
    757 	case FC_TEXT:
    758 	case FC_PASSWORD:
    759 	case FC_TEXTAREA:
    760 		fs->string = stracpy(form->default_value);
    761 		fs->state = (int)strlen(cast_const_char form->default_value);
    762 		fs->vpos = 0;
    763 		break;
    764 	case FC_FILE_UPLOAD:
    765 		fs->string = stracpy(cast_uchar "");
    766 		fs->state = 0;
    767 		fs->vpos = 0;
    768 		break;
    769 	case FC_CHECKBOX:
    770 	case FC_RADIO:
    771 		fs->state = form->default_state;
    772 		break;
    773 	case FC_SELECT:
    774 		fs->string = stracpy(form->default_value);
    775 		fs->state = form->default_state;
    776 		fixup_select_state(form, fs);
    777 		break;
    778 	}
    779 }
    780 
    781 struct form_state *
    782 find_form_state(struct f_data_c *f, struct form_control *form)
    783 {
    784 	struct view_state *vs = f->vs;
    785 	struct form_state *fs;
    786 	int n = form->g_ctrl_num;
    787 	if (n < vs->form_info_len)
    788 		fs = &vs->form_info[n];
    789 	else {
    790 		if ((unsigned)n > INT_MAX / sizeof(struct form_state) - 1)
    791 			overalloc();
    792 		fs = xrealloc(vs->form_info,
    793 		              (n + 1) * sizeof(struct form_state));
    794 		vs->form_info = fs;
    795 		memset(fs + vs->form_info_len, 0,
    796 		       (n + 1 - vs->form_info_len) * sizeof(struct form_state));
    797 		vs->form_info_len = n + 1;
    798 		fs = &vs->form_info[n];
    799 	}
    800 	if (fs->form_num == form->form_num && fs->ctrl_num == form->ctrl_num
    801 	    && fs->g_ctrl_num == form->g_ctrl_num
    802 	    && /*fs->position == form->position &&*/ fs->type == form->type)
    803 		return fs;
    804 	free_format_text_cache_entry(fs);
    805 	free(fs->string);
    806 	memset(fs, 0, sizeof(struct form_state));
    807 	fs->form_num = form->form_num;
    808 	fs->ctrl_num = form->ctrl_num;
    809 	fs->g_ctrl_num = form->g_ctrl_num;
    810 	fs->position = form->position;
    811 	fs->type = form->type;
    812 	init_ctrl(form, fs);
    813 	return fs;
    814 }
    815 
    816 static void
    817 draw_form_entry(struct terminal *t, struct f_data_c *f, struct link *l)
    818 {
    819 	int xp = f->xp;
    820 	int yp = f->yp;
    821 	int xw = f->xw;
    822 	int yw = f->yw;
    823 	struct view_state *vs = f->vs;
    824 	int vx = vs->view_posx;
    825 	int vy = vs->view_pos;
    826 	struct form_state *fs;
    827 	struct form_control *form = l->form;
    828 	int i, x, y, td;
    829 	size_t sl;
    830 
    831 	if (!form) {
    832 		internal("link %d has no form", (int)(l - f->f_data->links));
    833 		return;
    834 	}
    835 	fs = find_form_state(f, form);
    836 	switch (form->type) {
    837 		unsigned char *s;
    838 		struct format_text_cache_entry *ftce;
    839 		int lid;
    840 
    841 	case FC_TEXT:
    842 	case FC_PASSWORD:
    843 	case FC_FILE_UPLOAD:
    844 		if ((size_t)fs->vpos > strlen(cast_const_char fs->string))
    845 			fs->vpos = (int)strlen(cast_const_char fs->string);
    846 		sl = strlen((char *)fs->string);
    847 		td = textptr_diff(fs->string + fs->state, fs->string + fs->vpos,
    848 		                  f->f_data->opt.cp);
    849 
    850 		while (fs->vpos < sl && td >= form->size) {
    851 			unsigned char *p = fs->string + fs->vpos;
    852 			FWD_UTF_8(p);
    853 			fs->vpos = (int)(p - fs->string);
    854 			td--;
    855 		}
    856 		while (fs->vpos > fs->state) {
    857 			unsigned char *p = fs->string + fs->vpos;
    858 			BACK_UTF_8(p, fs->string);
    859 			fs->vpos = (int)(p - fs->string);
    860 		}
    861 		if (!l->n)
    862 			break;
    863 		x = l->pos[0].x + xp - vx;
    864 		y = l->pos[0].y + yp - vy;
    865 		s = fs->string + fs->vpos;
    866 		for (i = 0; i < form->size; i++, x++) {
    867 			unsigned ch;
    868 			if (!*s) {
    869 				ch = '_';
    870 			} else {
    871 				if (f->f_data->opt.cp) {
    872 					ch = *s++;
    873 				} else {
    874 					GET_UTF_8(s, ch);
    875 				}
    876 				if (form->type == FC_PASSWORD) {
    877 					ch = '*';
    878 				}
    879 			}
    880 			if (x >= xp && y >= yp && x < xp + xw && y < yp + yw) {
    881 				set_only_char(t, x, y, ch, 0);
    882 			}
    883 		}
    884 		break;
    885 	case FC_TEXTAREA:
    886 		if (!l->n)
    887 			break;
    888 		x = l->pos[0].x + xp - vx;
    889 		y = l->pos[0].y + yp - vy;
    890 		area_cursor(f, form, fs);
    891 		ftce = format_text(f, form, fs);
    892 		lid = fs->vypos;
    893 		for (; lid < ftce->n_lines
    894 		       && y < l->pos[0].y + yp - vy + form->rows;
    895 		     lid++, y++) {
    896 			s = textptr_add(fs->string, ftce->ln[lid].st_offs,
    897 			                f->f_data->opt.cp);
    898 			for (i = 0; i < form->cols; i++) {
    899 				unsigned ch;
    900 				if (s >= fs->string + ftce->ln[lid].en_offs) {
    901 					ch = '_';
    902 				} else {
    903 					if (f->f_data->opt.cp) {
    904 						ch = *s++;
    905 					} else {
    906 						GET_UTF_8(s, ch);
    907 					}
    908 				}
    909 				if (x + i >= xp && y >= yp && x + i < xp + xw
    910 				    && y < yp + yw) {
    911 					set_only_char(t, x + i, y, ch, 0);
    912 				}
    913 			}
    914 		}
    915 		for (; y < l->pos[0].y + yp - vy + form->rows; y++) {
    916 			for (i = 0; i < form->cols; i++) {
    917 				if (x + i >= xp && y >= yp && x + i < xp + xw
    918 				    && y < yp + yw)
    919 					set_only_char(t, x + i, y, '_', 0);
    920 			}
    921 		}
    922 
    923 		break;
    924 	case FC_CHECKBOX:
    925 		if (l->n < 2)
    926 			break;
    927 		x = l->pos[1].x + xp - vx;
    928 		y = l->pos[1].y + yp - vy;
    929 		if (x >= xp && y >= yp && x < xp + xw && y < yp + yw)
    930 			set_only_char(t, x, y, fs->state ? 'X' : ' ', 0);
    931 		break;
    932 	case FC_RADIO:
    933 		if (l->n < 2)
    934 			break;
    935 		x = l->pos[1].x + xp - vx;
    936 		y = l->pos[1].y + yp - vy;
    937 		if (x >= xp && y >= yp && x < xp + xw && y < yp + yw)
    938 			set_only_char(t, x, y, fs->state ? 'X' : ' ', 0);
    939 		break;
    940 	case FC_SELECT:
    941 		fixup_select_state(form, fs);
    942 		s = fs->state < form->nvalues ? form->labels[fs->state] : NULL;
    943 		if (!s)
    944 			s = cast_uchar "";
    945 		for (i = 0; i < l->n; i++) {
    946 			unsigned chr;
    947 			if (!*s)
    948 				chr = '_';
    949 			else
    950 				GET_UTF_8(s, chr);
    951 			x = l->pos[i].x + xp - vx;
    952 			y = l->pos[i].y + yp - vy;
    953 			if (x >= xp && y >= yp && x < xp + xw && y < yp + yw)
    954 				set_only_char(t, x, y, chr, 0);
    955 		}
    956 		break;
    957 	case FC_SUBMIT:
    958 	case FC_IMAGE:
    959 	case FC_RESET:
    960 	case FC_HIDDEN:
    961 	case FC_BUTTON:
    962 		break;
    963 	}
    964 }
    965 
    966 struct xdfe {
    967 	struct f_data_c *f;
    968 	struct link *l;
    969 };
    970 
    971 static void
    972 y_draw_form_entry(struct terminal *t, void *x_)
    973 {
    974 	struct xdfe *x = (struct xdfe *)x_;
    975 	draw_form_entry(t, x->f, x->l);
    976 }
    977 
    978 static void
    979 x_draw_form_entry(struct session *ses, struct f_data_c *f, struct link *l)
    980 {
    981 	struct xdfe x;
    982 	x.f = f;
    983 	x.l = l;
    984 	draw_to_window(ses->win, y_draw_form_entry, &x);
    985 }
    986 
    987 static void
    988 draw_forms(struct terminal *t, struct f_data_c *f)
    989 {
    990 	struct link *l1 = get_first_link(f);
    991 	struct link *l2 = get_last_link(f);
    992 	if (!l1 || !l2) {
    993 		if (l1 || l2)
    994 			internal("get_first_link == %p, get_last_link == %p",
    995 			         (void *)l1, (void *)l2);
    996 		return;
    997 	}
    998 	do {
    999 		if (l1->type != L_LINK)
   1000 			draw_form_entry(t, f, l1);
   1001 	} while (l1++ < l2);
   1002 }
   1003 
   1004 /* 0 -> 1 <- 2 v 3 ^ */
   1005 
   1006 static unsigned char fr_trans[2][4] = {
   1007 	{0xb3,  0xc3, 0xb4, 0xc5},
   1008         { 0xc4, 0xc2, 0xc1, 0xc5}
   1009 };
   1010 
   1011 static void
   1012 set_xchar(struct terminal *t, int x, int y, unsigned dir)
   1013 {
   1014 	const chr *co;
   1015 	if (x < 0 || x >= t->x || y < 0 || y >= t->y)
   1016 		return;
   1017 	co = get_char(t, x, y);
   1018 	if (!(co->at & ATTR_FRAME))
   1019 		return;
   1020 	if (co->ch == fr_trans[dir / 2][0])
   1021 		set_only_char(t, x, y, fr_trans[dir / 2][1 + (dir & 1)],
   1022 		              ATTR_FRAME);
   1023 	else if (co->ch == fr_trans[dir / 2][2 - (dir & 1)])
   1024 		set_only_char(t, x, y, fr_trans[dir / 2][3], ATTR_FRAME);
   1025 }
   1026 
   1027 static void
   1028 draw_frame_lines(struct session *ses, struct frameset_desc *fsd, int xp, int yp)
   1029 {
   1030 	struct terminal *t = ses->term;
   1031 	int i, j;
   1032 	int x, y;
   1033 	if (!fsd)
   1034 		return;
   1035 	y = yp - 1;
   1036 	for (j = 0; j < fsd->y; j++) {
   1037 		int wwy = fsd->f[j * fsd->x].yw;
   1038 		x = xp - 1;
   1039 		for (i = 0; i < fsd->x; i++) {
   1040 			int wwx = fsd->f[i].xw;
   1041 			if (i) {
   1042 				fill_area(t, x, y + 1, 1, wwy, 179,
   1043 				          ATTR_FRAME
   1044 				              | get_session_attribute(ses, 0));
   1045 				if (j == fsd->y - 1)
   1046 					set_xchar(t, x, y + wwy + 1, 3);
   1047 			} else if (j)
   1048 				set_xchar(t, x, y, 0);
   1049 			if (j) {
   1050 				fill_area(t, x + 1, y, wwx, 1, 196,
   1051 				          ATTR_FRAME
   1052 				              | get_session_attribute(ses, 0));
   1053 				if (i == fsd->x - 1)
   1054 					set_xchar(t, x + wwx + 1, y, 1);
   1055 			} else if (i)
   1056 				set_xchar(t, x, y, 2);
   1057 			if (i && j)
   1058 				set_char(t, x, y, 197,
   1059 				         ATTR_FRAME
   1060 				             | get_session_attribute(ses, 0));
   1061 			/*if (fsd->f[j * fsd->x + i].subframe) {
   1062 			        draw_frame_lines(ses, fsd->f[j * fsd->x +
   1063 			i].subframe, x + 1, y + 1);
   1064 			}*/
   1065 			x += wwx + 1;
   1066 		}
   1067 		y += wwy + 1;
   1068 	}
   1069 }
   1070 
   1071 void
   1072 draw_doc(struct terminal *t, void *scr_)
   1073 {
   1074 	struct f_data_c *scr = (struct f_data_c *)scr_;
   1075 	struct session *ses = scr->ses;
   1076 	int active = scr->active;
   1077 	int n, y;
   1078 	int xp = scr->xp;
   1079 	int yp = scr->yp;
   1080 	int xw = scr->xw;
   1081 	int yw = scr->yw;
   1082 	struct view_state *vs;
   1083 	int vx, vy;
   1084 	if (!scr->vs || !scr->f_data) {
   1085 		if (active) {
   1086 			if (!scr->parent)
   1087 				set_cursor(t, 0, 0, 0, 0);
   1088 			else
   1089 				set_cursor(t, xp, yp, xp, yp);
   1090 		}
   1091 		fill_area(t, xp, yp, xw, yw, ' ',
   1092 		          get_session_attribute(ses, 0));
   1093 		if (active)
   1094 			set_window_ptr(ses->win, xp, yp);
   1095 		return;
   1096 	}
   1097 	if (active) {
   1098 		set_cursor(t, xp + xw - 1, yp + yw - 1, xp + xw - 1,
   1099 		           yp + yw - 1);
   1100 		set_window_ptr(ses->win, xp, yp);
   1101 	}
   1102 	check_vs(scr);
   1103 	if (scr->f_data->frame_desc) {
   1104 		struct f_data_c *f = NULL;
   1105 		struct list_head *lf;
   1106 		fill_area(t, xp, yp, xw, yw, ' ',
   1107 		          scr->f_data->y ? scr->f_data->bg : 0);
   1108 		draw_frame_lines(ses, scr->f_data->frame_desc, xp, yp);
   1109 		n = 0;
   1110 		foreach (struct f_data_c, f, lf, scr->subframes) {
   1111 			f->active = active && n++ == scr->vs->frame_pos;
   1112 			draw_doc(t, f);
   1113 		}
   1114 		return;
   1115 	}
   1116 	vs = scr->vs;
   1117 	if (scr->goto_position
   1118 	    && (vy = find_tag(scr->f_data, scr->goto_position)) != -1) {
   1119 		if (vy > scr->f_data->y)
   1120 			vy = scr->f_data->y - 1;
   1121 		if (vy < 0)
   1122 			vy = 0;
   1123 		vs->view_pos = vy;
   1124 		vs->orig_view_pos = vy;
   1125 		vs->view_posx = 0;
   1126 		vs->orig_view_posx = 0;
   1127 		set_link(scr);
   1128 		free(scr->went_to_position);
   1129 		scr->went_to_position = scr->goto_position;
   1130 		scr->goto_position = NULL;
   1131 	}
   1132 	if (vs->view_pos != vs->orig_view_pos
   1133 	    || vs->view_posx != vs->orig_view_posx
   1134 	    || vs->current_link != vs->orig_link) {
   1135 		int ol;
   1136 		vs->view_pos = vs->orig_view_pos;
   1137 		vs->view_posx = vs->orig_view_posx;
   1138 		vs->brl_x = vs->orig_brl_x;
   1139 		vs->brl_y = vs->orig_brl_y;
   1140 		ol = vs->orig_link;
   1141 		if (ol < scr->f_data->nlinks)
   1142 			vs->current_link = ol;
   1143 		while (vs->view_pos >= scr->f_data->y)
   1144 			vs->view_pos -= yw ? yw : 1;
   1145 		if (vs->view_pos < 0)
   1146 			vs->view_pos = 0;
   1147 		set_link(scr);
   1148 		check_vs(scr);
   1149 		vs->orig_link = ol;
   1150 	}
   1151 	vx = vs->view_posx;
   1152 	vy = vs->view_pos;
   1153 	if (scr->xl == vx && scr->yl == vy && scr->xl != -1
   1154 	    && !ses->search_word) {
   1155 		clear_link(t, scr);
   1156 		draw_forms(t, scr);
   1157 		if (active)
   1158 			draw_current_link(t, scr);
   1159 		return;
   1160 	}
   1161 	free_link(scr);
   1162 	scr->xl = vx;
   1163 	scr->yl = vy;
   1164 	fill_area(t, xp, yp, xw, yw, ' ',
   1165 	          scr->f_data->y ? scr->f_data->bg
   1166 	                         : get_session_attribute(ses, 0));
   1167 	if (!scr->f_data->y)
   1168 		return;
   1169 	while (vs->view_pos >= scr->f_data->y)
   1170 		vs->view_pos -= yw ? yw : 1;
   1171 	if (vs->view_pos < 0)
   1172 		vs->view_pos = 0;
   1173 	if (vy != vs->view_pos) {
   1174 		vy = vs->view_pos;
   1175 		check_vs(scr);
   1176 	}
   1177 	for (y = vy <= 0 ? 0 : vy;
   1178 	     y < (-vy + scr->f_data->y <= yw ? scr->f_data->y : yw + vy); y++) {
   1179 		int st = vx <= 0 ? 0 : vx;
   1180 		int en = -vx + scr->f_data->data[y].l <= xw
   1181 		             ? scr->f_data->data[y].l
   1182 		             : xw + vx;
   1183 		set_line(t, xp + st - vx, yp + y - vy, en - st,
   1184 		         &scr->f_data->data[y].d[st]);
   1185 	}
   1186 	draw_forms(t, scr);
   1187 	if (active)
   1188 		draw_current_link(t, scr);
   1189 	if (ses->search_word)
   1190 		scr->xl = scr->yl = -1;
   1191 }
   1192 
   1193 static void
   1194 clr_xl(struct f_data_c *fd)
   1195 {
   1196 	struct f_data_c *fdd = NULL;
   1197 	struct list_head *lfdd;
   1198 	fd->xl = fd->yl = -1;
   1199 	foreach (struct f_data_c, fdd, lfdd, fd->subframes)
   1200 		clr_xl(fdd);
   1201 }
   1202 
   1203 static void
   1204 draw_doc_c(struct terminal *t, void *scr_)
   1205 {
   1206 	struct f_data_c *scr = (struct f_data_c *)scr_;
   1207 	clr_xl(scr);
   1208 	draw_doc(t, scr);
   1209 }
   1210 
   1211 void
   1212 draw_formatted(struct session *ses)
   1213 {
   1214 	/*clr_xl(ses->screen);*/
   1215 	ses->screen->active = 1;
   1216 	draw_to_window(ses->win, draw_doc_c, ses->screen);
   1217 	change_screen_status(ses);
   1218 	print_screen_status(ses);
   1219 }
   1220 
   1221 void
   1222 draw_fd(struct f_data_c *f)
   1223 {
   1224 	if (f->f_data)
   1225 		f->f_data->time_to_draw = -get_time();
   1226 	f->active = is_active_frame(f->ses, f);
   1227 	draw_to_window(f->ses->win, draw_doc_c, f);
   1228 	change_screen_status(f->ses);
   1229 	print_screen_status(f->ses);
   1230 	if (f->f_data)
   1231 		f->f_data->time_to_draw += get_time();
   1232 }
   1233 
   1234 static void
   1235 draw_fd_nrd(struct f_data_c *f)
   1236 {
   1237 	f->active = is_active_frame(f->ses, f);
   1238 	draw_to_window(f->ses->win, draw_doc, f);
   1239 	change_screen_status(f->ses);
   1240 	print_screen_status(f->ses);
   1241 }
   1242 
   1243 #define D_BUF 65536
   1244 
   1245 int
   1246 dump_to_file(struct f_data *fd, int h)
   1247 {
   1248 	int x, y;
   1249 	unsigned char *buf;
   1250 	int bptr = 0;
   1251 	int retval;
   1252 	buf = xmalloc(D_BUF);
   1253 	for (y = 0; y < fd->y; y++)
   1254 		for (x = 0; x <= fd->data[y].l; x++) {
   1255 			unsigned c;
   1256 			if (x == fd->data[y].l)
   1257 				c = '\n';
   1258 			else {
   1259 				c = fd->data[y].d[x].ch;
   1260 				if (c == 1)
   1261 					c = ' ';
   1262 				if (fd->data[y].d[x].at & ATTR_FRAME && c >= 176
   1263 				    && c < 224)
   1264 					c = frame_dumb[c - 176];
   1265 			}
   1266 			if (!fd->opt.cp && c >= 0x80) {
   1267 				unsigned char *enc = encode_utf_8(c);
   1268 				strcpy(cast_char(buf + bptr),
   1269 				       cast_const_char enc);
   1270 				bptr += (int)strlen(cast_const_char enc);
   1271 			} else
   1272 				buf[bptr++] = (unsigned char)c;
   1273 			if (bptr >= D_BUF - 7) {
   1274 				if ((retval = hard_write(h, buf, bptr))
   1275 				    != bptr) {
   1276 					free(buf);
   1277 					goto fail;
   1278 				}
   1279 				bptr = 0;
   1280 			}
   1281 		}
   1282 	if ((retval = hard_write(h, buf, bptr)) != bptr) {
   1283 		free(buf);
   1284 		goto fail;
   1285 	}
   1286 	free(buf);
   1287 	if (fd->opt.num_links && fd->nlinks) {
   1288 		static const unsigned char head[] = "\nLinks:\n";
   1289 		int i;
   1290 		if ((retval =
   1291 		         hard_write(h, head, (int)strlen(cast_const_char head)))
   1292 		    != (int)strlen(cast_const_char head))
   1293 			goto fail;
   1294 		for (i = 0; i < fd->nlinks; i++) {
   1295 			struct form_control *fc;
   1296 			struct link *lnk = &fd->links[i];
   1297 			unsigned char *s = NULL;
   1298 			size_t l;
   1299 			l = add_num_to_str(&s, 0, i + 1);
   1300 			l = add_to_str(&s, l, cast_uchar ". ");
   1301 			if (lnk->where) {
   1302 				l = add_to_str(&s, l, lnk->where);
   1303 			} else if (lnk->where_img) {
   1304 				l = add_to_str(&s, l, cast_uchar "Image: ");
   1305 				l = add_to_str(&s, l, lnk->where_img);
   1306 			} else if (lnk->type == L_BUTTON) {
   1307 				fc = lnk->form;
   1308 				if (fc->type == FC_RESET)
   1309 					l = add_to_str(&s, l,
   1310 					               cast_uchar "Reset form");
   1311 				else if (fc->type == FC_BUTTON || !fc->action)
   1312 					l = add_to_str(&s, l,
   1313 					               cast_uchar "Button");
   1314 				else {
   1315 					if (fc->method == FM_GET)
   1316 						l = add_to_str(&s, l,
   1317 						               cast_uchar
   1318 						               "Submit form: ");
   1319 					else
   1320 						l = add_to_str(&s, l,
   1321 						               cast_uchar
   1322 						               "Post form: ");
   1323 					l = add_to_str(&s, l, fc->action);
   1324 				}
   1325 			} else if (lnk->type == L_CHECKBOX
   1326 			           || lnk->type == L_SELECT
   1327 			           || lnk->type == L_FIELD
   1328 			           || lnk->type == L_AREA) {
   1329 				fc = lnk->form;
   1330 				switch (fc->type) {
   1331 				case FC_RADIO:
   1332 					l = add_to_str(
   1333 					    &s, l, cast_uchar "Radio button");
   1334 					break;
   1335 				case FC_CHECKBOX:
   1336 					l = add_to_str(&s, l,
   1337 					               cast_uchar "Checkbox");
   1338 					break;
   1339 				case FC_SELECT:
   1340 					l = add_to_str(
   1341 					    &s, l, cast_uchar "Select field");
   1342 					break;
   1343 				case FC_TEXT:
   1344 					l = add_to_str(&s, l,
   1345 					               cast_uchar "Text field");
   1346 					break;
   1347 				case FC_TEXTAREA:
   1348 					l = add_to_str(&s, l,
   1349 					               cast_uchar "Text area");
   1350 					break;
   1351 				case FC_FILE_UPLOAD:
   1352 					l = add_to_str(
   1353 					    &s, l, cast_uchar "File upload");
   1354 					break;
   1355 				case FC_PASSWORD:
   1356 					l = add_to_str(
   1357 					    &s, l, cast_uchar "Password field");
   1358 					break;
   1359 				default:
   1360 					goto unknown;
   1361 				}
   1362 				if (fc->name && fc->name[0]) {
   1363 					l = add_to_str(&s, l,
   1364 					               cast_uchar ", Name ");
   1365 					l = add_to_str(&s, l, fc->name);
   1366 				}
   1367 				if ((fc->type == FC_CHECKBOX
   1368 				     || fc->type == FC_RADIO)
   1369 				    && fc->default_value
   1370 				    && fc->default_value[0]) {
   1371 					l = add_to_str(&s, l,
   1372 					               cast_uchar ", Value ");
   1373 					l = add_to_str(&s, l,
   1374 					               fc->default_value);
   1375 				}
   1376 			}
   1377 unknown:
   1378 			l = add_chr_to_str(&s, l, '\n');
   1379 			if ((retval = hard_write(h, s, l)) != l) {
   1380 				free(s);
   1381 				goto fail;
   1382 			}
   1383 			free(s);
   1384 		}
   1385 	}
   1386 	return 0;
   1387 
   1388 fail:
   1389 	if (retval < 0)
   1390 		return get_error_from_errno(errno);
   1391 	else
   1392 		return S_CANT_WRITE;
   1393 }
   1394 
   1395 static int
   1396 in_viewx(struct f_data_c *f, struct link *l)
   1397 {
   1398 	int i;
   1399 	for (i = 0; i < l->n; i++) {
   1400 		if (l->pos[i].x >= f->vs->view_posx
   1401 		    && l->pos[i].x < f->vs->view_posx + f->xw)
   1402 			return 1;
   1403 	}
   1404 	return 0;
   1405 }
   1406 
   1407 static int
   1408 in_viewy(struct f_data_c *f, struct link *l)
   1409 {
   1410 	int i;
   1411 	for (i = 0; i < l->n; i++) {
   1412 		if (l->pos[i].y >= f->vs->view_pos
   1413 		    && l->pos[i].y < f->vs->view_pos + f->yw)
   1414 			return 1;
   1415 	}
   1416 	return 0;
   1417 }
   1418 
   1419 static int
   1420 in_view(struct f_data_c *f, struct link *l)
   1421 {
   1422 	return in_viewy(f, l) && in_viewx(f, l);
   1423 }
   1424 
   1425 static int
   1426 c_in_view(struct f_data_c *f)
   1427 {
   1428 	return f->vs->current_link != -1
   1429 	       && in_view(f, &f->f_data->links[f->vs->current_link]);
   1430 }
   1431 
   1432 static int
   1433 next_in_view(struct f_data_c *f, int p, int d,
   1434              int (*fn)(struct f_data_c *, struct link *),
   1435              void (*cntr)(struct f_data_c *, struct link *))
   1436 {
   1437 	int p1 = f->f_data->nlinks - 1;
   1438 	int p2 = 0;
   1439 	int y;
   1440 	int yl = f->vs->view_pos + f->yw;
   1441 	if (yl > f->f_data->y)
   1442 		yl = f->f_data->y;
   1443 	for (y = f->vs->view_pos < 0 ? 0 : f->vs->view_pos; y < yl; y++) {
   1444 		if (f->f_data->lines1[y]
   1445 		    && f->f_data->lines1[y] - f->f_data->links < p1)
   1446 			p1 = (int)(f->f_data->lines1[y] - f->f_data->links);
   1447 		if (f->f_data->lines2[y]
   1448 		    && f->f_data->lines2[y] - f->f_data->links > p2)
   1449 			p2 = (int)(f->f_data->lines2[y] - f->f_data->links);
   1450 	}
   1451 	while (p >= p1 && p <= p2) {
   1452 		if (fn(f, &f->f_data->links[p])) {
   1453 			f->vs->current_link = p;
   1454 			f->vs->orig_link = f->vs->current_link;
   1455 			if (cntr)
   1456 				cntr(f, &f->f_data->links[p]);
   1457 			return 1;
   1458 		}
   1459 		p += d;
   1460 	}
   1461 	f->vs->current_link = -1;
   1462 	f->vs->orig_link = f->vs->current_link;
   1463 	return 0;
   1464 }
   1465 
   1466 static void
   1467 set_pos_x(struct f_data_c *f, struct link *l)
   1468 {
   1469 	int i;
   1470 	int xm = 0;
   1471 	int xl = INT_MAX;
   1472 	for (i = 0; i < l->n; i++) {
   1473 		if (l->pos[i].y >= f->vs->view_pos
   1474 		    && l->pos[i].y < f->vs->view_pos + f->yw) {
   1475 			if (l->pos[i].x >= xm)
   1476 				xm = l->pos[i].x + 1;
   1477 			if (l->pos[i].x < xl)
   1478 				xl = l->pos[i].x;
   1479 		}
   1480 	}
   1481 	if (xl == INT_MAX)
   1482 		return;
   1483 	if (f->vs->view_posx + f->xw < xm)
   1484 		f->vs->view_posx = xm - f->xw;
   1485 	if (f->vs->view_posx > xl)
   1486 		f->vs->view_posx = xl;
   1487 	f->vs->orig_view_posx = f->vs->view_posx;
   1488 }
   1489 
   1490 static void
   1491 set_pos_y(struct f_data_c *f, struct link *l)
   1492 {
   1493 	int i;
   1494 	int ym = 0;
   1495 	int yl = f->f_data->y;
   1496 	for (i = 0; i < l->n; i++) {
   1497 		if (l->pos[i].y >= ym)
   1498 			ym = l->pos[i].y + 1;
   1499 		if (l->pos[i].y < yl)
   1500 			yl = l->pos[i].y;
   1501 	}
   1502 	if ((f->vs->view_pos = (ym + yl) / 2 - f->yw / 2)
   1503 	    > f->f_data->y - f->yw)
   1504 		f->vs->view_pos = f->f_data->y - f->yw;
   1505 	if (f->vs->view_pos < 0)
   1506 		f->vs->view_pos = 0;
   1507 	f->vs->orig_view_pos = f->vs->view_pos;
   1508 }
   1509 
   1510 static void
   1511 find_link(struct f_data_c *f, int p, int s)
   1512 { /* p=1 - top, p=-1 - bottom, s=0 - pgdn, s=1 - down */
   1513 	int y;
   1514 	int l;
   1515 	struct link *link;
   1516 	struct link **line;
   1517 	line = p == -1 ? f->f_data->lines2 : f->f_data->lines1;
   1518 	if (p == -1) {
   1519 		y = f->vs->view_pos + f->yw - 1;
   1520 		if (y >= f->f_data->y)
   1521 			y = f->f_data->y - 1;
   1522 	} else {
   1523 		y = f->vs->view_pos;
   1524 		if (y < 0)
   1525 			y = 0;
   1526 	}
   1527 	if (y < 0 || y >= f->f_data->y)
   1528 		goto nolink;
   1529 	link = NULL;
   1530 	do {
   1531 		if (line[y]
   1532 		    && (!link || (p > 0 ? line[y] < link : line[y] > link)))
   1533 			link = line[y];
   1534 		y += p;
   1535 	} while (!(y < 0 || y < f->vs->view_pos || y >= f->vs->view_pos + f->yw
   1536 	           || y >= f->f_data->y));
   1537 	if (!link)
   1538 		goto nolink;
   1539 	l = (int)(link - f->f_data->links);
   1540 	if (s == 0) {
   1541 		next_in_view(f, l, p, in_view, NULL);
   1542 		return;
   1543 	}
   1544 	f->vs->current_link = l;
   1545 	f->vs->orig_link = f->vs->current_link;
   1546 	set_pos_x(f, link);
   1547 	return;
   1548 nolink:
   1549 	f->vs->current_link = -1;
   1550 	f->vs->orig_link = f->vs->current_link;
   1551 }
   1552 
   1553 static void
   1554 page_down(struct session *ses, struct f_data_c *f, int a)
   1555 {
   1556 	if (f->vs->view_pos + f->yw < f->f_data->y) {
   1557 		f->vs->view_pos += f->yw;
   1558 		f->vs->orig_view_pos = f->vs->view_pos;
   1559 		find_link(f, 1, a);
   1560 	} else
   1561 		find_link(f, -1, a);
   1562 }
   1563 
   1564 static void
   1565 page_up(struct session *ses, struct f_data_c *f, int a)
   1566 {
   1567 	f->vs->view_pos -= f->yw;
   1568 	find_link(f, -1, a);
   1569 	if (f->vs->view_pos < 0) {
   1570 		f->vs->view_pos = 0;
   1571 	}
   1572 	f->vs->orig_view_pos = f->vs->view_pos;
   1573 }
   1574 
   1575 static void
   1576 down(struct session *ses, struct f_data_c *f, int a)
   1577 {
   1578 	int l = f->vs->current_link;
   1579 	if (f->vs->current_link == -1
   1580 	    || !next_in_view(f, f->vs->current_link + 1, 1, in_viewy,
   1581 	                     set_pos_x))
   1582 		page_down(ses, f, 1);
   1583 	if (l != f->vs->current_link)
   1584 		set_textarea(ses, f, -1);
   1585 }
   1586 
   1587 static void
   1588 up(struct session *ses, struct f_data_c *f, int a)
   1589 {
   1590 	int l = f->vs->current_link;
   1591 	if (f->vs->current_link == -1
   1592 	    || !next_in_view(f, f->vs->current_link - 1, -1, in_viewy,
   1593 	                     set_pos_x))
   1594 		page_up(ses, f, 1);
   1595 	if (l != f->vs->current_link)
   1596 		set_textarea(ses, f, 1);
   1597 }
   1598 
   1599 static void
   1600 scroll(struct session *ses, struct f_data_c *f, int a)
   1601 {
   1602 	if (f->vs->view_pos + f->yw >= f->f_data->y && a > 0)
   1603 		return;
   1604 	f->vs->view_pos += a;
   1605 	if (f->vs->view_pos > f->f_data->y - f->yw && a > 0)
   1606 		f->vs->view_pos = f->f_data->y - f->yw;
   1607 	if (f->vs->view_pos < 0)
   1608 		f->vs->view_pos = 0;
   1609 	f->vs->orig_view_pos = f->vs->view_pos;
   1610 	if (c_in_view(f))
   1611 		return;
   1612 	find_link(f, a < 0 ? -1 : 1, 0);
   1613 }
   1614 
   1615 static void
   1616 hscroll(struct session *ses, struct f_data_c *f, int a)
   1617 {
   1618 	f->vs->view_posx += a;
   1619 	if (f->vs->view_posx >= f->f_data->x)
   1620 		f->vs->view_posx = f->f_data->x - 1;
   1621 	if (f->vs->view_posx < 0)
   1622 		f->vs->view_posx = 0;
   1623 	f->vs->orig_view_posx = f->vs->view_posx;
   1624 	if (c_in_view(f))
   1625 		return;
   1626 	find_link(f, 1, 0);
   1627 	/* !!! FIXME: check right margin */
   1628 }
   1629 
   1630 static void
   1631 home(struct session *ses, struct f_data_c *f, int a)
   1632 {
   1633 	f->vs->view_pos = f->vs->view_posx = 0;
   1634 	f->vs->orig_view_pos = f->vs->view_pos;
   1635 	f->vs->orig_view_posx = f->vs->view_posx;
   1636 	find_link(f, 1, 0);
   1637 }
   1638 
   1639 static void
   1640 x_end(struct session *ses, struct f_data_c *f, int a)
   1641 {
   1642 	f->vs->view_posx = 0;
   1643 	if (f->vs->view_pos < f->f_data->y - f->yw)
   1644 		f->vs->view_pos = f->f_data->y - f->yw;
   1645 	if (f->vs->view_pos < 0)
   1646 		f->vs->view_pos = 0;
   1647 	f->vs->orig_view_pos = f->vs->view_pos;
   1648 	f->vs->orig_view_posx = f->vs->view_posx;
   1649 	find_link(f, -1, 0);
   1650 }
   1651 
   1652 static int
   1653 has_form_submit(struct f_data *f, struct form_control *form)
   1654 {
   1655 	struct form_control *i = NULL;
   1656 	struct list_head *li;
   1657 	int q = 0;
   1658 	foreach (struct form_control, i, li, f->forms)
   1659 		if (i->form_num == form->form_num) {
   1660 			if ((i->type == FC_SUBMIT || i->type == FC_IMAGE))
   1661 				return 1;
   1662 			q = 1;
   1663 		}
   1664 	if (!q)
   1665 		internal("form is not on list");
   1666 	return 0;
   1667 }
   1668 
   1669 struct submitted_value {
   1670 	list_entry_1st;
   1671 	int type;
   1672 	unsigned char *name;
   1673 	unsigned char *value;
   1674 	void *file_content;
   1675 	int fc_len;
   1676 	int position;
   1677 };
   1678 
   1679 static void
   1680 free_succesful_controls(struct list_head *submit)
   1681 {
   1682 	struct submitted_value *v = NULL;
   1683 	struct list_head *lv;
   1684 	foreach (struct submitted_value, v, lv, *submit) {
   1685 		free(v->name);
   1686 		free(v->value);
   1687 		free(v->file_content);
   1688 	}
   1689 	free_list(struct submitted_value, *submit);
   1690 }
   1691 
   1692 static unsigned char *
   1693 encode_textarea(unsigned char *t)
   1694 {
   1695 	size_t len = 0;
   1696 	unsigned char *o = NULL;
   1697 	for (; *t; t++) {
   1698 		if (*t != '\n')
   1699 			len = add_chr_to_str(&o, len, *t);
   1700 		else
   1701 			len = add_to_str(&o, len, cast_uchar "\r\n");
   1702 	}
   1703 	return o;
   1704 }
   1705 
   1706 static int
   1707 compare_submitted(struct submitted_value *sub1, struct submitted_value *sub2)
   1708 {
   1709 	return sub1->position - sub2->position;
   1710 }
   1711 
   1712 static void
   1713 get_succesful_controls(struct f_data_c *f, struct form_control *fc,
   1714                        struct list_head *subm)
   1715 {
   1716 	int ch;
   1717 	struct form_control *form = NULL;
   1718 	struct list_head *lform;
   1719 	init_list(*subm);
   1720 	foreach (struct form_control, form, lform, f->f_data->forms) {
   1721 		if (form->form_num == fc->form_num
   1722 		    && ((form->type != FC_SUBMIT && form->type != FC_IMAGE
   1723 		         && form->type != FC_RESET && form->type != FC_BUTTON)
   1724 		        || form == fc)
   1725 		    && form->name && form->name[0] && form->ro != 2) {
   1726 			struct submitted_value *sub;
   1727 			struct form_state *fs;
   1728 			int fi = form->type == FC_IMAGE && form->default_value
   1729 			                 && *form->default_value
   1730 			             ? -1
   1731 			             : 0;
   1732 			fs = find_form_state(f, form);
   1733 			if ((form->type == FC_CHECKBOX
   1734 			     || form->type == FC_RADIO)
   1735 			    && !fs->state)
   1736 				continue;
   1737 			if (form->type == FC_BUTTON)
   1738 				continue;
   1739 			if (form->type == FC_SELECT && !form->nvalues)
   1740 				continue;
   1741 fi_rep:
   1742 			sub = mem_calloc(sizeof(struct submitted_value));
   1743 			sub->type = form->type;
   1744 			sub->name = stracpy(form->name);
   1745 			switch (form->type) {
   1746 			case FC_TEXT:
   1747 			case FC_PASSWORD:
   1748 			case FC_FILE_UPLOAD:
   1749 				sub->value = stracpy(fs->string);
   1750 				break;
   1751 			case FC_TEXTAREA:
   1752 				sub->value = encode_textarea(fs->string);
   1753 				break;
   1754 			case FC_CHECKBOX:
   1755 			case FC_RADIO:
   1756 			case FC_SUBMIT:
   1757 			case FC_HIDDEN:
   1758 				sub->value =
   1759 				    encode_textarea(form->default_value);
   1760 				break;
   1761 			case FC_SELECT:
   1762 				fixup_select_state(form, fs);
   1763 				sub->value = encode_textarea(fs->string);
   1764 				break;
   1765 			case FC_IMAGE:
   1766 				if (fi == -1) {
   1767 					sub->value = encode_textarea(
   1768 					    form->default_value);
   1769 					break;
   1770 				}
   1771 				add_to_strn(&sub->name, fi ? cast_uchar ".x"
   1772 				                           : cast_uchar ".y");
   1773 				sub->value = NULL;
   1774 				add_num_to_str(&sub->value, 0,
   1775 				               fi ? ismap_x : ismap_y);
   1776 				break;
   1777 			default:
   1778 				internal("bad form control type");
   1779 			}
   1780 			sub->position = form->form_num + form->ctrl_num;
   1781 			add_to_list(*subm, sub);
   1782 			if (form->type == FC_IMAGE && fi < 1) {
   1783 				fi++;
   1784 				goto fi_rep;
   1785 			}
   1786 		}
   1787 	}
   1788 	do {
   1789 		struct submitted_value *sub = NULL, *nx;
   1790 		struct list_head *lsub;
   1791 		ch = 0;
   1792 		foreach (struct submitted_value, sub, lsub, *subm)
   1793 			if (sub->list_entry.next != subm) {
   1794 				nx = list_struct(sub->list_entry.next,
   1795 				                 struct submitted_value);
   1796 				if (compare_submitted(nx, sub) < 0) {
   1797 					del_from_list(sub);
   1798 					add_after_pos(nx, sub);
   1799 					lsub = &nx->list_entry;
   1800 					ch = 1;
   1801 				}
   1802 			}
   1803 		foreachback (struct submitted_value, sub, lsub, *subm)
   1804 			if (sub->list_entry.next != subm) {
   1805 				nx = list_struct(sub->list_entry.next,
   1806 				                 struct submitted_value);
   1807 				if (compare_submitted(nx, sub) < 0) {
   1808 					del_from_list(sub);
   1809 					add_after_pos(nx, sub);
   1810 					sub = nx;
   1811 					lsub = &nx->list_entry;
   1812 					ch = 1;
   1813 				}
   1814 			}
   1815 	} while (ch);
   1816 }
   1817 
   1818 static unsigned char *
   1819 strip_file_name(unsigned char *f)
   1820 {
   1821 	unsigned char *n;
   1822 	unsigned char *l = f - 1;
   1823 	for (n = f; *n; n++)
   1824 		if (dir_sep(*n))
   1825 			l = n;
   1826 	return l + 1;
   1827 }
   1828 
   1829 static inline int
   1830 safe_char(unsigned char c)
   1831 {
   1832 	return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')
   1833 	       || (c >= 'a' && c <= 'z') || c == '.' || c == '-' || c == '_';
   1834 }
   1835 
   1836 static size_t
   1837 encode_string(unsigned char *name, unsigned char **data, size_t len)
   1838 {
   1839 	for (; *name; name++) {
   1840 		if (*name == ' ')
   1841 			len = add_chr_to_str(data, len, '+');
   1842 		else if (safe_char(*name))
   1843 			len = add_chr_to_str(data, len, *name);
   1844 		else {
   1845 			unsigned char n[4];
   1846 			sprintf(cast_char n, "%%%02X", *name);
   1847 			len = add_to_str(data, len, n);
   1848 		}
   1849 	}
   1850 	return len;
   1851 }
   1852 
   1853 static size_t
   1854 encode_controls(struct list_head *l, unsigned char **data)
   1855 {
   1856 	struct submitted_value *sv = NULL;
   1857 	struct list_head *lsv;
   1858 	int lst = 0;
   1859 	unsigned char *p2;
   1860 	size_t len = 0;
   1861 	*data = NULL;
   1862 	foreach (struct submitted_value, sv, lsv, *l) {
   1863 		unsigned char *p = sv->value;
   1864 		if (lst)
   1865 			len = add_chr_to_str(data, len, '&');
   1866 		else
   1867 			lst = 1;
   1868 		len = encode_string(sv->name, data, len);
   1869 		len = add_chr_to_str(data, len, '=');
   1870 		p2 = stracpy(p);
   1871 		len = encode_string(p2, data, len);
   1872 		free(p2);
   1873 	}
   1874 	return len;
   1875 }
   1876 
   1877 #define BL  56
   1878 #define BL1 27
   1879 
   1880 static size_t
   1881 encode_multipart(struct session *ses, struct list_head *l, unsigned char **data,
   1882                  unsigned char *bound)
   1883 {
   1884 	int errn;
   1885 	int *bound_ptrs = NULL;
   1886 	int nbound_ptrs = 0;
   1887 	unsigned char *m1, *m2;
   1888 	struct submitted_value *sv = NULL; /* against warning */
   1889 	struct list_head *lsv;
   1890 	int i, j;
   1891 	int flg = 0;
   1892 	unsigned char *p;
   1893 	int rs;
   1894 	memset(bound, 'x', BL);
   1895 	size_t len = 0;
   1896 	*data = NULL;
   1897 	foreach (struct submitted_value, sv, lsv, *l) {
   1898 		unsigned char *ct;
   1899 bnd:
   1900 		len = add_to_str(data, len, cast_uchar "--");
   1901 		if (!(nbound_ptrs & (ALLOC_GR - 1))) {
   1902 			if ((unsigned)nbound_ptrs
   1903 			    > INT_MAX / sizeof(int) - ALLOC_GR)
   1904 				overalloc();
   1905 			bound_ptrs = xrealloc(
   1906 			    bound_ptrs, (nbound_ptrs + ALLOC_GR) * sizeof(int));
   1907 		}
   1908 		bound_ptrs[nbound_ptrs++] = len;
   1909 		len = add_bytes_to_str(data, len, bound, BL);
   1910 		if (flg)
   1911 			break;
   1912 		len = add_to_str(data, len,
   1913 		                 cast_uchar
   1914 		                 "\r\nContent-Disposition: form-data; name=\"");
   1915 		len = add_to_str(data, len, sv->name);
   1916 		len = add_chr_to_str(data, len, '"');
   1917 		if (sv->type == FC_FILE_UPLOAD) {
   1918 			len = add_to_str(data, len, cast_uchar "; filename=\"");
   1919 			len = add_to_str(data, len, strip_file_name(sv->value));
   1920 			/* It sends bad data if the file name contains ", but
   1921 			   Netscape does the same */
   1922 			len = add_chr_to_str(data, len, '"');
   1923 			if (*sv->value)
   1924 				if ((ct = get_content_type(NULL, sv->value))) {
   1925 					len = add_to_str(data, len,
   1926 					                 cast_uchar
   1927 					                 "\r\nContent-Type: ");
   1928 					len = add_to_str(data, len, ct);
   1929 					if (strlen(cast_const_char ct) >= 4
   1930 					    && !casecmp(ct, cast_uchar "text",
   1931 					                4)) {
   1932 						len = add_to_str(data, len,
   1933 						                 cast_uchar
   1934 						                 "; charset=");
   1935 						len = add_to_str(
   1936 						    data, len,
   1937 						    get_cp_mime_name(0));
   1938 					}
   1939 					free(ct);
   1940 				}
   1941 		}
   1942 		len = add_to_str(data, len, cast_uchar "\r\n\r\n");
   1943 		if (sv->type != FC_FILE_UPLOAD) {
   1944 			p = stracpy(sv->value);
   1945 			len = add_to_str(data, len, p);
   1946 			free(p);
   1947 		} else {
   1948 			int fh, rd;
   1949 #define F_BUFLEN 1024
   1950 			unsigned char buffer[F_BUFLEN];
   1951 			if (*sv->value) {
   1952 				unsigned char *wd;
   1953 				if (anonymous) {
   1954 					goto not_allowed;
   1955 				}
   1956 				wd = get_cwd();
   1957 				set_cwd(ses->term->cwd);
   1958 				fh = c_open(sv->value, O_RDONLY | O_NOCTTY);
   1959 				if (fh == -1) {
   1960 					errn = errno;
   1961 					if (wd) {
   1962 						set_cwd(wd);
   1963 						free(wd);
   1964 					}
   1965 					goto error;
   1966 				}
   1967 				if (wd) {
   1968 					set_cwd(wd);
   1969 					free(wd);
   1970 				}
   1971 				do {
   1972 					if ((rd = hard_read(fh, buffer,
   1973 					                    F_BUFLEN))
   1974 					    == -1) {
   1975 						errn = errno;
   1976 						EINTRLOOP(rs, close(fh));
   1977 						goto error;
   1978 					}
   1979 					if (rd)
   1980 						len = add_bytes_to_str(
   1981 						    data, len, buffer, rd);
   1982 				} while (rd);
   1983 				EINTRLOOP(rs, close(fh));
   1984 			}
   1985 		}
   1986 		len = add_to_str(data, len, cast_uchar "\r\n");
   1987 	}
   1988 	if (!flg) {
   1989 		flg = 1;
   1990 		goto bnd;
   1991 	}
   1992 	len = add_to_str(data, len, cast_uchar "--\r\n");
   1993 	memset(bound, '-', BL1);
   1994 	memset(bound + BL1, '0', BL - BL1);
   1995 again:
   1996 	for (i = 0; i <= len - BL; i++) {
   1997 		for (j = 0; j < BL; j++)
   1998 			if ((*data)[i + j] != bound[j])
   1999 				goto nb;
   2000 		for (j = BL - 1; j >= 0; j--) {
   2001 			if (bound[j] < '0')
   2002 				bound[j] = '0' - 1;
   2003 			if (bound[j]++ >= '9')
   2004 				bound[j] = '0';
   2005 			else
   2006 				goto again;
   2007 		}
   2008 		internal("Could not assign boundary");
   2009 nb:;
   2010 	}
   2011 	for (i = 0; i < nbound_ptrs; i++)
   2012 		memcpy(*data + bound_ptrs[i], bound, BL);
   2013 	free(bound_ptrs);
   2014 	return len;
   2015 
   2016 error:
   2017 	free(bound_ptrs);
   2018 	free(*data);
   2019 	*data = NULL;
   2020 	m1 = stracpy(sv->value);
   2021 	m2 = stracpy(cast_uchar strerror(errn));
   2022 	msg_box(ses->term, getml(m1, m2, NULL),
   2023 	        TEXT_(T_ERROR_WHILE_POSTING_FORM), AL_CENTER,
   2024 	        TEXT_(T_COULD_NOT_GET_FILE), cast_uchar " ", m1,
   2025 	        cast_uchar ": ", m2, MSG_BOX_END, (void *)ses, 1,
   2026 	        TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC);
   2027 	return len;
   2028 
   2029 not_allowed:
   2030 	free(bound_ptrs);
   2031 	free(*data);
   2032 	*data = NULL;
   2033 	msg_box(ses->term, NULL, TEXT_(T_ERROR_WHILE_POSTING_FORM), AL_CENTER,
   2034 	        TEXT_(T_READING_FILES_IS_NOT_ALLOWED), MSG_BOX_END, (void *)ses,
   2035 	        1, TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC);
   2036 	return len;
   2037 }
   2038 
   2039 void
   2040 reset_form(struct f_data_c *f, int form_num)
   2041 {
   2042 	struct form_control *form = NULL;
   2043 	struct list_head *lform;
   2044 	foreach (struct form_control, form, lform, f->f_data->forms)
   2045 		if (form->form_num == form_num) {
   2046 			struct form_state *fs;
   2047 			fs = find_form_state(f, form);
   2048 			init_ctrl(form, fs);
   2049 		}
   2050 }
   2051 
   2052 unsigned char *
   2053 get_form_url(struct session *ses, struct f_data_c *f, struct form_control *form,
   2054              int *onsubmit)
   2055 {
   2056 	struct list_head submit;
   2057 	unsigned char *data;
   2058 	unsigned char bound[BL];
   2059 	int len;
   2060 	unsigned char *go = NULL;
   2061 	if (!form)
   2062 		return NULL;
   2063 	if (form->type == FC_RESET) {
   2064 		reset_form(f, form->form_num);
   2065 		return NULL;
   2066 	}
   2067 	if (onsubmit)
   2068 		*onsubmit = 0;
   2069 	if (!form->action)
   2070 		return NULL;
   2071 	get_succesful_controls(f, form, &submit);
   2072 	if (form->method == FM_GET || form->method == FM_POST)
   2073 		len = encode_controls(&submit, &data);
   2074 	else
   2075 		len = encode_multipart(ses, &submit, &data, bound);
   2076 	if (!data)
   2077 		goto ff;
   2078 	if (!casecmp(form->action, cast_uchar "javascript:", 11)) {
   2079 		go = stracpy(form->action);
   2080 		goto x;
   2081 	}
   2082 	if (form->method == FM_GET) {
   2083 		unsigned char *pos, *da;
   2084 		size_t q;
   2085 		go = stracpy(form->action);
   2086 		pos = extract_position(go);
   2087 		if (!(da = get_url_data(go)))
   2088 			da = go;
   2089 		q = strlen(cast_const_char da);
   2090 		if (q && (da[q - 1] == '&' || da[q - 1] == '?'))
   2091 			;
   2092 		else if (strchr(cast_const_char da, '?'))
   2093 			add_to_strn(&go, cast_uchar "&");
   2094 		else
   2095 			add_to_strn(&go, cast_uchar "?");
   2096 		add_to_strn(&go, data);
   2097 		if (pos) {
   2098 			add_to_strn(&go, pos);
   2099 			free(pos);
   2100 		}
   2101 	} else {
   2102 		size_t l;
   2103 		int i;
   2104 		go = NULL;
   2105 		l = add_to_str(&go, 0, form->action);
   2106 		l = add_chr_to_str(&go, l, POST_CHAR);
   2107 		if (form->method == FM_POST)
   2108 			l = add_to_str(&go, l,
   2109 			               cast_uchar
   2110 			               "application/x-www-form-urlencoded\n");
   2111 		else {
   2112 			l = add_to_str(&go, l,
   2113 			               cast_uchar
   2114 			               "multipart/form-data; boundary=");
   2115 			l = add_bytes_to_str(&go, l, bound, BL);
   2116 			l = add_chr_to_str(&go, l, '\n');
   2117 		}
   2118 		for (i = 0; i < len; i++) {
   2119 			unsigned char p[3];
   2120 			sprintf(cast_char p, "%02x", (int)data[i]);
   2121 			l = add_to_str(&go, l, p);
   2122 		}
   2123 	}
   2124 x:
   2125 	free(data);
   2126 ff:
   2127 	free_succesful_controls(&submit);
   2128 	return go;
   2129 }
   2130 
   2131 int ismap_link = 0, ismap_x = 1, ismap_y = 1;
   2132 
   2133 /* if onsubmit is not NULL it will contain 1 if link is submit and the form has
   2134  * an onsubmit handler */
   2135 static unsigned char *
   2136 get_link_url(struct session *ses, struct f_data_c *f, struct link *l,
   2137              int *onsubmit)
   2138 {
   2139 	if (l->type == L_LINK) {
   2140 		if (!l->where) {
   2141 			if (l->where_img)
   2142 				return stracpy(l->where_img);
   2143 			return NULL;
   2144 		}
   2145 		if (ismap_link && strlen(cast_const_char l->where) >= 4
   2146 		    && !strcmp(
   2147 			cast_const_char(l->where
   2148 		                        + strlen(cast_const_char l->where) - 4),
   2149 			"?0,0")) {
   2150 			unsigned char *nu = NULL;
   2151 			size_t ll = add_bytes_to_str(
   2152 			    &nu, 0, l->where,
   2153 			    strlen(cast_const_char l->where) - 3);
   2154 			ll = add_num_to_str(&nu, ll, ismap_x);
   2155 			ll = add_chr_to_str(&nu, ll, ',');
   2156 			ll = add_num_to_str(&nu, ll, ismap_y);
   2157 			return nu;
   2158 		}
   2159 		return stracpy(l->where);
   2160 	}
   2161 	if (l->type != L_BUTTON && l->type != L_FIELD)
   2162 		return NULL;
   2163 	return get_form_url(ses, f, l->form, onsubmit);
   2164 }
   2165 
   2166 static struct menu_item *
   2167 clone_select_menu(struct menu_item *m)
   2168 {
   2169 	struct menu_item *n = NULL;
   2170 	int i = 0;
   2171 	do {
   2172 		if ((unsigned)i > INT_MAX / sizeof(struct menu_item) - 1)
   2173 			overalloc();
   2174 		n = xrealloc(n, (i + 1) * sizeof(struct menu_item));
   2175 		n[i].text = stracpy(m->text);
   2176 		n[i].rtext = stracpy(m->rtext);
   2177 		n[i].hotkey = stracpy(m->hotkey);
   2178 		n[i].in_m = m->in_m;
   2179 		n[i].free_i = 0;
   2180 		if ((n[i].func = m->func) != do_select_submenu) {
   2181 			n[i].data = m->data;
   2182 		} else
   2183 			n[i].data = clone_select_menu(m->data);
   2184 		i++;
   2185 	} while (m++->text);
   2186 	return n;
   2187 }
   2188 
   2189 static void
   2190 free_select_menu(void *m_)
   2191 {
   2192 	struct menu_item *m = (struct menu_item *)m_;
   2193 	struct menu_item *om = m;
   2194 	do {
   2195 		free(m->text);
   2196 		free(m->rtext);
   2197 		free(m->hotkey);
   2198 		if (m->func == do_select_submenu)
   2199 			free_select_menu(m->data);
   2200 	} while (m++->text);
   2201 	free(om);
   2202 }
   2203 
   2204 void
   2205 set_frame(struct session *ses, struct f_data_c *f, int a)
   2206 {
   2207 	if (f == ses->screen)
   2208 		return;
   2209 	if (!f->loc->url)
   2210 		return;
   2211 	goto_url_not_from_dialog(ses, f->loc->url, ses->screen);
   2212 }
   2213 
   2214 static struct link *
   2215 get_current_link(struct f_data_c *f)
   2216 {
   2217 	if (!f || !f->f_data || !f->vs)
   2218 		return NULL;
   2219 	if (f->vs->current_link >= 0 && f->vs->current_link < f->f_data->nlinks)
   2220 		return &f->f_data->links[f->vs->current_link];
   2221 	return NULL;
   2222 }
   2223 
   2224 /* pokud je a==1, tak se nebude submitovat formular, kdyz kliknu na input field
   2225  * a formular nema submit */
   2226 int
   2227 enter(struct session *ses, struct f_data_c *f, int a)
   2228 {
   2229 	struct link *link;
   2230 	unsigned char *u;
   2231 	link = get_current_link(f);
   2232 	if (!link)
   2233 		return 1;
   2234 	if (link->type == L_LINK || link->type == L_BUTTON) {
   2235 		int has_onsubmit;
   2236 		if (link->type == L_BUTTON && link->form->type == FC_BUTTON)
   2237 			return 1;
   2238 submit:
   2239 		if ((u = get_link_url(ses, f, link, &has_onsubmit))) {
   2240 			if (strlen(cast_const_char u) >= 4
   2241 			    && !casecmp(u, cast_uchar "MAP@", 4)) {
   2242 				goto_imgmap(ses, f, u + 4, stracpy(u + 4),
   2243 				            stracpy(link->target));
   2244 			} else if (ses->ds.target_in_new_window && link->target
   2245 			           && *link->target
   2246 			           && !find_frame(ses, link->target, f)
   2247 			           && can_open_in_new(
   2248 				       ses->term)) { /* open in new window */
   2249 				free(ses->wtd_target);
   2250 				ses->wtd_target = stracpy(link->target);
   2251 				open_in_new_window(
   2252 				    ses->term,
   2253 				    (void *)&send_open_in_new_xterm_ptr, ses);
   2254 				free(ses->wtd_target);
   2255 				ses->wtd_target = NULL;
   2256 			} else {
   2257 				goto_url_f(ses, NULL, u, link->target, f,
   2258 				           (link->type == L_BUTTON && link->form
   2259 				            && link->form->type == FC_SUBMIT)
   2260 				               ? link->form->form_num
   2261 				               : -1,
   2262 				           0, 0, 0);
   2263 			}
   2264 			free(u);
   2265 			return 2;
   2266 		}
   2267 		return 1;
   2268 	}
   2269 	if (link->type == L_CHECKBOX) {
   2270 		struct form_state *fs = find_form_state(f, link->form);
   2271 		if (link->form->ro)
   2272 			return 1;
   2273 		if (link->form->type == FC_CHECKBOX)
   2274 			fs->state = !fs->state;
   2275 		else {
   2276 			struct form_control *fc = NULL;
   2277 			struct list_head *lfc;
   2278 			foreach (struct form_control, fc, lfc, f->f_data->forms)
   2279 				if (fc->form_num == link->form->form_num
   2280 				    && fc->type == FC_RADIO
   2281 				    && !xstrcmp(fc->name, link->form->name)) {
   2282 					struct form_state *fffs =
   2283 					    find_form_state(f, fc);
   2284 					fffs->state = 0;
   2285 				}
   2286 			fs = find_form_state(f, link->form);
   2287 			fs->state = 1;
   2288 		}
   2289 		return 1;
   2290 	}
   2291 	if (link->type == L_SELECT) {
   2292 		struct menu_item *m;
   2293 		if (link->form->ro)
   2294 			return 1;
   2295 		m = clone_select_menu(link->form->menu);
   2296 		if (!m)
   2297 			return 1;
   2298 		/* execute onfocus code of the select object */
   2299 		add_empty_window(ses->term, free_select_menu, m);
   2300 		do_select_submenu(ses->term, m, ses);
   2301 		return 1;
   2302 	}
   2303 	if (link->type == L_FIELD || link->type == L_AREA) {
   2304 		/* pri enteru v textovem policku se bude posilat vzdycky --
   2305 		 * Brain */
   2306 		if (!has_form_submit(f->f_data, link->form) && !a)
   2307 			goto submit;
   2308 		down(ses, f, 0);
   2309 		return 1;
   2310 	}
   2311 	internal("bad link type %d", link->type);
   2312 	return 1;
   2313 }
   2314 
   2315 void
   2316 toggle(struct session *ses, struct f_data_c *f, int a)
   2317 {
   2318 	if (!f || !f->vs) {
   2319 		msg_box(ses->term, NULL, TEXT_(T_TOGGLE_HTML_PLAIN), AL_LEFT,
   2320 		        TEXT_(T_YOU_ARE_NOWHERE), MSG_BOX_END, NULL, 1,
   2321 		        TEXT_(T_OK), msg_box_null, B_ENTER | B_ESC);
   2322 		return;
   2323 	}
   2324 	if (f->vs->plain == -1)
   2325 		f->vs->plain = 1;
   2326 	else
   2327 		f->vs->plain = f->vs->plain ^ 1;
   2328 	html_interpret_recursive(f);
   2329 	draw_formatted(ses);
   2330 }
   2331 
   2332 void
   2333 selected_item(struct terminal *term, void *pitem, void *ses_)
   2334 {
   2335 	struct session *ses = (struct session *)ses_;
   2336 	int item = (int)(long)pitem;
   2337 	struct f_data_c *f = current_frame(ses);
   2338 	struct link *l;
   2339 	struct form_state *fs;
   2340 	struct form_control *form;
   2341 	l = get_current_link(f);
   2342 	if (!l)
   2343 		return;
   2344 	if (l->type != L_SELECT)
   2345 		return;
   2346 	form = l->form;
   2347 	fs = find_form_state(f, form);
   2348 	if (item >= 0 && item < form->nvalues) {
   2349 		free_format_text_cache_entry(fs);
   2350 		fs->state = item;
   2351 		free(fs->string);
   2352 		fs->string = stracpy(form->values[item]);
   2353 	}
   2354 	fixup_select_state(form, fs);
   2355 	f->active = 1;
   2356 	draw_to_window(ses->win, draw_doc, f);
   2357 	change_screen_status(ses);
   2358 	print_screen_status(ses);
   2359 }
   2360 
   2361 int
   2362 get_current_state(struct session *ses)
   2363 {
   2364 	struct f_data_c *f = current_frame(ses);
   2365 	struct link *l;
   2366 	struct form_state *fs;
   2367 	l = get_current_link(f);
   2368 	if (!l)
   2369 		return -1;
   2370 	if (l->type != L_SELECT)
   2371 		return -1;
   2372 	fs = find_form_state(f, l->form);
   2373 	return fs->state;
   2374 }
   2375 
   2376 static int find_pos_in_link(struct f_data_c *fd, struct link *l,
   2377                             struct links_event *ev, int *xx, int *yy);
   2378 
   2379 static void
   2380 set_form_position(struct f_data_c *fd, struct link *l, struct links_event *ev)
   2381 {
   2382 	/* if links is a field, set cursor position */
   2383 	if (l->form && (l->type == L_AREA || l->type == L_FIELD)) {
   2384 		struct form_state *fs = find_form_state(fd, l->form);
   2385 		int xx = 0, yy = 0; /* against uninitialized warning */
   2386 
   2387 		if (l->type == L_AREA) {
   2388 			struct format_text_cache_entry *ftce;
   2389 
   2390 			if (!find_pos_in_link(fd, l, ev, &xx, &yy)) {
   2391 				xx += fs->vpos;
   2392 				yy += fs->vypos;
   2393 				ftce = format_text(fd, l->form, fs);
   2394 				if (yy >= ftce->n_lines)
   2395 					yy = ftce->n_lines - 1;
   2396 				if (yy >= 0) {
   2397 					unsigned char *ptr;
   2398 					fs->state = ftce->ln[yy].st_offs;
   2399 					ptr =
   2400 					    textptr_add(fs->string + fs->state,
   2401 					                xx, fd->f_data->opt.cp);
   2402 					if (ptr
   2403 					    > fs->string + ftce->ln[yy].en_offs)
   2404 						ptr = fs->string
   2405 						      + ftce->ln[yy].en_offs;
   2406 					fs->state = (int)(ptr - fs->string);
   2407 					goto br;
   2408 				}
   2409 				fs->state =
   2410 				    (int)strlen(cast_const_char fs->string);
   2411 br:;
   2412 			}
   2413 		} else if (l->type == L_FIELD) {
   2414 			if (!find_pos_in_link(fd, l, ev, &xx, &yy)) {
   2415 				unsigned char *ptr;
   2416 				ptr = textptr_add(fs->string + fs->vpos, xx,
   2417 				                  fd->f_data->opt.cp);
   2418 				fs->state = (int)(ptr - fs->string);
   2419 			}
   2420 		}
   2421 
   2422 		fd->last_captured = 1;
   2423 	}
   2424 }
   2425 
   2426 static int
   2427 textarea_adjust_viewport(struct f_data_c *fd, struct link *l)
   2428 {
   2429 	struct form_control *fc = l->form;
   2430 	struct view_state *vs = fd->vs;
   2431 	int r = 0;
   2432 	if (l->pos[0].x + fc->cols > fd->xw + vs->view_posx) {
   2433 		vs->view_posx = l->pos[0].x + fc->cols - fd->xw;
   2434 		r = 1;
   2435 	}
   2436 	if (l->pos[0].x < vs->view_posx) {
   2437 		vs->view_posx = l->pos[0].x;
   2438 		r = 1;
   2439 	}
   2440 	if (l->pos[0].y + fc->rows > fd->yw + vs->view_pos) {
   2441 		vs->view_pos = l->pos[0].y + fc->rows - fd->yw;
   2442 		r = 1;
   2443 	}
   2444 	if (l->pos[0].y < vs->view_pos) {
   2445 		vs->view_pos = l->pos[0].y;
   2446 		r = 1;
   2447 	}
   2448 	vs->orig_view_pos = vs->view_pos;
   2449 	vs->orig_view_posx = vs->view_posx;
   2450 	return r;
   2451 }
   2452 
   2453 int
   2454 field_op(struct session *ses, struct f_data_c *f, struct link *l,
   2455          struct links_event *ev)
   2456 {
   2457 	struct form_control *form = l->form;
   2458 	struct form_state *fs;
   2459 	int r = 1;
   2460 	struct format_text_cache_entry *ftce;
   2461 	int y;
   2462 
   2463 	if (!form) {
   2464 		internal("link has no form control");
   2465 		return 0;
   2466 	}
   2467 	if (form->ro == 2)
   2468 		return 0;
   2469 	fs = find_form_state(f, form);
   2470 	if (!fs->string)
   2471 		return 0;
   2472 	if (ev->ev == EV_KBD) {
   2473 		if (!(ev->y & (KBD_CTRL | KBD_ALT)) && ev->x >= ' ') {
   2474 			if (!form->ro
   2475 			    && strlen((char *)fs->string) < form->maxlength) {
   2476 				unsigned char *v;
   2477 				unsigned char a_[2];
   2478 				unsigned char *nw;
   2479 				int ll;
   2480 				free_format_text_cache_entry(fs);
   2481 				v = fs->string = xrealloc(
   2482 				    fs->string,
   2483 				    strlen(cast_const_char fs->string) + 12);
   2484 				if (f->f_data->opt.cp) {
   2485 					nw = a_;
   2486 					a_[0] = (unsigned char)ev->x;
   2487 					a_[1] = 0;
   2488 				} else {
   2489 					nw = encode_utf_8(ev->x);
   2490 				}
   2491 				ll = (int)strlen(cast_const_char nw);
   2492 				if (ll > 10)
   2493 					goto done;
   2494 				memmove(v + fs->state + ll, v + fs->state,
   2495 				        strlen(cast_const_char(v + fs->state))
   2496 				            + 1);
   2497 				memcpy(&v[fs->state], nw, ll);
   2498 				fs->state += ll;
   2499 			}
   2500 			goto done;
   2501 		} else if (!(ev->y & (KBD_SHIFT | KBD_CTRL | KBD_ALT))
   2502 		           && ev->x == KBD_ENTER && form->type == FC_TEXTAREA) {
   2503 			if (!form->ro
   2504 			    && strlen(cast_const_char fs->string)
   2505 			           < (size_t)form->maxlength) {
   2506 				unsigned char *v;
   2507 				free_format_text_cache_entry(fs);
   2508 				v = xrealloc(fs->string,
   2509 				             strlen(cast_const_char fs->string)
   2510 				                 + 2);
   2511 				fs->string = v;
   2512 				memmove(v + fs->state + 1, v + fs->state,
   2513 				        strlen(cast_const_char(v + fs->state))
   2514 				            + 1);
   2515 				v[fs->state++] = '\n';
   2516 			}
   2517 			goto done;
   2518 		}
   2519 		if (ev->y & KBD_PASTING) {
   2520 			r = 0;
   2521 			goto done;
   2522 		}
   2523 		if (ev->x == KBD_LEFT) {
   2524 			if (f->f_data->opt.cp)
   2525 				fs->state = fs->state ? fs->state - 1 : 0;
   2526 			else {
   2527 				unsigned char *p = fs->string + fs->state;
   2528 				BACK_UTF_8(p, fs->string);
   2529 				fs->state = (int)(p - fs->string);
   2530 			}
   2531 		} else if (ev->x == KBD_RIGHT) {
   2532 			if ((size_t)fs->state
   2533 			    < strlen(cast_const_char fs->string)) {
   2534 				if (f->f_data->opt.cp)
   2535 					fs->state = fs->state + 1;
   2536 				else {
   2537 					unsigned char *p =
   2538 					    fs->string + fs->state;
   2539 					FWD_UTF_8(p);
   2540 					fs->state = (int)(p - fs->string);
   2541 				}
   2542 			} else
   2543 				fs->state =
   2544 				    (int)strlen(cast_const_char fs->string);
   2545 		} else if ((ev->x == KBD_HOME
   2546 		            || (upcase(ev->x) == 'A' && ev->y & KBD_CTRL))) {
   2547 			if (form->type == FC_TEXTAREA) {
   2548 				ftce = format_text(f, form, fs);
   2549 				y = find_cursor_line(ftce, fs->state);
   2550 				if (y >= 0) {
   2551 					fs->state = ftce->ln[y].st_offs;
   2552 					goto done;
   2553 				}
   2554 				fs->state = 0;
   2555 			} else
   2556 				fs->state = 0;
   2557 		} else if (ev->x == KBD_END
   2558 		           || (upcase(ev->x) == 'E' && ev->y & KBD_CTRL)) {
   2559 			if (form->type == FC_TEXTAREA) {
   2560 				ftce = format_text(f, form, fs);
   2561 				y = find_cursor_line(ftce, fs->state);
   2562 				if (y >= 0) {
   2563 					fs->state = ftce->ln[y].en_offs;
   2564 					if (fs->state && y < ftce->n_lines - 1
   2565 					    && ftce->ln[y + 1].st_offs
   2566 					           == ftce->ln[y].en_offs)
   2567 						fs->state--;
   2568 					goto done;
   2569 				}
   2570 				fs->state =
   2571 				    (int)strlen(cast_const_char fs->string);
   2572 			} else
   2573 				fs->state =
   2574 				    (int)strlen(cast_const_char fs->string);
   2575 		} else if (ev->x == KBD_UP) {
   2576 			if (form->type == FC_TEXTAREA) {
   2577 				ftce = format_text(f, form, fs);
   2578 				y = find_cursor_line(ftce, fs->state);
   2579 				if (y > 0) {
   2580 					fs->state =
   2581 					    (int)(textptr_add(
   2582 						      fs->string
   2583 							  + ftce->ln[y - 1]
   2584 								.st_offs,
   2585 						      textptr_diff(
   2586 							  fs->string
   2587 							      + fs->state,
   2588 							  fs->string
   2589 							      + ftce->ln[y]
   2590 								    .st_offs,
   2591 							  f->f_data->opt.cp),
   2592 						      f->f_data->opt.cp)
   2593 					          - fs->string);
   2594 					if (fs->state > ftce->ln[y - 1].en_offs)
   2595 						fs->state =
   2596 						    ftce->ln[y - 1].en_offs;
   2597 				} else
   2598 					goto b;
   2599 			} else {
   2600 				r = 0;
   2601 				f->vs->brl_in_field = 0;
   2602 			}
   2603 		} else if (ev->x == KBD_DOWN) {
   2604 			if (form->type == FC_TEXTAREA) {
   2605 				ftce = format_text(f, form, fs);
   2606 				y = find_cursor_line(ftce, fs->state);
   2607 				if (y >= 0) {
   2608 					if (y >= ftce->n_lines - 1)
   2609 						goto b;
   2610 					fs->state =
   2611 					    (int)(textptr_add(
   2612 						      fs->string
   2613 							  + ftce->ln[y + 1]
   2614 								.st_offs,
   2615 						      textptr_diff(
   2616 							  fs->string
   2617 							      + fs->state,
   2618 							  fs->string
   2619 							      + ftce->ln[y]
   2620 								    .st_offs,
   2621 							  f->f_data->opt.cp),
   2622 						      f->f_data->opt.cp)
   2623 					          - fs->string);
   2624 					if (fs->state > ftce->ln[y + 1].en_offs)
   2625 						fs->state =
   2626 						    ftce->ln[y + 1].en_offs;
   2627 				} else
   2628 					goto b;
   2629 			} else {
   2630 				r = 0;
   2631 				f->vs->brl_in_field = 0;
   2632 			}
   2633 		} else if ((ev->x == KBD_INS && ev->y & KBD_CTRL)
   2634 		           || (upcase(ev->x) == 'B' && ev->y & KBD_CTRL)
   2635 		           || ev->x == KBD_COPY) {
   2636 			set_clipboard_text(ses->term, fs->string);
   2637 		} else if ((ev->x == KBD_DEL && ev->y & KBD_SHIFT)
   2638 		           || (upcase(ev->x) == 'X' && ev->y & KBD_CTRL)
   2639 		           || ev->x == KBD_CUT) {
   2640 			set_clipboard_text(ses->term, fs->string);
   2641 			if (!form->ro) {
   2642 				free_format_text_cache_entry(fs);
   2643 				fs->string[0] = 0;
   2644 			}
   2645 			fs->state = 0;
   2646 		} else if ((ev->x == KBD_INS && ev->y & KBD_SHIFT)
   2647 		           || (upcase(ev->x) == 'V' && ev->y & KBD_CTRL)
   2648 		           || ev->x == KBD_PASTE) {
   2649 			unsigned char *clipboard;
   2650 			clipboard = get_clipboard_text(ses->term);
   2651 			if (!clipboard)
   2652 				goto done;
   2653 			if (form->type != FC_TEXTAREA) {
   2654 				unsigned char *nl = clipboard;
   2655 				while ((nl = cast_uchar strchr(
   2656 					    cast_const_char nl, '\n')))
   2657 					*nl = ' ';
   2658 			}
   2659 			if (!form->ro
   2660 			    && strlen(cast_const_char fs->string)
   2661 			               + strlen(cast_const_char clipboard)
   2662 			           <= form->maxlength) {
   2663 				unsigned char *v;
   2664 				free_format_text_cache_entry(fs);
   2665 				v = xrealloc(
   2666 				    fs->string,
   2667 				    strlen(cast_const_char fs->string)
   2668 					+ strlen(cast_const_char clipboard)
   2669 					+ 1);
   2670 				fs->string = v;
   2671 				memmove(v + fs->state
   2672 				            + strlen(cast_const_char clipboard),
   2673 				        v + fs->state,
   2674 				        strlen(cast_const_char v) - fs->state
   2675 				            + 1);
   2676 				memcpy(v + fs->state, clipboard,
   2677 				       strlen(cast_const_char clipboard));
   2678 				fs->state +=
   2679 				    (int)strlen(cast_const_char clipboard);
   2680 			}
   2681 			free(clipboard);
   2682 		} else if (ev->x == KBD_ENTER)
   2683 			r = 0;
   2684 		else if (ev->x == KBD_BS) {
   2685 			if (!form->ro && fs->state) {
   2686 				int ll = 1;
   2687 				free_format_text_cache_entry(fs);
   2688 				if (!f->f_data->opt.cp) {
   2689 					unsigned char *p =
   2690 					    fs->string + fs->state;
   2691 					BACK_UTF_8(p, fs->string);
   2692 					ll = (int)(fs->string + fs->state - p);
   2693 				}
   2694 				memmove(fs->string + fs->state - ll,
   2695 				        fs->string + fs->state,
   2696 				        strlen(cast_const_char(fs->string
   2697 				                               + fs->state))
   2698 				            + 1);
   2699 				fs->state -= ll;
   2700 			}
   2701 		} else if (ev->x == KBD_DEL
   2702 		           || (upcase(ev->x) == 'D' && ev->y & KBD_CTRL)) {
   2703 			int ll = 1;
   2704 			if (ev->x == KBD_DEL && !f->last_captured)
   2705 				return 0;
   2706 			if (!f->f_data->opt.cp) {
   2707 				unsigned char *p = fs->string + fs->state;
   2708 				FWD_UTF_8(p);
   2709 				ll = (int)(p - (fs->string + fs->state));
   2710 			}
   2711 			if (!form->ro
   2712 			    && (size_t)fs->state
   2713 			           < strlen(cast_const_char fs->string)) {
   2714 				free_format_text_cache_entry(fs);
   2715 				memmove(fs->string + fs->state,
   2716 				        fs->string + fs->state + ll,
   2717 				        strlen(cast_const_char(
   2718 					    fs->string + fs->state + ll))
   2719 				            + 1);
   2720 			}
   2721 		} else if (upcase(ev->x) == 'U' && ev->y & KBD_CTRL) {
   2722 			unsigned char *a;
   2723 			a = memacpy(fs->string, fs->state);
   2724 			set_clipboard_text(ses->term, a);
   2725 			free(a);
   2726 			if (!form->ro) {
   2727 				free_format_text_cache_entry(fs);
   2728 				memmove(fs->string, fs->string + fs->state,
   2729 				        strlen(cast_const_char(fs->string
   2730 				                               + fs->state))
   2731 				            + 1);
   2732 			}
   2733 			fs->state = 0;
   2734 		} else if (upcase(ev->x) == 'K' && ev->y & KBD_CTRL) {
   2735 			if (!form->ro) {
   2736 				if (form->type == FC_TEXTAREA) {
   2737 					ftce = format_text(f, form, fs);
   2738 					y = find_cursor_line(ftce, fs->state);
   2739 					if (y >= 0) {
   2740 						int l = ftce->ln[y].en_offs
   2741 						        - ftce->ln[y].st_offs;
   2742 						unsigned char *start_line =
   2743 						    fs->string
   2744 						    + ftce->ln[y].st_offs;
   2745 						unsigned char *cp = memacpy(
   2746 						    start_line,
   2747 						    ftce->ln[y].en_offs
   2748 							- ftce->ln[y].st_offs);
   2749 						set_clipboard_text(ses->term,
   2750 						                   cp);
   2751 						free(cp);
   2752 						l += y < ftce->n_lines - 1
   2753 						     && ftce->ln[y + 1].st_offs
   2754 						            > ftce->ln[y]
   2755 						                  .en_offs;
   2756 						memmove(
   2757 						    fs->string
   2758 							+ ftce->ln[y].st_offs,
   2759 						    fs->string
   2760 							+ ftce->ln[y].st_offs
   2761 							+ l,
   2762 						    strlen(cast_const_char(
   2763 							fs->string
   2764 							+ ftce->ln[y].st_offs
   2765 							+ l))
   2766 							+ 1);
   2767 						fs->state = ftce->ln[y].st_offs;
   2768 					}
   2769 				} else {
   2770 					set_clipboard_text(
   2771 					    ses->term, fs->state + fs->string);
   2772 					fs->string[fs->state] = 0;
   2773 				}
   2774 				free_format_text_cache_entry(fs);
   2775 			}
   2776 		} else {
   2777 b:
   2778 			f->vs->brl_in_field = 0;
   2779 			r = 0;
   2780 		}
   2781 	} else if (ev->ev == EV_MOUSE && BM_IS_WHEEL(ev->b)
   2782 	           && form->type == FC_TEXTAREA) {
   2783 		int xdiff = 0, ydiff = 0;
   2784 		int x;
   2785 		unsigned char *ap;
   2786 
   2787 		ftce = format_text(f, form, fs);
   2788 		y = find_cursor_line(ftce, fs->state);
   2789 		if (y >= 0)
   2790 			x = textptr_diff(fs->string + fs->state,
   2791 			                 fs->string + ftce->ln[y].st_offs,
   2792 			                 f->f_data->opt.cp);
   2793 		else {
   2794 			x = 0;
   2795 			y = 0;
   2796 		}
   2797 
   2798 		if ((ev->b & BM_BUTT) == B_WHEELUP)
   2799 			ydiff = form->rows >= 5 ? -5 : -form->rows;
   2800 		else if ((ev->b & BM_BUTT) == B_WHEELUP1)
   2801 			ydiff = -1;
   2802 		else if ((ev->b & BM_BUTT) == B_WHEELDOWN)
   2803 			ydiff = form->rows >= 5 ? 5 : form->rows;
   2804 		else if ((ev->b & BM_BUTT) == B_WHEELDOWN1)
   2805 			ydiff = 1;
   2806 		else if ((ev->b & BM_BUTT) == B_WHEELLEFT)
   2807 			xdiff = form->cols >= 5 ? -5 : -form->cols;
   2808 		else if ((ev->b & BM_BUTT) == B_WHEELLEFT1)
   2809 			xdiff = -1;
   2810 		else if ((ev->b & BM_BUTT) == B_WHEELRIGHT)
   2811 			xdiff = form->cols >= 5 ? 5 : form->cols;
   2812 		else if ((ev->b & BM_BUTT) == B_WHEELRIGHT1)
   2813 			xdiff = 1;
   2814 
   2815 		if (ydiff) {
   2816 			int target_y = -1;
   2817 			fs->vypos += ydiff;
   2818 			if (fs->vypos > ftce->n_lines - form->rows)
   2819 				fs->vypos = ftce->n_lines - form->rows;
   2820 			if (fs->vypos < 0)
   2821 				fs->vypos = 0;
   2822 			if (y >= form->rows + fs->vypos) {
   2823 				target_y = form->rows + fs->vypos - 1;
   2824 				if (target_y < 0)
   2825 					target_y = 0;
   2826 			}
   2827 			if (y < fs->vypos)
   2828 				target_y = fs->vypos;
   2829 
   2830 			if (target_y >= 0) {
   2831 				if (target_y >= ftce->n_lines)
   2832 					target_y = ftce->n_lines - 1;
   2833 				fs->state = ftce->ln[target_y].st_offs;
   2834 				if (x > ftce->ln[target_y].chars)
   2835 					x = ftce->ln[target_y].chars;
   2836 				ap = textptr_add(fs->string + fs->state, x,
   2837 				                 f->f_data->opt.cp);
   2838 				fs->state = (int)(ap - fs->string);
   2839 			}
   2840 		} else if (xdiff) {
   2841 			int j;
   2842 			int maxx = 0;
   2843 			int longest = y;
   2844 			for (j = fs->vypos;
   2845 			     j < fs->vypos + form->rows && j < ftce->n_lines;
   2846 			     j++) {
   2847 				if (ftce->ln[j].chars > maxx) {
   2848 					maxx = ftce->ln[j].chars;
   2849 					longest = j;
   2850 				}
   2851 			}
   2852 			maxx -= form->cols - 1;
   2853 			if (maxx < 0)
   2854 				maxx = 0;
   2855 			fs->vpos += xdiff;
   2856 			if (fs->vpos < 0)
   2857 				fs->vpos = 0;
   2858 			if (fs->vpos > maxx)
   2859 				fs->vpos = maxx;
   2860 			if (x > fs->vpos + form->cols - 1) {
   2861 				ap = textptr_add(fs->string
   2862 				                     + ftce->ln[y].st_offs,
   2863 				                 fs->vpos + form->cols - 1,
   2864 				                 f->f_data->opt.cp);
   2865 				fs->state = (int)(ap - fs->string);
   2866 			} else if (x < fs->vpos) {
   2867 				ap = textptr_add(fs->string
   2868 				                     + ftce->ln[y].st_offs,
   2869 				                 fs->vpos, f->f_data->opt.cp);
   2870 				if (y < ftce->n_lines - 1
   2871 				    && ap >= fs->string
   2872 				                 + ftce->ln[y + 1].st_offs)
   2873 					ap = textptr_add(
   2874 					    fs->string
   2875 						+ ftce->ln[longest].st_offs,
   2876 					    fs->vpos, f->f_data->opt.cp);
   2877 				fs->state = (int)(ap - fs->string);
   2878 			}
   2879 		}
   2880 		goto done;
   2881 	} else
   2882 		r = 0;
   2883 done:
   2884 	if (r) {
   2885 		/* FIXME: test might be broken */
   2886 		if ((ev->ev == EV_KBD && (ev->x == KBD_UP || ev->x == KBD_DOWN))
   2887 		    || form->type != FC_TEXTAREA
   2888 		    || textarea_adjust_viewport(f, l))
   2889 			x_draw_form_entry(ses, f, l);
   2890 	}
   2891 	return r;
   2892 }
   2893 
   2894 void
   2895 set_textarea(struct session *ses, struct f_data_c *f, int dir)
   2896 {
   2897 	struct link *l = get_current_link(f);
   2898 	if (l && l->type == L_AREA) {
   2899 		struct form_control *form = l->form;
   2900 		struct form_state *fs;
   2901 		struct format_text_cache_entry *ftce;
   2902 		int y;
   2903 
   2904 		if (form->ro == 2)
   2905 			return;
   2906 		fs = find_form_state(f, form);
   2907 		if (!fs->string)
   2908 			return;
   2909 
   2910 		ftce = format_text(f, form, fs);
   2911 		y = find_cursor_line(ftce, fs->state);
   2912 		if (y >= 0) {
   2913 			int ty = dir < 0 ? 0 : ftce->n_lines - 1;
   2914 			if (ty < 0)
   2915 				return;
   2916 
   2917 			fs->state =
   2918 			    (int)(textptr_add(
   2919 				      fs->string + ftce->ln[ty].st_offs,
   2920 				      textptr_diff(fs->string + fs->state,
   2921 			                           fs->string
   2922 			                               + ftce->ln[y].st_offs,
   2923 			                           f->f_data->opt.cp),
   2924 				      f->f_data->opt.cp)
   2925 			          - fs->string);
   2926 			if (fs->state > ftce->ln[ty].en_offs)
   2927 				fs->state = ftce->ln[ty].en_offs;
   2928 		}
   2929 	}
   2930 }
   2931 
   2932 void
   2933 search_for_back(void *ses_, unsigned char *str)
   2934 {
   2935 	struct session *ses = (struct session *)ses_;
   2936 	struct f_data_c *f = current_frame(ses);
   2937 	if (!f || !str || !str[0])
   2938 		return;
   2939 	free(ses->search_word);
   2940 	ses->search_word = stracpy(str);
   2941 	clr_spaces(ses->search_word, 0);
   2942 	charset_upcase_string(&ses->search_word, 0);
   2943 	free(ses->last_search_word);
   2944 	ses->last_search_word = stracpy(ses->search_word);
   2945 	ses->search_direction = -1;
   2946 	find_next(ses, f, 1);
   2947 }
   2948 
   2949 void
   2950 search_for(void *ses_, unsigned char *str)
   2951 {
   2952 	struct session *ses = (struct session *)ses_;
   2953 	struct f_data_c *f = current_frame(ses);
   2954 	if (!f || !f->vs || !f->f_data || !str || !str[0])
   2955 		return;
   2956 	free(ses->search_word);
   2957 	ses->search_word = stracpy(str);
   2958 	clr_spaces(ses->search_word, 0);
   2959 	charset_upcase_string(&ses->search_word, 0);
   2960 	free(ses->last_search_word);
   2961 	ses->last_search_word = stracpy(ses->search_word);
   2962 	ses->search_direction = 1;
   2963 	find_next(ses, f, 1);
   2964 }
   2965 
   2966 #define HASH_SIZE 4096
   2967 
   2968 #define HASH(p) (((p.y << 6) + p.x) & (HASH_SIZE - 1))
   2969 
   2970 static int
   2971 point_intersect(struct point *p1, int l1, struct point *p2, int l2)
   2972 {
   2973 	int i, j;
   2974 	static unsigned char hash[HASH_SIZE];
   2975 	static unsigned char init = 0;
   2976 	if (!init) {
   2977 		memset(hash, 0, HASH_SIZE);
   2978 		init = 1;
   2979 	}
   2980 	for (i = 0; i < l1; i++)
   2981 		hash[HASH(p1[i])] = 1;
   2982 	for (j = 0; j < l2; j++)
   2983 		if (hash[HASH(p2[j])]) {
   2984 			for (i = 0; i < l1; i++)
   2985 				if (p1[i].x == p2[j].x && p1[i].y == p2[j].y) {
   2986 					for (i = 0; i < l1; i++)
   2987 						hash[HASH(p1[i])] = 0;
   2988 					return 1;
   2989 				}
   2990 		}
   2991 	for (i = 0; i < l1; i++)
   2992 		hash[HASH(p1[i])] = 0;
   2993 	return 0;
   2994 }
   2995 
   2996 static int
   2997 find_next_link_in_search(struct f_data_c *f, int d)
   2998 {
   2999 	struct point *pt;
   3000 	int len;
   3001 	struct link *link;
   3002 	if (d == -2 || d == 2) {
   3003 		d /= 2;
   3004 		find_link(f, d, 0);
   3005 		if (f->vs->current_link == -1)
   3006 			return 1;
   3007 	} else
   3008 nx:
   3009 		if (f->vs->current_link == -1
   3010 		    || !(next_in_view(f, f->vs->current_link + d, d, in_view,
   3011 		                      NULL))) {
   3012 			find_link(f, d, 0);
   3013 			return 1;
   3014 		}
   3015 	link = &f->f_data->links[f->vs->current_link];
   3016 	if (get_searched(f, &pt, &len) < 0)
   3017 		return 1;
   3018 	if (point_intersect(pt, len, link->pos, link->n)) {
   3019 		free(pt);
   3020 		return 0;
   3021 	}
   3022 	free(pt);
   3023 	goto nx;
   3024 }
   3025 
   3026 void
   3027 find_next(struct session *ses, struct f_data_c *f, int a)
   3028 {
   3029 	int min, max;
   3030 	int c = 0;
   3031 	int p;
   3032 	if (!f->f_data || !f->vs) {
   3033 		msg_box(ses->term, NULL, TEXT_(T_SEARCH), AL_CENTER,
   3034 		        TEXT_(T_YOU_ARE_NOWHERE), MSG_BOX_END, NULL, 1,
   3035 		        TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC);
   3036 		return;
   3037 	}
   3038 	p = f->vs->view_pos;
   3039 	if (!a && ses->search_word) {
   3040 		if (!(find_next_link_in_search(f, ses->search_direction)))
   3041 			return;
   3042 		p += ses->search_direction * f->yw;
   3043 	}
   3044 	if (!ses->search_word) {
   3045 		if (!ses->last_search_word) {
   3046 			msg_box(ses->term, NULL, TEXT_(T_SEARCH), AL_CENTER,
   3047 			        TEXT_(T_NO_PREVIOUS_SEARCH), MSG_BOX_END, NULL,
   3048 			        1, TEXT_(T_CANCEL), msg_box_null,
   3049 			        B_ENTER | B_ESC);
   3050 			return;
   3051 		}
   3052 		ses->search_word = stracpy(ses->last_search_word);
   3053 	}
   3054 	print_progress(ses, TEXT_(T_SEARCHING));
   3055 	if (get_search_data(f->f_data) < 0) {
   3056 		free(ses->search_word);
   3057 		ses->search_word = NULL;
   3058 		msg_box(ses->term, NULL, TEXT_(T_SEARCH), AL_CENTER,
   3059 		        TEXT_(T_OUT_OF_MEMORY), MSG_BOX_END, NULL, 1,
   3060 		        TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC);
   3061 		return;
   3062 	}
   3063 	do {
   3064 		if (is_in_range(f->f_data, p, f->yw, ses->search_word, &min,
   3065 		                &max)) {
   3066 			f->vs->view_pos = p;
   3067 			if (max >= min) {
   3068 				if (max > f->vs->view_posx + f->xw)
   3069 					f->vs->view_posx = max - f->xw;
   3070 				if (min < f->vs->view_posx)
   3071 					f->vs->view_posx = min;
   3072 			}
   3073 			f->vs->orig_view_pos = f->vs->view_pos;
   3074 			f->vs->orig_view_posx = f->vs->view_posx;
   3075 			set_link(f);
   3076 			find_next_link_in_search(f, ses->search_direction * 2);
   3077 			return;
   3078 		}
   3079 		if ((p += ses->search_direction * f->yw) > f->f_data->y)
   3080 			p = 0;
   3081 		if (p < 0) {
   3082 			p = 0;
   3083 			while (p < f->f_data->y)
   3084 				p += f->yw ? f->yw : 1;
   3085 			p -= f->yw;
   3086 		}
   3087 	} while ((c += f->yw ? f->yw : 1) < f->f_data->y + f->yw);
   3088 	msg_box(ses->term, NULL, TEXT_(T_SEARCH), AL_CENTER,
   3089 	        TEXT_(T_SEARCH_STRING_NOT_FOUND), MSG_BOX_END, NULL, 1,
   3090 	        TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC);
   3091 }
   3092 
   3093 void
   3094 find_next_back(struct session *ses, struct f_data_c *f, int a)
   3095 {
   3096 	ses->search_direction = -ses->search_direction;
   3097 	find_next(ses, f, a);
   3098 	ses->search_direction = -ses->search_direction;
   3099 }
   3100 
   3101 static void
   3102 rep_ev(struct session *ses, struct f_data_c *fd,
   3103        void (*f)(struct session *, struct f_data_c *, int), int a)
   3104 {
   3105 	int i = ses->kbdprefix.rep ? ses->kbdprefix.rep_num : 1;
   3106 	while (i--)
   3107 		f(ses, fd, a);
   3108 }
   3109 
   3110 static struct link *
   3111 choose_mouse_link(struct f_data_c *f, struct links_event *ev)
   3112 {
   3113 	return get_link_at_location(f->f_data, ev->x + f->vs->view_posx,
   3114 	                            ev->y + f->vs->view_pos);
   3115 }
   3116 
   3117 static void
   3118 goto_link_number(void *ses_, unsigned char *num)
   3119 {
   3120 	struct session *ses = (struct session *)ses_;
   3121 	int n = atoi(cast_const_char num);
   3122 	struct f_data_c *f = current_frame(ses);
   3123 	struct link *link;
   3124 	if (!f || !f->vs)
   3125 		return;
   3126 	if (n < 0 || n > f->f_data->nlinks)
   3127 		return;
   3128 	f->vs->current_link = n - 1;
   3129 	f->vs->orig_link = f->vs->current_link;
   3130 	link = &f->f_data->links[f->vs->current_link];
   3131 	check_vs(f);
   3132 	f->vs->orig_view_pos = f->vs->view_pos;
   3133 	f->vs->orig_view_posx = f->vs->view_posx;
   3134 	if (link->type != L_AREA && link->type != L_FIELD)
   3135 		enter(ses, f, 0);
   3136 }
   3137 
   3138 /* l must be a valid link, ev must be a mouse event */
   3139 static int
   3140 find_pos_in_link(struct f_data_c *fd, struct link *l, struct links_event *ev,
   3141                  int *xx, int *yy)
   3142 {
   3143 	int a;
   3144 	int minx, miny;
   3145 	int found = 0;
   3146 
   3147 	if (!l->n)
   3148 		return 1;
   3149 	minx = l->pos[0].x;
   3150 	miny = l->pos[0].y;
   3151 	for (a = 0; a < l->n; a++) {
   3152 		if (l->pos[a].x < minx)
   3153 			minx = l->pos[a].x;
   3154 		if (l->pos[a].y < miny)
   3155 			miny = l->pos[a].y;
   3156 		if (l->pos[a].x - fd->vs->view_posx == ev->x
   3157 		    && l->pos[a].y - fd->vs->view_pos == ev->y) {
   3158 			(*xx = l->pos[a].x);
   3159 			(*yy = l->pos[a].y);
   3160 			found = 1;
   3161 		}
   3162 	}
   3163 	if (!found)
   3164 		return 1;
   3165 	*xx -= minx;
   3166 	*yy -= miny;
   3167 	return 0;
   3168 }
   3169 
   3170 static int
   3171 frame_ev(struct session *ses, struct f_data_c *fd, struct links_event *ev)
   3172 {
   3173 	int x = 1;
   3174 
   3175 	if (!fd || !fd->vs || !fd->f_data)
   3176 		return 0;
   3177 	if (fd->vs->current_link >= 0
   3178 	    && (fd->f_data->links[fd->vs->current_link].type == L_FIELD
   3179 	        || fd->f_data->links[fd->vs->current_link].type == L_AREA)) {
   3180 		if (field_op(ses, fd, &fd->f_data->links[fd->vs->current_link],
   3181 		             ev)) {
   3182 			fd->last_captured = 1;
   3183 			fd->vs->brl_in_field = 1;
   3184 			return 1;
   3185 		}
   3186 	}
   3187 	fd->last_captured = 0;
   3188 	if (ev->ev == EV_KBD && !(ev->y & KBD_PASTING)) {
   3189 		if (ev->x >= '0' + !ses->kbdprefix.rep && ev->x <= '9'
   3190 		    && (!fd->f_data->opt.num_links
   3191 		        || (ev->y & (KBD_CTRL | KBD_ALT)))) {
   3192 			if (!ses->kbdprefix.rep)
   3193 				ses->kbdprefix.rep_num = 0;
   3194 			if ((ses->kbdprefix.rep_num =
   3195 			         ses->kbdprefix.rep_num * 10 + ev->x - '0')
   3196 			    > 65536)
   3197 				ses->kbdprefix.rep_num = 65536;
   3198 			ses->kbdprefix.rep = 1;
   3199 			return 1;
   3200 		}
   3201 		if (ev->x == KBD_PAGE_DOWN
   3202 		    || (ev->x == ' ' && (!(ev->y & KBD_ALT)))
   3203 		    || (upcase(ev->x) == 'F' && ev->y & KBD_CTRL))
   3204 			rep_ev(ses, fd, page_down, 0);
   3205 		else if (ev->x == KBD_PAGE_UP
   3206 		         || (upcase(ev->x) == 'B' && (!(ev->y & KBD_ALT))))
   3207 			rep_ev(ses, fd, page_up, 0);
   3208 		else if (ev->x == KBD_DOWN)
   3209 			rep_ev(ses, fd, down, 0);
   3210 		else if (ev->x == KBD_UP)
   3211 			rep_ev(ses, fd, up, 0);
   3212 		/* Copy current link to clipboard */
   3213 		else if ((ev->x == KBD_INS && ev->y & KBD_CTRL)
   3214 		         || (upcase(ev->x) == 'C' && ev->y & KBD_CTRL)) {
   3215 			unsigned char *current_link = print_current_link(ses);
   3216 			if (current_link) {
   3217 				set_clipboard_text(ses->term, current_link);
   3218 				free(current_link);
   3219 			}
   3220 		} else if (ev->x == KBD_INS
   3221 		           || (upcase(ev->x) == 'P' && ev->y & KBD_CTRL)
   3222 		           || (ev->x == 'p' && !(ev->y & (KBD_CTRL | KBD_ALT))))
   3223 			rep_ev(ses, fd, scroll, -1 - !ses->kbdprefix.rep);
   3224 		else if (ev->x == KBD_DEL
   3225 		         || (upcase(ev->x) == 'N' && ev->y & KBD_CTRL)
   3226 		         || (ev->x == 'l' && !(ev->y & (KBD_CTRL | KBD_ALT))))
   3227 			rep_ev(ses, fd, scroll, 1 + !ses->kbdprefix.rep);
   3228 		else if (ev->x == '[')
   3229 			rep_ev(ses, fd, hscroll, -1 - 7 * !ses->kbdprefix.rep);
   3230 		else if (ev->x == ']')
   3231 			rep_ev(ses, fd, hscroll, 1 + 7 * !ses->kbdprefix.rep);
   3232 		else if (ev->x == KBD_HOME
   3233 		         || (upcase(ev->x) == 'A' && ev->y & KBD_CTRL))
   3234 			rep_ev(ses, fd, home, 0);
   3235 		else if (ev->x == KBD_END
   3236 		         || (upcase(ev->x) == 'E' && ev->y & KBD_CTRL))
   3237 			rep_ev(ses, fd, x_end, 0);
   3238 		else if (ev->x == KBD_RIGHT || ev->x == KBD_ENTER) {
   3239 			x = enter(ses, fd, 0);
   3240 		} else if (ev->x == '*') {
   3241 			ses->ds.images ^= 1;
   3242 			html_interpret_recursive(ses->screen);
   3243 			draw_formatted(ses);
   3244 		} else if (ev->x == 'i' && !(ev->y & KBD_ALT)) {
   3245 			frm_view_image(ses, fd);
   3246 		} else if (ev->x == 'I' && !(ev->y & KBD_ALT)) {
   3247 			if (!anonymous)
   3248 				frm_download_image(ses, fd);
   3249 		} else if (upcase(ev->x) == 'D' && !(ev->y & KBD_ALT)) {
   3250 			if (!anonymous)
   3251 				frm_download(ses, fd);
   3252 		} else if (ev->x == '/'
   3253 		           || (ev->x == KBD_FIND
   3254 		               && !(ev->y & (KBD_SHIFT | KBD_CTRL | KBD_ALT))))
   3255 			search_dlg(ses, fd, 0);
   3256 		else if (ev->x == '?'
   3257 		         || (ev->x == KBD_FIND
   3258 		             && ev->y & (KBD_SHIFT | KBD_CTRL | KBD_ALT)))
   3259 			search_back_dlg(ses, fd, 0);
   3260 		else if ((ev->x == 'n' && !(ev->y & KBD_ALT))
   3261 		         || ev->x == KBD_REDO)
   3262 			find_next(ses, fd, 0);
   3263 		else if ((ev->x == 'N' && !(ev->y & KBD_ALT))
   3264 		         || ev->x == KBD_UNDO)
   3265 			find_next_back(ses, fd, 0);
   3266 		else if ((upcase(ev->x) == 'F'
   3267 		          && !(ev->y & (KBD_ALT | KBD_CTRL)))
   3268 		         || ev->x == KBD_FRONT)
   3269 			set_frame(ses, fd, 0);
   3270 		else if (ev->x == 'H' && !(ev->y & (KBD_CTRL | KBD_ALT)))
   3271 			find_link(fd, 1, 1);
   3272 		else if (ev->x == 'L' && !(ev->y & (KBD_CTRL | KBD_ALT)))
   3273 			find_link(fd, -1, 1);
   3274 		else if (ev->x >= '1' && ev->x <= '9'
   3275 		         && !(ev->y & (KBD_CTRL | KBD_ALT))) {
   3276 			struct f_data *f_data = fd->f_data;
   3277 			int nl, lnl;
   3278 			unsigned char d[2];
   3279 			d[0] = (unsigned char)ev->x;
   3280 			d[1] = 0;
   3281 			nl = f_data->nlinks;
   3282 			lnl = 1;
   3283 			while (nl) {
   3284 				nl /= 10;
   3285 				lnl++;
   3286 			}
   3287 			if (lnl > 1)
   3288 				input_field(
   3289 				    ses->term, NULL, TEXT_(T_GO_TO_LINK),
   3290 				    TEXT_(T_ENTER_LINK_NUMBER), ses, NULL, lnl,
   3291 				    d, 1, f_data->nlinks, check_number, 2,
   3292 				    TEXT_(T_OK), goto_link_number,
   3293 				    TEXT_(T_CANCEL), input_field_null);
   3294 		} else
   3295 			x = 0;
   3296 	} else if (ev->ev == EV_MOUSE && (ev->b & BM_BUTT) == B_WHEELUP)
   3297 		rep_ev(ses, fd, scroll, -1 - 5 * !ses->kbdprefix.rep);
   3298 	else if (ev->ev == EV_MOUSE && (ev->b & BM_BUTT) == B_WHEELDOWN)
   3299 		rep_ev(ses, fd, scroll, 1 + 5 * !ses->kbdprefix.rep);
   3300 	else if (ev->ev == EV_MOUSE && (ev->b & BM_BUTT) == B_WHEELLEFT)
   3301 		rep_ev(ses, fd, hscroll, -1 - 3 * !ses->kbdprefix.rep);
   3302 	else if (ev->ev == EV_MOUSE && (ev->b & BM_BUTT) == B_WHEELRIGHT)
   3303 		rep_ev(ses, fd, hscroll, 1 + 3 * !ses->kbdprefix.rep);
   3304 	else if (ev->ev == EV_MOUSE && (ev->b & BM_BUTT) <= B_RIGHT) {
   3305 		struct link *l = choose_mouse_link(fd, ev);
   3306 		if (l) {
   3307 			x = 1;
   3308 			fd->vs->current_link = (int)(l - fd->f_data->links);
   3309 			fd->vs->orig_link = fd->vs->current_link;
   3310 			if (l->type == L_LINK || l->type == L_BUTTON
   3311 			    || l->type == L_CHECKBOX || l->type == L_SELECT)
   3312 				if ((ev->b & BM_ACT) == B_UP) {
   3313 					fd->active = 1;
   3314 					draw_to_window(ses->win, draw_doc_c,
   3315 					               fd);
   3316 					change_screen_status(ses);
   3317 					print_screen_status(ses);
   3318 					if ((ev->b & BM_BUTT) == B_LEFT)
   3319 						x = enter(ses, fd, 0);
   3320 					else
   3321 						link_menu(ses->term, NULL, ses);
   3322 				}
   3323 
   3324 			set_form_position(fd, l, ev);
   3325 		}
   3326 	} else
   3327 		x = 0;
   3328 	ses->kbdprefix.rep = 0;
   3329 	return x;
   3330 }
   3331 
   3332 struct f_data_c *
   3333 current_frame(struct session *ses)
   3334 {
   3335 	struct f_data_c *fd, *fdd = NULL;
   3336 	struct list_head *lfdd;
   3337 	fd = ses->screen;
   3338 	while (!list_empty(fd->subframes)) {
   3339 		int n = fd->vs->frame_pos;
   3340 		if (n == -1)
   3341 			break;
   3342 		foreach (struct f_data_c, fdd, lfdd, fd->subframes)
   3343 			if (!n--) {
   3344 				fd = fdd;
   3345 				goto r;
   3346 			}
   3347 		fd = list_struct(fd->subframes.next, struct f_data_c);
   3348 r:;
   3349 	}
   3350 	return fd;
   3351 }
   3352 
   3353 static int
   3354 is_active_frame(struct session *ses, struct f_data_c *f)
   3355 {
   3356 	struct f_data_c *fd, *fdd = NULL;
   3357 	struct list_head *lfdd;
   3358 	fd = ses->screen;
   3359 	if (f == fd)
   3360 		return 1;
   3361 	while (!list_empty(fd->subframes)) {
   3362 		int n = fd->vs->frame_pos;
   3363 		if (n == -1)
   3364 			break;
   3365 		foreach (struct f_data_c, fdd, lfdd, fd->subframes)
   3366 			if (!n--) {
   3367 				fd = fdd;
   3368 				goto r;
   3369 			}
   3370 		fd = list_struct(fd->subframes.next, struct f_data_c);
   3371 r:
   3372 		if (f == fd)
   3373 			return 1;
   3374 	}
   3375 	return 0;
   3376 }
   3377 
   3378 static int
   3379 send_to_frame(struct session *ses, struct links_event *ev)
   3380 {
   3381 	int r;
   3382 	struct f_data_c *fd;
   3383 	fd = current_frame(ses);
   3384 	if (!fd)
   3385 		return 0;
   3386 
   3387 	r = frame_ev(ses, fd, ev);
   3388 	if (r == 1) {
   3389 		fd->active = 1;
   3390 		draw_to_window(ses->win, draw_doc_c, fd);
   3391 		change_screen_status(ses);
   3392 		print_screen_status(ses);
   3393 	}
   3394 	if (r == 3)
   3395 		draw_fd_nrd(fd);
   3396 	return r;
   3397 }
   3398 
   3399 void
   3400 next_frame(struct session *ses, int p)
   3401 {
   3402 	int n;
   3403 	struct view_state *vs;
   3404 	struct f_data_c *fd, *fdd = NULL;
   3405 	struct list_head *lfdd;
   3406 
   3407 	if (!(fd = current_frame(ses)))
   3408 		return;
   3409 	while ((fd = fd->parent)) {
   3410 		n = (int)list_size(&fd->subframes);
   3411 		vs = fd->vs;
   3412 		vs->frame_pos += p;
   3413 		if (vs->frame_pos < -!fd->f_data->frame_desc) {
   3414 			vs->frame_pos = n - 1;
   3415 			continue;
   3416 		}
   3417 		if (vs->frame_pos >= n) {
   3418 			vs->frame_pos = -!fd->f_data->frame_desc;
   3419 			continue;
   3420 		}
   3421 		break;
   3422 	}
   3423 	if (!fd)
   3424 		fd = ses->screen;
   3425 	vs = fd->vs;
   3426 	n = 0;
   3427 	foreach (struct f_data_c, fdd, lfdd, fd->subframes)
   3428 		if (n++ == vs->frame_pos) {
   3429 			fd = fdd;
   3430 next_sub:
   3431 			if (list_empty(fd->subframes))
   3432 				break;
   3433 			fd = list_struct(p < 0 ? fd->subframes.prev
   3434 			                       : fd->subframes.next,
   3435 			                 struct f_data_c);
   3436 			vs = fd->vs;
   3437 			vs->frame_pos = -1;
   3438 			if (!fd->f_data || (!fd->f_data->frame_desc && p > 0))
   3439 				break;
   3440 			if (p < 0)
   3441 				vs->frame_pos += (int)list_size(&fd->subframes);
   3442 			else
   3443 				vs->frame_pos = 0;
   3444 			goto next_sub;
   3445 		}
   3446 }
   3447 
   3448 void
   3449 do_for_frame(struct session *ses,
   3450              void (*f)(struct session *, struct f_data_c *, int), int a)
   3451 {
   3452 	struct f_data_c *fd = current_frame(ses);
   3453 	if (!fd)
   3454 		return;
   3455 	f(ses, fd, a);
   3456 	fd->active = 1;
   3457 	draw_to_window(ses->win, draw_doc_c, fd);
   3458 	change_screen_status(ses);
   3459 	print_screen_status(ses);
   3460 }
   3461 
   3462 static void
   3463 do_mouse_event(struct session *ses, struct links_event *ev)
   3464 {
   3465 	struct links_event evv;
   3466 	struct f_data_c *fdd, *fd = current_frame(ses);
   3467 	if (!fd)
   3468 		return;
   3469 	if (ev->x >= fd->xp && ev->x < fd->xp + fd->xw && ev->y >= fd->yp
   3470 	    && ev->y < fd->yp + fd->yw)
   3471 		goto ok;
   3472 r:
   3473 	next_frame(ses, 1);
   3474 	fdd = current_frame(ses);
   3475 	/*o = &fdd->f_data->opt;*/
   3476 	if (ev->x >= fdd->xp && ev->x < fdd->xp + fdd->xw && ev->y >= fdd->yp
   3477 	    && ev->y < fdd->yp + fdd->yw) {
   3478 		draw_formatted(ses);
   3479 		fd = fdd;
   3480 		goto ok;
   3481 	}
   3482 	if (fdd != fd)
   3483 		goto r;
   3484 	return;
   3485 ok:
   3486 	memcpy(&evv, ev, sizeof(struct links_event));
   3487 	evv.x -= fd->xp;
   3488 	evv.y -= fd->yp;
   3489 	send_to_frame(ses, &evv);
   3490 }
   3491 
   3492 void
   3493 send_event(struct session *ses, struct links_event *ev)
   3494 {
   3495 	if (ses->brl_cursor_mode) {
   3496 		ses->brl_cursor_mode = 0;
   3497 		print_screen_status(ses);
   3498 	}
   3499 	if (ev->ev == EV_KBD) {
   3500 		if (send_to_frame(ses, ev))
   3501 			return;
   3502 		if (ev->y & KBD_PASTING)
   3503 			goto x;
   3504 		if (ev->y & KBD_ALT && ev->x != KBD_TAB
   3505 		    && !KBD_ESCAPE_MENU(ev->x)) {
   3506 			struct window *m;
   3507 			ev->y &= ~KBD_ALT;
   3508 			activate_bfu_technology(ses, -1);
   3509 			m = list_struct(ses->term->windows.next, struct window);
   3510 			m->handler(m, ev, 0);
   3511 			if (ses->term->windows.next == &m->list_entry) {
   3512 				delete_window(m);
   3513 			} else
   3514 				goto x;
   3515 			ev->y |= KBD_ALT;
   3516 		}
   3517 		if (ev->x == KBD_F1 || ev->x == KBD_HELP) {
   3518 			activate_keys(ses);
   3519 			goto x;
   3520 		}
   3521 		if (ev->x == KBD_ESC || ev->x == KBD_F9) {
   3522 			activate_bfu_technology(ses, -1);
   3523 			goto x;
   3524 		}
   3525 		if (ev->x == KBD_F10) {
   3526 			activate_bfu_technology(ses, 0);
   3527 			goto x;
   3528 		}
   3529 		if (ev->x == KBD_TAB) {
   3530 			next_frame(ses, ev->y ? -1 : 1);
   3531 			draw_formatted(ses);
   3532 		}
   3533 		if (ev->x == KBD_LEFT) {
   3534 			go_back(ses, 1);
   3535 			goto x;
   3536 		}
   3537 		if ((upcase(ev->x) == 'Z' && !(ev->y & (KBD_CTRL | KBD_ALT)))
   3538 		    || ev->x == KBD_BS || ev->x == KBD_BACK) {
   3539 			go_back(ses, 1);
   3540 			goto x;
   3541 		}
   3542 		if ((upcase(ev->x) == 'X' && !(ev->y & (KBD_CTRL | KBD_ALT)))
   3543 		    || ev->x == '\'' || ev->x == KBD_FORWARD) {
   3544 			go_back(ses, -1);
   3545 			goto x;
   3546 		}
   3547 		if ((upcase(ev->x) == 'R' && ev->y & KBD_CTRL)
   3548 		    || ev->x == KBD_RELOAD) {
   3549 			reload(ses, -1);
   3550 			goto x;
   3551 		}
   3552 		if ((ev->x == 'g' && !(ev->y & (KBD_CTRL | KBD_ALT)))
   3553 		    || (ev->x == KBD_OPEN
   3554 		        && !(ev->y & (KBD_SHIFT | KBD_CTRL)))) {
   3555 quak:
   3556 			dialog_goto_url(ses, cast_uchar "");
   3557 			goto x;
   3558 		}
   3559 		if ((ev->x == 'G' && !(ev->y & (KBD_CTRL | KBD_ALT)))
   3560 		    || (ev->x == KBD_OPEN && ev->y & KBD_SHIFT)) {
   3561 			unsigned char *s;
   3562 			if (list_empty(ses->history) || !ses->screen->rq->url)
   3563 				goto quak;
   3564 			s = display_url(ses->term, ses->screen->rq->url, 0);
   3565 			dialog_goto_url(ses, s);
   3566 			free(s);
   3567 			goto x;
   3568 		}
   3569 		if ((upcase(ev->x) == 'G' && ev->y & KBD_CTRL)
   3570 		    || (ev->x == KBD_OPEN && ev->y & KBD_CTRL)) {
   3571 			struct f_data_c *fd = current_frame(ses);
   3572 			unsigned char *s;
   3573 			if (!fd->vs || !fd->f_data || fd->vs->current_link < 0
   3574 			    || fd->vs->current_link >= fd->f_data->nlinks)
   3575 				goto quak;
   3576 			s = display_url(
   3577 			    ses->term,
   3578 			    fd->f_data->links[fd->vs->current_link].where, 0);
   3579 			dialog_goto_url(ses, s);
   3580 			free(s);
   3581 			goto x;
   3582 		}
   3583 		if (ev->x == KBD_PROPS) {
   3584 			dialog_html_options(ses);
   3585 			goto x;
   3586 		}
   3587 		if ((upcase(ev->x) == 'S' && !(ev->y & (KBD_CTRL | KBD_ALT)))
   3588 		    || ev->x == KBD_BOOKMARKS) {
   3589 			if (!anonymous)
   3590 				menu_bookmark_manager(ses->term, NULL, ses);
   3591 			goto x;
   3592 		}
   3593 		if ((upcase(ev->x) == 'Q' && !(ev->y & (KBD_CTRL | KBD_ALT)))
   3594 		    || ev->x == KBD_CTRL_C) {
   3595 			exit_prog(
   3596 			    ses->term,
   3597 			    (int *)(long)(ev->x == KBD_CTRL_C || ev->x == 'Q'),
   3598 			    ses);
   3599 			goto x;
   3600 		}
   3601 		if (ev->x == KBD_CLOSE) {
   3602 			really_exit_prog(ses);
   3603 			goto x;
   3604 		}
   3605 		if (ev->x == '=') {
   3606 			state_msg(ses);
   3607 			goto x;
   3608 		}
   3609 		if (ev->x == '|') {
   3610 			head_msg(ses);
   3611 			goto x;
   3612 		}
   3613 		if (ev->x == '\\') {
   3614 			toggle(ses, ses->screen, 0);
   3615 			goto x;
   3616 		}
   3617 	}
   3618 	if (ev->ev == EV_MOUSE) {
   3619 		if (ev->b == (B_DOWN | B_FOURTH)) {
   3620 			go_back(ses, 1);
   3621 			goto x;
   3622 		}
   3623 		if (ev->b == (B_DOWN | B_FIFTH)) {
   3624 			go_back(ses, -1);
   3625 			goto x;
   3626 		}
   3627 		if (ev->y >= 0 && ev->y < 1 && ev->x >= 0
   3628 		    && ev->x < ses->term->x && (ev->b & BM_ACT) == B_DOWN) {
   3629 			struct window *m;
   3630 			activate_bfu_technology(ses, -1);
   3631 			m = list_struct(ses->term->windows.next, struct window);
   3632 			m->handler(m, ev, 0);
   3633 			goto x;
   3634 		}
   3635 		do_mouse_event(ses, ev);
   3636 	}
   3637 	return;
   3638 x:
   3639 	ses->kbdprefix.rep = 0;
   3640 }
   3641 
   3642 static void
   3643 send_enter(struct terminal *term, void *xxx, void *ses_)
   3644 {
   3645 	struct session *ses = (struct session *)ses_;
   3646 	struct links_event ev = { EV_KBD, KBD_ENTER, 0, 0 };
   3647 	send_event(ses, &ev);
   3648 }
   3649 
   3650 void
   3651 frm_download(struct session *ses, struct f_data_c *fd)
   3652 {
   3653 	struct link *link = get_current_link(fd);
   3654 	if (!link)
   3655 		return;
   3656 	free(ses->dn_url);
   3657 	ses->dn_url = NULL;
   3658 	if (link->type != L_LINK && link->type != L_BUTTON)
   3659 		return;
   3660 	if ((ses->dn_url = get_link_url(ses, fd, link, NULL))) {
   3661 		ses->dn_allow_flags = f_data_c_allow_flags(fd);
   3662 		if (!casecmp(ses->dn_url, cast_uchar "MAP@", 4)) {
   3663 			free(ses->dn_url);
   3664 			ses->dn_url = NULL;
   3665 			return;
   3666 		}
   3667 		query_file(ses, ses->dn_url, NULL, start_download, NULL,
   3668 		           DOWNLOAD_CONTINUE);
   3669 	}
   3670 }
   3671 
   3672 void
   3673 frm_view_image(struct session *ses, struct f_data_c *fd)
   3674 {
   3675 	struct link *link = get_current_link(fd);
   3676 	if (!link)
   3677 		return;
   3678 	if (link->type != L_LINK && link->type != L_BUTTON)
   3679 		return;
   3680 	if (!link->where_img)
   3681 		return;
   3682 	goto_url_not_from_dialog(ses, link->where_img, fd);
   3683 }
   3684 
   3685 void
   3686 frm_download_image(struct session *ses, struct f_data_c *fd)
   3687 {
   3688 	struct link *link = get_current_link(fd);
   3689 	if (!link)
   3690 		return;
   3691 	free(ses->dn_url);
   3692 	ses->dn_url = NULL;
   3693 	if (link->type != L_LINK && link->type != L_BUTTON)
   3694 		return;
   3695 	if (!link->where_img)
   3696 		return;
   3697 	if ((ses->dn_url = stracpy(link->where_img))) {
   3698 		ses->dn_allow_flags = f_data_c_allow_flags(fd);
   3699 		if (!casecmp(ses->dn_url, cast_uchar "MAP@", 4)) {
   3700 			free(ses->dn_url);
   3701 			ses->dn_url = NULL;
   3702 			return;
   3703 		}
   3704 		query_file(ses, ses->dn_url, NULL, start_download, NULL,
   3705 		           DOWNLOAD_CONTINUE);
   3706 	}
   3707 }
   3708 
   3709 static void
   3710 send_download_image(struct terminal *term, void *xxx, void *ses_)
   3711 {
   3712 	struct session *ses = (struct session *)ses_;
   3713 	struct f_data_c *fd = current_frame(ses);
   3714 	struct link *link = get_current_link(fd);
   3715 	if (!link)
   3716 		return;
   3717 	free(ses->dn_url);
   3718 	if ((ses->dn_url = stracpy(link->where_img))) {
   3719 		ses->dn_allow_flags = f_data_c_allow_flags(fd);
   3720 		query_file(ses, ses->dn_url, NULL, start_download, NULL,
   3721 		           DOWNLOAD_CONTINUE);
   3722 	}
   3723 }
   3724 
   3725 static void
   3726 send_download(struct terminal *term, void *xxx, void *ses_)
   3727 {
   3728 	struct session *ses = (struct session *)ses_;
   3729 	struct f_data_c *fd = current_frame(ses);
   3730 	struct link *link = get_current_link(fd);
   3731 	if (!link)
   3732 		return;
   3733 	free(ses->dn_url);
   3734 	if ((ses->dn_url = get_link_url(ses, fd, link, NULL))) {
   3735 		ses->dn_allow_flags = f_data_c_allow_flags(fd);
   3736 		query_file(ses, ses->dn_url, NULL, start_download, NULL,
   3737 		           DOWNLOAD_CONTINUE);
   3738 	}
   3739 }
   3740 
   3741 static void
   3742 send_submit(struct terminal *term, void *xxx, void *ses_)
   3743 {
   3744 	struct session *ses = (struct session *)ses_;
   3745 	int has_onsubmit;
   3746 	struct form_control *form;
   3747 	struct f_data_c *fd = current_frame(ses);
   3748 	struct link *link = get_current_link(fd);
   3749 	unsigned char *u;
   3750 
   3751 	if (!link)
   3752 		return;
   3753 	if (!(form = link->form))
   3754 		return;
   3755 	u = get_form_url(ses, fd, form, &has_onsubmit);
   3756 	if (u) {
   3757 		goto_url_f(fd->ses, NULL, u, NULL, fd, form->form_num,
   3758 		           has_onsubmit, 0, 0);
   3759 		free(u);
   3760 	}
   3761 	draw_fd(fd);
   3762 }
   3763 
   3764 static void
   3765 send_reset(struct terminal *term, void *xxx, void *ses_)
   3766 {
   3767 	struct session *ses = ses_;
   3768 	struct form_control *form;
   3769 	struct f_data_c *fd = current_frame(ses);
   3770 	struct link *link = get_current_link(fd);
   3771 
   3772 	if (!link)
   3773 		return;
   3774 	if (!(form = link->form))
   3775 		return;
   3776 	reset_form(fd, form->form_num);
   3777 	draw_fd(fd);
   3778 }
   3779 
   3780 static void
   3781 copy_link_location(struct terminal *term, void *xxx, void *ses_)
   3782 {
   3783 	struct session *ses = ses_;
   3784 	unsigned char *current_link = print_current_link(ses);
   3785 
   3786 	if (current_link) {
   3787 		set_clipboard_text(term, current_link);
   3788 		free(current_link);
   3789 	}
   3790 }
   3791 
   3792 void
   3793 copy_url_location(struct terminal *term, void *xxx, void *ses_)
   3794 {
   3795 	struct session *ses = ses_;
   3796 	struct location *current_location;
   3797 	unsigned char *url;
   3798 
   3799 	if (list_empty(ses->history))
   3800 		return;
   3801 
   3802 	current_location = cur_loc(ses);
   3803 
   3804 	url = display_url(term, current_location->url, 0);
   3805 	set_clipboard_text(term, url);
   3806 	free(url);
   3807 }
   3808 
   3809 static void
   3810 cant_open_new_window(struct terminal *term)
   3811 {
   3812 	msg_box(term, NULL, TEXT_(T_NEW_WINDOW), AL_CENTER,
   3813 	        TEXT_(T_UNABLE_TO_OPEN_NEW_WINDOW), MSG_BOX_END, NULL, 1,
   3814 	        TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC);
   3815 }
   3816 
   3817 /* open a link in a new xterm, pass target frame name */
   3818 static void
   3819 send_open_in_new_xterm(struct terminal *term, void *open_window_, void *ses_)
   3820 {
   3821 	int (*open_window)(struct terminal *, unsigned char *,
   3822 	                   unsigned char *) =
   3823 	    *(int (*const *)(struct terminal *, unsigned char *,
   3824 	                     unsigned char *))open_window_;
   3825 	struct session *ses = (struct session *)ses_;
   3826 	struct f_data_c *fd = current_frame(ses);
   3827 	struct link *l;
   3828 	l = get_current_link(fd);
   3829 	if (!l)
   3830 		return;
   3831 	free(ses->dn_url);
   3832 	if ((ses->dn_url = get_link_url(ses, fd, l, NULL))) {
   3833 		unsigned char *p;
   3834 		size_t pl;
   3835 		unsigned char *enc_url;
   3836 		unsigned char *path;
   3837 
   3838 		ses->dn_allow_flags = f_data_c_allow_flags(fd);
   3839 		if (disallow_url(ses->dn_url, ses->dn_allow_flags)) {
   3840 			free(ses->dn_url);
   3841 			ses->dn_url = NULL;
   3842 			return;
   3843 		}
   3844 
   3845 		p = NULL;
   3846 		pl = add_to_str(&p, 0, cast_uchar "-base-session ");
   3847 		pl = add_num_to_str(&p, pl, ses->id);
   3848 		pl = add_chr_to_str(&p, pl, ' ');
   3849 
   3850 		if (ses->wtd_target && *ses->wtd_target) {
   3851 			unsigned char *tgt = stracpy(ses->wtd_target);
   3852 
   3853 			check_shell_security(&tgt);
   3854 			pl = add_to_str(&p, pl, cast_uchar "-target ");
   3855 			pl = add_to_str(&p, pl, tgt);
   3856 			pl = add_chr_to_str(&p, pl, ' ');
   3857 			free(tgt);
   3858 		}
   3859 		enc_url = encode_url(ses->dn_url);
   3860 		pl = add_to_str(&p, pl, enc_url);
   3861 		free(enc_url);
   3862 		path = escape_path(g_argv[0]);
   3863 		if (open_window(term, path, p))
   3864 			cant_open_new_window(term);
   3865 		free(p);
   3866 		free(path);
   3867 	}
   3868 }
   3869 
   3870 static void
   3871 send_open_new_xterm(struct terminal *term, void *open_window_, void *ses_)
   3872 {
   3873 	int (*open_window)(struct terminal *, unsigned char *,
   3874 	                   unsigned char *) =
   3875 	    *(int (*const *)(struct terminal *, unsigned char *,
   3876 	                     unsigned char *))open_window_;
   3877 	struct session *ses = ses_;
   3878 	unsigned char *p = NULL;
   3879 	size_t pl;
   3880 	unsigned char *path;
   3881 	pl = add_to_str(&p, 0, cast_uchar "-base-session ");
   3882 	pl = add_num_to_str(&p, pl, ses->id);
   3883 	path = escape_path(g_argv[0]);
   3884 	if (open_window(term, path, p))
   3885 		cant_open_new_window(term);
   3886 	free(path);
   3887 	free(p);
   3888 }
   3889 
   3890 void (*const send_open_new_xterm_ptr)(struct terminal *, void *fn_,
   3891                                       void *ses_) = send_open_new_xterm;
   3892 
   3893 void
   3894 open_in_new_window(struct terminal *term, void *fn_, void *ses_)
   3895 {
   3896 	struct session *ses = ses_;
   3897 	void (*fn)(struct terminal *, void *, void *) =
   3898 	    *(void (*const *)(struct terminal *, void *, void *))fn_;
   3899 	struct menu_item *mi;
   3900 	struct open_in_new *oin, *oi;
   3901 	if (!(oin = get_open_in_new(term->environment)))
   3902 		return;
   3903 	if (!oin[1].text) {
   3904 		fn(term, (void *)oin[0].open_window_fn, ses);
   3905 		free(oin);
   3906 		return;
   3907 	}
   3908 	mi = new_menu(MENU_FREE_ITEMS);
   3909 	for (oi = oin; oi->text; oi++)
   3910 		add_to_menu(&mi, oi->text, cast_uchar "", oi->hk, fn,
   3911 		            (void *)oi->open_window_fn, 0, -1);
   3912 	free(oin);
   3913 	do_menu(term, mi, ses);
   3914 }
   3915 
   3916 int
   3917 can_open_in_new(struct terminal *term)
   3918 {
   3919 	struct open_in_new *oin = get_open_in_new(term->environment);
   3920 	if (!oin)
   3921 		return 0;
   3922 	if (!oin[1].text) {
   3923 		free(oin);
   3924 		return 1;
   3925 	}
   3926 	free(oin);
   3927 	return 2;
   3928 }
   3929 
   3930 void
   3931 save_url(void *ses_, unsigned char *url)
   3932 {
   3933 	struct session *ses = (struct session *)ses_;
   3934 	unsigned char *u1, *u2;
   3935 	u1 = stracpy(url);
   3936 	u2 = translate_url(u1, ses->term->cwd);
   3937 	free(u1);
   3938 	if (!u2) {
   3939 		struct status stat = { init_list_1st(NULL) NULL,
   3940 			               NULL,
   3941 			               S_BAD_URL,
   3942 			               PRI_CANCEL,
   3943 			               0,
   3944 			               NULL,
   3945 			               NULL,
   3946 			               NULL };
   3947 		print_error_dialog(ses, &stat, url);
   3948 		return;
   3949 	}
   3950 	free(ses->dn_url);
   3951 	ses->dn_url = u2;
   3952 	ses->dn_allow_flags = ALLOW_ALL;
   3953 	query_file(ses, ses->dn_url, NULL, start_download, NULL,
   3954 	           DOWNLOAD_CONTINUE);
   3955 }
   3956 
   3957 static void
   3958 send_image(struct terminal *term, void *xxx, void *ses_)
   3959 {
   3960 	struct session *ses = ses_;
   3961 	unsigned char *u;
   3962 	struct f_data_c *fd = current_frame(ses);
   3963 	struct link *l;
   3964 	l = get_current_link(fd);
   3965 	if (!l)
   3966 		return;
   3967 	if (!(u = l->where_img))
   3968 		return;
   3969 	goto_url_not_from_dialog(ses, u, fd);
   3970 }
   3971 
   3972 void
   3973 save_as(struct terminal *term, void *xxx, void *ses_)
   3974 {
   3975 	struct session *ses = ses_;
   3976 	unsigned char *head;
   3977 	if (list_empty(ses->history))
   3978 		return;
   3979 	free(ses->dn_url);
   3980 	ses->dn_url = stracpy(ses->screen->rq->url);
   3981 	ses->dn_allow_flags = ALLOW_ALL;
   3982 	if (!ses->dn_url)
   3983 		return;
   3984 	head = stracpy(ses->screen->rq->ce ? ses->screen->rq->ce->head : NULL);
   3985 	if (head) {
   3986 		unsigned char *p, *q;
   3987 		/* remove Content-Encoding from the header */
   3988 		q = parse_http_header(head, cast_uchar "Content-Encoding", &p);
   3989 		if (q) {
   3990 			free(q);
   3991 			if (p > head
   3992 			    && p < (unsigned char *)strchr(cast_const_char head,
   3993 			                                   0)) {
   3994 				for (q = p - 1; q > head && *q != 10; q--)
   3995 					;
   3996 				q[1] = 'X';
   3997 			}
   3998 		}
   3999 	}
   4000 	query_file(ses, ses->dn_url, head, start_download, NULL,
   4001 	           DOWNLOAD_CONTINUE);
   4002 	free(head);
   4003 }
   4004 
   4005 static void
   4006 save_formatted(struct session *ses, unsigned char *file, int mode)
   4007 {
   4008 	int h, rs, err;
   4009 	struct f_data_c *f;
   4010 	int download_mode = mode == DOWNLOAD_DEFAULT ? CDF_EXCL : 0;
   4011 	if (!(f = current_frame(ses)) || !f->f_data)
   4012 		return;
   4013 	if ((h = create_download_file(ses, ses->term->cwd, file, download_mode,
   4014 	                              0))
   4015 	    < 0)
   4016 		return;
   4017 	if ((err = dump_to_file(f->f_data, h))) {
   4018 		msg_box(ses->term, NULL, TEXT_(T_SAVE_ERROR), AL_CENTER,
   4019 		        TEXT_(T_ERROR_WRITING_TO_FILE), cast_uchar ": ",
   4020 		        get_err_msg(err), MSG_BOX_END, NULL, 1, TEXT_(T_CANCEL),
   4021 		        msg_box_null, B_ENTER | B_ESC);
   4022 	}
   4023 	EINTRLOOP(rs, close(h));
   4024 }
   4025 
   4026 void
   4027 menu_save_formatted(struct terminal *term, void *xxx, void *ses_)
   4028 {
   4029 	struct session *ses = ses_;
   4030 	struct f_data_c *f;
   4031 	if (!(f = current_frame(ses)) || !f->f_data)
   4032 		return;
   4033 	query_file(ses, f->rq->url, NULL, save_formatted, NULL,
   4034 	           DOWNLOAD_OVERWRITE);
   4035 }
   4036 
   4037 void
   4038 link_menu(struct terminal *term, void *xxx, void *ses_)
   4039 {
   4040 	struct session *ses = ses_;
   4041 	struct f_data_c *f = current_frame(ses);
   4042 	struct link *link;
   4043 	struct menu_item *mi;
   4044 	free(ses->wtd_target);
   4045 	ses->wtd_target = NULL;
   4046 	mi = new_menu(MENU_FREE_ITEMS);
   4047 	link = get_current_link(f);
   4048 	if (!link)
   4049 		goto no_l;
   4050 	if (link->type == L_LINK && link->where) {
   4051 		if (strlen(cast_const_char link->where) >= 4
   4052 		    && !casecmp(link->where, cast_uchar "MAP@", 4)) {
   4053 			add_to_menu(&mi, TEXT_(T_DISPLAY_USEMAP),
   4054 			            cast_uchar ">", TEXT_(T_HK_DISPLAY_USEMAP),
   4055 			            send_enter, NULL, 1, -1);
   4056 		} else {
   4057 			int c = can_open_in_new(term);
   4058 			add_to_menu(&mi, TEXT_(T_FOLLOW_LINK),
   4059 			            cast_uchar "Enter", TEXT_(T_HK_FOLLOW_LINK),
   4060 			            send_enter, NULL, 0, -1);
   4061 			if (c)
   4062 				add_to_menu(&mi, TEXT_(T_OPEN_IN_NEW_WINDOW),
   4063 				            c - 1 ? cast_uchar ">"
   4064 				                  : cast_uchar "",
   4065 				            TEXT_(T_HK_OPEN_IN_NEW_WINDOW),
   4066 				            open_in_new_window,
   4067 				            (void *)&send_open_in_new_xterm_ptr,
   4068 				            c - 1, -1);
   4069 			if (!anonymous)
   4070 				add_to_menu(&mi, TEXT_(T_DOWNLOAD_LINK),
   4071 				            cast_uchar "d",
   4072 				            TEXT_(T_HK_DOWNLOAD_LINK),
   4073 				            send_download, NULL, 0, -1);
   4074 			if (clipboard_support(term))
   4075 				add_to_menu(&mi, TEXT_(T_COPY_LINK_LOCATION),
   4076 				            cast_uchar "",
   4077 				            TEXT_(T_HK_COPY_LINK_LOCATION),
   4078 				            copy_link_location, NULL, 0, -1);
   4079 		}
   4080 	}
   4081 	if ((link->type == L_CHECKBOX || link->type == L_SELECT
   4082 	     || link->type == L_FIELD || link->type == L_AREA)
   4083 	    && link->form) {
   4084 		int c = can_open_in_new(term);
   4085 		add_to_menu(&mi, TEXT_(T_SUBMIT_FORM), cast_uchar "",
   4086 		            TEXT_(T_HK_SUBMIT_FORM), send_submit, NULL, 0, -1);
   4087 		if (c && link->form->method == FM_GET)
   4088 			add_to_menu(
   4089 			    &mi, TEXT_(T_SUBMIT_FORM_AND_OPEN_IN_NEW_WINDOW),
   4090 			    c - 1 ? cast_uchar ">" : cast_uchar "",
   4091 			    TEXT_(T_HK_SUBMIT_FORM_AND_OPEN_IN_NEW_WINDOW),
   4092 			    open_in_new_window,
   4093 			    (void *)&send_open_in_new_xterm_ptr, c - 1, -1);
   4094 		/*if (!anonymous) add_to_menu(&mi,
   4095 		 * TEXT_(T_SUBMIT_FORM_AND_DOWNLOAD), cast_uchar "d",
   4096 		 * TEXT_(T_HK_SUBMIT_FORM_AND_DOWNLOAD), send_download, NULL, 0,
   4097 		 * -1);*/
   4098 		add_to_menu(&mi, TEXT_(T_RESET_FORM), cast_uchar "",
   4099 		            TEXT_(T_HK_RESET_FORM), send_reset, NULL, 0, -1);
   4100 	}
   4101 	if (link->type == L_BUTTON && link->form) {
   4102 		if (link->form->type == FC_RESET)
   4103 			add_to_menu(&mi, TEXT_(T_RESET_FORM), cast_uchar "",
   4104 			            TEXT_(T_HK_RESET_FORM), send_enter, NULL, 0,
   4105 			            -1);
   4106 		else if (link->form->type == FC_BUTTON)
   4107 			;
   4108 		else if (link->form->type == FC_SUBMIT
   4109 		         || link->form->type == FC_IMAGE) {
   4110 			int c = can_open_in_new(term);
   4111 			add_to_menu(&mi, TEXT_(T_SUBMIT_FORM), cast_uchar "",
   4112 			            TEXT_(T_HK_SUBMIT_FORM), send_enter, NULL,
   4113 			            0, -1);
   4114 			if (c && link->form->method == FM_GET)
   4115 				add_to_menu(
   4116 				    &mi,
   4117 				    TEXT_(T_SUBMIT_FORM_AND_OPEN_IN_NEW_WINDOW),
   4118 				    c - 1 ? cast_uchar ">" : cast_uchar "",
   4119 				    TEXT_(
   4120 					T_HK_SUBMIT_FORM_AND_OPEN_IN_NEW_WINDOW),
   4121 				    open_in_new_window,
   4122 				    (void *)&send_open_in_new_xterm_ptr, c - 1,
   4123 				    -1);
   4124 			if (!anonymous)
   4125 				add_to_menu(
   4126 				    &mi, TEXT_(T_SUBMIT_FORM_AND_DOWNLOAD),
   4127 				    cast_uchar "d",
   4128 				    TEXT_(T_HK_SUBMIT_FORM_AND_DOWNLOAD),
   4129 				    send_download, NULL, 0, -1);
   4130 		}
   4131 	}
   4132 	if (link->where_img) {
   4133 		add_to_menu(&mi, TEXT_(T_VIEW_IMAGE), cast_uchar "i",
   4134 		            TEXT_(T_HK_VIEW_IMAGE), send_image, NULL, 0, -1);
   4135 		if (!anonymous)
   4136 			add_to_menu(&mi, TEXT_(T_DOWNLOAD_IMAGE),
   4137 			            cast_uchar "I", TEXT_(T_HK_DOWNLOAD_IMAGE),
   4138 			            send_download_image, NULL, 0, -1);
   4139 	}
   4140 no_l:
   4141 	if (!mi->text)
   4142 		add_to_menu(&mi, TEXT_(T_NO_LINK_SELECTED), cast_uchar "",
   4143 		            M_BAR, NULL, NULL, 0, -1);
   4144 	do_menu(term, mi, ses);
   4145 }
   4146 
   4147 static unsigned char *
   4148 print_current_titlex(struct f_data_c *fd, int w)
   4149 {
   4150 	int mul, pul;
   4151 	size_t ml = 0, pl = 0;
   4152 	unsigned char *m, *p;
   4153 	if (!fd || !fd->vs || !fd->f_data)
   4154 		return NULL;
   4155 	w -= 1;
   4156 	p = NULL;
   4157 	if (fd->yw < fd->f_data->y) {
   4158 		int pp, pe;
   4159 		if (fd->yw) {
   4160 			pp = (fd->vs->view_pos + fd->yw / 2) / fd->yw + 1;
   4161 			pe = (fd->f_data->y + fd->yw - 1) / fd->yw;
   4162 		} else
   4163 			pp = pe = 1;
   4164 		if (pp > pe)
   4165 			pp = pe;
   4166 		if (fd->vs->view_pos + fd->yw >= fd->f_data->y)
   4167 			pp = pe;
   4168 		if (fd->f_data->title)
   4169 			pl = add_chr_to_str(&p, pl, ' ');
   4170 		pl = add_to_str(
   4171 		    &p, pl,
   4172 		    get_text_translation(TEXT_(T_PAGE_P), fd->ses->term));
   4173 		pl = add_num_to_str(&p, pl, pp);
   4174 		pl = add_to_str(
   4175 		    &p, pl,
   4176 		    get_text_translation(TEXT_(T_PAGE_OF), fd->ses->term));
   4177 		pl = add_num_to_str(&p, pl, pe);
   4178 		pl = add_to_str(
   4179 		    &p, pl,
   4180 		    get_text_translation(TEXT_(T_PAGE_CL), fd->ses->term));
   4181 	}
   4182 	if (!fd->f_data->title)
   4183 		return p;
   4184 	m = NULL;
   4185 	ml = add_to_str(&m, ml, fd->f_data->title);
   4186 	mul = strlen((char *)m);
   4187 	if (p != NULL)
   4188 		pul = strlen((char *)p);
   4189 	else
   4190 		pul = 0;
   4191 	if (mul + pul > w) {
   4192 		unsigned char *mm;
   4193 		if ((mul = w - pul) < 0)
   4194 			mul = 0;
   4195 		for (mm = m; mul--; GET_TERM_CHAR(fd->ses->term, &mm))
   4196 			;
   4197 		ml = (int)(mm - m);
   4198 	}
   4199 	if (p != NULL)
   4200 		ml = add_to_str(&m, ml, p);
   4201 	free(p);
   4202 	return m;
   4203 }
   4204 
   4205 static unsigned char *
   4206 print_current_linkx(struct f_data_c *fd, struct terminal *term)
   4207 {
   4208 	size_t ll = 0;
   4209 	struct link *l;
   4210 	unsigned char *d;
   4211 	unsigned char *m = NULL;
   4212 	if (!fd || !fd->vs || !fd->f_data)
   4213 		return NULL;
   4214 	if (fd->vs->current_link == -1
   4215 	    || fd->vs->current_link >= fd->f_data->nlinks
   4216 	    || fd->f_data->frame_desc)
   4217 		return NULL;
   4218 	l = &fd->f_data->links[fd->vs->current_link];
   4219 	if (l->type == L_LINK) {
   4220 		if (!l->where && l->where_img) {
   4221 			m = NULL;
   4222 			ll = 0;
   4223 			if (l->img_alt) {
   4224 				unsigned char *txt;
   4225 
   4226 				txt =
   4227 				    convert(fd->f_data->cp, fd->f_data->opt.cp,
   4228 				            l->img_alt, &fd->f_data->opt);
   4229 				ll = add_to_str(&m, ll, txt);
   4230 				free(txt);
   4231 			} else {
   4232 				ll = add_to_str(
   4233 				    &m, ll,
   4234 				    get_text_translation(TEXT_(T_IMAGE), term));
   4235 				ll = add_chr_to_str(&m, ll, ' ');
   4236 				d = display_url(term, l->where_img, 1);
   4237 				ll = add_to_str(&m, ll, d);
   4238 				free(d);
   4239 			}
   4240 			goto p;
   4241 		}
   4242 		if (l->where && strlen(cast_const_char l->where) >= 4
   4243 		    && !casecmp(l->where, cast_uchar "MAP@", 4)) {
   4244 			m = NULL;
   4245 			ll = 0;
   4246 			ll = add_to_str(
   4247 			    &m, ll,
   4248 			    get_text_translation(TEXT_(T_USEMAP), term));
   4249 			ll = add_chr_to_str(&m, ll, ' ');
   4250 			d = display_url(term, l->where + 4, 1);
   4251 			ll = add_to_str(&m, ll, d);
   4252 			free(d);
   4253 			goto p;
   4254 		}
   4255 		if (l->where) {
   4256 			m = display_url(term, l->where, 1);
   4257 			goto p;
   4258 		}
   4259 		m = stracpy((unsigned char *)"");
   4260 		goto p;
   4261 	}
   4262 	if (!l->form)
   4263 		return NULL;
   4264 	if (l->type == L_BUTTON) {
   4265 		if (l->form->type == FC_BUTTON) {
   4266 			m = NULL;
   4267 			ll = add_to_str(
   4268 			    &m, 0, get_text_translation(TEXT_(T_BUTTON), term));
   4269 			goto p;
   4270 		}
   4271 		if (l->form->type == FC_RESET) {
   4272 			m = stracpy(
   4273 			    get_text_translation(TEXT_(T_RESET_FORM), term));
   4274 			goto p;
   4275 		}
   4276 		if (!l->form->action)
   4277 			return NULL;
   4278 		m = NULL;
   4279 		if (l->form->method == FM_GET)
   4280 			ll = add_to_str(&m, 0,
   4281 			                get_text_translation(
   4282 					    TEXT_(T_SUBMIT_FORM_TO), term));
   4283 		else
   4284 			ll = add_to_str(
   4285 			    &m, 0,
   4286 			    get_text_translation(TEXT_(T_POST_FORM_TO), term));
   4287 		ll = add_chr_to_str(&m, ll, ' ');
   4288 		ll = add_to_str(&m, ll, l->form->action);
   4289 		goto p;
   4290 	}
   4291 	if (l->type == L_CHECKBOX || l->type == L_SELECT || l->type == L_FIELD
   4292 	    || l->type == L_AREA) {
   4293 		m = NULL;
   4294 		switch (l->form->type) {
   4295 		case FC_RADIO:
   4296 			ll = add_to_str(
   4297 			    &m, 0,
   4298 			    get_text_translation(TEXT_(T_RADIO_BUTTON), term));
   4299 			break;
   4300 		case FC_CHECKBOX:
   4301 			ll = add_to_str(
   4302 			    &m, 0,
   4303 			    get_text_translation(TEXT_(T_CHECKBOX), term));
   4304 			break;
   4305 		case FC_SELECT:
   4306 			ll = add_to_str(
   4307 			    &m, 0,
   4308 			    get_text_translation(TEXT_(T_SELECT_FIELD), term));
   4309 			break;
   4310 		case FC_TEXT:
   4311 			ll = add_to_str(
   4312 			    &m, 0,
   4313 			    get_text_translation(TEXT_(T_TEXT_FIELD), term));
   4314 			break;
   4315 		case FC_TEXTAREA:
   4316 			ll = add_to_str(
   4317 			    &m, 0,
   4318 			    get_text_translation(TEXT_(T_TEXT_AREA), term));
   4319 			break;
   4320 		case FC_FILE_UPLOAD:
   4321 			ll = add_to_str(
   4322 			    &m, 0,
   4323 			    get_text_translation(TEXT_(T_FILE_UPLOAD), term));
   4324 			break;
   4325 		case FC_PASSWORD:
   4326 			ll = add_to_str(&m, 0,
   4327 			                get_text_translation(
   4328 					    TEXT_(T_PASSWORD_FIELD), term));
   4329 			break;
   4330 		default:
   4331 			return NULL;
   4332 		}
   4333 		if (l->form->name && l->form->name[0]) {
   4334 			ll = add_to_str(&m, ll, cast_uchar ", ");
   4335 			ll = add_to_str(
   4336 			    &m, ll, get_text_translation(TEXT_(T_NAME), term));
   4337 			ll = add_chr_to_str(&m, ll, ' ');
   4338 			ll = add_to_str(&m, ll, l->form->name);
   4339 		}
   4340 		if ((l->form->type == FC_CHECKBOX || l->form->type == FC_RADIO)
   4341 		    && l->form->default_value && l->form->default_value[0]) {
   4342 			ll = add_to_str(&m, ll, cast_uchar ", ");
   4343 			ll = add_to_str(
   4344 			    &m, ll, get_text_translation(TEXT_(T_VALUE), term));
   4345 			ll = add_chr_to_str(&m, ll, ' ');
   4346 			ll = add_to_str(&m, ll, l->form->default_value);
   4347 		}
   4348 		/* pri enteru se bude posilat vzdycky   -- Brain */
   4349 		if (l->type == L_FIELD && !has_form_submit(fd->f_data, l->form)
   4350 		    && l->form->action) {
   4351 			ll = add_to_str(&m, ll, cast_uchar ", ");
   4352 			ll = add_to_str(
   4353 			    &m, ll,
   4354 			    get_text_translation(TEXT_(T_HIT_ENTER_TO), term));
   4355 			ll = add_chr_to_str(&m, ll, ' ');
   4356 			if (l->form->method == FM_GET)
   4357 				ll = add_to_str(&m, ll,
   4358 				                get_text_translation(
   4359 						    TEXT_(T_SUBMIT_TO), term));
   4360 			else
   4361 				ll = add_to_str(&m, ll,
   4362 				                get_text_translation(
   4363 						    TEXT_(T_POST_TO), term));
   4364 			ll = add_chr_to_str(&m, ll, ' ');
   4365 			ll = add_to_str(&m, ll, l->form->action);
   4366 		}
   4367 		goto p;
   4368 	}
   4369 p:
   4370 	return m;
   4371 }
   4372 
   4373 /* jako print_current_linkx, ale vypisuje vice informaci o obrazku
   4374    pouziva se v informacich o dokumentu
   4375 
   4376    Ach jo, to Brain kopiroval kod, snad to nedela i v ty firme,
   4377    kde ted pracuje... -- mikulas
   4378  */
   4379 static unsigned char *
   4380 print_current_linkx_plus(struct f_data_c *fd, struct terminal *term)
   4381 {
   4382 	size_t ll = 0;
   4383 	struct link *l;
   4384 	unsigned char *d;
   4385 	unsigned char *m = NULL;
   4386 	if (!fd || !fd->vs || !fd->f_data)
   4387 		return NULL;
   4388 	if (fd->vs->current_link == -1
   4389 	    || fd->vs->current_link >= fd->f_data->nlinks
   4390 	    || fd->f_data->frame_desc)
   4391 		return NULL;
   4392 	l = &fd->f_data->links[fd->vs->current_link];
   4393 	if (l->type == L_LINK) {
   4394 		unsigned char *spc;
   4395 		m = NULL;
   4396 		ll = 0;
   4397 		if (l->where && strlen(cast_const_char l->where) >= 4
   4398 		    && !casecmp(l->where, cast_uchar "MAP@", 4)) {
   4399 			ll = add_to_str(
   4400 			    &m, ll,
   4401 			    get_text_translation(TEXT_(T_USEMAP), term));
   4402 			ll = add_chr_to_str(&m, ll, ' ');
   4403 			d = display_url(term, l->where + 4, 1);
   4404 			ll = add_to_str(&m, ll, d);
   4405 			free(d);
   4406 		} else if (l->where) {
   4407 			d = display_url(term, l->where, 1);
   4408 			ll = add_to_str(&m, ll, d);
   4409 			free(d);
   4410 		}
   4411 		spc = stracpy((unsigned char *)"");
   4412 		if (spc && *spc) {
   4413 			ll = add_chr_to_str(&m, ll, '\n');
   4414 			ll = add_to_str(
   4415 			    &m, ll,
   4416 			    get_text_translation(TEXT_(T_JAVASCRIPT), term));
   4417 			ll = add_to_str(&m, ll, cast_uchar ": ");
   4418 			ll = add_to_str(&m, ll, spc);
   4419 		}
   4420 		free(spc);
   4421 		if (l->where_img) {
   4422 			ll = add_chr_to_str(&m, ll, '\n');
   4423 			ll = add_to_str(
   4424 			    &m, ll, get_text_translation(TEXT_(T_IMAGE), term));
   4425 			ll = add_to_str(&m, ll, cast_uchar ": src='");
   4426 			d = display_url(term, l->where_img, 1);
   4427 			ll = add_to_str(&m, ll, d);
   4428 			free(d);
   4429 			ll = add_chr_to_str(&m, ll, '\'');
   4430 
   4431 			if (l->img_alt) {
   4432 				unsigned char *txt;
   4433 
   4434 				ll = add_to_str(&m, ll, cast_uchar " alt='");
   4435 				txt =
   4436 				    convert(fd->f_data->cp, fd->f_data->opt.cp,
   4437 				            l->img_alt, &fd->f_data->opt);
   4438 				ll = add_to_str(&m, ll, txt);
   4439 				ll = add_chr_to_str(&m, ll, '\'');
   4440 				free(txt);
   4441 			}
   4442 			goto p;
   4443 		}
   4444 		goto p;
   4445 	}
   4446 	if (!l->form)
   4447 		return NULL;
   4448 	if (l->type == L_BUTTON) {
   4449 		if (l->form->type == FC_BUTTON) {
   4450 			m = NULL;
   4451 			ll = add_to_str(
   4452 			    &m, 0, get_text_translation(TEXT_(T_BUTTON), term));
   4453 			goto p;
   4454 		}
   4455 		if (l->form->type == FC_RESET) {
   4456 			m = stracpy(
   4457 			    get_text_translation(TEXT_(T_RESET_FORM), term));
   4458 			goto p;
   4459 		}
   4460 		if (!l->form->action)
   4461 			return NULL;
   4462 		m = NULL;
   4463 		if (l->form->method == FM_GET)
   4464 			ll = add_to_str(&m, 0,
   4465 			                get_text_translation(
   4466 					    TEXT_(T_SUBMIT_FORM_TO), term));
   4467 		else
   4468 			ll = add_to_str(
   4469 			    &m, 0,
   4470 			    get_text_translation(TEXT_(T_POST_FORM_TO), term));
   4471 		ll = add_chr_to_str(&m, ll, ' ');
   4472 		ll = add_to_str(&m, ll, l->form->action);
   4473 		goto p;
   4474 	}
   4475 	if (l->type == L_CHECKBOX || l->type == L_SELECT || l->type == L_FIELD
   4476 	    || l->type == L_AREA) {
   4477 		m = NULL;
   4478 		switch (l->form->type) {
   4479 		case FC_RADIO:
   4480 			ll = add_to_str(
   4481 			    &m, 0,
   4482 			    get_text_translation(TEXT_(T_RADIO_BUTTON), term));
   4483 			break;
   4484 		case FC_CHECKBOX:
   4485 			ll = add_to_str(
   4486 			    &m, 0,
   4487 			    get_text_translation(TEXT_(T_CHECKBOX), term));
   4488 			break;
   4489 		case FC_SELECT:
   4490 			ll = add_to_str(
   4491 			    &m, 0,
   4492 			    get_text_translation(TEXT_(T_SELECT_FIELD), term));
   4493 			break;
   4494 		case FC_TEXT:
   4495 			ll = add_to_str(
   4496 			    &m, 0,
   4497 			    get_text_translation(TEXT_(T_TEXT_FIELD), term));
   4498 			break;
   4499 		case FC_TEXTAREA:
   4500 			ll = add_to_str(
   4501 			    &m, 0,
   4502 			    get_text_translation(TEXT_(T_TEXT_AREA), term));
   4503 			break;
   4504 		case FC_FILE_UPLOAD:
   4505 			ll = add_to_str(
   4506 			    &m, 0,
   4507 			    get_text_translation(TEXT_(T_FILE_UPLOAD), term));
   4508 			break;
   4509 		case FC_PASSWORD:
   4510 			ll = add_to_str(&m, 0,
   4511 			                get_text_translation(
   4512 					    TEXT_(T_PASSWORD_FIELD), term));
   4513 			break;
   4514 		default:
   4515 			return NULL;
   4516 		}
   4517 		if (l->form->name && l->form->name[0]) {
   4518 			ll = add_to_str(&m, ll, cast_uchar ", ");
   4519 			ll = add_to_str(
   4520 			    &m, ll, get_text_translation(TEXT_(T_NAME), term));
   4521 			ll = add_chr_to_str(&m, ll, ' ');
   4522 			ll = add_to_str(&m, ll, l->form->name);
   4523 		}
   4524 		if ((l->form->type == FC_CHECKBOX || l->form->type == FC_RADIO)
   4525 		    && l->form->default_value && l->form->default_value[0]) {
   4526 			ll = add_to_str(&m, ll, cast_uchar ", ");
   4527 			ll = add_to_str(
   4528 			    &m, ll, get_text_translation(TEXT_(T_VALUE), term));
   4529 			ll = add_chr_to_str(&m, ll, ' ');
   4530 			ll = add_to_str(&m, ll, l->form->default_value);
   4531 		}
   4532 		/* pri enteru se bude posilat vzdycky   -- Brain */
   4533 		if (l->type == L_FIELD && !has_form_submit(fd->f_data, l->form)
   4534 		    && l->form->action) {
   4535 			ll = add_to_str(&m, ll, cast_uchar ", ");
   4536 			ll = add_to_str(
   4537 			    &m, ll,
   4538 			    get_text_translation(TEXT_(T_HIT_ENTER_TO), term));
   4539 			ll = add_chr_to_str(&m, ll, ' ');
   4540 			if (l->form->method == FM_GET)
   4541 				ll = add_to_str(&m, ll,
   4542 				                get_text_translation(
   4543 						    TEXT_(T_SUBMIT_TO), term));
   4544 			else
   4545 				ll = add_to_str(&m, ll,
   4546 				                get_text_translation(
   4547 						    TEXT_(T_POST_TO), term));
   4548 			ll = add_chr_to_str(&m, ll, ' ');
   4549 			ll = add_to_str(&m, ll, l->form->action);
   4550 		}
   4551 		goto p;
   4552 	}
   4553 p:
   4554 	return m;
   4555 }
   4556 
   4557 unsigned char *
   4558 print_current_link(struct session *ses)
   4559 {
   4560 	return print_current_linkx(current_frame(ses), ses->term);
   4561 }
   4562 
   4563 unsigned char *
   4564 print_current_title(struct session *ses)
   4565 {
   4566 	return print_current_titlex(current_frame(ses), ses->term->x);
   4567 }
   4568 
   4569 void
   4570 loc_msg(struct terminal *term, struct location *lo, struct f_data_c *frame)
   4571 {
   4572 	struct cache_entry *ce;
   4573 	unsigned char *s;
   4574 	int l;
   4575 	unsigned char *a;
   4576 	if (!lo || !frame || !frame->vs || !frame->f_data) {
   4577 		msg_box(term, NULL, TEXT_(T_INFO), AL_LEFT,
   4578 		        TEXT_(T_YOU_ARE_NOWHERE), MSG_BOX_END, NULL, 1,
   4579 		        TEXT_(T_OK), msg_box_null, B_ENTER | B_ESC);
   4580 		return;
   4581 	}
   4582 	s = NULL;
   4583 	l = add_to_str(&s, 0, get_text_translation(TEXT_(T_URL), term));
   4584 	l = add_to_str(&s, l, cast_uchar ": ");
   4585 	a = display_url(term, lo->url, 1);
   4586 	l = add_to_str(&s, l, a);
   4587 	free(a);
   4588 	if (!find_in_cache(lo->url, &ce)) {
   4589 		unsigned char *start;
   4590 		size_t len;
   4591 		if (ce->ip_address) {
   4592 			l = add_chr_to_str(&s, l, '\n');
   4593 			if (!strchr(cast_const_char ce->ip_address, ' '))
   4594 				l = add_to_str(&s, l,
   4595 				               get_text_translation(
   4596 						   TEXT_(T_IP_ADDRESS), term));
   4597 			else
   4598 				l = add_to_str(
   4599 				    &s, l,
   4600 				    get_text_translation(TEXT_(T_IP_ADDRESSES),
   4601 				                         term));
   4602 			l = add_to_str(&s, l, cast_uchar ": ");
   4603 			l = add_to_str(&s, l, ce->ip_address);
   4604 		}
   4605 		l = add_chr_to_str(&s, l, '\n');
   4606 		l = add_to_str(&s, l,
   4607 		               get_text_translation(TEXT_(T_SIZE), term));
   4608 		l = add_to_str(&s, l, cast_uchar ": ");
   4609 		get_file_by_term(NULL, ce, &start, &len, NULL);
   4610 		if (ce->decompressed) {
   4611 			unsigned char *enc;
   4612 			add_unsigned_long_num_to_str(&s, &l, len);
   4613 			enc = get_content_encoding(ce->head, ce->url, 0);
   4614 			if (enc) {
   4615 				l = add_to_str(&s, l, cast_uchar " (");
   4616 				l = add_num_to_str(&s, l, ce->length);
   4617 				l = add_chr_to_str(&s, l, ' ');
   4618 				l = add_to_str(
   4619 				    &s, l,
   4620 				    get_text_translation(
   4621 					TEXT_(T_COMPRESSED_WITH), term));
   4622 				l = add_chr_to_str(&s, l, ' ');
   4623 				l = add_to_str(&s, l, enc);
   4624 				l = add_chr_to_str(&s, l, ')');
   4625 				free(enc);
   4626 			}
   4627 		} else {
   4628 			l = add_num_to_str(&s, l, ce->length);
   4629 		}
   4630 		if (ce->incomplete) {
   4631 			l = add_to_str(&s, l, cast_uchar " (");
   4632 			l = add_to_str(
   4633 			    &s, l,
   4634 			    get_text_translation(TEXT_(T_INCOMPLETE), term));
   4635 			l = add_chr_to_str(&s, l, ')');
   4636 		}
   4637 		if (frame->f_data->ass >= 0) {
   4638 			l = add_chr_to_str(&s, l, '\n');
   4639 			l = add_to_str(
   4640 			    &s, l,
   4641 			    get_text_translation(TEXT_(T_CODEPAGE), term));
   4642 			l = add_to_str(&s, l, cast_uchar ": ");
   4643 			l = add_to_str(&s, l, get_cp_name(frame->f_data->cp));
   4644 			if (frame->f_data->ass == 1) {
   4645 				l = add_to_str(&s, l, cast_uchar " (");
   4646 				l = add_to_str(&s, l,
   4647 				               get_text_translation(
   4648 						   TEXT_(T_ASSUMED), term));
   4649 				l = add_chr_to_str(&s, l, ')');
   4650 			}
   4651 			if (frame->f_data->ass == 2) {
   4652 				l = add_to_str(&s, l, cast_uchar " (");
   4653 				l = add_to_str(
   4654 				    &s, l,
   4655 				    get_text_translation(
   4656 					TEXT_(T_IGNORING_SERVER_SETTING),
   4657 					term));
   4658 				l = add_chr_to_str(&s, l, ')');
   4659 			}
   4660 		}
   4661 		if (ce->head && ce->head[0] != '\n' && ce->head[0] != '\r'
   4662 		    && (a = parse_http_header(
   4663 			    ce->head, cast_uchar "Content-Type", NULL))) {
   4664 			l = add_chr_to_str(&s, l, '\n');
   4665 			l = add_to_str(
   4666 			    &s, l,
   4667 			    get_text_translation(TEXT_(T_CONTENT_TYPE), term));
   4668 			l = add_to_str(&s, l, cast_uchar ": ");
   4669 			l = add_to_str(&s, l, a);
   4670 			free(a);
   4671 		}
   4672 		if ((a = parse_http_header(ce->head, cast_uchar "Server",
   4673 		                           NULL))) {
   4674 			l = add_chr_to_str(&s, l, '\n');
   4675 			l = add_to_str(
   4676 			    &s, l, get_text_translation(TEXT_(T_SERVER), term));
   4677 			l = add_to_str(&s, l, cast_uchar ": ");
   4678 			l = add_to_str(&s, l, a);
   4679 			free(a);
   4680 		}
   4681 		if ((a = parse_http_header(ce->head, cast_uchar "Date",
   4682 		                           NULL))) {
   4683 			l = add_chr_to_str(&s, l, '\n');
   4684 			l = add_to_str(
   4685 			    &s, l, get_text_translation(TEXT_(T_DATE), term));
   4686 			l = add_to_str(&s, l, cast_uchar ": ");
   4687 			l = add_to_str(&s, l, a);
   4688 			free(a);
   4689 		}
   4690 		if ((a = parse_http_header(ce->head, cast_uchar "Last-Modified",
   4691 		                           NULL))) {
   4692 			l = add_chr_to_str(&s, l, '\n');
   4693 			l = add_to_str(
   4694 			    &s, l,
   4695 			    get_text_translation(TEXT_(T_LAST_MODIFIED), term));
   4696 			l = add_to_str(&s, l, cast_uchar ": ");
   4697 			l = add_to_str(&s, l, a);
   4698 			free(a);
   4699 		}
   4700 		if (ce->ssl_info) {
   4701 			l = add_chr_to_str(&s, l, '\n');
   4702 			l = add_to_str(
   4703 			    &s, l,
   4704 			    get_text_translation(TEXT_(T_SSL_CIPHER), term));
   4705 			l = add_to_str(&s, l, cast_uchar ": ");
   4706 			l = add_to_str(&s, l, ce->ssl_info);
   4707 		}
   4708 		if (ce->ssl_authority) {
   4709 			l = add_chr_to_str(&s, l, '\n');
   4710 			if (strstr(cast_const_char ce->ssl_authority,
   4711 			           cast_const_char CERT_RIGHT_ARROW))
   4712 				l = add_to_str(
   4713 				    &s, l,
   4714 				    get_text_translation(
   4715 					TEXT_(T_CERTIFICATE_AUTHORITIES),
   4716 					term));
   4717 			else
   4718 				l = add_to_str(
   4719 				    &s, l,
   4720 				    get_text_translation(
   4721 					TEXT_(T_CERTIFICATE_AUTHORITY), term));
   4722 			l = add_to_str(&s, l, cast_uchar ": ");
   4723 			l = add_to_str(&s, l, ce->ssl_authority);
   4724 		}
   4725 		ce->refcount--;
   4726 	}
   4727 	if ((a = print_current_linkx_plus(frame, term))) {
   4728 		l = add_to_str(&s, l, cast_uchar "\n\n");
   4729 		if (*a != '\n') {
   4730 			l = add_to_str(
   4731 			    &s, l, get_text_translation(TEXT_(T_LINK), term));
   4732 			l = add_to_str(&s, l, cast_uchar ": ");
   4733 			l = add_to_str(&s, l, a);
   4734 		} else
   4735 			l = add_to_str(&s, l, a + 1);
   4736 		free(a);
   4737 	}
   4738 	msg_box(term, getml(s, NULL), TEXT_(T_INFO), AL_LEFT, s, MSG_BOX_END,
   4739 	        NULL, 1, TEXT_(T_OK), msg_box_null, B_ENTER | B_ESC);
   4740 }
   4741 
   4742 void
   4743 state_msg(struct session *ses)
   4744 {
   4745 	if (list_empty(ses->history))
   4746 		loc_msg(ses->term, NULL, NULL);
   4747 	else
   4748 		loc_msg(ses->term, cur_loc(ses), current_frame(ses));
   4749 }
   4750 
   4751 void
   4752 head_msg(struct session *ses)
   4753 {
   4754 	struct cache_entry *ce;
   4755 	unsigned char *s, *ss;
   4756 	int len;
   4757 	if (list_empty(ses->history)) {
   4758 		msg_box(ses->term, NULL, TEXT_(T_HEADER_INFO), AL_LEFT,
   4759 		        TEXT_(T_YOU_ARE_NOWHERE), MSG_BOX_END, NULL, 1,
   4760 		        TEXT_(T_OK), msg_box_null, B_ENTER | B_ESC);
   4761 		return;
   4762 	}
   4763 	if (!find_in_cache(cur_loc(ses)->url, &ce)) {
   4764 		if (ce->head)
   4765 			s = stracpy(ce->head);
   4766 		else
   4767 			s = stracpy(cast_uchar "");
   4768 		len = (int)strlen(cast_const_char s) - 1;
   4769 		if (len > 0) {
   4770 			while (
   4771 			    (ss = cast_uchar strstr(cast_const_char s, "\r\n")))
   4772 				memmove(ss, ss + 1, strlen(cast_const_char ss));
   4773 			while (*s && s[strlen(cast_const_char s) - 1] == '\n')
   4774 				s[strlen(cast_const_char s) - 1] = 0;
   4775 		}
   4776 		if (*s && *s != '\n') {
   4777 			msg_box(ses->term, getml(s, NULL), TEXT_(T_HEADER_INFO),
   4778 			        AL_LEFT, s, MSG_BOX_END, NULL, 1, TEXT_(T_OK),
   4779 			        msg_box_null, B_ENTER | B_ESC);
   4780 		} else {
   4781 			msg_box(ses->term, getml(s, NULL), TEXT_(T_HEADER_INFO),
   4782 			        AL_CENTER, TEXT_(T_NO_HEADER), MSG_BOX_END,
   4783 			        NULL, 1, TEXT_(T_OK), msg_box_null,
   4784 			        B_ENTER | B_ESC);
   4785 		}
   4786 		ce->refcount--;
   4787 	}
   4788 }