1 #include "links.h"
2 
3 struct list_head mailto_prog = { &mailto_prog, &mailto_prog };
4 struct list_head telnet_prog = { &telnet_prog, &telnet_prog };
5 struct list_head tn3270_prog = { &tn3270_prog, &tn3270_prog };
6 struct list_head mms_prog = { &mms_prog, &mms_prog };
7 
8 struct list_head assoc = { &assoc, &assoc };
9 
get_assoc_cnt()10 unsigned get_assoc_cnt()
11 {
12 	static unsigned assoc_cnt = 0;
13 	if (!++assoc_cnt) assoc_cnt = 1;
14 	return assoc_cnt;
15 }
16 
17 struct list_head extensions = { &extensions, &extensions };
18 
delete_association(struct assoc * del)19 void delete_association(struct assoc *del)
20 {
21 	del_from_list(del);
22 	mem_free(del->label);
23 	mem_free(del->ct);
24 	mem_free(del->prog);
25 	mem_free(del);
26 }
27 
delete_extension(struct extension * del)28 void delete_extension(struct extension *del)
29 {
30 	del_from_list(del);
31 	mem_free(del->ext);
32 	mem_free(del->ct);
33 	mem_free(del);
34 }
35 
is_in_list(unsigned char * list,unsigned char * str,int l)36 int is_in_list(unsigned char *list, unsigned char *str, int l)
37 {
38 	unsigned char *l2, *l3;
39 	if (!l) return 0;
40 	rep:
41 	while (*list && *list <= ' ') list++;
42 	if (!*list) return 0;
43 	for (l2 = list; *l2 && *l2 != ','; l2++) ;
44 	for (l3 = l2 - 1; l3 >= list && *l3 <= ' '; l3--) ;
45 	l3++;
46 	if (l3 - list == l && !casecmp(str, list, l)) return 1;
47 	list = l2;
48 	if (*list == ',') list++;
49 	goto rep;
50 }
51 
get_content_type(unsigned char * head,unsigned char * url)52 unsigned char *get_content_type(unsigned char *head, unsigned char *url)
53 {
54 	struct extension *e;
55 	struct assoc *a;
56 	unsigned char *ct, *ext, *exxt;
57 	int extl, el;
58 	int code, vers;
59 	if (head && (ct = parse_http_header(head, "Content-Type", NULL))) {
60 		unsigned char *s;
61 		if ((s = strchr(ct, ';'))) *s = 0;
62 		while (*ct && ct[strlen(ct) - 1] <= ' ') ct[strlen(ct) - 1] = 0;
63 		return ct;
64 	}
65 	if (!get_http_code(head, &code, &vers) && code >= 300)
66 		return stracpy("text/html");
67 	ext = NULL, extl = 0;
68 	for (ct = url; *ct && !end_of_dir(*ct); ct++)
69 		if (*ct == '.') ext = ct + 1;
70 		else if (dir_sep(*ct)) ext = NULL;
71 	if (ext) while (ext[extl] && !dir_sep(ext[extl]) && !end_of_dir(ext[extl])) extl++;
72 	if (force_html ||
73 	    (extl == 3 && !casecmp(ext, "htm", 3)) ||
74 	    (extl == 4 && !casecmp(ext, "html", 4))) return stracpy("text/html");
75 	foreach(e, extensions) if (is_in_list(e->ext, ext, extl)) return stracpy(e->ct);
76 	exxt = init_str(); el = 0;
77 	add_to_str(&exxt, &el, "application/x-");
78 	add_bytes_to_str(&exxt, &el, ext, extl);
79 	foreach(a, assoc) if (is_in_list(a->ct, exxt, el)) return exxt;
80 	mem_free(exxt);
81 	return !force_html ? stracpy("text/plain") : stracpy("text/html");
82 }
83 
get_type_assoc(struct terminal * term,unsigned char * type)84 struct assoc *get_type_assoc(struct terminal *term, unsigned char *type)
85 {
86 	struct assoc *a;
87 	foreach(a, assoc) if (a->system == SYSTEM_ID && (term->environment & ENV_XWIN ? a->xwin : a->cons) && is_in_list(a->ct, type, strlen(type))) return a;
88 	return NULL;
89 }
90 
free_types()91 void free_types()
92 {
93 	struct assoc *a;
94 	struct extension *e;
95 	struct protocol_program *p;
96 	foreach(a, assoc) {
97 		mem_free(a->ct);
98 		mem_free(a->prog);
99 		mem_free(a->label);
100 	}
101 	free_list(assoc);
102 	foreach(e, extensions) {
103 		mem_free(e->ext);
104 		mem_free(e->ct);
105 	}
106 	free_list(extensions);
107 	foreach(p, mailto_prog) mem_free(p->prog);
108 	free_list(mailto_prog);
109 	foreach(p, telnet_prog) mem_free(p->prog);
110 	free_list(telnet_prog);
111 	foreach(p, tn3270_prog) mem_free(p->prog);
112 	free_list(tn3270_prog);
113 	foreach(p, mms_prog) mem_free(p->prog);
114 	free_list(mms_prog);
115 }
116 
117 unsigned char *ct_msg[] = {
118 	TEXT_(T_LABEL),
119 	TEXT_(T_CONTENT_TYPES),
120 	TEXT_(T_PROGRAM__IS_REPLACED_WITH_FILE_NAME),
121 #ifdef ASSOC_BLOCK
122 	TEXT_(T_BLOCK_TERMINAL_WHILE_PROGRAM_RUNNING),
123 #endif
124 #ifdef ASSOC_CONS_XWIN
125 	TEXT_(T_RUN_ON_TERMINAL),
126 	TEXT_(T_RUN_IN_XWINDOW),
127 #endif
128 	TEXT_(T_ASK_BEFORE_OPENING),
129 };
130 
add_ct_fn(struct dialog_data * dlg)131 void add_ct_fn(struct dialog_data *dlg)
132 {
133 	struct terminal *term = dlg->win->term;
134 	int max = 0, min = 0;
135 	int w, rw;
136 	int y = -1;
137 	int p = 1;
138 #ifdef ASSOC_BLOCK
139 	p++;
140 #endif
141 #ifdef ASSOC_CONS_XWIN
142 	p += 2;
143 #endif
144 	max_text_width(term, ct_msg[0], &max);
145 	min_text_width(term, ct_msg[0], &min);
146 	max_text_width(term, ct_msg[1], &max);
147 	min_text_width(term, ct_msg[1], &min);
148 	max_text_width(term, ct_msg[2], &max);
149 	min_text_width(term, ct_msg[2], &min);
150 	max_group_width(term, ct_msg + 3, dlg->items + 3, p, &max);
151 	min_group_width(term, ct_msg + 3, dlg->items + 3, p, &min);
152 	max_buttons_width(term, dlg->items + 3 + p, 2, &max);
153 	min_buttons_width(term, dlg->items + 3 + p, 2, &min);
154 	w = term->x * 9 / 10 - 2 * DIALOG_LB;
155 	if (w > max) w = max;
156 	if (w < min) w = min;
157 	if (w > term->x - 2 * DIALOG_LB) w = term->x - 2 * DIALOG_LB;
158 	if (w < 1) w = 1;
159 	rw = 0;
160 	dlg_format_text(NULL, term, _(ct_msg[0], term), 0, &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
161 	y += 2;
162 	dlg_format_text(NULL, term, _(ct_msg[1], term), 0, &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
163 	y += 2;
164 	dlg_format_text(NULL, term, _(ct_msg[2], term), 0, &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
165 	y += 2;
166 	dlg_format_group(NULL, term, ct_msg + 3, dlg->items + 3, p, 0, &y, w, &rw);
167 	y++;
168 	dlg_format_buttons(NULL, term, dlg->items + 3 + p, 2, 0, &y, w, &rw, AL_CENTER);
169 	w = rw;
170 	dlg->xw = w + 2 * DIALOG_LB;
171 	dlg->yw = y + 2 * DIALOG_TB;
172 	center_dlg(dlg);
173 	draw_dlg(dlg);
174 	y = dlg->y + DIALOG_TB;
175 	dlg_format_text(term, term, ct_msg[0], dlg->x + DIALOG_LB, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
176 	dlg_format_field(term, term, &dlg->items[0], dlg->x + DIALOG_LB, &y, w, NULL, AL_LEFT);
177 	y++;
178 	dlg_format_text(term, term, ct_msg[1], dlg->x + DIALOG_LB, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
179 	dlg_format_field(term, term, &dlg->items[1], dlg->x + DIALOG_LB, &y, w, NULL, AL_LEFT);
180 	y++;
181 	dlg_format_text(term, term, ct_msg[2], dlg->x + DIALOG_LB, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
182 	dlg_format_field(term, term, &dlg->items[2], dlg->x + DIALOG_LB, &y, w, NULL, AL_LEFT);
183 	y++;
184 	dlg_format_group(term, term, ct_msg + 3, &dlg->items[3], p, dlg->x + DIALOG_LB, &y, w, NULL);
185 	y++;
186 	dlg_format_buttons(term, term, &dlg->items[3 + p], 2, dlg->x + DIALOG_LB, &y, w, NULL, AL_CENTER);
187 }
188 
update_assoc(struct assoc * new)189 void update_assoc(struct assoc *new)
190 {
191 	struct assoc *repl;
192 	if (!new->label[0] || !new->ct[0] || !new->prog[0]) return;
193 	if (new->cnt) {
194 		foreach(repl, assoc) if (repl->cnt == new->cnt) {
195 			mem_free(repl->label);
196 			mem_free(repl->ct);
197 			mem_free(repl->prog);
198 			goto replace;
199 		}
200 		return;
201 	}
202 	foreach(repl, assoc) if (!strcmp(repl->label, new->label) && !strcmp(repl->ct, new->ct) && !strcmp(repl->prog, new->prog) && repl->block == new->block && repl->cons == new->cons && repl->xwin == new->xwin && repl->ask == new->ask && repl->system == new->system) {
203 		del_from_list(repl);
204 		add_to_list(assoc, repl);
205 		return;
206 	}
207 	new->cnt = get_assoc_cnt();
208 	repl = mem_alloc(sizeof(struct assoc));
209 	add_to_list(assoc, repl);
210 	replace:
211 	repl->label = stracpy(new->label);
212 	repl->ct = stracpy(new->ct);
213 	repl->prog = stracpy(new->prog);
214 	repl->block = new->block;
215 	repl->cons = new->cons;
216 	repl->xwin = new->xwin;
217 	repl->ask = new->ask;
218 	repl->system = new->system;
219 	repl->cnt = new->cnt;
220 }
221 
really_del_ct(void * fcp)222 void really_del_ct(void *fcp)
223 {
224 	unsigned fc = (unsigned)(my_uintptr_t)fcp;
225 	struct assoc *del;
226 	foreach(del, assoc) if (del->cnt == fc) goto ok;
227 	return;
228 	ok:
229 	delete_association(del);
230 }
231 
menu_del_ct(struct terminal * term,void * fcp,void * xxx2)232 void menu_del_ct(struct terminal *term, void *fcp, void *xxx2)
233 {
234 	unsigned char *str;
235 	int l;
236 	unsigned fc = (unsigned)(my_uintptr_t)fcp;
237 	struct assoc *del;
238 	foreach(del, assoc) if (del->cnt == fc) goto ok;
239 	return;
240 	ok:
241 	str = init_str(), l = 0;
242 	add_to_str(&str, &l, del->ct);
243 	add_to_str(&str, &l, " -> ");
244 	add_to_str(&str, &l, del->prog);
245 	msg_box(term, getml(str, NULL), TEXT_(T_DELETE_ASSOCIATION), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_DELETE_ASSOCIATION), ": ", str, "?", NULL, fcp, 2, TEXT_(T_YES), really_del_ct, B_ENTER, TEXT_(T_NO), NULL, B_ESC);
246 }
247 
menu_add_ct(struct terminal * term,void * fcp,void * xxx2)248 void menu_add_ct(struct terminal *term, void *fcp, void *xxx2)
249 {
250 	int p;
251 	unsigned fc = (unsigned)(my_uintptr_t)fcp;
252 	struct assoc *new, *from;
253 	unsigned char *label;
254 	unsigned char *ct;
255 	unsigned char *prog;
256 	struct dialog *d;
257 	if (fc) {
258 		foreach(from, assoc) if (from->cnt == fc) goto ok;
259 		return;
260 	}
261 	from = NULL;
262 	ok:
263 	d = mem_alloc(sizeof(struct dialog) + 10 * sizeof(struct dialog_item) + sizeof(struct assoc) + 3 * MAX_STR_LEN);
264 	memset(d, 0, sizeof(struct dialog) + 10 * sizeof(struct dialog_item) + sizeof(struct assoc) + 3 * MAX_STR_LEN);
265 	new = (struct assoc *)&d->items[10];
266 	new->label = label = (unsigned char *)(new + 1);
267 	new->ct = ct = label + MAX_STR_LEN;
268 	new->prog = prog = ct + MAX_STR_LEN;
269 	if (from) {
270 		safe_strncpy(label, from->label, MAX_STR_LEN);
271 		safe_strncpy(ct, from->ct, MAX_STR_LEN);
272 		safe_strncpy(prog, from->prog, MAX_STR_LEN);
273 		new->block = from->block;
274 		new->cons = from->cons;
275 		new->xwin = from->xwin;
276 		new->ask = from->ask;
277 		new->system = from->system;
278 		new->cnt = from->cnt;
279 	} else {
280 		new->block = new->xwin = new->cons = 1;
281 		new->ask = 1;
282 		new->system = SYSTEM_ID;
283 	}
284 	d->title = TEXT_(T_ASSOCIATION);
285 	d->fn = add_ct_fn;
286 	d->refresh = (void (*)(void *))update_assoc;
287 	d->refresh_data = new;
288 	d->items[0].type = D_FIELD;
289 	d->items[0].dlen = MAX_STR_LEN;
290 	d->items[0].data = label;
291 	d->items[0].fn = check_nonempty;
292 	d->items[1].type = D_FIELD;
293 	d->items[1].dlen = MAX_STR_LEN;
294 	d->items[1].data = ct;
295 	d->items[1].fn = check_nonempty;
296 	d->items[2].type = D_FIELD;
297 	d->items[2].dlen = MAX_STR_LEN;
298 	d->items[2].data = prog;
299 	d->items[2].fn = check_nonempty;
300 	p = 3;
301 #ifdef ASSOC_BLOCK
302 	d->items[p].type = D_CHECKBOX;
303 	d->items[p].data = (unsigned char *)&new->block;
304 	d->items[p++].dlen = sizeof(int);
305 #endif
306 #ifdef ASSOC_CONS_XWIN
307 	d->items[p].type = D_CHECKBOX;
308 	d->items[p].data = (unsigned char *)&new->cons;
309 	d->items[p++].dlen = sizeof(int);
310 	d->items[p].type = D_CHECKBOX;
311 	d->items[p].data = (unsigned char *)&new->xwin;
312 	d->items[p++].dlen = sizeof(int);
313 #endif
314 	d->items[p].type = D_CHECKBOX;
315 	d->items[p].data = (unsigned char *)&new->ask;
316 	d->items[p++].dlen = sizeof(int);
317 	d->items[p].type = D_BUTTON;
318 	d->items[p].gid = B_ENTER;
319 	d->items[p].fn = ok_dialog;
320 	d->items[p++].text = TEXT_(T_OK);
321 	d->items[p].type = D_BUTTON;
322 	d->items[p].gid = B_ESC;
323 	d->items[p].text = TEXT_(T_CANCEL);
324 	d->items[p++].fn = cancel_dialog;
325 	d->items[p++].type = D_END;
326 	do_dialog(term, d, getml(d, NULL));
327 }
328 
329 struct menu_item mi_no_assoc[] = {
330 	{ TEXT_(T_NO_ASSOCIATIONS), "", M_BAR, NULL, NULL, 0, 0 },
331 	{ NULL, NULL, 0, NULL, NULL, 0, 0 },
332 };
333 
menu_list_assoc(struct terminal * term,void * fn,void * xxx)334 void menu_list_assoc(struct terminal *term, void *fn, void *xxx)
335 {
336 	struct assoc *a;
337 	struct menu_item *mi = NULL;
338 	int n = 0;
339 	foreachback(a, assoc) if (a->system == SYSTEM_ID) {
340 		if (!mi && !(mi = new_menu(7))) return;
341 		add_to_menu(&mi, stracpy(a->label), stracpy(a->ct), "", MENU_FUNC fn, (void *)(my_uintptr_t)a->cnt, 0), n++;
342 	}
343 	if (!mi) do_menu(term, mi_no_assoc, xxx);
344 	else do_menu(term, mi, xxx);
345 }
346 
347 unsigned char *ext_msg[] = {
348 	TEXT_(T_EXTENSION_S),
349 	TEXT_(T_CONTENT_TYPE),
350 };
351 
add_ext_fn(struct dialog_data * dlg)352 void add_ext_fn(struct dialog_data *dlg)
353 {
354 	struct terminal *term = dlg->win->term;
355 	int max = 0, min = 0;
356 	int w, rw;
357 	int y = -1;
358 	max_text_width(term, ext_msg[0], &max);
359 	min_text_width(term, ext_msg[0], &min);
360 	max_text_width(term, ext_msg[1], &max);
361 	min_text_width(term, ext_msg[1], &min);
362 	max_buttons_width(term, dlg->items + 2, 2, &max);
363 	min_buttons_width(term, dlg->items + 2, 2, &min);
364 	w = term->x * 9 / 10 - 2 * DIALOG_LB;
365 	if (w > max) w = max;
366 	if (w < min) w = min;
367 	if (w > term->x - 2 * DIALOG_LB) w = term->x - 2 * DIALOG_LB;
368 	if (w < 1) w = 1;
369 	rw = 0;
370 	dlg_format_text(NULL, term, ext_msg[0], 0, &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
371 	y += 2;
372 	dlg_format_text(NULL, term, ext_msg[1], 0, &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
373 	y += 2;
374 	dlg_format_buttons(NULL, term, dlg->items + 2, 2, 0, &y, w, &rw, AL_CENTER);
375 	w = rw;
376 	dlg->xw = w + 2 * DIALOG_LB;
377 	dlg->yw = y + 2 * DIALOG_TB;
378 	center_dlg(dlg);
379 	draw_dlg(dlg);
380 	y = dlg->y + DIALOG_TB;
381 	dlg_format_text(term, term, ext_msg[0], dlg->x + DIALOG_LB, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
382 	dlg_format_field(term, term, &dlg->items[0], dlg->x + DIALOG_LB, &y, w, NULL, AL_LEFT);
383 	y++;
384 	dlg_format_text(term, term, ext_msg[1], dlg->x + DIALOG_LB, &y, w, NULL, COLOR_DIALOG_TEXT, AL_LEFT);
385 	dlg_format_field(term, term, &dlg->items[1], dlg->x + DIALOG_LB, &y, w, NULL, AL_LEFT);
386 	y++;
387 	dlg_format_buttons(term, term, &dlg->items[2], 2, dlg->x + DIALOG_LB, &y, w, NULL, AL_CENTER);
388 }
389 
update_ext(struct extension * new)390 void update_ext(struct extension *new)
391 {
392 	struct extension *repl;
393 	if (!new->ext[0] || !new->ct[0]) return;
394 	if (new->cnt) {
395 		foreach(repl, extensions) if (repl->cnt == new->cnt) {
396 			mem_free(repl->ext);
397 			mem_free(repl->ct);
398 			goto replace;
399 		}
400 		return;
401 	}
402 	foreach(repl, extensions) if (!strcmp(repl->ext, new->ext) && !strcmp(repl->ct, new->ct)) {
403 		del_from_list(repl);
404 		add_to_list(extensions, repl);
405 		return;
406 	}
407 	new->cnt = get_assoc_cnt();
408 	repl = mem_alloc(sizeof(struct extension));
409 	add_to_list(extensions, repl);
410 	replace:
411 	repl->ext = stracpy(new->ext);
412 	repl->ct = stracpy(new->ct);
413 	repl->cnt = new->cnt;
414 }
415 
really_del_ext(void * fcp)416 void really_del_ext(void *fcp)
417 {
418 	unsigned fc = (unsigned)(my_uintptr_t)fcp;
419 	struct extension *del;
420 	foreach(del, extensions) if (del->cnt == fc) goto ok;
421 	return;
422 	ok:
423 	delete_extension(del);
424 }
425 
menu_del_ext(struct terminal * term,void * fcp,void * xxx2)426 void menu_del_ext(struct terminal *term, void *fcp, void *xxx2)
427 {
428 	unsigned char *str;
429 	int l;
430 	unsigned fc = (unsigned)(my_uintptr_t)fcp;
431 	struct extension *del;
432 	foreach(del, extensions) if (del->cnt == fc) goto ok;
433 	return;
434 	ok:
435 	str = init_str(), l = 0;
436 	add_to_str(&str, &l, del->ext);
437 	add_to_str(&str, &l, " -> ");
438 	add_to_str(&str, &l, del->ct);
439 	msg_box(term, getml(str, NULL), TEXT_(T_DELETE_EXTENSION), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_DELETE_EXTENSION), " ", str, "?", NULL, fcp, 2, TEXT_(T_YES), really_del_ext, B_ENTER, TEXT_(T_NO), NULL, B_ESC);
440 }
441 
menu_add_ext(struct terminal * term,void * fcp,void * xxx2)442 void menu_add_ext(struct terminal *term, void *fcp, void *xxx2)
443 {
444 	unsigned fc = (unsigned)(my_uintptr_t)fcp;
445 	struct extension *new, *from;
446 	unsigned char *ext;
447 	unsigned char *ct;
448 	struct dialog *d;
449 	if (fc) {
450 		foreach(from, extensions) if (from->cnt == fc) goto ok;
451 		return;
452 	}
453 	from = NULL;
454 	ok:
455 	d = mem_alloc(sizeof(struct dialog) + 5 * sizeof(struct dialog_item) + sizeof(struct extension) + 2 * MAX_STR_LEN);
456 	memset(d, 0, sizeof(struct dialog) + 5 * sizeof(struct dialog_item) + sizeof(struct extension) + 2 * MAX_STR_LEN);
457 	new = (struct extension *)&d->items[5];
458 	new->ext = ext = (unsigned char *)(new + 1);
459 	new->ct = ct = ext + MAX_STR_LEN;
460 	if (from) {
461 		safe_strncpy(ext, from->ext, MAX_STR_LEN);
462 		safe_strncpy(ct, from->ct, MAX_STR_LEN);
463 		new->cnt = from->cnt;
464 	}
465 	d->title = TEXT_(T_EXTENSION);
466 	d->fn = add_ext_fn;
467 	d->refresh = (void (*)(void *))update_ext;
468 	d->refresh_data = new;
469 	d->items[0].type = D_FIELD;
470 	d->items[0].dlen = MAX_STR_LEN;
471 	d->items[0].data = ext;
472 	d->items[0].fn = check_nonempty;
473 	d->items[1].type = D_FIELD;
474 	d->items[1].dlen = MAX_STR_LEN;
475 	d->items[1].data = ct;
476 	d->items[1].fn = check_nonempty;
477 	d->items[2].type = D_BUTTON;
478 	d->items[2].gid = B_ENTER;
479 	d->items[2].fn = ok_dialog;
480 	d->items[2].text = TEXT_(T_OK);
481 	d->items[3].type = D_BUTTON;
482 	d->items[3].gid = B_ESC;
483 	d->items[3].text = TEXT_(T_CANCEL);
484 	d->items[3].fn = cancel_dialog;
485 	d->items[4].type = D_END;
486 	do_dialog(term, d, getml(d, NULL));
487 }
488 
489 struct menu_item mi_no_ext[] = {
490 	{ TEXT_(T_NO_EXTENSIONS), "", M_BAR, NULL, NULL, 0, 0 },
491 	{ NULL, NULL, 0, NULL, NULL, 0, 0 },
492 };
493 
menu_list_ext(struct terminal * term,void * fn,void * xxx)494 void menu_list_ext(struct terminal *term, void *fn, void *xxx)
495 {
496 	struct extension *a;
497 	struct menu_item *mi = NULL;
498 	int n = 0;
499 	foreachback(a, extensions) {
500 		if (!mi && !(mi = new_menu(7))) return;
501 		add_to_menu(&mi, stracpy(a->ext), stracpy(a->ct), "", MENU_FUNC fn, (void *)(my_uintptr_t)a->cnt, 0), n++;
502 	}
503 	if (!mi) do_menu(term, mi_no_ext, xxx);
504 	else do_menu(term, mi, xxx);
505 }
506 
update_prog(struct list_head * l,unsigned char * p,int s)507 void update_prog(struct list_head *l, unsigned char *p, int s)
508 {
509 	struct protocol_program *repl;
510 	foreach(repl, *l) if (repl->system == s) {
511 		mem_free(repl->prog);
512 		goto ss;
513 	}
514 	repl = mem_alloc(sizeof(struct protocol_program));
515 	add_to_list(*l, repl);
516 	repl->system = s;
517 	ss:
518 	repl->prog = mem_alloc(MAX_STR_LEN);
519 	safe_strncpy(repl->prog, p, MAX_STR_LEN);
520 }
521 
get_prog(struct list_head * l)522 unsigned char *get_prog(struct list_head *l)
523 {
524 	struct protocol_program *repl;
525 	foreach(repl, *l) if (repl->system == SYSTEM_ID) return repl->prog;
526 	update_prog(l, "", SYSTEM_ID);
527 	foreach(repl, *l) if (repl->system == SYSTEM_ID) return repl->prog;
528 	return NULL;
529 }
530 
is_html_type(unsigned char * ct)531 int is_html_type(unsigned char *ct)
532 {
533 	return !strcasecmp(ct, "text/html") || !strcasecmp(ct, "text/x-server-parsed-html") || !casecmp(ct, "application/xhtml", strlen("application/xhtml"));
534 }
535