links

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

types.c (42736B)


      1 /* types.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 /*------------------------ ASSOCIATIONS -----------------------*/
     11 
     12 /* DECLARATIONS */
     13 
     14 static void assoc_edit_item(struct dialog_data *, struct list *,
     15                             void (*)(struct dialog_data *, struct list *,
     16                                      struct list *, struct list_description *),
     17                             struct list *, unsigned char);
     18 static void assoc_copy_item(struct list *, struct list *);
     19 static struct list *assoc_new_item(void *);
     20 static void assoc_delete_item(struct list *);
     21 static struct list *assoc_find_item(struct list *start, unsigned char *str,
     22                                     int direction);
     23 static unsigned char *assoc_type_item(struct terminal *, struct list *, int);
     24 
     25 struct list assoc = { init_list_1st(&assoc.list_entry) 0, -1, NULL };
     26 
     27 static struct history assoc_search_history = {
     28 	0, {&assoc_search_history.items, &assoc_search_history.items}
     29 };
     30 
     31 struct assoc_ok_struct {
     32 	void (*fn)(struct dialog_data *, struct list *, struct list *,
     33 	           struct list_description *);
     34 	struct list *data;
     35 	struct dialog_data *dlg;
     36 };
     37 
     38 static struct list_description assoc_ld = {
     39 	0,      /* 0= flat; 1=tree */
     40 	&assoc, /* list */
     41 	assoc_new_item,
     42 	assoc_edit_item,
     43 	NULL,
     44 	assoc_delete_item,
     45 	assoc_copy_item,
     46 	assoc_type_item,
     47 	assoc_find_item,
     48 	&assoc_search_history,
     49 	0,  /* this is set in init_assoc function */
     50 	15, /* # of items in main window */
     51 	T_ASSOCIATION,
     52 	T_ASSOCIATIONS_ALREADY_IN_USE,
     53 	T_ASSOCIATIONS_MANAGER,
     54 	T_DELETE_ASSOCIATION,
     55 	0,    /* no button */
     56 	NULL, /* no button */
     57 	NULL, /* no save*/
     58 
     59 	NULL,
     60 	NULL,
     61 	0,
     62 	0, /* internal vars */
     63 	0, /* modified */
     64 	NULL,
     65 	NULL,
     66 	1,
     67 };
     68 
     69 static struct list *
     70 assoc_new_item(void *ignore)
     71 {
     72 	struct assoc *neww;
     73 
     74 	neww = mem_calloc(sizeof(struct assoc));
     75 	neww->label = stracpy(cast_uchar "");
     76 	neww->ct = stracpy(cast_uchar "");
     77 	neww->prog = stracpy(cast_uchar "");
     78 	neww->block = neww->xwin = neww->cons = 1;
     79 	neww->ask = 1;
     80 	neww->accept_http = 0;
     81 	neww->accept_ftp = 0;
     82 	neww->head.type = 0;
     83 	neww->system = SYSTEM_ID;
     84 	return &neww->head;
     85 }
     86 
     87 static void
     88 assoc_delete_item(struct list *data)
     89 {
     90 	struct assoc *del = get_struct(data, struct assoc, head);
     91 
     92 	if (del->head.list_entry.next)
     93 		del_from_list(&del->head);
     94 	free(del->label);
     95 	free(del->ct);
     96 	free(del->prog);
     97 	free(del);
     98 }
     99 
    100 static void
    101 assoc_copy_item(struct list *in, struct list *out)
    102 {
    103 	struct assoc *item_in = get_struct(in, struct assoc, head);
    104 	struct assoc *item_out = get_struct(out, struct assoc, head);
    105 
    106 	item_out->cons = item_in->cons;
    107 	item_out->xwin = item_in->xwin;
    108 	item_out->block = item_in->block;
    109 	item_out->ask = item_in->ask;
    110 	item_out->accept_http = item_in->accept_http;
    111 	item_out->accept_ftp = item_in->accept_ftp;
    112 	item_out->system = item_in->system;
    113 
    114 	free(item_out->label);
    115 	free(item_out->ct);
    116 	free(item_out->prog);
    117 
    118 	item_out->label = stracpy(item_in->label);
    119 	item_out->ct = stracpy(item_in->ct);
    120 	item_out->prog = stracpy(item_in->prog);
    121 }
    122 
    123 /* allocate string and print association into it */
    124 /* x: 0=type all, 1=type title only */
    125 static unsigned char *
    126 assoc_type_item(struct terminal *term, struct list *data, int x)
    127 {
    128 	unsigned char *txt, *txt1;
    129 	struct assoc *item;
    130 
    131 	if (data == &assoc)
    132 		return stracpy(
    133 		    get_text_translation(TEXT_(T_ASSOCIATIONS), term));
    134 
    135 	item = get_struct(data, struct assoc, head);
    136 	txt = stracpy(cast_uchar "");
    137 	if (item->system != SYSTEM_ID)
    138 		add_to_strn(&txt, cast_uchar "XX ");
    139 	add_to_strn(&txt, item->label);
    140 	add_to_strn(&txt, cast_uchar ": ");
    141 	add_to_strn(&txt, item->ct);
    142 	if (!x) {
    143 		add_to_strn(&txt, cast_uchar " -> ");
    144 		if (item->prog)
    145 			add_to_strn(&txt, item->prog);
    146 	}
    147 	txt1 = stracpy(txt);
    148 	free(txt);
    149 
    150 	return txt1;
    151 }
    152 
    153 void
    154 menu_assoc_manager(struct terminal *term, void *fcp, void *ses_)
    155 {
    156 	struct session *ses = (struct session *)ses_;
    157 	create_list_window(&assoc_ld, &assoc, term, ses);
    158 }
    159 
    160 static unsigned char *const ct_msg[] = {
    161 	TEXT_(T_LABEL),
    162 	TEXT_(T_CONTENT_TYPES),
    163 	TEXT_(T_PROGRAM__IS_REPLACED_WITH_FILE_NAME),
    164 	TEXT_(T_BLOCK_TERMINAL_WHILE_PROGRAM_RUNNING),
    165 	TEXT_(T_RUN_ON_TERMINAL),
    166 	TEXT_(T_RUN_IN_XWINDOW),
    167 	TEXT_(T_ASK_BEFORE_OPENING),
    168 	TEXT_(T_ACCEPT_HTTP),
    169 	TEXT_(T_ACCEPT_FTP),
    170 };
    171 
    172 static void
    173 assoc_edit_item_fn(struct dialog_data *dlg)
    174 {
    175 	struct terminal *term = dlg->win->term;
    176 	int max = 0, min = 0;
    177 	int w, rw;
    178 	int y = -1;
    179 	int p = 3;
    180 	p++;
    181 	p += 2;
    182 	max_text_width(term, ct_msg[0], &max, AL_LEFT);
    183 	min_text_width(term, ct_msg[0], &min, AL_LEFT);
    184 	max_text_width(term, ct_msg[1], &max, AL_LEFT);
    185 	min_text_width(term, ct_msg[1], &min, AL_LEFT);
    186 	max_text_width(term, ct_msg[2], &max, AL_LEFT);
    187 	min_text_width(term, ct_msg[2], &min, AL_LEFT);
    188 	max_group_width(term, ct_msg + 3, dlg->items + 3, p, &max);
    189 	min_group_width(term, ct_msg + 3, dlg->items + 3, p, &min);
    190 	max_buttons_width(term, dlg->items + 3 + p, 2, &max);
    191 	min_buttons_width(term, dlg->items + 3 + p, 2, &min);
    192 	w = term->x * 9 / 10 - 2 * DIALOG_LB;
    193 	if (w > max)
    194 		w = max;
    195 	if (w < min)
    196 		w = min;
    197 	if (w > term->x - 2 * DIALOG_LB)
    198 		w = term->x - 2 * DIALOG_LB;
    199 	if (w < 1)
    200 		w = 1;
    201 	rw = 0;
    202 	dlg_format_text_and_field(
    203 	    dlg, NULL, get_text_translation(ct_msg[0], term), &dlg->items[0], 0,
    204 	    &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
    205 	y++;
    206 	dlg_format_text_and_field(
    207 	    dlg, NULL, get_text_translation(ct_msg[1], term), &dlg->items[1], 0,
    208 	    &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
    209 	y++;
    210 	dlg_format_text_and_field(
    211 	    dlg, NULL, get_text_translation(ct_msg[2], term), &dlg->items[2], 0,
    212 	    &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
    213 	y++;
    214 	dlg_format_group(dlg, NULL, ct_msg + 3, dlg->items + 3, p, 0, &y, w,
    215 	                 &rw);
    216 	y++;
    217 	dlg_format_buttons(dlg, NULL, dlg->items + 3 + p, 2, 0, &y, w, &rw,
    218 	                   AL_CENTER);
    219 	w = rw;
    220 	dlg->xw = w + 2 * DIALOG_LB;
    221 	dlg->yw = y + 2 * DIALOG_TB;
    222 	center_dlg(dlg);
    223 	draw_dlg(dlg);
    224 	y = dlg->y + DIALOG_TB;
    225 	dlg_format_text_and_field(dlg, term, ct_msg[0], &dlg->items[0],
    226 	                          dlg->x + DIALOG_LB, &y, w, NULL,
    227 	                          COLOR_DIALOG_TEXT, AL_LEFT);
    228 	y++;
    229 	dlg_format_text_and_field(dlg, term, ct_msg[1], &dlg->items[1],
    230 	                          dlg->x + DIALOG_LB, &y, w, NULL,
    231 	                          COLOR_DIALOG_TEXT, AL_LEFT);
    232 	y++;
    233 	dlg_format_text_and_field(dlg, term, ct_msg[2], &dlg->items[2],
    234 	                          dlg->x + DIALOG_LB, &y, w, NULL,
    235 	                          COLOR_DIALOG_TEXT, AL_LEFT);
    236 	y++;
    237 	dlg_format_group(dlg, term, ct_msg + 3, &dlg->items[3], p,
    238 	                 dlg->x + DIALOG_LB, &y, w, NULL);
    239 	y++;
    240 	dlg_format_buttons(dlg, term, &dlg->items[3 + p], 2, dlg->x + DIALOG_LB,
    241 	                   &y, w, NULL, AL_CENTER);
    242 }
    243 
    244 /* Puts url and title into the bookmark item */
    245 static void
    246 assoc_edit_done(void *data)
    247 {
    248 	struct dialog *d = (struct dialog *)data;
    249 	struct assoc *item = (struct assoc *)d->udata;
    250 	struct assoc_ok_struct *s = (struct assoc_ok_struct *)d->udata2;
    251 	unsigned char *txt;
    252 	unsigned char *label, *ct, *prog;
    253 
    254 	label = (unsigned char *)&d->items[12];
    255 	ct = label + MAX_STR_LEN;
    256 	prog = ct + MAX_STR_LEN;
    257 
    258 	txt = stracpy(label);
    259 	free(item->label);
    260 	item->label = txt;
    261 
    262 	txt = stracpy(ct);
    263 	free(item->ct);
    264 	item->ct = txt;
    265 
    266 	txt = stracpy(prog);
    267 	free(item->prog);
    268 	item->prog = txt;
    269 
    270 	s->fn(s->dlg, s->data, &item->head, &assoc_ld);
    271 	d->udata = NULL; /* for abort function */
    272 }
    273 
    274 /* destroys an item, this function is called when edit window is aborted */
    275 static void
    276 assoc_edit_abort(struct dialog_data *data)
    277 {
    278 	struct dialog *dlg = data->dlg;
    279 	struct assoc *item = (struct assoc *)dlg->udata;
    280 
    281 	free(dlg->udata2);
    282 	if (item)
    283 		assoc_delete_item(&item->head);
    284 }
    285 
    286 static void
    287 assoc_edit_item(struct dialog_data *dlg, struct list *data,
    288                 void (*ok_fn)(struct dialog_data *, struct list *,
    289                               struct list *, struct list_description *),
    290                 struct list *ok_arg, unsigned char dlg_title)
    291 {
    292 	int p;
    293 	struct assoc *neww = get_struct(data, struct assoc, head);
    294 	struct terminal *term = dlg->win->term;
    295 	struct dialog *d;
    296 	struct assoc_ok_struct *s;
    297 	unsigned char *ct, *prog, *label;
    298 
    299 	d = mem_calloc(sizeof(struct dialog) + 11 * sizeof(struct dialog_item)
    300 	               + 3 * MAX_STR_LEN);
    301 
    302 	label = (unsigned char *)&d->items[12];
    303 	ct = label + MAX_STR_LEN;
    304 	prog = ct + MAX_STR_LEN;
    305 
    306 	safe_strncpy(label, neww->label, MAX_STR_LEN);
    307 	safe_strncpy(ct, neww->ct, MAX_STR_LEN);
    308 	safe_strncpy(prog, neww->prog, MAX_STR_LEN);
    309 
    310 	/* Create the dialog */
    311 	s = xmalloc(sizeof(struct assoc_ok_struct));
    312 	s->fn = ok_fn;
    313 	s->data = ok_arg;
    314 	s->dlg = dlg;
    315 
    316 	switch (dlg_title) {
    317 	case TITLE_EDIT:
    318 		d->title = TEXT_(T_EDIT_ASSOCIATION);
    319 		break;
    320 
    321 	case TITLE_ADD:
    322 		d->title = TEXT_(T_ADD_ASSOCIATION);
    323 		break;
    324 
    325 	default:
    326 		internal("Unsupported dialog title.\n");
    327 	}
    328 
    329 	d->udata = neww;
    330 	d->udata2 = s;
    331 	d->fn = assoc_edit_item_fn;
    332 	d->abort = assoc_edit_abort;
    333 	d->refresh = assoc_edit_done;
    334 	d->refresh_data = d;
    335 	d->items[0].type = D_FIELD;
    336 	d->items[0].dlen = MAX_STR_LEN;
    337 	d->items[0].data = label;
    338 	d->items[0].fn = check_nonempty;
    339 	d->items[1].type = D_FIELD;
    340 	d->items[1].dlen = MAX_STR_LEN;
    341 	d->items[1].data = ct;
    342 	d->items[1].fn = check_nonempty;
    343 	d->items[2].type = D_FIELD;
    344 	d->items[2].dlen = MAX_STR_LEN;
    345 	d->items[2].data = prog;
    346 	d->items[2].fn = check_nonempty;
    347 	p = 3;
    348 	d->items[p].type = D_CHECKBOX;
    349 	d->items[p].data = (unsigned char *)&neww->block;
    350 	d->items[p++].dlen = sizeof(int);
    351 	d->items[p].type = D_CHECKBOX;
    352 	d->items[p].data = (unsigned char *)&neww->cons;
    353 	d->items[p++].dlen = sizeof(int);
    354 	d->items[p].type = D_CHECKBOX;
    355 	d->items[p].data = (unsigned char *)&neww->xwin;
    356 	d->items[p++].dlen = sizeof(int);
    357 	d->items[p].type = D_CHECKBOX;
    358 	d->items[p].data = (unsigned char *)&neww->ask;
    359 	d->items[p++].dlen = sizeof(int);
    360 	d->items[p].type = D_CHECKBOX;
    361 	d->items[p].data = (unsigned char *)&neww->accept_http;
    362 	d->items[p++].dlen = sizeof(int);
    363 	d->items[p].type = D_CHECKBOX;
    364 	d->items[p].data = (unsigned char *)&neww->accept_ftp;
    365 	d->items[p++].dlen = sizeof(int);
    366 	d->items[p].type = D_BUTTON;
    367 	d->items[p].gid = B_ENTER;
    368 	d->items[p].fn = ok_dialog;
    369 	d->items[p++].text = TEXT_(T_OK);
    370 	d->items[p].type = D_BUTTON;
    371 	d->items[p].gid = B_ESC;
    372 	d->items[p].text = TEXT_(T_CANCEL);
    373 	d->items[p++].fn = cancel_dialog;
    374 	d->items[p++].type = D_END;
    375 	do_dialog(term, d, getml(d, NULL));
    376 }
    377 
    378 static int
    379 assoc_test_entry(struct list *e, unsigned char *str)
    380 {
    381 	struct assoc *a = get_struct(e, struct assoc, head);
    382 	return casestrstr(a->label, str) || casestrstr(a->ct, str);
    383 }
    384 
    385 static struct list *
    386 assoc_find_item(struct list *s, unsigned char *str, int direction)
    387 {
    388 	struct list *e;
    389 
    390 	if (direction >= 0)
    391 		for (e = list_next(s); e != s; e = list_next(e)) {
    392 			if (e->depth >= 0 && assoc_test_entry(e, str))
    393 				return e;
    394 		}
    395 	else
    396 		for (e = list_prev(s); e != s; e = list_prev(e)) {
    397 			if (e->depth >= 0 && assoc_test_entry(e, str))
    398 				return e;
    399 		}
    400 
    401 	if (e->depth >= 0 && assoc_test_entry(e, str))
    402 		return e;
    403 
    404 	return NULL;
    405 }
    406 
    407 void
    408 update_assoc(struct assoc *neww)
    409 {
    410 	struct assoc *repl;
    411 	struct list *r = NULL;
    412 	struct list_head *lr;
    413 	if (!neww->label[0] || !neww->ct[0] || !neww->prog[0])
    414 		return;
    415 	foreach (struct list, r, lr, assoc.list_entry) {
    416 		repl = get_struct(r, struct assoc, head);
    417 		if (!strcmp(cast_const_char repl->label,
    418 		            cast_const_char neww->label)
    419 		    && !strcmp(cast_const_char repl->ct,
    420 		               cast_const_char neww->ct)
    421 		    && !strcmp(cast_const_char repl->prog,
    422 		               cast_const_char neww->prog)
    423 		    && repl->block == neww->block && repl->cons == neww->cons
    424 		    && repl->xwin == neww->xwin && repl->ask == neww->ask
    425 		    && repl->accept_http == neww->accept_http
    426 		    && repl->accept_ftp == neww->accept_ftp
    427 		    && repl->system == neww->system) {
    428 			del_from_list(&repl->head);
    429 			add_to_list(assoc.list_entry, &repl->head);
    430 			return;
    431 		}
    432 	}
    433 	repl = mem_calloc(sizeof(struct assoc));
    434 	repl->label = stracpy(neww->label);
    435 	repl->ct = stracpy(neww->ct);
    436 	repl->prog = stracpy(neww->prog);
    437 	repl->block = neww->block;
    438 	repl->cons = neww->cons;
    439 	repl->xwin = neww->xwin;
    440 	repl->ask = neww->ask;
    441 	repl->accept_http = neww->accept_http;
    442 	repl->accept_ftp = neww->accept_ftp;
    443 	repl->system = neww->system;
    444 	repl->head.type = 0;
    445 	add_to_list(assoc.list_entry, &repl->head);
    446 }
    447 
    448 /*------------------------ EXTENSIONS -----------------------*/
    449 
    450 /* DECLARATIONS */
    451 static void ext_edit_item(struct dialog_data *, struct list *,
    452                           void (*)(struct dialog_data *, struct list *,
    453                                    struct list *, struct list_description *),
    454                           struct list *, unsigned char);
    455 static void ext_copy_item(struct list *, struct list *);
    456 static struct list *ext_new_item(void *);
    457 static void ext_delete_item(struct list *);
    458 static struct list *ext_find_item(struct list *start, unsigned char *str,
    459                                   int direction);
    460 static unsigned char *ext_type_item(struct terminal *, struct list *, int);
    461 
    462 struct list extensions = { init_list_1st(&extensions.list_entry) 0, -1, NULL };
    463 
    464 static struct history ext_search_history = {
    465 	0, {&ext_search_history.items, &ext_search_history.items}
    466 };
    467 
    468 static struct list_description ext_ld = {
    469 	0,           /* 0= flat; 1=tree */
    470 	&extensions, /* list */
    471 	ext_new_item,
    472 	ext_edit_item,
    473 	NULL,
    474 	ext_delete_item,
    475 	ext_copy_item,
    476 	ext_type_item,
    477 	ext_find_item,
    478 	&ext_search_history,
    479 	0,  /* this is set in init_assoc function */
    480 	15, /* # of items in main window */
    481 	T_eXTENSION,
    482 	T_EXTENSIONS_ALREADY_IN_USE,
    483 	T_EXTENSIONS_MANAGER,
    484 	T_DELETE_EXTENSION,
    485 	0,    /* no button */
    486 	NULL, /* no button */
    487 	NULL, /* no save*/
    488 
    489 	NULL,
    490 	NULL,
    491 	0,
    492 	0, /* internal vars */
    493 	0, /* modified */
    494 	NULL,
    495 	NULL,
    496 	0,
    497 };
    498 
    499 static struct list *
    500 ext_new_item(void *ignore)
    501 {
    502 	struct extension *neww;
    503 
    504 	neww = mem_calloc(sizeof(struct extension));
    505 	neww->ext = stracpy(cast_uchar "");
    506 	neww->ct = stracpy(cast_uchar "");
    507 	neww->head.type = 0;
    508 	return &neww->head;
    509 }
    510 
    511 static void
    512 ext_delete_item(struct list *data)
    513 {
    514 	struct extension *del = get_struct(data, struct extension, head);
    515 
    516 	if (del->head.list_entry.next)
    517 		del_from_list(&del->head);
    518 	free(del->ext);
    519 	free(del->ct);
    520 	free(del);
    521 }
    522 
    523 static void
    524 ext_copy_item(struct list *in, struct list *out)
    525 {
    526 	struct extension *item_in = get_struct(in, struct extension, head);
    527 	struct extension *item_out = get_struct(out, struct extension, head);
    528 
    529 	free(item_out->ext);
    530 	free(item_out->ct);
    531 
    532 	item_out->ext = stracpy(item_in->ext);
    533 	item_out->ct = stracpy(item_in->ct);
    534 }
    535 
    536 /* allocate string and print extension into it */
    537 /* x: 0=type all, 1=type title only */
    538 static unsigned char *
    539 ext_type_item(struct terminal *term, struct list *data, int x)
    540 {
    541 	unsigned char *txt;
    542 	struct extension *item;
    543 
    544 	if (data == &extensions)
    545 		return stracpy(
    546 		    get_text_translation(TEXT_(T_FILE_EXTENSIONS), term));
    547 
    548 	item = get_struct(data, struct extension, head);
    549 	txt = stracpy(item->ext);
    550 	add_to_strn(&txt, cast_uchar ": ");
    551 	add_to_strn(&txt, item->ct);
    552 
    553 	return txt;
    554 }
    555 
    556 void
    557 menu_ext_manager(struct terminal *term, void *fcp, void *ses_)
    558 {
    559 	struct session *ses = (struct session *)ses_;
    560 	create_list_window(&ext_ld, &extensions, term, ses);
    561 }
    562 
    563 static unsigned char *const ext_msg[] = {
    564 	TEXT_(T_EXTENSION_S),
    565 	TEXT_(T_CONTENT_TYPE),
    566 };
    567 
    568 static void
    569 ext_edit_item_fn(struct dialog_data *dlg)
    570 {
    571 	struct terminal *term = dlg->win->term;
    572 	int max = 0, min = 0;
    573 	int w, rw;
    574 	int y = -1;
    575 	max_text_width(term, ext_msg[0], &max, AL_LEFT);
    576 	min_text_width(term, ext_msg[0], &min, AL_LEFT);
    577 	max_text_width(term, ext_msg[1], &max, AL_LEFT);
    578 	min_text_width(term, ext_msg[1], &min, AL_LEFT);
    579 	max_buttons_width(term, dlg->items + 2, 2, &max);
    580 	min_buttons_width(term, dlg->items + 2, 2, &min);
    581 	w = term->x * 9 / 10 - 2 * DIALOG_LB;
    582 	if (w > max)
    583 		w = max;
    584 	if (w < min)
    585 		w = min;
    586 	if (w > term->x - 2 * DIALOG_LB)
    587 		w = term->x - 2 * DIALOG_LB;
    588 	if (w < 1)
    589 		w = 1;
    590 	rw = 0;
    591 	dlg_format_text_and_field(dlg, NULL, ext_msg[0], &dlg->items[0], 0, &y,
    592 	                          w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
    593 	y++;
    594 	dlg_format_text_and_field(dlg, NULL, ext_msg[1], &dlg->items[1], 0, &y,
    595 	                          w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
    596 	y++;
    597 	dlg_format_buttons(dlg, NULL, dlg->items + 2, 2, 0, &y, w, &rw,
    598 	                   AL_CENTER);
    599 	w = rw;
    600 	dlg->xw = w + 2 * DIALOG_LB;
    601 	dlg->yw = y + 2 * DIALOG_TB;
    602 	center_dlg(dlg);
    603 	draw_dlg(dlg);
    604 	y = dlg->y + DIALOG_TB;
    605 	dlg_format_text_and_field(dlg, term, ext_msg[0], &dlg->items[0],
    606 	                          dlg->x + DIALOG_LB, &y, w, NULL,
    607 	                          COLOR_DIALOG_TEXT, AL_LEFT);
    608 	y++;
    609 	dlg_format_text_and_field(dlg, term, ext_msg[1], &dlg->items[1],
    610 	                          dlg->x + DIALOG_LB, &y, w, NULL,
    611 	                          COLOR_DIALOG_TEXT, AL_LEFT);
    612 	y++;
    613 	dlg_format_buttons(dlg, term, &dlg->items[2], 2, dlg->x + DIALOG_LB, &y,
    614 	                   w, NULL, AL_CENTER);
    615 }
    616 
    617 /* Puts url and title into the bookmark item */
    618 static void
    619 ext_edit_done(void *data)
    620 {
    621 	struct dialog *d = (struct dialog *)data;
    622 	struct extension *item = (struct extension *)d->udata;
    623 	struct assoc_ok_struct *s = (struct assoc_ok_struct *)d->udata2;
    624 	unsigned char *txt;
    625 	unsigned char *ext, *ct;
    626 
    627 	ext = (unsigned char *)&d->items[5];
    628 	ct = ext + MAX_STR_LEN;
    629 
    630 	txt = stracpy(ext);
    631 	free(item->ext);
    632 	item->ext = txt;
    633 
    634 	txt = stracpy(ct);
    635 	free(item->ct);
    636 	item->ct = txt;
    637 
    638 	s->fn(s->dlg, s->data, &item->head, &ext_ld);
    639 	d->udata = NULL; /* for abort function */
    640 }
    641 
    642 /* destroys an item, this function is called when edit window is aborted */
    643 static void
    644 ext_edit_abort(struct dialog_data *data)
    645 {
    646 	struct dialog *dlg = data->dlg;
    647 	struct extension *item = (struct extension *)dlg->udata;
    648 
    649 	free(dlg->udata2);
    650 	if (item)
    651 		ext_delete_item(&item->head);
    652 }
    653 
    654 static void
    655 ext_edit_item(struct dialog_data *dlg, struct list *data,
    656               void (*ok_fn)(struct dialog_data *, struct list *, struct list *,
    657                             struct list_description *),
    658               struct list *ok_arg, unsigned char dlg_title)
    659 {
    660 	struct extension *neww = get_struct(data, struct extension, head);
    661 	struct terminal *term = dlg->win->term;
    662 	struct dialog *d;
    663 	struct assoc_ok_struct *s;
    664 	unsigned char *ext;
    665 	unsigned char *ct;
    666 
    667 	d = mem_calloc(sizeof(struct dialog) + 4 * sizeof(struct dialog_item)
    668 	               + 2 * MAX_STR_LEN);
    669 
    670 	ext = (unsigned char *)&d->items[5];
    671 	ct = ext + MAX_STR_LEN;
    672 	safe_strncpy(ext, neww->ext, MAX_STR_LEN);
    673 	safe_strncpy(ct, neww->ct, MAX_STR_LEN);
    674 
    675 	/* Create the dialog */
    676 	s = xmalloc(sizeof(struct assoc_ok_struct));
    677 	s->fn = ok_fn;
    678 	s->data = ok_arg;
    679 	s->dlg = dlg;
    680 
    681 	switch (dlg_title) {
    682 	case TITLE_EDIT:
    683 		d->title = TEXT_(T_EDIT_EXTENSION);
    684 		break;
    685 
    686 	case TITLE_ADD:
    687 		d->title = TEXT_(T_ADD_EXTENSION);
    688 		break;
    689 
    690 	default:
    691 		internal("Unsupported dialog title.\n");
    692 	}
    693 
    694 	d->udata = neww;
    695 	d->udata2 = s;
    696 	d->abort = ext_edit_abort;
    697 	d->refresh = ext_edit_done;
    698 	d->refresh_data = d;
    699 	d->title = TEXT_(T_EXTENSION);
    700 	d->fn = ext_edit_item_fn;
    701 	d->items[0].type = D_FIELD;
    702 	d->items[0].dlen = MAX_STR_LEN;
    703 	d->items[0].data = ext;
    704 	d->items[0].fn = check_nonempty;
    705 	d->items[1].type = D_FIELD;
    706 	d->items[1].dlen = MAX_STR_LEN;
    707 	d->items[1].data = ct;
    708 	d->items[1].fn = check_nonempty;
    709 	d->items[2].type = D_BUTTON;
    710 	d->items[2].gid = B_ENTER;
    711 	d->items[2].fn = ok_dialog;
    712 	d->items[2].text = TEXT_(T_OK);
    713 	d->items[3].type = D_BUTTON;
    714 	d->items[3].gid = B_ESC;
    715 	d->items[3].text = TEXT_(T_CANCEL);
    716 	d->items[3].fn = cancel_dialog;
    717 	d->items[4].type = D_END;
    718 	do_dialog(term, d, getml(d, NULL));
    719 }
    720 
    721 static int
    722 ext_test_entry(struct list *e, unsigned char *str)
    723 {
    724 	struct extension *ext = get_struct(e, struct extension, head);
    725 	return casestrstr(ext->ext, str) || casestrstr(ext->ct, str);
    726 }
    727 
    728 static struct list *
    729 ext_find_item(struct list *s, unsigned char *str, int direction)
    730 {
    731 	struct list *e;
    732 
    733 	if (direction >= 0)
    734 		for (e = list_next(s); e != s; e = list_next(e)) {
    735 			if (e->depth >= 0 && ext_test_entry(e, str))
    736 				return e;
    737 		}
    738 	else
    739 		for (e = list_prev(s); e != s; e = list_prev(e)) {
    740 			if (e->depth >= 0 && ext_test_entry(e, str))
    741 				return e;
    742 		}
    743 
    744 	if (e->depth >= 0 && ext_test_entry(e, str))
    745 		return e;
    746 
    747 	return NULL;
    748 }
    749 
    750 void
    751 update_ext(struct extension *neww)
    752 {
    753 	struct extension *repl;
    754 	struct list *r = NULL;
    755 	struct list_head *lr;
    756 	if (!neww->ext[0] || !neww->ct[0])
    757 		return;
    758 	foreach (struct list, r, lr, extensions.list_entry) {
    759 		repl = get_struct(r, struct extension, head);
    760 		if (!strcmp(cast_const_char repl->ext,
    761 		            cast_const_char neww->ext)
    762 		    && !strcmp(cast_const_char repl->ct,
    763 		               cast_const_char neww->ct)) {
    764 			del_from_list(&repl->head);
    765 			add_to_list(extensions.list_entry, &repl->head);
    766 			return;
    767 		}
    768 	}
    769 	repl = mem_calloc(sizeof(struct extension));
    770 	repl->ext = stracpy(neww->ext);
    771 	repl->ct = stracpy(neww->ct);
    772 	repl->head.type = 0;
    773 	add_to_list(extensions.list_entry, &repl->head);
    774 }
    775 
    776 void
    777 update_prog(struct list_head *l, unsigned char *p, int s)
    778 {
    779 	struct protocol_program *repl = NULL;
    780 	struct list_head *lrepl;
    781 	foreach (struct protocol_program, repl, lrepl, *l)
    782 		if (repl->system == s) {
    783 			free(repl->prog);
    784 			goto ss;
    785 		}
    786 	repl = xmalloc(sizeof(struct protocol_program));
    787 	add_to_list(*l, repl);
    788 	repl->system = s;
    789 ss:
    790 	repl->prog = xmalloc(MAX_STR_LEN);
    791 	safe_strncpy(repl->prog, p, MAX_STR_LEN);
    792 }
    793 
    794 unsigned char *
    795 get_prog(struct list_head *l)
    796 {
    797 	struct protocol_program *repl = NULL;
    798 	struct list_head *lrepl;
    799 	foreach (struct protocol_program, repl, lrepl, *l)
    800 		if (repl->system == SYSTEM_ID)
    801 			return repl->prog;
    802 	update_prog(l, cast_uchar "", SYSTEM_ID);
    803 	foreach (struct protocol_program, repl, lrepl, *l)
    804 		if (repl->system == SYSTEM_ID)
    805 			return repl->prog;
    806 	internal("get_prog: program was not added");
    807 	return cast_uchar "";
    808 	;
    809 }
    810 
    811 /* creates default extensions if extension list is empty */
    812 void
    813 create_initial_extensions(void)
    814 {
    815 	struct extension ext;
    816 
    817 	if (!list_empty(extensions.list_entry))
    818 		return;
    819 
    820 	/* here you can add any default extension you want */
    821 	ext.ext = cast_uchar "xpm";
    822 	ext.ct = cast_uchar "image/x-xpixmap";
    823 	update_ext(&ext);
    824 	ext.ext = cast_uchar "xls";
    825 	ext.ct = cast_uchar "application/excel";
    826 	update_ext(&ext);
    827 	ext.ext = cast_uchar "xbm";
    828 	ext.ct = cast_uchar "image/x-xbitmap";
    829 	update_ext(&ext);
    830 	ext.ext = cast_uchar "wav";
    831 	ext.ct = cast_uchar "audio/x-wav";
    832 	update_ext(&ext);
    833 	ext.ext = cast_uchar "tiff,tif";
    834 	ext.ct = cast_uchar "image/tiff";
    835 	update_ext(&ext);
    836 	ext.ext = cast_uchar "tga";
    837 	ext.ct = cast_uchar "image/targa";
    838 	update_ext(&ext);
    839 	ext.ext = cast_uchar "sxw";
    840 	ext.ct = cast_uchar "application/x-openoffice";
    841 	update_ext(&ext);
    842 	ext.ext = cast_uchar "swf";
    843 	ext.ct = cast_uchar "application/x-shockwave-flash";
    844 	update_ext(&ext);
    845 	ext.ext = cast_uchar "svg";
    846 	ext.ct = cast_uchar "image/svg+xml";
    847 	update_ext(&ext);
    848 	ext.ext = cast_uchar "sch";
    849 	ext.ct = cast_uchar "application/gschem";
    850 	update_ext(&ext);
    851 	ext.ext = cast_uchar "rtf";
    852 	ext.ct = cast_uchar "application/rtf";
    853 	update_ext(&ext);
    854 	ext.ext = cast_uchar "ra,rm,ram";
    855 	ext.ct = cast_uchar "audio/x-pn-realaudio";
    856 	update_ext(&ext);
    857 	ext.ext = cast_uchar "qt,mov";
    858 	ext.ct = cast_uchar "video/quicktime";
    859 	update_ext(&ext);
    860 	ext.ext = cast_uchar "ps,eps,ai";
    861 	ext.ct = cast_uchar "application/postscript";
    862 	update_ext(&ext);
    863 	ext.ext = cast_uchar "ppt";
    864 	ext.ct = cast_uchar "application/powerpoint";
    865 	update_ext(&ext);
    866 	ext.ext = cast_uchar "ppm";
    867 	ext.ct = cast_uchar "image/x-portable-pixmap";
    868 	update_ext(&ext);
    869 	ext.ext = cast_uchar "pnm";
    870 	ext.ct = cast_uchar "image/x-portable-anymap";
    871 	update_ext(&ext);
    872 	ext.ext = cast_uchar "png";
    873 	ext.ct = cast_uchar "image/png";
    874 	update_ext(&ext);
    875 	ext.ext = cast_uchar "pgp";
    876 	ext.ct = cast_uchar "application/pgp-signature";
    877 	update_ext(&ext);
    878 	ext.ext = cast_uchar "pgm";
    879 	ext.ct = cast_uchar "image/x-portable-graymap";
    880 	update_ext(&ext);
    881 	ext.ext = cast_uchar "pdf";
    882 	ext.ct = cast_uchar "application/pdf";
    883 	update_ext(&ext);
    884 	ext.ext = cast_uchar "pcb";
    885 	ext.ct = cast_uchar "application/pcb";
    886 	update_ext(&ext);
    887 	ext.ext = cast_uchar "pbm";
    888 	ext.ct = cast_uchar "image/x-portable-bitmap";
    889 	update_ext(&ext);
    890 	ext.ext = cast_uchar "mpeg,mpg,mpe";
    891 	ext.ct = cast_uchar "video/mpeg";
    892 	update_ext(&ext);
    893 	ext.ext = cast_uchar "mp3";
    894 	ext.ct = cast_uchar "audio/mpeg";
    895 	update_ext(&ext);
    896 	ext.ext = cast_uchar "mid,midi";
    897 	ext.ct = cast_uchar "audio/midi";
    898 	update_ext(&ext);
    899 	ext.ext = cast_uchar "jpg,jpeg,jpe";
    900 	ext.ct = cast_uchar "image/jpeg";
    901 	update_ext(&ext);
    902 	ext.ext = cast_uchar "grb";
    903 	ext.ct = cast_uchar "application/gerber";
    904 	update_ext(&ext);
    905 	ext.ext = cast_uchar "gl";
    906 	ext.ct = cast_uchar "video/gl";
    907 	update_ext(&ext);
    908 	ext.ext = cast_uchar "gif";
    909 	ext.ct = cast_uchar "image/gif";
    910 	update_ext(&ext);
    911 	ext.ext = cast_uchar "gbr";
    912 	ext.ct = cast_uchar "application/gerber";
    913 	update_ext(&ext);
    914 	ext.ext = cast_uchar "g";
    915 	ext.ct = cast_uchar "application/brlcad";
    916 	update_ext(&ext);
    917 	ext.ext = cast_uchar "fli";
    918 	ext.ct = cast_uchar "video/fli";
    919 	update_ext(&ext);
    920 	ext.ext = cast_uchar "dxf";
    921 	ext.ct = cast_uchar "application/dxf";
    922 	update_ext(&ext);
    923 	ext.ext = cast_uchar "dvi";
    924 	ext.ct = cast_uchar "application/x-dvi";
    925 	update_ext(&ext);
    926 	ext.ext = cast_uchar "dl";
    927 	ext.ct = cast_uchar "video/dl";
    928 	update_ext(&ext);
    929 	ext.ext = cast_uchar "deb";
    930 	ext.ct = cast_uchar "application/x-debian-package";
    931 	update_ext(&ext);
    932 	ext.ext = cast_uchar "avi";
    933 	ext.ct = cast_uchar "video/x-msvideo";
    934 	update_ext(&ext);
    935 	ext.ext = cast_uchar "au,snd";
    936 	ext.ct = cast_uchar "audio/basic";
    937 	update_ext(&ext);
    938 	ext.ext = cast_uchar "aif,aiff,aifc";
    939 	ext.ct = cast_uchar "audio/x-aiff";
    940 	update_ext(&ext);
    941 }
    942 
    943 /* --------------------------- PROG -----------------------------*/
    944 
    945 static int
    946 is_in_list(unsigned char *list, unsigned char *str, int l)
    947 {
    948 	unsigned char *l2, *l3;
    949 	if (!l)
    950 		return 0;
    951 rep:
    952 	while (*list && *list <= ' ')
    953 		list++;
    954 	if (!*list)
    955 		return 0;
    956 	for (l2 = list; *l2 && *l2 != ','; l2++)
    957 		;
    958 	for (l3 = l2 - 1; l3 >= list && *l3 <= ' '; l3--)
    959 		;
    960 	l3++;
    961 	if (l3 - list == l && !casecmp(str, list, l))
    962 		return 1;
    963 	list = l2;
    964 	if (*list == ',')
    965 		list++;
    966 	goto rep;
    967 }
    968 
    969 /* FIXME */
    970 static char *
    971 canonical_compressed_ext(char *ext, char *ext_end)
    972 {
    973 	size_t len;
    974 	if (!ext_end)
    975 		ext_end = strchr(ext, 0);
    976 	len = ext_end - ext;
    977 	switch (len) {
    978 	case 3:
    979 		if (!strncasecmp(ext, "tgz", 3))
    980 			return "gz";
    981 		else if (!strncasecmp(ext, "txz", 3))
    982 			return "xz";
    983 		else if (!strncasecmp(ext, "tbz", 3))
    984 			return "bz2";
    985 		break;
    986 	case 6:
    987 		if (!strncasecmp(ext, "tar-gz", 3))
    988 			return "gz";
    989 		else if (!strncasecmp(ext, "tar-xz", 3))
    990 			return "xz";
    991 		break;
    992 	case 7:
    993 		if (!strncasecmp(ext, "tar-bz2", 3))
    994 			return "bz2";
    995 		/* fallthrough */
    996 	default:
    997 		break;
    998 	}
    999 	return NULL;
   1000 }
   1001 
   1002 /* FIXME */
   1003 unsigned char *
   1004 get_compress_by_extension(char *ext, char *ext_end)
   1005 {
   1006 	size_t len;
   1007 	char *x;
   1008 	if ((x = canonical_compressed_ext(ext, ext_end))) {
   1009 		ext = x;
   1010 		ext_end = strchr(x, 0);
   1011 	}
   1012 	len = ext_end - ext;
   1013 	switch (len) {
   1014 	case 1:
   1015 		if (!strncasecmp(ext, "z", 1))
   1016 			return cast_uchar "compress";
   1017 		break;
   1018 	case 2:
   1019 		if (!strncasecmp(ext, "br", 2))
   1020 			return cast_uchar "br";
   1021 		else if (!strncasecmp(ext, "gz", 2))
   1022 			return cast_uchar "gzip";
   1023 		else if (!strncasecmp(ext, "xz", 2))
   1024 			return cast_uchar "lzma2";
   1025 		else if (!strncasecmp(ext, "lz", 2))
   1026 			return cast_uchar "lzip";
   1027 		break;
   1028 	case 3:
   1029 		if (!strncasecmp(ext, "bz2", 3))
   1030 			return cast_uchar "bzip2";
   1031 		break;
   1032 	case 4:
   1033 		if (!strncasecmp(ext, "lzma", 4))
   1034 			return cast_uchar "lzma";
   1035 		/* fallthrough */
   1036 	default:
   1037 		break;
   1038 	}
   1039 	return NULL;
   1040 }
   1041 
   1042 unsigned char *
   1043 get_content_type_by_extension(unsigned char *url)
   1044 {
   1045 	struct list *l = NULL;
   1046 	struct list_head *ll;
   1047 	unsigned char *ct, *eod, *ext, *exxt;
   1048 	int extl;
   1049 	size_t el;
   1050 	ext = NULL;
   1051 	extl = 0;
   1052 	if (!(ct = get_url_data(url)))
   1053 		ct = url;
   1054 	for (eod = ct; *eod && !end_of_dir(url, *eod); eod++)
   1055 		;
   1056 	for (; ct < eod; ct++)
   1057 		if (*ct == '.') {
   1058 			if (ext)
   1059 				if (get_compress_by_extension((char *)(ct + 1),
   1060 				                              (char *)eod))
   1061 					break;
   1062 			ext = ct + 1;
   1063 		} else if (dir_sep(*ct))
   1064 			ext = NULL;
   1065 	if (ext)
   1066 		while (ext[extl] && ext[extl] != '.' && !dir_sep(ext[extl])
   1067 		       && !end_of_dir(url, ext[extl]))
   1068 			extl++;
   1069 	if ((extl == 3 && !casecmp(ext, cast_uchar "htm", 3))
   1070 	    || (extl == 4 && !casecmp(ext, cast_uchar "html", 4))
   1071 	    || (extl == 5 && !casecmp(ext, cast_uchar "xhtml", 5)))
   1072 		return stracpy(cast_uchar "text/html");
   1073 	foreach (struct list, l, ll, extensions.list_entry) {
   1074 		struct extension *e = get_struct(l, struct extension, head);
   1075 		unsigned char *fname = NULL;
   1076 		if (!(ct = get_url_data(url)))
   1077 			ct = url;
   1078 		for (; *ct && !end_of_dir(url, *ct); ct++)
   1079 			if (dir_sep(*ct))
   1080 				fname = ct + 1;
   1081 		if (!fname) {
   1082 			if (is_in_list(e->ext, ext, extl))
   1083 				return stracpy(e->ct);
   1084 		} else {
   1085 			int fnlen = 0;
   1086 			int x;
   1087 			while (fname[fnlen] && !end_of_dir(url, fname[fnlen]))
   1088 				fnlen++;
   1089 			for (x = 0; x < fnlen; x++)
   1090 				if (fname[x] == '.')
   1091 					if (is_in_list(e->ext, fname + x + 1,
   1092 					               fnlen - x - 1))
   1093 						return stracpy(e->ct);
   1094 		}
   1095 	}
   1096 
   1097 	if ((extl == 3 && !casecmp(ext, cast_uchar "jpg", 3))
   1098 	    || (extl == 4 && !casecmp(ext, cast_uchar "pjpg", 4))
   1099 	    || (extl == 4 && !casecmp(ext, cast_uchar "jpeg", 4))
   1100 	    || (extl == 5 && !casecmp(ext, cast_uchar "pjpeg", 5)))
   1101 		return stracpy(cast_uchar "image/jpeg");
   1102 	if ((extl == 3 && !casecmp(ext, cast_uchar "png", 3)))
   1103 		return stracpy(cast_uchar "image/png");
   1104 	if ((extl == 3 && !casecmp(ext, cast_uchar "gif", 3)))
   1105 		return stracpy(cast_uchar "image/gif");
   1106 	if ((extl == 3 && !casecmp(ext, cast_uchar "xbm", 3)))
   1107 		return stracpy(cast_uchar "image/x-xbitmap");
   1108 	if ((extl == 3 && !casecmp(ext, cast_uchar "tif", 3))
   1109 	    || (extl == 4 && !casecmp(ext, cast_uchar "tiff", 4)))
   1110 		return stracpy(cast_uchar "image/tiff");
   1111 	exxt = NULL;
   1112 	el = add_to_str(&exxt, 0, cast_uchar "application/x-");
   1113 	el = add_bytes_to_str(&exxt, el, ext, extl);
   1114 	foreach (struct list, l, ll, assoc.list_entry) {
   1115 		struct assoc *a = get_struct(l, struct assoc, head);
   1116 		if (is_in_list(a->ct, exxt, el))
   1117 			return exxt;
   1118 	}
   1119 	free(exxt);
   1120 	return NULL;
   1121 }
   1122 
   1123 static unsigned char *
   1124 get_content_type_by_header_and_extension(unsigned char *head,
   1125                                          unsigned char *url)
   1126 {
   1127 	unsigned char *ct, *file;
   1128 	ct = get_content_type_by_extension(url);
   1129 	if (ct)
   1130 		return ct;
   1131 	file = get_filename_from_header(head);
   1132 	if (file) {
   1133 		ct = get_content_type_by_extension(file);
   1134 		free(file);
   1135 		if (ct)
   1136 			return ct;
   1137 	}
   1138 	return NULL;
   1139 }
   1140 
   1141 static unsigned char *
   1142 get_extension_by_content_type(unsigned char *ct)
   1143 {
   1144 	struct list *l = NULL;
   1145 	struct list_head *ll;
   1146 	unsigned char *x, *y;
   1147 	if (is_html_type(ct))
   1148 		return stracpy(cast_uchar "html");
   1149 	foreach (struct list, l, ll, extensions.list_entry) {
   1150 		struct extension *e = get_struct(l, struct extension, head);
   1151 		if (!casestrcmp(e->ct, ct)) {
   1152 			x = stracpy(e->ext);
   1153 			if ((y = cast_uchar strchr(cast_const_char x, ',')))
   1154 				*y = 0;
   1155 			return x;
   1156 		}
   1157 	}
   1158 	if (!casestrcmp(ct, cast_uchar "image/jpeg")
   1159 	    || !casestrcmp(ct, cast_uchar "image/jpg")
   1160 	    || !casestrcmp(ct, cast_uchar "image/jpe")
   1161 	    || !casestrcmp(ct, cast_uchar "image/pjpe")
   1162 	    || !casestrcmp(ct, cast_uchar "image/pjpeg")
   1163 	    || !casestrcmp(ct, cast_uchar "image/pjpg"))
   1164 		return stracpy(cast_uchar "jpg");
   1165 	if (!casestrcmp(ct, cast_uchar "image/png")
   1166 	    || !casestrcmp(ct, cast_uchar "image/x-png"))
   1167 		return stracpy(cast_uchar "png");
   1168 	if (!casestrcmp(ct, cast_uchar "image/gif"))
   1169 		return stracpy(cast_uchar "gif");
   1170 	if (!casestrcmp(ct, cast_uchar "image/x-bitmap"))
   1171 		return stracpy(cast_uchar "xbm");
   1172 	if (!casestrcmp(ct, cast_uchar "image/tiff")
   1173 	    || !casestrcmp(ct, cast_uchar "image/tif"))
   1174 		return stracpy(cast_uchar "tiff");
   1175 	if (!casestrcmp(ct, cast_uchar "image/svg")
   1176 	    || !casestrcmp(ct, cast_uchar "image/svg+xml"))
   1177 		return stracpy(cast_uchar "svg");
   1178 	if (!cmpbeg(ct, cast_uchar "application/x-")) {
   1179 		x = ct + strlen("application/x-");
   1180 		if (casestrcmp(x, cast_uchar "z")
   1181 		    && casestrcmp(x, cast_uchar "gz")
   1182 		    && casestrcmp(x, cast_uchar "gzip")
   1183 		    && casestrcmp(x, cast_uchar "br")
   1184 		    && casestrcmp(x, cast_uchar "bz2")
   1185 		    && casestrcmp(x, cast_uchar "bzip2")
   1186 		    && casestrcmp(x, cast_uchar "lzma")
   1187 		    && casestrcmp(x, cast_uchar "lzma2")
   1188 		    && casestrcmp(x, cast_uchar "xz")
   1189 		    && casestrcmp(x, cast_uchar "lz")
   1190 		    && !strchr(cast_const_char x, '-')
   1191 		    && strlen(cast_const_char x) <= 4)
   1192 			return stracpy(x);
   1193 	}
   1194 	return NULL;
   1195 }
   1196 
   1197 static unsigned char *
   1198 get_content_encoding_from_content_type(unsigned char *ct)
   1199 {
   1200 	if (!casestrcmp(ct, cast_uchar "application/x-gzip")
   1201 	    || !casestrcmp(ct, cast_uchar "application/x-tgz")
   1202 	    || !casestrcmp(ct, cast_uchar "application/x-gtar"))
   1203 		return cast_uchar "gzip";
   1204 	if (!casestrcmp(ct, cast_uchar "application/x-br"))
   1205 		return cast_uchar "br";
   1206 	if (!casestrcmp(ct, cast_uchar "application/x-bzip2")
   1207 	    || !casestrcmp(ct, cast_uchar "application/x-bzip"))
   1208 		return cast_uchar "bzip2";
   1209 	if (!casestrcmp(ct, cast_uchar "application/x-lzma"))
   1210 		return cast_uchar "lzma";
   1211 	if (!casestrcmp(ct, cast_uchar "application/x-lzma2")
   1212 	    || !casestrcmp(ct, cast_uchar "application/x-xz"))
   1213 		return cast_uchar "lzma2";
   1214 	if (!casestrcmp(ct, cast_uchar "application/x-lz")
   1215 	    || !casestrcmp(ct, cast_uchar "application/x-lzip"))
   1216 		return cast_uchar "lzip";
   1217 	return NULL;
   1218 }
   1219 
   1220 unsigned char *
   1221 get_content_type(unsigned char *head, unsigned char *url)
   1222 {
   1223 	unsigned char *ct;
   1224 	int code;
   1225 	if ((ct = parse_http_header(head, cast_uchar "Content-Type", NULL))) {
   1226 		unsigned char *s;
   1227 		if ((s = cast_uchar strchr(cast_const_char ct, ';')))
   1228 			*s = 0;
   1229 		while (*ct && ct[strlen(cast_const_char ct) - 1] <= ' ')
   1230 			ct[strlen(cast_const_char ct) - 1] = 0;
   1231 		if (*ct == '"' && ct[1]
   1232 		    && ct[strlen(cast_const_char ct) - 1] == '"') {
   1233 			memmove(ct, ct + 1, strlen(cast_const_char ct));
   1234 			ct[strlen(cast_const_char ct) - 1] = 0;
   1235 		}
   1236 		if (!casestrcmp(ct, cast_uchar "text/plain")
   1237 		    || !casestrcmp(ct, cast_uchar "application/octet-stream")
   1238 		    || !casestrcmp(ct, cast_uchar "application/octetstream")
   1239 		    || !casestrcmp(ct, cast_uchar "application/octet_stream")
   1240 		    || !casestrcmp(ct, cast_uchar "application/binary")
   1241 		    || !casestrcmp(ct, cast_uchar
   1242 		                   "application/x-www-form-urlencoded")
   1243 		    || get_content_encoding_from_content_type(ct)) {
   1244 			unsigned char *ctt;
   1245 			if (!get_http_code(head, &code, NULL) && code >= 300)
   1246 				goto no_code_by_extension;
   1247 			ctt =
   1248 			    get_content_type_by_header_and_extension(head, url);
   1249 			if (ctt) {
   1250 				free(ct);
   1251 				return ctt;
   1252 			}
   1253 		}
   1254 no_code_by_extension:
   1255 		if (!*ct)
   1256 			free(ct);
   1257 		else
   1258 			return ct;
   1259 	}
   1260 	if (!get_http_code(head, &code, NULL) && code >= 300)
   1261 		return stracpy(cast_uchar "text/html");
   1262 	ct = get_content_type_by_header_and_extension(head, url);
   1263 	if (ct)
   1264 		return ct;
   1265 	return !force_html ? stracpy(cast_uchar "text/plain")
   1266 	                   : stracpy(cast_uchar "text/html");
   1267 }
   1268 
   1269 unsigned char *
   1270 get_content_encoding(unsigned char *head, unsigned char *url, int just_ce)
   1271 {
   1272 	unsigned char *ce, *ct, *ext;
   1273 	char *extd;
   1274 	unsigned char *u;
   1275 	int code;
   1276 	if ((ce = parse_http_header(head, cast_uchar "Content-Encoding", NULL)))
   1277 		return ce;
   1278 	if (just_ce)
   1279 		return NULL;
   1280 	if ((ct = parse_http_header(head, cast_uchar "Content-Type", NULL))) {
   1281 		unsigned char *s;
   1282 		if ((s = cast_uchar strchr(cast_const_char ct, ';')))
   1283 			*s = 0;
   1284 		ce = get_content_encoding_from_content_type(ct);
   1285 		if (ce) {
   1286 			free(ct);
   1287 			return stracpy(ce);
   1288 		}
   1289 		if (is_html_type(ct)) {
   1290 			free(ct);
   1291 			return NULL;
   1292 		}
   1293 		free(ct);
   1294 	}
   1295 	if (!get_http_code(head, &code, NULL) && code >= 300)
   1296 		return NULL;
   1297 	if (!(ext = get_url_data(url)))
   1298 		ext = url;
   1299 	for (u = ext; *u; u++)
   1300 		if (end_of_dir(url, *u))
   1301 			goto skip_ext;
   1302 	extd = strrchr((char *)ext, '.');
   1303 	if (extd) {
   1304 		ce = get_compress_by_extension(extd + 1, strchr(extd + 1, 0));
   1305 		if (ce)
   1306 			return stracpy(ce);
   1307 	}
   1308 skip_ext:
   1309 	if ((ext = get_filename_from_header(head))) {
   1310 		extd = strrchr((char *)ext, '.');
   1311 		if (extd) {
   1312 			ce = get_compress_by_extension(extd + 1,
   1313 			                               strchr(extd + 1, 0));
   1314 			if (ce) {
   1315 				free(ext);
   1316 				return stracpy(ce);
   1317 			}
   1318 		}
   1319 		free(ext);
   1320 	}
   1321 	return NULL;
   1322 }
   1323 
   1324 unsigned char *
   1325 encoding_2_extension(unsigned char *encoding)
   1326 {
   1327 	if (!casestrcmp(encoding, cast_uchar "gzip")
   1328 	    || !casestrcmp(encoding, cast_uchar "x-gzip"))
   1329 		return cast_uchar "gz";
   1330 	if (!casestrcmp(encoding, cast_uchar "compress")
   1331 	    || !casestrcmp(encoding, cast_uchar "x-compress"))
   1332 		return cast_uchar "Z";
   1333 	if (!casestrcmp(encoding, cast_uchar "bzip2"))
   1334 		return cast_uchar "bz2";
   1335 	if (!casestrcmp(encoding, cast_uchar "lzma"))
   1336 		return cast_uchar "lzma";
   1337 	if (!casestrcmp(encoding, cast_uchar "lzma2"))
   1338 		return cast_uchar "xz";
   1339 	if (!casestrcmp(encoding, cast_uchar "lzip"))
   1340 		return cast_uchar "lz";
   1341 	return NULL;
   1342 }
   1343 
   1344 /* returns field with associations */
   1345 struct assoc *
   1346 get_type_assoc(struct terminal *term, unsigned char *type, int *n)
   1347 {
   1348 	struct assoc *assoc_array;
   1349 	struct list *l = NULL;
   1350 	struct list_head *ll;
   1351 	int count = 0;
   1352 	foreach (struct list, l, ll, assoc.list_entry) {
   1353 		struct assoc *a = get_struct(l, struct assoc, head);
   1354 		if (a->system == SYSTEM_ID
   1355 		    && (term->environment & ENV_XWIN ? a->xwin : a->cons)
   1356 		    && is_in_list(a->ct, type,
   1357 		                  (int)strlen(cast_const_char type))) {
   1358 			if (count == INT_MAX)
   1359 				overalloc();
   1360 			count++;
   1361 		}
   1362 	}
   1363 	*n = count;
   1364 	if (!count)
   1365 		return NULL;
   1366 	if ((unsigned)count > INT_MAX / sizeof(struct assoc))
   1367 		overalloc();
   1368 	assoc_array = xmalloc(count * sizeof(struct assoc));
   1369 	count = 0;
   1370 	foreach (struct list, l, ll, assoc.list_entry) {
   1371 		struct assoc *a = get_struct(l, struct assoc, head);
   1372 		if (a->system == SYSTEM_ID
   1373 		    && (term->environment & ENV_XWIN ? a->xwin : a->cons)
   1374 		    && is_in_list(a->ct, type,
   1375 		                  (int)strlen(cast_const_char type)))
   1376 			assoc_array[count++] = *a;
   1377 	}
   1378 	return assoc_array;
   1379 }
   1380 
   1381 int
   1382 is_html_type(unsigned char *ct)
   1383 {
   1384 	return !casestrcmp(ct, cast_uchar "text/html")
   1385 	       || !casestrcmp(ct, cast_uchar "text-html")
   1386 	       || !casestrcmp(ct, cast_uchar "text/x-server-parsed-html")
   1387 	       || !casestrcmp(ct, cast_uchar "text/xml")
   1388 	       || !casecmp(ct, cast_uchar "application/xhtml",
   1389 	                   strlen("application/xhtml"));
   1390 }
   1391 
   1392 unsigned char *
   1393 get_filename_from_header(unsigned char *head)
   1394 {
   1395 	int extended = 0;
   1396 	unsigned char *ct, *x, *y, *codepage;
   1397 	if ((ct = parse_http_header(head, cast_uchar "Content-Disposition",
   1398 	                            NULL))) {
   1399 		x = parse_header_param(ct, cast_uchar "filename*", 1);
   1400 		if (x)
   1401 			extended = 1;
   1402 		else
   1403 			x = parse_header_param(ct, cast_uchar "filename", 1);
   1404 		free(ct);
   1405 		if (x) {
   1406 			if (*x)
   1407 				goto ret_x;
   1408 			free(x);
   1409 		}
   1410 	}
   1411 	if ((ct = parse_http_header(head, cast_uchar "Content-Type", NULL))) {
   1412 		x = parse_header_param(ct, cast_uchar "name*", 0);
   1413 		if (x)
   1414 			extended = 1;
   1415 		else
   1416 			x = parse_header_param(ct, cast_uchar "name", 0);
   1417 		free(ct);
   1418 		if (x) {
   1419 			if (*x)
   1420 				goto ret_x;
   1421 			free(x);
   1422 		}
   1423 	}
   1424 	return NULL;
   1425 ret_x:
   1426 	codepage = NULL;
   1427 	if (extended) {
   1428 		unsigned char *ap1, *ap2;
   1429 		ap1 = cast_uchar strchr(cast_const_char x, '\'');
   1430 		if (!ap1)
   1431 			goto no_extended;
   1432 		ap2 = cast_uchar strchr(cast_const_char(ap1 + 1), '\'');
   1433 		if (ap2)
   1434 			ap2++;
   1435 		else
   1436 			ap2 = ap1 + 1;
   1437 		codepage = memacpy(x, ap1 - x);
   1438 		memmove(x, ap2, strlen(cast_const_char ap2) + 1);
   1439 	}
   1440 
   1441 no_extended:
   1442 	y = NULL;
   1443 	add_conv_str(&y, 0, x, (int)strlen(cast_const_char x), -2);
   1444 	free(x);
   1445 	free(codepage);
   1446 
   1447 	for (x = y; *y; y++)
   1448 		if (dir_sep(*y))
   1449 			*y = '-';
   1450 	return x;
   1451 }
   1452 
   1453 unsigned char *
   1454 get_filename_from_url(unsigned char *url, unsigned char *head, int tmp)
   1455 {
   1456 	unsigned char *u, *s, *e, *f, *x, *ww;
   1457 	unsigned char *ct, *want_ext;
   1458 	if (!casecmp(url, cast_uchar "data:", 5))
   1459 		url = cast_uchar "data:/data";
   1460 	want_ext = stracpy(cast_uchar "");
   1461 	f = get_filename_from_header(head);
   1462 	if (f)
   1463 		goto no_ct;
   1464 	if (!(u = get_url_data(url)))
   1465 		u = url;
   1466 	for (e = s = u; *e && !end_of_dir(url, *e); e++)
   1467 		if (dir_sep(*e))
   1468 			s = e + 1;
   1469 	f = NULL;
   1470 	add_conv_str(&f, 0, s, (int)(e - s), -2);
   1471 	if (!(ct = parse_http_header(head, cast_uchar "Content-Type", NULL)))
   1472 		goto no_ct;
   1473 	free(ct);
   1474 	ct = get_content_type(head, url);
   1475 	if (ct) {
   1476 		x = get_extension_by_content_type(ct);
   1477 		if (x) {
   1478 			add_to_strn(&want_ext, cast_uchar ".");
   1479 			add_to_strn(&want_ext, x);
   1480 			free(x);
   1481 		}
   1482 		free(ct);
   1483 	}
   1484 no_ct:
   1485 	if (!*want_ext) {
   1486 		x = cast_uchar strrchr(cast_const_char f, '.');
   1487 		if (x) {
   1488 			free(want_ext);
   1489 			want_ext = stracpy(x);
   1490 		}
   1491 	}
   1492 	ct = get_content_encoding(head, url, 0);
   1493 	if (ct) {
   1494 		x = encoding_2_extension(ct);
   1495 		if (!tmp) {
   1496 			unsigned char *ct1;
   1497 			ct1 = get_content_encoding(head, url, 1);
   1498 			if (ct1)
   1499 				free(ct1);
   1500 			else if (x) {
   1501 				unsigned char *w = cast_uchar strrchr(
   1502 				    cast_const_char want_ext, '.');
   1503 				if (w
   1504 				    && (ww = (unsigned char *)
   1505 				            canonical_compressed_ext(
   1506 						(char *)(w + 1), NULL))
   1507 				    && !casestrcmp(x, ww))
   1508 					goto skip_want_ext;
   1509 				if (w && !casestrcmp(w + 1, x))
   1510 					goto skip_want_ext;
   1511 				add_to_strn(&want_ext, cast_uchar ".");
   1512 				add_to_strn(&want_ext, x);
   1513 skip_want_ext:;
   1514 			}
   1515 		} else if (x) {
   1516 			if (strlen(cast_const_char x) + 1
   1517 			        < strlen(cast_const_char f)
   1518 			    && f[strlen(cast_const_char f)
   1519 			         - strlen(cast_const_char x) - 1]
   1520 			           == '.'
   1521 			    && !casestrcmp(f + strlen(cast_const_char f)
   1522 			                       - strlen(cast_const_char x),
   1523 			                   x)) {
   1524 				f[strlen(cast_const_char f)
   1525 				  - strlen(cast_const_char x) - 1] = 0;
   1526 			}
   1527 		}
   1528 		free(ct);
   1529 	}
   1530 	if (strlen(cast_const_char want_ext) > strlen(cast_const_char f)
   1531 	    || casestrcmp(want_ext, f + strlen(cast_const_char f)
   1532 	                                - strlen(cast_const_char want_ext))) {
   1533 		x = cast_uchar strrchr(cast_const_char f, '.');
   1534 		if (x
   1535 		    && (ww = (unsigned char *)canonical_compressed_ext(
   1536 			    (char *)(x + 1), NULL))
   1537 		    && want_ext[0] == '.' && !casestrcmp(want_ext + 1, ww))
   1538 			goto skip_tgz_2;
   1539 		if (x)
   1540 			*x = 0;
   1541 		add_to_strn(&f, want_ext);
   1542 skip_tgz_2:;
   1543 	}
   1544 	free(want_ext);
   1545 	return f;
   1546 }
   1547 
   1548 void
   1549 free_types(void)
   1550 {
   1551 	struct list *l = NULL;
   1552 	struct list_head *ll;
   1553 	foreach (struct list, l, ll, assoc.list_entry) {
   1554 		struct assoc *a = get_struct(l, struct assoc, head);
   1555 		free(a->ct);
   1556 		free(a->prog);
   1557 		free(a->label);
   1558 		ll = ll->prev;
   1559 		del_from_list(&a->head);
   1560 		free(a);
   1561 	}
   1562 	foreach (struct list, l, ll, extensions.list_entry) {
   1563 		struct extension *e = get_struct(l, struct extension, head);
   1564 		free(e->ext);
   1565 		free(e->ct);
   1566 		ll = ll->prev;
   1567 		del_from_list(&e->head);
   1568 		free(e);
   1569 	}
   1570 
   1571 	free_history(ext_search_history);
   1572 	free_history(assoc_search_history);
   1573 }