1 #include "links.h"
2 
3 struct list_head html_stack = {&html_stack, &html_stack};
4 
atchr(unsigned char c)5 static inline int atchr(unsigned char c)
6 {
7 	return /*isA(c) ||*/ (c > ' ' && c != '=' && c != '<' && c != '>');
8 }
9 
parse_element(unsigned char * e,unsigned char * eof,unsigned char ** name,int * namelen,unsigned char ** attr,unsigned char ** end)10 int parse_element(unsigned char *e, unsigned char *eof, unsigned char **name, int *namelen, unsigned char **attr, unsigned char **end)
11 {
12 	if (eof - e < 3 || *(e++) != '<') return -1;
13 	if (name) *name = e;
14 	if (*e == '/') e++;
15 	if (!isA(*e)) return -1;
16 	goto x1;
17 	while (isA(*e) || *e == '=') {
18 		x1:
19 		e++;
20 		if (e >= eof) return -1;
21 	}
22 	/*if ((!WHITECHAR(*e) && *e != '>' && *e != '<' && *e != '/' && *e != ':')) return -1;*/
23 	if (name && namelen) *namelen = e - *name;
24 	while ((WHITECHAR(*e) || *e == '/' || *e == ':')) {
25 		e++;
26 		if (e >= eof) return -1;
27 	}
28 	if ((!atchr(*e) && *e != '>' && *e != '<')) return -1;
29 	if (attr) *attr = e;
30 	nextattr:
31 	while (WHITECHAR(*e)) {
32 		e++;
33 		if (e >= eof) return -1;
34 	}
35 	if ((!atchr(*e) && *e != '>' && *e != '<')) return -1;
36 	if (*e == '>' || *e == '<') goto en;
37 	while (atchr(*e)) {
38 		e++;
39 		if (e >= eof) return -1;
40 	}
41 	while (WHITECHAR(*e)) {
42 		e++;
43 		if (e >= eof) return -1;
44 	}
45 	if (*e != '=') goto endattr;
46 	goto x2;
47 	while (WHITECHAR(*e)) {
48 		x2:
49 		e++;
50 		if (e >= eof) return -1;
51 	}
52 	if (U(*e)) {
53 		unsigned char uu = *e;
54 		/*u:*/
55 		goto x3;
56 		while (e < eof && *e != uu && *e /*(WHITECHAR(*e) || *e > ' ')*/) {
57 			x3:
58 			e++;
59 			if (e >= eof) return -1;
60 		}
61 		if (*e < ' ') return -1;
62 		e++;
63 		if (e >= eof /*|| (!WHITECHAR(*e) && *e != uu && *e != '>' && *e != '<')*/) return -1;
64 		/*if (*e == uu) goto u;*/
65 	} else {
66 		while (!WHITECHAR(*e) && *e != '>' && *e != '<') {
67 			e++;
68 			if (e >= eof) return -1;
69 		}
70 	}
71 	while (WHITECHAR(*e)) {
72 		e++;
73 		if (e >= eof) return -1;
74 	}
75 	endattr:
76 	if (*e != '>' && *e != '<') goto nextattr;
77 	en:
78 	if (end) *end = e + (*e == '>');
79 	return 0;
80 }
81 
82 #define add_chr(s, l, c)						\
83 do {									\
84 	if (!((l) & (32 - 1))) {					\
85 		if ((unsigned)(l) > MAXINT - 32) overalloc();		\
86 		(s) = mem_realloc((s), (l) + 32);			\
87 	}								\
88 	(s)[(l)++] = (c);						\
89 } while (0)
90 
91 int get_attr_val_nl = 0;
92 
93 /* parses html element attributes */
94 /* e is attr pointer previously get from parse_element, DON'T PASS HERE ANY OTHER VALUE!!! */
95 /* name is searched attribute */
96 /* returns allocated string containing the attribute, or NULL on unsuccess */
get_attr_val(unsigned char * e,unsigned char * name)97 unsigned char *get_attr_val(unsigned char *e, unsigned char *name)
98 {
99 	unsigned char *n;
100 	unsigned char *a = DUMMY;
101 	int l = 0;
102 	int f;
103 	aa:
104 	while (WHITECHAR(*e)) e++;
105 	if (*e == '>' || *e == '<') return NULL;
106 	n = name;
107 	while (*n && upcase(*e) == upcase(*n)) e++, n++;
108 	f = *n;
109 	while (atchr(*e)) f = 1, e++;
110 	while (WHITECHAR(*e)) e++;
111 	if (*e != '=') goto ea;
112 	e++;
113 	while (WHITECHAR(*e)) e++;
114 	if (!U(*e)) {
115 		while (!WHITECHAR(*e) && *e != '>' && *e != '<') {
116 			if (!f) add_chr(a, l, *e);
117 			e++;
118 		}
119 	} else {
120 		char uu = *e;
121 		/*a:*/
122 		e++;
123 		while (*e != uu) {
124 			if (!*e) {
125 				mem_free(a);
126 				return NULL;
127 			}
128 			if (!f) {
129 				if (get_attr_val_nl == 2) goto exact;
130 				if (*e != 13) {
131 					if (*e != 9 && *e != 10) exact:add_chr(a, l, *e);
132 					else if (!get_attr_val_nl) add_chr(a, l, ' ');
133 				}
134 			}
135 			e++;
136 		}
137 		e++;
138 		/*if (*e == uu) {
139 			if (!f) add_chr(a, l, *e);
140 			goto a;
141 		}*/
142 	}
143 	ea:
144 	if (!f) {
145 		unsigned char *b;
146 		add_chr(a, l, 0);
147 		if (strchr(a, '&')) {
148 			unsigned char *aa = a;
149 			int c = d_opt->cp;
150 			d_opt->cp = d_opt->real_cp;
151 			a = convert_string(NULL, aa, strlen(aa));
152 			d_opt->cp = c;
153 			mem_free(aa);
154 		}
155 		while ((b = strchr(a, 1))) *b = ' ';
156 		if (get_attr_val_nl != 2) {
157 			for (b = a; *b == ' '; b++);
158 			if (b != a) memmove(a, b, strlen(b) + 1);
159 			for (b = a + strlen(a) - 1; b >= a && *b == ' '; b--) *b = 0;
160 		}
161 		set_mem_comment(a, name, strlen(name));
162 		return a;
163 	}
164 	goto aa;
165 }
166 
has_attr(unsigned char * e,unsigned char * name)167 int has_attr(unsigned char *e, unsigned char *name)
168 {
169 	char *a;
170 	if (!(a = get_attr_val(e, name))) return 0;
171 	mem_free(a);
172 	return 1;
173 }
174 
175 /*
176 unsigned char *get_url_val(unsigned char *e, unsigned char *name)
177 {
178 	int n = 0;
179 	unsigned char *p, *q, *pp;
180 	if (!(pp = get_attr_val(e, name))) return NULL;
181 	p = pp; q = pp;
182 	while (1) {
183 		if (*p == '#') n = 1;
184 		if ((*p = *q) != ' ' || n) p++;
185 		if (!*q) break;
186 		q++;
187 	}
188 	return pp;
189 }
190 */
191 
get_url_val(unsigned char * e,unsigned char * name)192 unsigned char *get_url_val(unsigned char *e, unsigned char *name)
193 {
194 	unsigned char *a;
195 	get_attr_val_nl = 1;
196 	a = get_attr_val(e, name);
197 	get_attr_val_nl = 0;
198 	return a;
199 }
200 
get_exact_attr_val(unsigned char * e,unsigned char * name)201 unsigned char *get_exact_attr_val(unsigned char *e, unsigned char *name)
202 {
203 	unsigned char *a;
204 	get_attr_val_nl = 2;
205 	a = get_attr_val(e, name);
206 	get_attr_val_nl = 0;
207 	if (a) {
208 		unsigned char *x1, *x2;
209 		for (x1 = x2 = a; *x1; x1++, x2++) {
210 			if (x1[0] == '\r') {
211 				*x2 = '\n';
212 				if (x1[1] == '\n') x1++;
213 			} else {
214 				*x2 = *x1;
215 			}
216 		}
217 		*x2 = 0;
218 	}
219 	return a;
220 }
221 
222 struct {
223 	unsigned short int n;
224 	char *s;
225 } roman_tbl[] = {
226 	{ 1000,	"m" },
227 	{ 999,	"im" },
228 /*	{ 995,	"vm" },*/
229 	{ 990,	"xm" },
230 /*	{ 950,	"lm" },*/
231 	{ 900,	"cm" },
232 	{ 500,	"d" },
233 	{ 499,	"id" },
234 /*	{ 495,	"vd" },*/
235 	{ 490,	"xd" },
236 /*	{ 450,	"ld" },*/
237 	{ 400,	"cd" },
238 	{ 100,	"c" },
239 	{ 99,	"ic" },
240 /*	{ 95,	"vc" },*/
241 	{ 90,	"xc" },
242 	{ 50,	"l" },
243 	{ 49,	"il" },
244 /*	{ 45,	"vl" },*/
245 	{ 40,	"xl" },
246 	{ 10,	"x" },
247 	{ 9,	"ix" },
248 	{ 5,	"v" },
249 	{ 4,	"iv" },
250 	{ 1,	"i" },
251 	{ 0,	NULL },
252 };
253 
roman(char * p,unsigned n)254 void roman(char *p, unsigned n)
255 {
256 	int i = 0;
257 	if (n >= 4000) {
258 		strcpy(p, "---");
259 		return;
260 	}
261 	if (!n) {
262 		strcpy(p, "o");
263 		return;
264 	}
265 	p[0] = 0;
266 	while (n) {
267 		while (roman_tbl[i].n <= n) {
268 			n -= roman_tbl[i].n;
269 			strcat(p, roman_tbl[i].s);
270 		}
271 		i++;
272 		if (n && !roman_tbl[i].n) {
273 			internal("BUG in roman number convertor");
274 			return;
275 		}
276 	}
277 }
278 
279 struct color_spec {
280 	char *name;
281 	int rgb;
282 };
283 
284 struct color_spec color_specs[] = {
285 	{"aliceblue",		0xF0F8FF},
286 	{"antiquewhite",	0xFAEBD7},
287 	{"aqua",		0x00FFFF},
288 	{"aquamarine",		0x7FFFD4},
289 	{"azure",		0xF0FFFF},
290 	{"beige",		0xF5F5DC},
291 	{"bisque",		0xFFE4C4},
292 	{"black",		0x000000},
293 	{"blanchedalmond",	0xFFEBCD},
294 	{"blue",		0x0000FF},
295 	{"blueviolet",		0x8A2BE2},
296 	{"brown",		0xA52A2A},
297 	{"burlywood",		0xDEB887},
298 	{"cadetblue",		0x5F9EA0},
299 	{"chartreuse",		0x7FFF00},
300 	{"chocolate",		0xD2691E},
301 	{"coral",		0xFF7F50},
302 	{"cornflowerblue",	0x6495ED},
303 	{"cornsilk",		0xFFF8DC},
304 	{"crimson",		0xDC143C},
305 	{"cyan",		0x00FFFF},
306 	{"darkblue",		0x00008B},
307 	{"darkcyan",		0x008B8B},
308 	{"darkgoldenrod",	0xB8860B},
309 	{"darkgray",		0xA9A9A9},
310 	{"darkgreen",		0x006400},
311 	{"darkkhaki",		0xBDB76B},
312 	{"darkmagenta",		0x8B008B},
313 	{"darkolivegreen",	0x556B2F},
314 	{"darkorange",		0xFF8C00},
315 	{"darkorchid",		0x9932CC},
316 	{"darkred",		0x8B0000},
317 	{"darksalmon",		0xE9967A},
318 	{"darkseagreen",	0x8FBC8F},
319 	{"darkslateblue",	0x483D8B},
320 	{"darkslategray",	0x2F4F4F},
321 	{"darkturquoise",	0x00CED1},
322 	{"darkviolet",		0x9400D3},
323 	{"deeppink",		0xFF1493},
324 	{"deepskyblue",		0x00BFFF},
325 	{"dimgray",		0x696969},
326 	{"dodgerblue",		0x1E90FF},
327 	{"firebrick",		0xB22222},
328 	{"floralwhite",		0xFFFAF0},
329 	{"forestgreen",		0x228B22},
330 	{"fuchsia",		0xFF00FF},
331 	{"gainsboro",		0xDCDCDC},
332 	{"ghostwhite",		0xF8F8FF},
333 	{"gold",		0xFFD700},
334 	{"goldenrod",		0xDAA520},
335 	{"gray",		0x808080},
336 	{"green",		0x008000},
337 	{"greenyellow",		0xADFF2F},
338 	{"honeydew",		0xF0FFF0},
339 	{"hotpink",		0xFF69B4},
340 	{"indianred",		0xCD5C5C},
341 	{"indigo",		0x4B0082},
342 	{"ivory",		0xFFFFF0},
343 	{"khaki",		0xF0E68C},
344 	{"lavender",		0xE6E6FA},
345 	{"lavenderblush",	0xFFF0F5},
346 	{"lawngreen",		0x7CFC00},
347 	{"lemonchiffon",	0xFFFACD},
348 	{"lightblue",		0xADD8E6},
349 	{"lightcoral",		0xF08080},
350 	{"lightcyan",		0xE0FFFF},
351 	{"lightgoldenrodyellow",	0xFAFAD2},
352 	{"lightgreen",		0x90EE90},
353 	{"lightgrey",		0xD3D3D3},
354 	{"lightpink",		0xFFB6C1},
355 	{"lightsalmon",		0xFFA07A},
356 	{"lightseagreen",	0x20B2AA},
357 	{"lightskyblue",	0x87CEFA},
358 	{"lightslategray",	0x778899},
359 	{"lightsteelblue",	0xB0C4DE},
360 	{"lightyellow",		0xFFFFE0},
361 	{"lime",		0x00FF00},
362 	{"limegreen",		0x32CD32},
363 	{"linen",		0xFAF0E6},
364 	{"magenta",		0xFF00FF},
365 	{"maroon",		0x800000},
366 	{"mediumaquamarine",	0x66CDAA},
367 	{"mediumblue",		0x0000CD},
368 	{"mediumorchid",	0xBA55D3},
369 	{"mediumpurple",	0x9370DB},
370 	{"mediumseagreen",	0x3CB371},
371 	{"mediumslateblue",	0x7B68EE},
372 	{"mediumspringgreen",	0x00FA9A},
373 	{"mediumturquoise",	0x48D1CC},
374 	{"mediumvioletred",	0xC71585},
375 	{"midnightblue",	0x191970},
376 	{"mintcream",		0xF5FFFA},
377 	{"mistyrose",		0xFFE4E1},
378 	{"moccasin",		0xFFE4B5},
379 	{"navajowhite",		0xFFDEAD},
380 	{"navy",		0x000080},
381 	{"oldlace",		0xFDF5E6},
382 	{"olive",		0x808000},
383 	{"olivedrab",		0x6B8E23},
384 	{"orange",		0xFFA500},
385 	{"orangered",		0xFF4500},
386 	{"orchid",		0xDA70D6},
387 	{"palegoldenrod",	0xEEE8AA},
388 	{"palegreen",		0x98FB98},
389 	{"paleturquoise",	0xAFEEEE},
390 	{"palevioletred",	0xDB7093},
391 	{"papayawhip",		0xFFEFD5},
392 	{"peachpuff",		0xFFDAB9},
393 	{"peru",		0xCD853F},
394 	{"pink",		0xFFC0CB},
395 	{"plum",		0xDDA0DD},
396 	{"powderblue",		0xB0E0E6},
397 	{"purple",		0x800080},
398 	{"red",			0xFF0000},
399 	{"rosybrown",		0xBC8F8F},
400 	{"royalblue",		0x4169E1},
401 	{"saddlebrown",		0x8B4513},
402 	{"salmon",		0xFA8072},
403 	{"sandybrown",		0xF4A460},
404 	{"seagreen",		0x2E8B57},
405 	{"seashell",		0xFFF5EE},
406 	{"sienna",		0xA0522D},
407 	{"silver",		0xC0C0C0},
408 	{"skyblue",		0x87CEEB},
409 	{"slateblue",		0x6A5ACD},
410 	{"slategray",		0x708090},
411 	{"snow",		0xFFFAFA},
412 	{"springgreen",		0x00FF7F},
413 	{"steelblue",		0x4682B4},
414 	{"tan",			0xD2B48C},
415 	{"teal",		0x008080},
416 	{"thistle",		0xD8BFD8},
417 	{"tomato",		0xFF6347},
418 	{"turquoise",		0x40E0D0},
419 	{"violet",		0xEE82EE},
420 	{"wheat",		0xF5DEB3},
421 	{"white",		0xFFFFFF},
422 	{"whitesmoke",		0xF5F5F5},
423 	{"yellow",		0xFFFF00},
424 	{"yellowgreen",		0x9ACD32},
425 };
426 
427 #define endof(T) ((T)+sizeof(T)/sizeof(*(T)))
428 
decode_color(unsigned char * str,struct rgb * col)429 int decode_color(unsigned char *str, struct rgb *col)
430 {
431 	unsigned long ch;
432 	if (*str != '#') {
433 		struct color_spec *cs;
434 		for (cs = color_specs; cs < endof(color_specs); cs++)
435 			if (!strcasecmp(cs->name, str)) {
436 				ch = cs->rgb;
437 				goto found;
438 			}
439 	} else {
440 		str++;
441 	}
442 	if (strlen(str) == 6) {
443 		char *end;
444 		ch = strtoul(str, &end, 16);
445 		if (!*end && ch < 0x1000000) {
446 found:
447 			col->r = ch / 0x10000;
448 			col->g = ch / 0x100 % 0x100;
449 			col->b = ch % 0x100;
450 			return 0;
451 		}
452 	}
453 	return -1;
454 }
455 
get_color(unsigned char * a,unsigned char * c,struct rgb * rgb)456 int get_color(unsigned char *a, unsigned char *c, struct rgb *rgb)
457 {
458 	char *at;
459 	int r = -1;
460 	if (d_opt->col >= 1) if ((at = get_attr_val(a, c))) {
461 		r = decode_color(at, rgb);
462 		mem_free(at);
463 	}
464 	return r;
465 }
466 
get_bgcolor(unsigned char * a,struct rgb * rgb)467 int get_bgcolor(unsigned char *a, struct rgb *rgb)
468 {
469 	if (d_opt->col < 2) return -1;
470 	return get_color(a, "bgcolor", rgb);
471 }
472 
get_target(unsigned char * a)473 unsigned char *get_target(unsigned char *a)
474 {
475 	unsigned char *v = get_attr_val(a, "target");
476 	if (v) {
477 		if (!strcasecmp(v, "_self")) {
478 			mem_free(v);
479 			v = stracpy(d_opt->framename);
480 		}
481 	}
482 	return v;
483 }
484 
kill_html_stack_item(struct html_element * e)485 void kill_html_stack_item(struct html_element *e)
486 {
487 	if (!e || (void *)e == &html_stack) {
488 		internal("trying to free bad html element");
489 		return;
490 	}
491 	if (e->dontkill == 2) {
492 		internal("trying to kill unkillable element");
493 		return;
494 	}
495 	if (e->attr.link) mem_free(e->attr.link);
496 	if (e->attr.target) mem_free(e->attr.target);
497 	if (e->attr.image) mem_free(e->attr.image);
498 	if (e->attr.href_base) mem_free(e->attr.href_base);
499 	if (e->attr.target_base) mem_free(e->attr.target_base);
500 	if (e->attr.select) mem_free(e->attr.select);
501 	del_from_list(e);
502 	mem_free(e);
503 	/*if ((void *)(html_stack.next) == &html_stack || !html_stack.next) {
504 		debug("killing last element");
505 	}*/
506 }
507 
508 #ifdef DEBUG
debug_stack()509 void debug_stack()
510 {
511 	struct html_element *e;
512 	printf("HTML stack debug: \n");
513 	foreachback(e, html_stack) {
514 		int i;
515 		printf("\"");
516 		for (i = 0; i < e->namelen; i++) printf("%c", e->name[i]);
517 		printf("\"\n");
518 	}
519 	printf("%c", 7);
520 	fflush(stdout);
521 	sleep(1);
522 }
523 #endif
524 
html_stack_dup()525 void html_stack_dup()
526 {
527 	struct html_element *e;
528 	struct html_element *ep;
529 	if ((void *)(ep = html_stack.next) == &html_stack || !html_stack.next) {
530 	   	internal("html stack empty");
531 		return;
532 	}
533 	e = mem_alloc(sizeof(struct html_element));
534 	memcpy(e, ep, sizeof(struct html_element));
535 	e->attr.link = stracpy(ep->attr.link);
536 	e->attr.target = stracpy(ep->attr.target);
537 	e->attr.image = stracpy(ep->attr.image);
538 	e->attr.href_base = stracpy(ep->attr.href_base);
539 	e->attr.target_base = stracpy(ep->attr.target_base);
540 	e->attr.select = stracpy(ep->attr.select);
541 	/*if (e->name) {
542 		if (e->attr.link) set_mem_comment(e->attr.link, e->name, e->namelen);
543 		if (e->attr.target) set_mem_comment(e->attr.target, e->name, e->namelen);
544 		if (e->attr.image) set_mem_comment(e->attr.image, e->name, e->namelen);
545 		if (e->attr.href_base) set_mem_comment(e->attr.href_base, e->name, e->namelen);
546 		if (e->attr.target_base) set_mem_comment(e->attr.target_base, e->name, e->namelen);
547 		if (e->attr.select) set_mem_comment(e->attr.select, e->name, e->namelen);
548 	}*/
549 	e->name = e->options = NULL;
550 	e->namelen = 0;
551 	e->dontkill = 0;
552 	add_to_list(html_stack, e);
553 }
554 
555 void *ff;
556 int (*put_chars_f)(void *, unsigned char *, int);
557 void (*line_break_f)(void *);
558 void *(*special_f)(void *, int, ...);
559 
560 unsigned char *eoff;
561 unsigned char *eofff;
562 unsigned char *startf;
563 
564 int line_breax;
565 int pos;
566 int putsp;
567 
568 int was_br;
569 int table_level;
570 int empty_format;
571 
ln_break(int n,void (* line_break)(void *),void * f)572 void ln_break(int n, void (*line_break)(void *), void *f)
573 {
574 	if (!n || html_top.invisible) return;
575 	while (n > line_breax) line_breax++, line_break(f);
576 	pos = 0;
577 	putsp = -1;
578 }
579 
put_chrs(unsigned char * start,int len,int (* put_chars)(void *,unsigned char *,int),void * f)580 void put_chrs(unsigned char *start, int len, int (*put_chars)(void *, unsigned char *, int), void *f)
581 {
582 	if (par_format.align == AL_NO) putsp = 0;
583 	if (!len || html_top.invisible) return;
584 	if (putsp == 1) pos += put_chars(f, " ", 1), putsp = -1;
585 	if (putsp == -1) {
586 		if (start[0] == ' ') start++, len--;
587 		putsp = 0;
588 	}
589 	if (!len) {
590 		putsp = -1;
591 		if (par_format.align == AL_NO) putsp = 0;
592 		return;
593 	}
594 	if (start[len - 1] == ' ') putsp = -1;
595 	if (par_format.align == AL_NO) putsp = 0;
596 	was_br = 0;
597 	pos += put_chars(f, start, len);
598 	line_breax = 0;
599 }
600 
kill_until(int ls,...)601 void kill_until(int ls, ...)
602 {
603 	int l;
604 	struct html_element *e = &html_top;
605 	if (ls) e = e->next;
606 	while ((void *)e != &html_stack) {
607 		int sk = 0;
608 		va_list arg;
609 		va_start(arg, ls);
610 		while (1) {
611 			char *s = va_arg(arg, char *);
612 			if (!s) break;
613 			if (!*s) sk++;
614 			else if ((size_t)e->namelen == strlen(s) && !casecmp(e->name, s, strlen(s))) {
615 				if (!sk) {
616 					if (e->dontkill) break;
617 					va_end(arg);
618 					goto killll;
619 				} else if (sk == 1) {
620 					va_end(arg);
621 					goto killl;
622 				} else break;
623 			}
624 		}
625 		va_end(arg);
626 		if (e->dontkill || (e->namelen == 5 && !casecmp(e->name, "TABLE", 5))) break;
627 		if (e->namelen == 2 && upcase(e->name[0]) == 'T' && (upcase(e->name[1]) == 'D' || upcase(e->name[1]) == 'H' || upcase(e->name[1]) == 'R')) break;
628 		e = e->next;
629 	}
630 	return;
631 	killl:
632 	e = e->prev;
633 	killll:
634 	l = 0;
635 	while ((void *)e != &html_stack) {
636 		if (ls && e == html_stack.next) break;
637 		if (e->linebreak > l) l = e->linebreak;
638 		e = e->prev;
639 		kill_html_stack_item(e->next);
640 	}
641 	ln_break(l, line_break_f, ff);
642 }
643 
top_href_base(void)644 static inline unsigned char *top_href_base(void)
645 {
646 	return ((struct html_element *)html_stack.prev)->attr.href_base;
647 }
648 
get_num(unsigned char * a,unsigned char * n)649 int get_num(unsigned char *a, unsigned char *n)
650 {
651 	char *al;
652 	if ((al = get_attr_val(a, n))) {
653 		char *end;
654 		unsigned long s = strtoul(al, &end, 10);
655 		if (!*al || *end || s > 10000) s = -1;
656 		mem_free(al);
657 		return s;
658 	}
659 	return -1;
660 }
661 
parse_width(unsigned char * w,int trunc)662 int parse_width(unsigned char *w, int trunc)
663 {
664 	unsigned char *end;
665 	int p = 0;
666 	long s;
667 	int l;
668 	while (WHITECHAR(*w)) w++;
669 	for (l = 0; w[l] && w[l] != ','; l++) ;
670 	while (l && WHITECHAR(w[l - 1])) l--;
671 	if (!l) return -1;
672 	if (w[l - 1] == '%') l--, p = 1;
673 	while (l && WHITECHAR(w[l - 1])) l--;
674 	if (!l) return -1;
675 	s = strtoul((char *)w, (char **)(void *)&end, 10);
676 	if (end - w < l || s < 0 || s > 10000) return -1;
677 	if (p) {
678 		if (trunc) s = s * (par_format.width - par_format.leftmargin - par_format.rightmargin) / 100;
679 		else return -1;
680 	} else s = (s + (HTML_CHAR_WIDTH - 1) / 2) / HTML_CHAR_WIDTH;
681 	if (trunc && s > par_format.width - par_format.leftmargin - par_format.rightmargin) s = par_format.width - par_format.leftmargin - par_format.rightmargin;
682 	if (s < 0) s = 0;
683 	return s;
684 }
685 
get_width(unsigned char * a,unsigned char * n,int trunc)686 int get_width(unsigned char *a, unsigned char *n, int trunc)
687 {
688 	int r;
689 	unsigned char *w;
690 	if (!(w = get_attr_val(a, n))) return -1;
691 	r = parse_width(w, trunc);
692 	mem_free(w);
693 	return r;
694 }
695 
696 struct form form = { NULL, NULL, 0, 0 };
697 
698 unsigned char *last_form_tag;
699 unsigned char *last_form_attr;
700 unsigned char *last_input_tag;
701 
put_link_line(unsigned char * prefix,unsigned char * linkname,unsigned char * link,unsigned char * target)702 void put_link_line(unsigned char *prefix, unsigned char *linkname, unsigned char *link, unsigned char *target)
703 {
704 	html_stack_dup();
705 	ln_break(1, line_break_f, ff);
706 	if (format_.link) mem_free(format_.link), format_.link = NULL;
707 	if (format_.target) mem_free(format_.target), format_.target = NULL;
708 	format_.form = NULL;
709 	put_chrs(prefix, strlen(prefix), put_chars_f, ff);
710 	format_.link = join_urls(format_.href_base, link);
711 	format_.target = stracpy(target);
712 	memcpy(&format_.fg, &format_.clink, sizeof(struct rgb));
713 	put_chrs(linkname, strlen(linkname), put_chars_f, ff);
714 	ln_break(1, line_break_f, ff);
715 	kill_html_stack_item(&html_top);
716 }
717 
html_span(unsigned char * a)718 void html_span(unsigned char *a) { }
html_bold(unsigned char * a)719 void html_bold(unsigned char *a) { format_.attr |= AT_BOLD; }
html_italic(unsigned char * a)720 void html_italic(unsigned char *a) { format_.attr |= AT_ITALIC; }
html_underline(unsigned char * a)721 void html_underline(unsigned char *a) { format_.attr |= AT_UNDERLINE; }
html_fixed(unsigned char * a)722 void html_fixed(unsigned char *a) { format_.attr |= AT_FIXED; }
723 
html_a(unsigned char * a)724 void html_a(unsigned char *a)
725 {
726 	char *al;
727 	if ((al = get_url_val(a, "href"))) {
728 		char *all = al;
729 		while (all[0] == ' ') all++;
730 		while (all[0] && all[strlen(all) - 1] == ' ') all[strlen(all) - 1] = 0;
731 		if (format_.link) mem_free(format_.link);
732 		format_.link = join_urls(format_.href_base, all);
733 		mem_free(al);
734 		if ((al = get_target(a))) {
735 			if (format_.target) mem_free(format_.target);
736 			format_.target = al;
737 		} else {
738 			if (format_.target) mem_free(format_.target);
739 			format_.target = stracpy(format_.target_base);
740 		}
741 		/*format_.attr ^= AT_BOLD;*/
742 		memcpy(&format_.fg, &format_.clink, sizeof(struct rgb));
743 	} else kill_html_stack_item(&html_top);
744 	if ((al = get_attr_val(a, "name"))) {
745 		special_f(ff, SP_TAG, al);
746 		mem_free(al);
747 	}
748 }
749 
html_font(unsigned char * a)750 void html_font(unsigned char *a)
751 {
752 	char *al;
753 	if ((al = get_attr_val(a, "size"))) {
754 		int p = 0;
755 		unsigned long s;
756 		char *nn = al;
757 		char *end;
758 		if (*al == '+') p = 1, nn++;
759 		if (*al == '-') p = -1, nn++;
760 		s = strtoul(nn, &end, 10);
761 		if (*nn && !*end) {
762 			if (s > 7) s = 7;
763 			if (!p) format_.fontsize = s;
764 			else format_.fontsize += p * s;
765 			if (format_.fontsize < 1) format_.fontsize = 1;
766 			if (format_.fontsize > 7) format_.fontsize = 7;
767 		}
768 		mem_free(al);
769 	}
770 	get_color(a, "color", &format_.fg);
771 }
772 
html_img(unsigned char * a)773 void html_img(unsigned char *a)
774 {
775 	unsigned char *al;
776 	int ismap, usemap = 0;
777 	/*put_chrs(" ", 1, put_chars_f, ff);*/
778 	if ((al = get_attr_val(a, "usemap"))) {
779 		unsigned char *u;
780 		usemap = 1;
781 		html_stack_dup();
782 		if (format_.link) mem_free(format_.link);
783 		if (format_.form) format_.form = NULL;
784 		u = join_urls(*al == '#' ? top_href_base() : format_.href_base, al);
785 		format_.link = mem_alloc(strlen(u) + 5);
786 		strcpy(format_.link, "MAP@");
787 		strcat(format_.link, u);
788 		format_.attr |= AT_BOLD;
789 		mem_free(u);
790 		mem_free(al);
791 	}
792 	ismap = format_.link && has_attr(a, "ismap") && !usemap;
793 	if ((!(al = get_attr_val(a, "alt")) && !(al = get_attr_val(a, "title"))) || !*al) {
794 		if (al) mem_free(al);
795 		if (!d_opt->images && !format_.link) return;
796 		if (usemap) al = stracpy("[USEMAP]");
797 		else if (ismap) al = stracpy("[ISMAP]");
798 		else al = stracpy("[IMG]");
799 	}
800 	if (format_.image) mem_free(format_.image), format_.image = NULL;
801 	if (al) {
802 		unsigned char *s;
803 		if ((s = get_url_val(a, "src")) || (s = get_attr_val(a, "dynsrc"))) {
804 			format_.image = join_urls(format_.href_base, s);
805 			mem_free(s);
806 		}
807 		if (ismap) {
808 			unsigned char *h;
809 			html_stack_dup();
810 			h = stracpy(format_.link);
811 			add_to_strn(&h, "?0,0");
812 			mem_free(format_.link);
813 			format_.link = h;
814 		}
815 		put_chrs(al, strlen(al), put_chars_f, ff);
816 		if (ismap) kill_html_stack_item(&html_top);
817 	}
818 	if (format_.image) mem_free(format_.image), format_.image = NULL;
819 	mem_free(al);
820 	if (usemap) kill_html_stack_item(&html_top);
821 	/*put_chrs(" ", 1, put_chars_f, ff);*/
822 }
823 
html_body(unsigned char * a)824 void html_body(unsigned char *a)
825 {
826 	get_color(a, "text", &format_.fg);
827 	get_color(a, "link", &format_.clink);
828 	get_color(a, "vlink", &format_.vlink);
829 	get_bgcolor(a, &format_.bg);
830 	get_bgcolor(a, &par_format.bgcolor);
831 }
832 
html_skip(unsigned char * a)833 void html_skip(unsigned char *a) { html_top.invisible = html_top.dontkill = 1; }
834 
html_title(unsigned char * a)835 void html_title(unsigned char *a) { html_top.invisible = html_top.dontkill = 1; }
836 
html_center(unsigned char * a)837 void html_center(unsigned char *a)
838 {
839 	par_format.align = AL_CENTER;
840 	if (!table_level) par_format.leftmargin = par_format.rightmargin = 0;
841 }
842 
html_linebrk(unsigned char * a)843 void html_linebrk(unsigned char *a)
844 {
845 	char *al;
846 	if ((al = get_attr_val(a, "align"))) {
847 		if (!strcasecmp(al, "left")) par_format.align = AL_LEFT;
848 		if (!strcasecmp(al, "right")) par_format.align = AL_RIGHT;
849 		if (!strcasecmp(al, "center")) {
850 			par_format.align = AL_CENTER;
851 			if (!table_level) par_format.leftmargin = par_format.rightmargin = 0;
852 		}
853 		if (!strcasecmp(al, "justify")) par_format.align = AL_BLOCK;
854 		mem_free(al);
855 	}
856 }
857 
html_br(unsigned char * a)858 void html_br(unsigned char *a)
859 {
860 	html_linebrk(a);
861 	if (was_br) ln_break(2, line_break_f, ff);
862 	was_br = 1;
863 }
864 
html_form(unsigned char * a)865 void html_form(unsigned char *a)
866 {
867 	was_br = 1;
868 }
869 
html_p(unsigned char * a)870 void html_p(unsigned char *a)
871 {
872 	if (par_format.leftmargin < margin) par_format.leftmargin = margin;
873 	if (par_format.rightmargin < margin) par_format.rightmargin = margin;
874 	/*par_format.align = AL_LEFT;*/
875 	html_linebrk(a);
876 }
877 
html_address(unsigned char * a)878 void html_address(unsigned char *a)
879 {
880 	par_format.leftmargin += 1;
881 	par_format.align = AL_LEFT;
882 }
883 
html_blockquote(unsigned char * a)884 void html_blockquote(unsigned char *a)
885 {
886 	par_format.leftmargin += 2;
887 	par_format.align = AL_LEFT;
888 }
889 
html_h(int h,char * a)890 void html_h(int h, char *a)
891 {
892 	do_not_optimize_here(&h);
893 	par_format.align = AL_LEFT;
894 	html_linebrk(a);
895 	switch (par_format.align) {
896 		case AL_LEFT:
897 			par_format.leftmargin = (h - 2) * 2;
898 			par_format.rightmargin = 0;
899 			break;
900 		case AL_RIGHT:
901 			par_format.leftmargin = 0;
902 			par_format.rightmargin = (h - 2) * 2;
903 			break;
904 		case AL_CENTER:
905 			par_format.leftmargin = par_format.rightmargin = 0;
906 			break;
907 		case AL_BLOCK:
908 			par_format.leftmargin = par_format.rightmargin = (h - 2) * 2;
909 			break;
910 	}
911 }
912 
html_h2(unsigned char * a)913 void html_h2(unsigned char *a) { html_h(2, a); }
html_h3(unsigned char * a)914 void html_h3(unsigned char *a) { html_h(3, a); }
html_h4(unsigned char * a)915 void html_h4(unsigned char *a) { html_h(4, a); }
html_h5(unsigned char * a)916 void html_h5(unsigned char *a) { html_h(5, a); }
html_h6(unsigned char * a)917 void html_h6(unsigned char *a) { html_h(6, a); }
918 
html_pre(unsigned char * a)919 void html_pre(unsigned char *a)
920 {
921 	par_format.align = AL_NO;
922 	par_format.leftmargin = par_format.leftmargin > 1;
923 	par_format.rightmargin = 0;
924 }
925 
html_hr(unsigned char * a)926 void html_hr(unsigned char *a)
927 {
928 	int i/* = par_format.width - 10*/;
929 	unsigned char r = 205;
930 	int q = get_num(a, "size");
931 	if (q >= 0 && q < 2) r = 196;
932 	html_stack_dup();
933 	par_format.align = AL_CENTER;
934 	if (format_.link) mem_free(format_.link), format_.link = NULL;
935 	format_.form = NULL;
936 	html_linebrk(a);
937 	if (par_format.align == AL_BLOCK) par_format.align = AL_CENTER;
938 	par_format.leftmargin = margin;
939 	par_format.rightmargin = margin;
940 	if ((i = get_width(a, "width", 1)) == -1) i = par_format.width - 2 * margin - 4;
941 	format_.attr = AT_GRAPHICS;
942 	special_f(ff, SP_NOWRAP, 1);
943 	while (i-- > 0) put_chrs(&r, 1, put_chars_f, ff);
944 	special_f(ff, SP_NOWRAP, 0);
945 	ln_break(2, line_break_f, ff);
946 	kill_html_stack_item(&html_top);
947 }
948 
html_table(unsigned char * a)949 void html_table(unsigned char *a)
950 {
951 	par_format.leftmargin = margin;
952 	par_format.rightmargin = margin;
953 	par_format.align = AL_LEFT;
954 	html_linebrk(a);
955 	format_.attr = 0;
956 }
957 
html_tr(unsigned char * a)958 void html_tr(unsigned char *a)
959 {
960 	html_linebrk(a);
961 }
962 
html_th(unsigned char * a)963 void html_th(unsigned char *a)
964 {
965 	/*html_linebrk(a);*/
966 	kill_until(1, "TD", "TH", "", "TR", "TABLE", NULL);
967 	format_.attr |= AT_BOLD;
968 	put_chrs(" ", 1, put_chars_f, ff);
969 }
970 
html_td(unsigned char * a)971 void html_td(unsigned char *a)
972 {
973 	/*html_linebrk(a);*/
974 	kill_until(1, "TD", "TH", "", "TR", "TABLE", NULL);
975 	format_.attr &= ~AT_BOLD;
976 	put_chrs(" ", 1, put_chars_f, ff);
977 }
978 
html_base(unsigned char * a)979 void html_base(unsigned char *a)
980 {
981 	char *al;
982 	if ((al = get_url_val(a, "href"))) {
983 		if (format_.href_base) mem_free(format_.href_base);
984 		format_.href_base = join_urls(top_href_base(), al);
985 		mem_free(al);
986 	}
987 	if ((al = get_target(a))) {
988 		if (format_.target_base) mem_free(format_.target_base);
989 		format_.target_base = al;
990 	}
991 }
992 
html_ul(unsigned char * a)993 void html_ul(unsigned char *a)
994 {
995 	char *al;
996 	/*debug_stack();*/
997 	par_format.list_level++;
998 	par_format.list_number = 0;
999 	par_format.flags = P_STAR;
1000 	if ((al = get_attr_val(a, "type"))) {
1001 		if (!strcasecmp(al, "disc") || !strcasecmp(al, "circle")) par_format.flags = P_O;
1002 		if (!strcasecmp(al, "square")) par_format.flags = P_PLUS;
1003 		mem_free(al);
1004 	}
1005 	if ((par_format.leftmargin += 2 + (par_format.list_level > 1)) > par_format.width * 2 / 3 && !table_level)
1006 		par_format.leftmargin = par_format.width * 2 / 3;
1007 	par_format.align = AL_LEFT;
1008 	html_top.dontkill = 1;
1009 }
1010 
html_ol(unsigned char * a)1011 void html_ol(unsigned char *a)
1012 {
1013 	char *al;
1014 	int st;
1015 	par_format.list_level++;
1016 	st = get_num(a, "start");
1017 	if (st == -1) st = 1;
1018 	par_format.list_number = st;
1019 	par_format.flags = P_NUMBER;
1020 	if ((al = get_attr_val(a, "type"))) {
1021 		if (!strcmp(al, "1")) par_format.flags = P_NUMBER;
1022 		if (!strcmp(al, "a")) par_format.flags = P_alpha;
1023 		if (!strcmp(al, "A")) par_format.flags = P_ALPHA;
1024 		if (!strcmp(al, "r")) par_format.flags = P_roman;
1025 		if (!strcmp(al, "R")) par_format.flags = P_ROMAN;
1026 		if (!strcmp(al, "i")) par_format.flags = P_roman;
1027 		if (!strcmp(al, "I")) par_format.flags = P_ROMAN;
1028 		mem_free(al);
1029 	}
1030 	if ((par_format.leftmargin += (par_format.list_level > 1)) > par_format.width * 2 / 3 && !table_level)
1031 		par_format.leftmargin = par_format.width * 2 / 3;
1032 	par_format.align = AL_LEFT;
1033 	html_top.dontkill = 1;
1034 }
1035 
html_li(unsigned char * a)1036 void html_li(unsigned char *a)
1037 {
1038 	/*kill_until(0, "", "UL", "OL", NULL);*/
1039 	if (!par_format.list_number) {
1040 		char x[7] = "*&nbsp;";
1041 		if ((par_format.flags & P_LISTMASK) == P_O) x[0] = 'o';
1042 		if ((par_format.flags & P_LISTMASK) == P_PLUS) x[0] = '+';
1043 		put_chrs(x, 7, put_chars_f, ff);
1044 		par_format.leftmargin += 2;
1045 		par_format.align = AL_LEFT;
1046 		putsp = -1;
1047 	} else {
1048 		char c = 0;
1049 		char n[32];
1050 		int t = par_format.flags & P_LISTMASK;
1051 		int s = get_num(a, "value");
1052 		if (s != -1) par_format.list_number = s;
1053 		if ((t != P_roman && t != P_ROMAN && par_format.list_number < 10) || t == P_alpha || t == P_ALPHA) put_chrs("&nbsp;", 6, put_chars_f, ff), c = 1;
1054 		if (t == P_ALPHA || t == P_alpha) {
1055 			n[0] = par_format.list_number ? (par_format.list_number - 1) % 26 + (t == P_ALPHA ? 'A' : 'a') : 0;
1056 			n[1] = 0;
1057 		} else if (t == P_ROMAN || t == P_roman) {
1058 			roman(n, par_format.list_number);
1059 			if (t == P_ROMAN) {
1060 				char *x;
1061 				for (x = n; *x; x++) *x = upcase(*x);
1062 			}
1063 		} else sprintf(n, "%d", par_format.list_number);
1064 		put_chrs(n, strlen(n), put_chars_f, ff);
1065 		put_chrs(".&nbsp;", 7, put_chars_f, ff);
1066 		par_format.leftmargin += strlen(n) + c + 2;
1067 		par_format.align = AL_LEFT;
1068 		html_top.next->parattr.list_number = par_format.list_number + 1;
1069 		par_format.list_number = 0;
1070 		putsp = -1;
1071 	}
1072 	line_breax = 2;
1073 }
1074 
html_dl(unsigned char * a)1075 void html_dl(unsigned char *a)
1076 {
1077 	par_format.flags &= ~P_COMPACT;
1078 	if (has_attr(a, "compact")) par_format.flags |= P_COMPACT;
1079 	if (par_format.list_level) par_format.leftmargin += 5;
1080 	par_format.list_level++;
1081 	par_format.list_number = 0;
1082 	par_format.align = AL_LEFT;
1083 	par_format.dd_margin = par_format.leftmargin;
1084 	html_top.dontkill = 1;
1085 	if (!(par_format.flags & P_COMPACT)) {
1086 		ln_break(2, line_break_f, ff);
1087 		html_top.linebreak = 2;
1088 	}
1089 }
1090 
html_dt(unsigned char * a)1091 void html_dt(unsigned char *a)
1092 {
1093 	kill_until(0, "", "DL", NULL);
1094 	par_format.align = AL_LEFT;
1095 	par_format.leftmargin = par_format.dd_margin;
1096 	if (!(par_format.flags & P_COMPACT) && !has_attr(a, "compact"))
1097 		ln_break(2, line_break_f, ff);
1098 }
1099 
html_dd(unsigned char * a)1100 void html_dd(unsigned char *a)
1101 {
1102 	kill_until(0, "", "DL", NULL);
1103 	if ((par_format.leftmargin = par_format.dd_margin + (table_level ? 3 : 8)) > par_format.width * 2 / 3 && !table_level)
1104 		par_format.leftmargin = par_format.width * 2 / 3;
1105 	par_format.align = AL_LEFT;
1106 }
1107 
get_html_form(unsigned char * a,struct form * form)1108 void get_html_form(unsigned char *a, struct form *form)
1109 {
1110 	unsigned char *al;
1111 	unsigned char *ch;
1112 	form->method = FM_GET;
1113 	if ((al = get_attr_val(a, "method"))) {
1114 		if (!strcasecmp(al, "post")) {
1115 			char *ax;
1116 			form->method = FM_POST;
1117 			if ((ax = get_attr_val(a, "enctype"))) {
1118 				if (!strcasecmp(ax, "multipart/form-data"))
1119 					form->method = FM_POST_MP;
1120 				mem_free(ax);
1121 			}
1122 		}
1123 		mem_free(al);
1124 	}
1125 	if ((al = get_url_val(a, "action"))) {
1126 		char *all = al;
1127 		while (all[0] == ' ') all++;
1128 		while (all[0] && all[strlen(all) - 1] == ' ') all[strlen(all) - 1] = 0;
1129 		form->action = join_urls(format_.href_base, all);
1130 		mem_free(al);
1131 	} else {
1132 		if ((ch = strchr(form->action = stracpy(format_.href_base), POST_CHAR))) *ch = 0;
1133 		if (form->method == FM_GET && (ch = strchr(form->action, '?'))) *ch = 0;
1134 	}
1135 	if ((al = get_target(a))) {
1136 		form->target = al;
1137 	} else {
1138 		form->target = stracpy(format_.target_base);
1139 	}
1140 	form->num = a - startf;
1141 }
1142 
find_form_for_input(unsigned char * i,int when_unused)1143 void find_form_for_input(unsigned char *i, int when_unused)
1144 {
1145 	unsigned char *s, *ss, *name, *attr, *lf, *la;
1146 	int namelen;
1147 	if (form.action) mem_free(form.action);
1148 	if (form.target) mem_free(form.target);
1149 	memset(&form, 0, sizeof(form));
1150 	if (!when_unused && !special_f(ff, SP_USED, NULL)) return;
1151 	if (last_form_tag && last_input_tag && i <= last_input_tag && i > last_form_tag) {
1152 		get_html_form(last_form_attr, &form);
1153 		return;
1154 	}
1155 	if (last_form_tag && last_input_tag && i > last_input_tag) {
1156 		if (parse_element(last_form_tag, i, &name, &namelen, &la, &s))
1157 			internal("couldn't parse already parsed tag");
1158 		lf = last_form_tag;
1159 		s = last_input_tag;
1160 	} else {
1161 		lf = NULL, la = NULL;
1162 		s = startf;
1163 	}
1164 	se:
1165 	while (s < i && *s != '<') sp:s++;
1166 	if (s >= i) goto end_parse;
1167 	if (eofff - s >= 2 && (s[1] == '!' || s[1] == '?')) {
1168 		s = skip_comment(s, i);
1169 		goto se;
1170 	}
1171 	ss = s;
1172 	if (parse_element(s, i, &name, &namelen, &attr, &s)) goto sp;
1173 	if (namelen != 4 || casecmp(name, "FORM", 4)) goto se;
1174 	lf = ss;
1175 	la = attr;
1176 	goto se;
1177 
1178 	end_parse:
1179 	if (lf) {
1180 		last_form_tag = lf;
1181 		last_form_attr = la;
1182 		last_input_tag = i;
1183 		get_html_form(la, &form);
1184 	} else {
1185 		memset(&form, 0, sizeof(struct form));
1186 	}
1187 }
1188 
html_button(unsigned char * a)1189 void html_button(unsigned char *a)
1190 {
1191 	char *al;
1192 	struct form_control *fc;
1193 	find_form_for_input(a, 0);
1194 	fc = mem_alloc(sizeof(struct form_control));
1195 	memset(fc, 0, sizeof(struct form_control));
1196 	if (!(al = get_attr_val(a, "type"))) {
1197 		fc->type = FC_SUBMIT;
1198 		goto xxx;
1199 	}
1200 	if (!strcasecmp(al, "submit")) fc->type = FC_SUBMIT;
1201 	else if (!strcasecmp(al, "reset")) fc->type = FC_RESET;
1202 	else if (!strcasecmp(al, "button")) {
1203 		mem_free(al);
1204 		put_chrs(" [&nbsp;", 8, put_chars_f, ff);
1205 		if ((al = get_attr_val(a, "value"))) {
1206 			put_chrs(al, strlen(al), put_chars_f, ff);
1207 			mem_free(al);
1208 		} else put_chrs("BUTTON", 6, put_chars_f, ff);
1209 		put_chrs("&nbsp;] ", 8, put_chars_f, ff);
1210 		mem_free(fc);
1211 		return;
1212 	} else {
1213 		mem_free(al);
1214 		mem_free(fc);
1215 		return;
1216 	}
1217 	mem_free(al);
1218 	xxx:
1219 	fc->form_num = last_form_tag - startf;
1220 	fc->ctrl_num = a - last_form_tag;
1221 	fc->position = a - startf;
1222 	fc->method = form.method;
1223 	fc->action = stracpy(form.action);
1224 	fc->name = get_attr_val(a, "name");
1225 	fc->default_value = get_exact_attr_val(a, "value");
1226 	fc->ro = has_attr(a, "disabled") ? 2 : has_attr(a, "readonly") ? 1 : 0;
1227 	if (fc->type == FC_IMAGE) fc->alt = get_attr_val(a, "alt");
1228 	if (fc->type == FC_SUBMIT && !fc->default_value) fc->default_value = stracpy("Submit");
1229 	if (fc->type == FC_RESET && !fc->default_value) fc->default_value = stracpy("Reset");
1230 	if (!fc->default_value) fc->default_value = stracpy("");
1231 	special_f(ff, SP_CONTROL, fc);
1232 	format_.form = fc;
1233 	format_.attr |= AT_BOLD;
1234 	/*put_chrs("[&nbsp;", 7, put_chars_f, ff);
1235 	if (fc->default_value) put_chrs(fc->default_value, strlen(fc->default_value), put_chars_f, ff);
1236 	put_chrs("&nbsp;]", 7, put_chars_f, ff);
1237 	put_chrs(" ", 1, put_chars_f, ff);*/
1238 }
1239 
set_max_textarea_width(int * w)1240 void set_max_textarea_width(int *w)
1241 {
1242 	int limit;
1243 	if (!table_level) {
1244 		limit = par_format.width - (par_format.leftmargin + par_format.rightmargin);
1245 	} else {
1246 		limit = d_opt->xw - 2;
1247 	}
1248 	if (*w > limit) {
1249 		*w = limit;
1250 		if (*w < HTML_MINIMAL_TEXTAREA_WIDTH) *w = HTML_MINIMAL_TEXTAREA_WIDTH;
1251 	}
1252 }
1253 
html_input(unsigned char * a)1254 void html_input(unsigned char *a)
1255 {
1256 	int i;
1257 	int size;
1258 	unsigned char *al;
1259 	struct form_control *fc;
1260 	find_form_for_input(a, 0);
1261 	fc = mem_alloc(sizeof(struct form_control));
1262 	memset(fc, 0, sizeof(struct form_control));
1263 	if (!(al = get_attr_val(a, "type"))) {
1264 		fc->type = FC_TEXT;
1265 		goto xxx;
1266 	}
1267 	if (!strcasecmp(al, "text")) fc->type = FC_TEXT;
1268 	else if (!strcasecmp(al, "password")) fc->type = FC_PASSWORD;
1269 	else if (!strcasecmp(al, "checkbox")) fc->type = FC_CHECKBOX;
1270 	else if (!strcasecmp(al, "radio")) fc->type = FC_RADIO;
1271 	else if (!strcasecmp(al, "submit")) fc->type = FC_SUBMIT;
1272 	else if (!strcasecmp(al, "reset")) fc->type = FC_RESET;
1273 	else if (!strcasecmp(al, "file")) fc->type = FC_FILE;
1274 	else if (!strcasecmp(al, "hidden")) fc->type = FC_HIDDEN;
1275 	else if (!strcasecmp(al, "image")) fc->type = FC_IMAGE;
1276 	else if (!strcasecmp(al, "button")) {
1277 		mem_free(al);
1278 		put_chrs(" [&nbsp;", 8, put_chars_f, ff);
1279 		if ((al = get_attr_val(a, "value"))) {
1280 			put_chrs(al, strlen(al), put_chars_f, ff);
1281 			mem_free(al);
1282 		} else put_chrs("BUTTON", 6, put_chars_f, ff);
1283 		put_chrs("&nbsp;] ", 8, put_chars_f, ff);
1284 		mem_free(fc);
1285 		return;
1286 	} else fc->type = FC_TEXT;
1287 	mem_free(al);
1288 	xxx:
1289 	fc->form_num = last_form_tag - startf;
1290 	fc->ctrl_num = a - last_form_tag;
1291 	fc->position = a - startf;
1292 	fc->method = form.method;
1293 	fc->action = stracpy(form.action);
1294 	fc->target = stracpy(form.target);
1295 	fc->name = get_attr_val(a, "name");
1296 	if (fc->type == FC_TEXT || fc->type == FC_PASSWORD) fc->default_value = get_attr_val(a, "value");
1297 	else if (fc->type != FC_FILE) fc->default_value = get_exact_attr_val(a, "value");
1298 	if (fc->type == FC_CHECKBOX && !fc->default_value) fc->default_value = stracpy("on");
1299 	if ((size = get_num(a, "size")) <= 0) size = HTML_DEFAULT_INPUT_SIZE;
1300 	size++;
1301 	if (size > HTML_MINIMAL_TEXTAREA_WIDTH) {
1302 		set_max_textarea_width(&size);
1303 	}
1304 	fc->size = size;
1305 	if ((fc->maxlength = get_num(a, "maxlength")) == -1) fc->maxlength = MAXINT / 4;
1306 	if (fc->type == FC_CHECKBOX || fc->type == FC_RADIO) fc->default_state = has_attr(a, "checked");
1307 	fc->ro = has_attr(a, "disabled") ? 2 : has_attr(a, "readonly") ? 1 : 0;
1308 	if (fc->type == FC_IMAGE) {
1309 		fc->alt = get_attr_val(a, "alt");
1310 		if (!fc->alt) fc->alt = get_attr_val(a, "title");
1311 		if (!fc->alt) fc->alt = get_attr_val(a, "name");
1312 	}
1313 	if (fc->type == FC_SUBMIT && !fc->default_value) fc->default_value = stracpy("Submit");
1314 	if (fc->type == FC_RESET && !fc->default_value) fc->default_value = stracpy("Reset");
1315 	if (!fc->default_value) fc->default_value = stracpy("");
1316 	if (fc->type == FC_HIDDEN) goto hid;
1317 	put_chrs(" ", 1, put_chars_f, ff);
1318 	html_stack_dup();
1319 	format_.form = fc;
1320 	switch (fc->type) {
1321 		case FC_TEXT:
1322 		case FC_PASSWORD:
1323 		case FC_FILE:
1324 			format_.attr |= AT_BOLD;
1325 			for (i = 0; i < fc->size; i++) put_chrs("_", 1, put_chars_f, ff);
1326 			break;
1327 		case FC_CHECKBOX:
1328 		case FC_RADIO:
1329 			format_.attr |= AT_BOLD;
1330 			put_chrs("[&nbsp;]", 8, put_chars_f, ff);
1331 			break;
1332 		case FC_IMAGE:
1333 			if (format_.image) mem_free(format_.image), format_.image = NULL;
1334 			if ((al = get_url_val(a, "src")) || (al = get_url_val(a, "dynsrc"))) {
1335 				format_.image = join_urls(format_.href_base, al);
1336 				mem_free(al);
1337 			}
1338 			format_.attr |= AT_BOLD;
1339 			put_chrs("[&nbsp;", 7, put_chars_f, ff);
1340 			if (fc->alt) put_chrs(fc->alt, strlen(fc->alt), put_chars_f, ff);
1341 			else put_chrs("Submit", 6, put_chars_f, ff);
1342 			put_chrs("&nbsp;]", 7, put_chars_f, ff);
1343 			break;
1344 		case FC_SUBMIT:
1345 		case FC_RESET:
1346 			format_.attr |= AT_BOLD;
1347 			put_chrs("[&nbsp;", 7, put_chars_f, ff);
1348 			if (fc->default_value) put_chrs(fc->default_value, strlen(fc->default_value), put_chars_f, ff);
1349 			put_chrs("&nbsp;]", 7, put_chars_f, ff);
1350 			break;
1351 		default:
1352 			internal("bad control type");
1353 	}
1354 	kill_html_stack_item(&html_top);
1355 	put_chrs(" ", 1, put_chars_f, ff);
1356 
1357 	hid:
1358 	special_f(ff, SP_CONTROL, fc);
1359 }
1360 
html_select(unsigned char * a)1361 void html_select(unsigned char *a)
1362 {
1363 	char *al;
1364 	if (!(al = get_attr_val(a, "name"))) return;
1365 	html_top.dontkill = 1;
1366 	if (format_.select) mem_free(format_.select);
1367 	format_.select = al;
1368 	format_.select_disabled = 2 * has_attr(a, "disabled");
1369 }
1370 
html_option(unsigned char * a)1371 void html_option(unsigned char *a)
1372 {
1373 	struct form_control *fc;
1374 	unsigned char *val;
1375 	find_form_for_input(a, 0);
1376 	if (!format_.select) return;
1377 	fc = mem_alloc(sizeof(struct form_control));
1378 	memset(fc, 0, sizeof(struct form_control));
1379 	if (!(val = get_exact_attr_val(a, "value"))) {
1380 		unsigned char *p, *r;
1381 		unsigned char *name;
1382 		int namelen;
1383 		int l = 0;
1384 		for (p = a - 1; *p != '<'; p--) ;
1385 		val = init_str();
1386 		if (parse_element(p, eoff, NULL, NULL, NULL, &p)) {
1387 			internal("parse element failed");
1388 			goto x;
1389 		}
1390 		rrrr:
1391 		while (p < eoff && WHITECHAR(*p)) p++;
1392 		while (p < eoff && !WHITECHAR(*p) && *p != '<') {
1393 			pppp:
1394 			add_chr_to_str(&val, &l, *p), p++;
1395 		}
1396 		r = p;
1397 		while (r < eoff && WHITECHAR(*r)) r++;
1398 		if (r >= eoff) goto x;
1399 		if (eoff - r >= 2 && (r[1] == '!' || r[1] == '?')) {
1400 			p = skip_comment(r, eoff);
1401 			goto rrrr;
1402 		}
1403 		if (parse_element(r, eoff, &name, &namelen, NULL, &p)) goto pppp;
1404 		if (!((namelen == 6 && !casecmp(name, "OPTION", 6)) ||
1405 		    (namelen == 7 && !casecmp(name, "/OPTION", 7)) ||
1406 		    (namelen == 6 && !casecmp(name, "SELECT", 6)) ||
1407 		    (namelen == 7 && !casecmp(name, "/SELECT", 7)) ||
1408 		    (namelen == 8 && !casecmp(name, "OPTGROUP", 8)) ||
1409 		    (namelen == 9 && !casecmp(name, "/OPTGROUP", 9)))) goto rrrr;
1410 	}
1411 	x:
1412 	fc->form_num = last_form_tag - startf;
1413 	fc->ctrl_num = a - last_form_tag;
1414 	fc->position = a - startf;
1415 	fc->method = form.method;
1416 	fc->action = stracpy(form.action);
1417 	fc->type = FC_CHECKBOX;
1418 	fc->name = stracpy(format_.select);
1419 	fc->default_value = val;
1420 	fc->default_state = has_attr(a, "selected");
1421 	fc->ro = format_.select_disabled;
1422 	if (has_attr(a, "disabled")) fc->ro = 2;
1423 	put_chrs(" ", 1, put_chars_f, ff);
1424 	html_stack_dup();
1425 	format_.form = fc;
1426 	format_.attr |= AT_BOLD;
1427 	put_chrs("[ ]", 3, put_chars_f, ff);
1428 	kill_html_stack_item(&html_top);
1429 	put_chrs(" ", 1, put_chars_f, ff);
1430 	special_f(ff, SP_CONTROL, fc);
1431 }
1432 
clr_spaces(unsigned char * name)1433 void clr_spaces(unsigned char *name)
1434 {
1435 	unsigned char *n1, *n2;
1436 	for (n1 = name; *n1; n1++)
1437 		if (WHITECHAR(*n1) || *n1 == 1) *n1 = ' ';
1438 	/*for (nm = name; *nm; nm++)
1439 		while (nm[0] == ' ' && (nm == name || nm[1] == ' ' || !nm[1]))
1440 			memmove(nm, nm + 1, strlen(nm));*/
1441 	if (!strchr(name, ' ')) return;
1442 	for (n1 = name, n2 = name; *n1; n1++)
1443 		if (!(n1[0] == ' ' && (n2 == name || n1[1] == ' ' || !n1[1])))
1444 			*n2++ = *n1;
1445 	*n2 = 0;
1446 }
1447 
1448 int menu_stack_size;
1449 struct menu_item **menu_stack;
1450 
new_menu_item(unsigned char * name,int data,int fullname)1451 void new_menu_item(unsigned char *name, int data, int fullname)
1452 	/* name == NULL - up;	data == -1 - down */
1453 {
1454 	struct menu_item *top, *item, *nmenu = NULL; /* no uninitialized warnings */
1455 	if (name) {
1456 		clr_spaces(name);
1457 		if (!name[0]) mem_free(name), name = stracpy(" ");
1458 		if (name[0] == 1) name[0] = ' ';
1459 	}
1460 	if (name && data == -1) {
1461 		nmenu = mem_alloc(sizeof(struct menu_item));
1462 		memset(nmenu, 0, sizeof(struct menu_item));
1463 		/*nmenu->text = "";*/
1464 	}
1465 	if (menu_stack_size && name) {
1466 		top = item = menu_stack[menu_stack_size - 1];
1467 		while (item->text) item++;
1468 		if ((size_t)((char *)(item + 2) - (char *)top) > MAXINT) overalloc();
1469 		top = mem_realloc(top, (char *)(item + 2) - (char *)top);
1470 		item = item - menu_stack[menu_stack_size - 1] + top;
1471 		menu_stack[menu_stack_size - 1] = top;
1472 		if (menu_stack_size >= 2) {
1473 			struct menu_item *below = menu_stack[menu_stack_size - 2];
1474 			while (below->text) below++;
1475 			below[-1].data = top;
1476 		}
1477 		item->text = name;
1478 		item->rtext = data == -1 ? ">" : "";
1479 		item->hotkey = fullname ? "\000\001" : "\000\000"; /* dirty */
1480 		item->func = data == -1 ? MENU_FUNC do_select_submenu : MENU_FUNC selected_item;
1481 		item->data = data == -1 ? nmenu : (void *)(my_uintptr_t)data;
1482 		item->in_m = data == -1 ? 1 : 0;
1483 		item->free_i = 0;
1484 		item++;
1485 		memset(item, 0, sizeof(struct menu_item));
1486 		/*item->text = "";*/
1487 	} else if (name) mem_free(name);
1488 	if (name && data == -1) {
1489 		if ((unsigned)menu_stack_size > MAXINT / sizeof(struct menu_item *) - 1) overalloc();
1490 		menu_stack = mem_realloc(menu_stack, (menu_stack_size + 1) * sizeof(struct menu_item *));
1491 		menu_stack[menu_stack_size++] = nmenu;
1492 	}
1493 	if (!name) menu_stack_size--;
1494 }
1495 
init_menu()1496 void init_menu()
1497 {
1498 	menu_stack_size = 0;
1499 	menu_stack = DUMMY;
1500 	new_menu_item(stracpy(""), -1, 0);
1501 }
1502 
free_menu(struct menu_item * m)1503 void free_menu(struct menu_item *m) /* Grrr. Recursion */
1504 {
1505 	struct menu_item *mm;
1506 	for (mm = m; mm->text; mm++) {
1507 		mem_free(mm->text);
1508 		if (mm->func == MENU_FUNC do_select_submenu) free_menu(mm->data);
1509 	}
1510 	mem_free(m);
1511 }
1512 
detach_menu()1513 struct menu_item *detach_menu()
1514 {
1515 	struct menu_item *i = NULL;
1516 	if (menu_stack_size) i = menu_stack[0];
1517 	if (menu_stack) mem_free(menu_stack);
1518 	return i;
1519 }
1520 
destroy_menu()1521 void destroy_menu()
1522 {
1523 	if (menu_stack && menu_stack != DUMMY) free_menu(menu_stack[0]);
1524 	detach_menu();
1525 }
1526 
menu_labels(struct menu_item * m,unsigned char * base,unsigned char ** lbls)1527 void menu_labels(struct menu_item *m, unsigned char *base, unsigned char **lbls)
1528 {
1529 	unsigned char *bs;
1530 	for (; m->text; m++) {
1531 		if (m->func == MENU_FUNC do_select_submenu) {
1532 			if ((bs = stracpy(base))) {
1533 				add_to_strn(&bs, m->text);
1534 				add_to_strn(&bs, " ");
1535 				menu_labels(m->data, bs, lbls);
1536 				mem_free(bs);
1537 			}
1538 		} else {
1539 			if ((bs = stracpy(m->hotkey[1] ? (unsigned char *)"" : base))) add_to_strn(&bs, m->text);
1540 			lbls[(int)(my_uintptr_t)m->data] = bs;
1541 		}
1542 	}
1543 }
1544 
menu_contains(struct menu_item * m,int f)1545 int menu_contains(struct menu_item *m, int f)
1546 {
1547 	if (m->func != MENU_FUNC do_select_submenu) return (int)(my_uintptr_t)m->data == f;
1548 	for (m = m->data; m->text; m++) if (menu_contains(m, f)) return 1;
1549 	return 0;
1550 }
1551 
do_select_submenu(struct terminal * term,struct menu_item * menu,struct session * ses)1552 void do_select_submenu(struct terminal *term, struct menu_item *menu, struct session *ses)
1553 {
1554 	struct menu_item *m;
1555 	int def = get_current_state(ses);
1556 	int sel = 0;
1557 	if (def < 0) def = 0;
1558 	for (m = menu; m->text; m++, sel++) if (menu_contains(m, def)) goto f;
1559 	sel = 0;
1560 	f:
1561 	do_menu_selected(term, menu, ses, sel, NULL, NULL);
1562 }
1563 
do_html_select(unsigned char * attr,unsigned char * html,unsigned char * eof,unsigned char ** end,void * f)1564 int do_html_select(unsigned char *attr, unsigned char *html, unsigned char *eof, unsigned char **end, void *f)
1565 {
1566 	struct form_control *fc;
1567 	unsigned char *t_name, *t_attr, *en;
1568 	int t_namelen;
1569 	unsigned char *lbl;
1570 	int lbl_l;
1571 	unsigned char *vlbl;
1572 	int vlbl_l;
1573 	int nnmi = 0;
1574 	struct conv_table *ct = special_f(f, SP_TABLE, NULL);
1575 	unsigned char **val, **lbls;
1576 	int order, preselect, group;
1577 	int i, mw;
1578 	if (has_attr(attr, "multiple")) return 1;
1579 	find_form_for_input(attr, 0);
1580 	lbl = NULL;
1581 	lbl_l = 0;
1582 	vlbl = NULL;
1583 	vlbl_l = 0;
1584 	val = DUMMY;
1585 	order = 0, group = 0, preselect = -1;
1586 	init_menu();
1587 	se:
1588 	en = html;
1589 	see:
1590 	html = en;
1591 	while (html < eof && *html != '<') html++;
1592 	if (html >= eof) {
1593 		int i;
1594 		abort:
1595 		*end = html;
1596 		if (lbl) mem_free(lbl);
1597 		if (vlbl) mem_free(vlbl);
1598 		for (i = 0; i < order; i++) if (val[i]) mem_free(val[i]);
1599 		mem_free(val);
1600 		destroy_menu();
1601 		*end = en;
1602 		return 0;
1603 	}
1604 	if (lbl) {
1605 		unsigned char *q, *s = en;
1606 		int l = html - en;
1607 		while (l && WHITECHAR(s[0])) s++, l--;
1608 		while (l && WHITECHAR(s[l-1])) l--;
1609 		q = convert_string(ct, s, l);
1610 		if (q) add_to_str(&lbl, &lbl_l, q), mem_free(q);
1611 		add_bytes_to_str(&vlbl, &vlbl_l, s, l);
1612 	}
1613 	if (eof - html >= 2 && (html[1] == '!' || html[1] == '?')) {
1614 		html = skip_comment(html, eof);
1615 		goto se;
1616 	}
1617 	if (parse_element(html, eof, &t_name, &t_namelen, &t_attr, &en)) {
1618 		html++;
1619 		goto se;
1620 	}
1621 	if (t_namelen == 7 && !casecmp(t_name, "/SELECT", 7)) {
1622 		if (lbl) {
1623 			if (!val[order - 1]) val[order - 1] = stracpy(vlbl);
1624 			if (!nnmi) new_menu_item(lbl, order - 1, 1), lbl = NULL;
1625 			else mem_free(lbl), lbl = NULL;
1626 			mem_free(vlbl), vlbl = NULL;
1627 		}
1628 		goto end_parse;
1629 	}
1630 	if (t_namelen == 7 && !casecmp(t_name, "/OPTION", 7)) {
1631 		if (lbl) {
1632 			if (!val[order - 1]) val[order - 1] = stracpy(vlbl);
1633 			if (!nnmi) new_menu_item(lbl, order - 1, 1), lbl = NULL;
1634 			else mem_free(lbl), lbl = NULL;
1635 			mem_free(vlbl), vlbl = NULL;
1636 		}
1637 		goto see;
1638 	}
1639 	if (t_namelen == 6 && !casecmp(t_name, "OPTION", 6)) {
1640 		unsigned char *v, *vx;
1641 		if (lbl) {
1642 			if (!val[order - 1]) val[order - 1] = stracpy(vlbl);
1643 			if (!nnmi) new_menu_item(lbl, order - 1, 1), lbl = NULL;
1644 			else mem_free(lbl), lbl = NULL;
1645 			mem_free(vlbl), vlbl = NULL;
1646 		}
1647 		if (has_attr(t_attr, "disabled")) goto see;
1648 		if (preselect == -1 && has_attr(t_attr, "selected")) preselect = order;
1649 		v = get_exact_attr_val(t_attr, "value");
1650 		if (!(order & (ALLOC_GR - 1))) {
1651 			if ((unsigned)order > MAXINT / sizeof(unsigned char *) - ALLOC_GR) overalloc();
1652 			if ((unsigned)order > MAXINT / sizeof(char *) - ALLOC_GR) overalloc();
1653 			val = mem_realloc(val, (order + ALLOC_GR) * sizeof(unsigned char *));
1654 		}
1655 		val[order++] = v;
1656 		if ((vx = get_attr_val(t_attr, "label"))) {
1657 			new_menu_item(convert_string(ct, vx, strlen(vx)), order - 1, 0);
1658 			mem_free(vx);
1659 		}
1660 		if (!v || !vx) {
1661 			lbl = init_str(), lbl_l = 0;
1662 			vlbl = init_str(), vlbl_l = 0;
1663 			nnmi = !!vx;
1664 		}
1665 		goto see;
1666 	}
1667 	if ((t_namelen == 8 && !casecmp(t_name, "OPTGROUP", 8)) || (t_namelen == 9 && !casecmp(t_name, "/OPTGROUP", 9))) {
1668 		if (lbl) {
1669 			if (!val[order - 1]) val[order - 1] = stracpy(vlbl);
1670 			if (!nnmi) new_menu_item(lbl, order - 1, 1), lbl = NULL;
1671 			else mem_free(lbl), lbl = NULL;
1672 			mem_free(vlbl), vlbl = NULL;
1673 		}
1674 		if (group) new_menu_item(NULL, -1, 0), group = 0;
1675 	}
1676 	if (t_namelen == 8 && !casecmp(t_name, "OPTGROUP", 8)) {
1677 		char *la;
1678 		if (!(la = get_attr_val(t_attr, "label"))) la = stracpy("");
1679 		new_menu_item(convert_string(ct, la, strlen(la)), -1, 0);
1680 		mem_free(la);
1681 		group = 1;
1682 	}
1683 	goto see;
1684 
1685 	end_parse:
1686 	*end = en;
1687 	if (!order) goto abort;
1688 	fc = mem_alloc(sizeof(struct form_control));
1689 	memset(fc, 0, sizeof(struct form_control));
1690 	lbls = mem_alloc(order * sizeof(char *));
1691 	memset(lbls, 0, order * sizeof(char *));
1692 	fc->form_num = last_form_tag - startf;
1693 	fc->ctrl_num = attr - last_form_tag;
1694 	fc->position = attr - startf;
1695 	fc->method = form.method;
1696 	fc->action = stracpy(form.action);
1697 	fc->name = get_attr_val(attr, "name");
1698 	fc->type = FC_SELECT;
1699 	fc->default_state = preselect < 0 ? 0 : preselect;
1700 	fc->default_value = order ? stracpy(val[fc->default_state]) : stracpy("");
1701 	fc->ro = has_attr(attr, "disabled") ? 2 : has_attr(attr, "readonly") ? 1 : 0;
1702 	fc->nvalues = order;
1703 	fc->values = val;
1704 	fc->menu = detach_menu();
1705 	fc->labels = lbls;
1706 	menu_labels(fc->menu, "", lbls);
1707 	put_chrs("[", 1, put_chars_f, f);
1708 	html_stack_dup();
1709 	format_.form = fc;
1710 	format_.attr |= AT_BOLD;
1711 	mw = 0;
1712 	for (i = 0; i < order; i++) if (lbls[i] && strlen(lbls[i]) > (size_t)mw) mw = strlen(lbls[i]);
1713 	for (i = 0; i < mw; i++) put_chrs("_", 1, put_chars_f, f);
1714 	kill_html_stack_item(&html_top);
1715 	put_chrs("]", 1, put_chars_f, f);
1716 	special_f(ff, SP_CONTROL, fc);
1717 	return 0;
1718 }
1719 
html_textarea(unsigned char * a)1720 void html_textarea(unsigned char *a)
1721 {
1722 	internal("This should be never called");
1723 }
1724 
do_html_textarea(unsigned char * attr,unsigned char * html,unsigned char * eof,unsigned char ** end,void * f)1725 void do_html_textarea(unsigned char *attr, unsigned char *html, unsigned char *eof, unsigned char **end, void *f)
1726 {
1727 	struct form_control *fc;
1728 	unsigned char *p, *t_name, *w;
1729 	int t_namelen;
1730 	int cols, rows;
1731 	int i;
1732 	find_form_for_input(attr, 1);
1733 	while (html < eof && (*html == '\n' || *html == '\r')) html++;
1734 	p = html;
1735 	while (p < eof && *p != '<') {
1736 		pp:
1737 		p++;
1738 	}
1739 	if (p >= eof) {
1740 		*end = eof;
1741 		return;
1742 	}
1743 	if (parse_element(p, eof, &t_name, &t_namelen, NULL, end)) goto pp;
1744 	if (t_namelen != 9 || casecmp(t_name, "/TEXTAREA", 9)) goto pp;
1745 	if (!last_form_tag) return;
1746 	fc = mem_alloc(sizeof(struct form_control));
1747 	memset(fc, 0, sizeof(struct form_control));
1748 	fc->form_num = last_form_tag - startf;
1749 	fc->ctrl_num = attr - last_form_tag;
1750 	fc->position = attr - startf;
1751 	fc->method = form.method;
1752 	fc->action = stracpy(form.action);
1753 	fc->name = get_attr_val(attr, "name");
1754 	fc->type = FC_TEXTAREA;;
1755 	fc->ro = has_attr(attr, "disabled") ? 2 : has_attr(attr, "readonly") ? 1 : 0;
1756 	fc->default_value = memacpy(html, p - html);
1757 	if ((cols = get_num(attr, "cols")) < HTML_MINIMAL_TEXTAREA_WIDTH) cols = HTML_DEFAULT_TEXTAREA_WIDTH;
1758 	cols++;
1759 	set_max_textarea_width(&cols);
1760 	if ((rows = get_num(attr, "rows")) <= 0) rows = HTML_DEFAULT_TEXTAREA_HEIGHT;
1761 	if (rows > d_opt->yw) {
1762 		rows = d_opt->yw;
1763 		if (rows <= 0) rows = 1;
1764 	}
1765 	fc->cols = cols;
1766 	fc->rows = rows;
1767 	fc->wrap = 1;
1768 	if ((w = get_attr_val(attr, "wrap"))) {
1769 		if (!strcasecmp(w, "hard") || !strcasecmp(w, "physical")) fc->wrap = 2;
1770 		else if (!strcasecmp(w, "off")) fc->wrap = 0;
1771 		mem_free(w);
1772 	}
1773 	if ((fc->maxlength = get_num(attr, "maxlength")) == -1) fc->maxlength = MAXINT / 4;
1774 	if (rows > 1) ln_break(1, line_break_f, f);
1775 	else put_chrs(" ", 1, put_chars_f, f);
1776 	html_stack_dup();
1777 	format_.form = fc;
1778 	format_.attr |= AT_BOLD;
1779 	for (i = 0; i < rows; i++) {
1780 		int j;
1781 		for (j = 0; j < cols; j++) put_chrs("_", 1, put_chars_f, f);
1782 		if (i < rows - 1) ln_break(1, line_break_f, f);
1783 	}
1784 	kill_html_stack_item(&html_top);
1785 	if (rows > 1) ln_break(1, line_break_f, f);
1786 	else put_chrs(" ", 1, put_chars_f, f);
1787 	special_f(f, SP_CONTROL, fc);
1788 }
1789 
html_iframe(unsigned char * a)1790 void html_iframe(unsigned char *a)
1791 {
1792 	unsigned char *name, *url;
1793 	if (!(url = get_url_val(a, "src"))) return;
1794 	if (!(name = get_attr_val(a, "name"))) name = stracpy("");
1795 	if (*name) put_link_line("IFrame: ", name, url, d_opt->framename);
1796 	else put_link_line("", "IFrame", url, d_opt->framename);
1797 	mem_free(name);
1798 	mem_free(url);
1799 }
1800 
html_noframes(unsigned char * a)1801 void html_noframes(unsigned char *a)
1802 {
1803 	if (d_opt->frames) html_skip(a);
1804 }
1805 
html_frame(unsigned char * a)1806 void html_frame(unsigned char *a)
1807 {
1808 	unsigned char *name, *u2, *url;
1809 	if (!(u2 = get_url_val(a, "src"))) {
1810 		url = stracpy("");
1811 	} else {
1812 		url = join_urls(format_.href_base, u2);
1813 		mem_free(u2);
1814 	}
1815 	if (!url) return;
1816 	name = get_attr_val (a, "name");
1817 	if (!name)
1818 		name = stracpy(url);
1819 	else if (!name[0]) { /* When name doesn't have a value */
1820 		mem_free(name);
1821 		name = stracpy(url);
1822 	}
1823 	if (!d_opt->frames || !html_top.frameset) put_link_line("Frame: ", name, url, "");
1824 	else {
1825 		struct frame_param fp;
1826 		fp.name = name;
1827 		fp.url = url;
1828 		fp.parent = html_top.frameset;
1829 		if (special_f(ff, SP_USED, NULL)) special_f(ff, SP_FRAME, &fp);
1830 	}
1831 	mem_free(name);
1832 	mem_free(url);
1833 }
1834 
parse_frame_widths(unsigned char * a,int ww,int www,int ** op,int * olp)1835 void parse_frame_widths(unsigned char *a, int ww, int www, int **op, int *olp)
1836 {
1837 	unsigned char *aa;
1838 	int q, qq, i, d, nn;
1839 	unsigned long n;
1840 	int *oo, *o;
1841 	int ol;
1842 	ol = 0;
1843 	o = DUMMY;
1844 	new_ch:
1845 	while (WHITECHAR(*a)) a++;
1846 	n = strtoul(a, (char **)(void *)&a, 10);
1847 	if (n > 10000) n = 10000;
1848 	q = n;
1849 	if (*a == '%') q = q * ww / 100;
1850 	else if (*a != '*') q = (q + (www - 1) / 2) / (www ? www : 1);
1851 	else if (!(q = -q)) q = -1;
1852 	if ((unsigned)ol > MAXINT / sizeof(int) - 1) overalloc();
1853 	o = mem_realloc(o, (ol + 1) * sizeof(int));
1854 	o[ol++] = q;
1855 	if ((aa = strchr(a, ','))) {
1856 		a = aa + 1;
1857 		goto new_ch;
1858 	}
1859 	*op = o;
1860 	*olp = ol;
1861 	q = 2 * ol - 1;
1862 	for (i = 0; i < ol; i++) if (o[i] > 0) q += o[i] - 1;
1863 	if (q >= ww) {
1864 		distribute:
1865 		for (i = 0; i < ol; i++) if (o[i] < 1) o[i] = 1;
1866 		q -= ww;
1867 		d = 0;
1868 		for (i = 0; i < ol; i++) d += o[i];
1869 		qq = q;
1870 		for (i = 0; i < ol; i++) {
1871 			q -= o[i] - o[i] * (d - qq) / (d ? d : 1);
1872 			do_not_optimize_here(&d);
1873 				/* SIGH! gcc 2.7.2.* has an optimizer bug! */
1874 			o[i] = o[i] * (d - qq) / (d ? d : 1);
1875 		}
1876 		while (q) {
1877 			nn = 0;
1878 			for (i = 0; i < ol; i++) {
1879 				if (q < 0) o[i]++, q++, nn = 1;
1880 				if (q > 0 && o[i] > 1) o[i]--, q--, nn = 1;
1881 				if (!q) break;
1882 			}
1883 			if (!nn) break;
1884 		}
1885 	} else {
1886 		int nn = 0;
1887 		for (i = 0; i < ol; i++) if (o[i] < 0) nn = 1;
1888 		if (!nn) goto distribute;
1889 		if ((unsigned)ol > MAXINT / sizeof(int)) overalloc();
1890 		oo = mem_alloc(ol * sizeof(int));
1891 		memcpy(oo, o, ol * sizeof(int));
1892 		for (i = 0; i < ol; i++) if (o[i] < 1) o[i] = 1;
1893 		q = ww - q;
1894 		d = 0;
1895 		for (i = 0; i < ol; i++) if (oo[i] < 0) d += -oo[i];
1896 		qq = q;
1897 		for (i = 0; i < ol; i++) if (oo[i] < 0) {
1898 			o[i] += (-oo[i] * qq / (d ? d : 1));
1899 			q -= (-oo[i] * qq / (d ? d : 1));
1900 		}
1901 		if (q < 0) {
1902 			q = 0;
1903 			/*internal("parse_frame_widths: q < 0"); may happen when page contains too big values */
1904 		}
1905 		for (i = 0; i < ol; i++) if (oo[i] < 0) {
1906 			if (q) o[i]++, q--;
1907 		}
1908 		if (q > 0) {
1909 			q = 0;
1910 			/*internal("parse_frame_widths: q > 0"); may happen when page contains too big values */
1911 		}
1912 		mem_free(oo);
1913 	}
1914 	for (i = 0; i < ol; i++) if (!o[i]) {
1915 		int j;
1916 		int m = 0;
1917 		int mj = 0;
1918 		for (j = 0; j < ol; j++) if (o[j] > m) m = o[j], mj = j;
1919 		if (m) o[i] = 1, o[mj]--;
1920 	}
1921 }
1922 
html_frameset(unsigned char * a)1923 void html_frameset(unsigned char *a)
1924 {
1925 	int x, y;
1926 	struct frameset_param fp;
1927 	unsigned char *c, *d;
1928 	if (!d_opt->frames || !special_f(ff, SP_USED, NULL)) return;
1929 	if (!(c = get_attr_val(a, "cols"))) c = stracpy("100%");
1930 	if (!(d = get_attr_val(a, "rows"))) d = stracpy("100%");
1931 	if (!html_top.frameset) {
1932 		x = d_opt->xw;
1933 		y = d_opt->yw;
1934 	} else {
1935 		struct frameset_desc *f = html_top.frameset;
1936 		if (f->yp >= f->y) goto free_cd;
1937 		x = f->f[f->xp + f->yp * f->x].xw;
1938 		y = f->f[f->xp + f->yp * f->x].yw;
1939 	}
1940 	parse_frame_widths(c, x, HTML_FRAME_CHAR_WIDTH, &fp.xw, &fp.x);
1941 	parse_frame_widths(d, y, HTML_FRAME_CHAR_HEIGHT, &fp.yw, &fp.y);
1942 	fp.parent = html_top.frameset;
1943 	if (fp.x && fp.y) html_top.frameset = special_f(ff, SP_FRAMESET, &fp);
1944 	mem_free(fp.xw);
1945 	mem_free(fp.yw);
1946 	free_cd:
1947 	mem_free(c);
1948 	mem_free(d);
1949 }
1950 
1951 /*void html_frameset(unsigned char *a)
1952 {
1953 	int w;
1954 	int horiz = 0;
1955 	struct frameset_param *fp;
1956 	unsigned char *c, *d;
1957 	if (!d_opt->frames || !special_f(ff, SP_USED, NULL)) return;
1958 	if (!(c = get_attr_val(a, "cols"))) {
1959 		horiz = 1;
1960 		if (!(c = get_attr_val(a, "rows"))) return;
1961 	}
1962 	fp = mem_alloc(sizeof(struct frameset_param));
1963 	fp->n = 0;
1964 	fp->horiz = horiz;
1965 	par_format.leftmargin = par_format.rightmargin = 0;
1966 	d = c;
1967 	while (1) {
1968 		while (WHITECHAR(*d)) d++;
1969 		if (!*d) break;
1970 		if (*d == ',') {
1971 			d++;
1972 			continue;
1973 		}
1974 		if ((w = parse_width(d, 1)) != -1) {
1975 			if ((unsigned)fp->n > (MAXINT - sizeof(struct frameset_param)) / sizeof(int) - 1) overalloc();
1976 			fp = mem_realloc(fp, sizeof(struct frameset_param) + (fp->n + 1) * sizeof(int));
1977 			fp->width[fp->n++] = w;
1978 		}
1979 		if (!(d = strchr(d, ','))) break;
1980 		d++;
1981 	}
1982 	fp->parent = html_top.frameset;
1983 	if (fp->n) html_top.frameset = special_f(ff, SP_FRAMESET, fp);
1984 	mem_free(fp);
1985 	f:
1986 	mem_free(c);
1987 }*/
1988 
html_link(unsigned char * a)1989 void html_link(unsigned char *a)
1990 {
1991 	unsigned char *name, *url, *title;
1992 	if ((name = get_attr_val(a, "type"))) {
1993 		if (strcasecmp(name, "text/html")) {
1994 			mem_free(name);
1995 			return;
1996 		}
1997 		mem_free(name);
1998 	}
1999 	if (!(url = get_url_val(a, "href"))) return;
2000 	if (!(name = get_attr_val(a, "rel")))
2001 		if (!(name = get_attr_val(a, "rev")))
2002 			name = get_attr_val(a, "ref");
2003 	if (name) {
2004 		unsigned char *lang;
2005 		if ((lang = get_attr_val(a, "hreflang"))) {
2006 			add_to_strn(&name, " ");
2007 			add_to_strn(&name, lang);
2008 			mem_free(lang);
2009 		}
2010 	}
2011 	if (!name)
2012 		name = stracpy(url);
2013 	if (
2014 	    !casecmp(name, cast_uchar "apple-touch-icon", 16) ||
2015 	    !casecmp(name, cast_uchar "schema", 6) ||
2016 	    !strcasecmp(cast_const_char name, "Edit-Time-Data") ||
2017 	    !strcasecmp(cast_const_char name, "File-List") ||
2018 	    !strcasecmp(cast_const_char name, "alternate stylesheet") ||
2019 	    !strcasecmp(cast_const_char name, "generator-home") ||
2020 	    !strcasecmp(cast_const_char name, "https://github.com/WP-API/WP-API") ||
2021 	    !strcasecmp(cast_const_char name, "icon") ||
2022 	    !strcasecmp(cast_const_char name, "made") ||
2023 	    !strcasecmp(cast_const_char name, "meta") ||
2024 	    !strcasecmp(cast_const_char name, "pingback") ||
2025 	    !strcasecmp(cast_const_char name, "preconnect") ||
2026 	    !strcasecmp(cast_const_char name, "shortcut icon") ||
2027 	    !strcasecmp(cast_const_char name, "stylesheet") ||
2028 	    !strcasecmp(cast_const_char name, "https://api.w.org/") ||
2029 	    !strcasecmp(cast_const_char name, "prefetch") ||
2030 	    !strcasecmp(cast_const_char name, "dns-prefetch") ||
2031 	    !strcasecmp(cast_const_char name, "prerender") ||
2032 	    !strcasecmp(cast_const_char name, "preload") ||
2033 	    0) goto skip;
2034 	if ((title = get_attr_val(a, "title"))) {
2035 		add_to_strn(&name, ": ");
2036 		add_to_strn(&name, title);
2037 		mem_free(title);
2038 	}
2039 	put_link_line("Link: ", name, url, format_.target_base);
2040 	skip:
2041 	mem_free(name);
2042 	mem_free(url);
2043 }
2044 
2045 struct element_info {
2046 	char *name;
2047 	void (*func)(unsigned char *);
2048 	int linebreak;
2049 	int nopair;
2050 };
2051 
2052 struct element_info elements[] = {
2053 	{"SPAN",	html_span,	0, 0},
2054 	{"B",		html_bold,	0, 0},
2055 	{"STRONG",	html_bold,	0, 0},
2056 	{"DFN",		html_bold,	0, 0},
2057 	{"I",		html_italic,	0, 0},
2058 	{"Q",		html_italic,	0, 0},
2059 	{"CITE",	html_italic,	0, 0},
2060 	{"EM",		html_italic,	0, 0},
2061 	{"ABBR",	html_italic,	0, 0},
2062 	{"U",		html_underline,	0, 0},
2063 	{"S",		html_underline,	0, 0},
2064 	{"STRIKE",	html_underline,	0, 0},
2065 	{"FIXED",	html_fixed,	0, 0},
2066 	{"CODE",	html_fixed,	0, 0},
2067 	{"FONT",	html_font,	0, 0},
2068 	{"A",		html_a,		0, 2},
2069 	{"IMG",		html_img,	0, 1},
2070 
2071 	{"BASE",	html_base,	0, 1},
2072 	{"BASEFONT",	html_font,	0, 1},
2073 
2074 	{"BODY",	html_body,	0, 0},
2075 
2076 /*	{"HEAD",	html_skip,	0, 0},*/
2077 	{"TITLE",	html_title,	0, 0},
2078 	{"SCRIPT",	html_skip,	0, 0},
2079 	{"STYLE",	html_skip,	0, 0},
2080 
2081 	{"BR",		html_br,	1, 1},
2082 	{"DIV",		html_linebrk,	1, 0},
2083 	{"CENTER",	html_center,	1, 0},
2084 	{"CAPTION",	html_center,	1, 0},
2085 	{"P",		html_p,		2, 2},
2086 	{"HR",		html_hr,	2, 1},
2087 	{"H1",		html_center,	2, 2},
2088 	{"H2",		html_h2,	2, 2},
2089 	{"H3",		html_h3,	2, 2},
2090 	{"H4",		html_h4,	2, 2},
2091 	{"H5",		html_h5,	2, 2},
2092 	{"H6",		html_h6,	2, 2},
2093 	{"BLOCKQUOTE",	html_blockquote,2, 0},
2094 	{"ADDRESS",	html_address,	2, 0},
2095 	{"PRE",		html_pre,	2, 0},
2096 	{"LISTING",	html_pre,	2, 0},
2097 
2098 	{"UL",		html_ul,	1, 0},
2099 	{"DIR",		html_ul,	1, 0},
2100 	{"MENU",	html_ul,	1, 0},
2101 	{"OL",		html_ol,	1, 0},
2102 	{"LI",		html_li,	1, 3},
2103 	{"DL",		html_dl,	1, 0},
2104 	{"DT",		html_dt,	1, 1},
2105 	{"DD",		html_dd,	1, 1},
2106 
2107 	{"TABLE",	html_table,	2, 0},
2108 	{"TR",		html_tr,	1, 0},
2109 	{"TD",		html_td,	0, 0},
2110 	{"TH",		html_th,	0, 0},
2111 
2112 	{"FORM",	html_form,	1, 0},
2113 	{"INPUT",	html_input,	0, 1},
2114 	{"TEXTAREA",	html_textarea,	0, 1},
2115 	{"SELECT",	html_select,	0, 0},
2116 	{"OPTION",	html_option,	1, 1},
2117 	{"BUTTON",	html_button,	0, 0},
2118 
2119 	{"LINK",	html_link,	1, 1},
2120 	{"IFRAME",	html_iframe,	1, 1},
2121 	{"FRAME",	html_frame,	1, 1},
2122 	{"FRAMESET",	html_frameset,	1, 0},
2123 	{"NOFRAMES",	html_noframes,	0, 0},
2124 
2125 	{NULL,		NULL, 0, 0},
2126 };
2127 
skip_comment(unsigned char * html,unsigned char * eof)2128 unsigned char *skip_comment(unsigned char *html, unsigned char *eof)
2129 {
2130 	int comm = eof - html >= 4 && html[2] == '-' && html[3] == '-';
2131 	html += comm ? 4 : 2;
2132 	while (html < eof) {
2133 		if (!comm && html[0] == '>') return html + 1;
2134 		if (comm && eof - html >= 2 && html[0] == '-' && html[1] == '-') {
2135 			html += 2;
2136 			while (html < eof && *html == '-') html++;
2137 			while (html < eof && WHITECHAR(*html)) html++;
2138 			if (html >= eof) return eof;
2139 			if (*html == '>') return html + 1;
2140 			continue;
2141 		}
2142 		html++;
2143 	}
2144 	return eof;
2145 }
2146 
process_head(unsigned char * head)2147 void process_head(unsigned char *head)
2148 {
2149 	unsigned char *r, *p;
2150 	if ((r = parse_http_header(head, "Refresh", NULL))) {
2151 		if ((p = parse_header_param(r, "URL")) || (p = parse_header_param(r, ""))) {
2152 			put_link_line("Refresh: ", p, p, d_opt->framename);
2153 			mem_free(p);
2154 		}
2155 		mem_free(r);
2156 	}
2157 }
2158 
qd(unsigned char * html,unsigned char * eof,int * len)2159 int qd(unsigned char *html, unsigned char *eof, int *len)
2160 {
2161 	int l;
2162 	*len = 1;
2163 	if (html >= eof) {
2164 		internal("qd: out of data, html == %p, eof == %p", html, eof);
2165 		return -1;
2166 	}
2167 	if (html[0] != '&' || d_opt->plain) return html[0];
2168 	if (html + 1 >= eof) return -1;
2169 	if (html[1] != '#') return -1;
2170 	for (l = 2; l < 10 && eof - html > l; l++) if (html[l] == ';') {
2171 		int n = get_entity_number(html + 2, l - 2);
2172 		if (n >= 0) {
2173 			*len = l + 1;
2174 			return n;
2175 		}
2176 		break;
2177 	}
2178 	return -1;
2179 }
2180 
parse_html(unsigned char * html,unsigned char * eof,int (* put_chars)(void *,unsigned char *,int),void (* line_break)(void *),void * (* special)(void *,int,...),void * f,unsigned char * head)2181 void parse_html(unsigned char *html, unsigned char *eof, int (*put_chars)(void *, unsigned char *, int), void (*line_break)(void *), void *(*special)(void *, int, ...), void *f, unsigned char *head)
2182 {
2183 	/*unsigned char *start = html;*/
2184 	unsigned char *lt;
2185 	putsp = -1;
2186 	line_breax = table_level ? 2 : 1;
2187 	pos = 0;
2188 	was_br = 0;
2189 	put_chars_f = put_chars;
2190 	line_break_f = line_break;
2191 	special_f = special;
2192 	ff = f;
2193 	eoff = eof;
2194 	if (head) process_head(head);
2195 	set_lt:
2196 	put_chars_f = put_chars;
2197 	line_break_f = line_break;
2198 	special_f = special;
2199 	ff = f;
2200 	eoff = eof;
2201 	lt = html;
2202 	while (html < eof) {
2203 		unsigned char *name, *attr, *end;
2204 		int namelen;
2205 		struct element_info *ei;
2206 		int inv;
2207 		if (WHITECHAR(*html) && par_format.align != AL_NO) {
2208 			unsigned char *h = html;
2209 			/*if (putsp == -1) {
2210 				while (html < eof && WHITECHAR(*html)) html++;
2211 				goto set_lt;
2212 			}
2213 			putsp = 0;*/
2214 			while (h < eof && WHITECHAR(*h)) h++;
2215 			if (eof - h > 1 && h[0] == '<' && h[1] == '/') {
2216 				if (!parse_element(h, eof, &name, &namelen, &attr, &end)) {
2217 					put_chrs(lt, html - lt, put_chars, f);
2218 					lt = html = h;
2219 					if (!html_top.invisible) putsp = 1;
2220 					goto element;
2221 				}
2222 			}
2223 			html++;
2224 			if (!(pos + (html-lt-1))) goto skip_w; /* ??? */
2225 			if (*(html - 1) == ' ') {
2226 				if (html < eof && !WHITECHAR(*html)) continue;	/* BIG performance win; not sure if it doesn't cause any bug */
2227 				put_chrs(lt, html - lt, put_chars, f);
2228 			} else {
2229 				put_chrs(lt, html - 1 - lt, put_chars, f);
2230 				put_chrs(" ", 1, put_chars, f);
2231 			}
2232 			skip_w:
2233 			while (html < eof && WHITECHAR(*html)) html++;
2234 			/*putsp = -1;*/
2235 			goto set_lt;
2236 			put_sp:
2237 			put_chrs(" ", 1, put_chars, f);
2238 			/*putsp = -1;*/
2239 		}
2240 		if (par_format.align == AL_NO && (*html < 32 || *html == '&')) {
2241 			int l;
2242 			int q = qd(html, eof, &l);
2243 			putsp = 0;
2244 			if (q == 9) {
2245 				put_chrs(lt, html - lt, put_chars, f);
2246 				put_chrs("        ", 8 - pos % 8, put_chars, f);
2247 				html += l;
2248 				goto set_lt;
2249 			} else if (q == 13 || q == 10) {
2250 				put_chrs(lt, html - lt, put_chars, f);
2251 				next_break:
2252 				html += l;
2253 				if (q == 13 && eof - html > 1 && qd(html, eof, &l) == 10) html += l;
2254 				ln_break(1, line_break, f);
2255 				if (html >= eof) goto set_lt;
2256 				q = qd(html, eof, &l);
2257 				if (q == 13 || q == 10) {
2258 					line_breax = 0;
2259 					goto next_break;
2260 				}
2261 				goto set_lt;
2262 			}
2263 		}
2264 		if (*html < ' ') {
2265 			/*if (putsp == 1) goto put_sp;
2266 			putsp = 0;*/
2267 			put_chrs(lt, html - lt, put_chars, f);
2268 			put_chrs(".", 1, put_chars, f);
2269 			html++;
2270 			goto set_lt;
2271 		}
2272 		if (eof - html >= 2 && html[0] == '<' && (html[1] == '!' || html[1] == '?') && !d_opt->plain) {
2273 			/*if (putsp == 1) goto put_sp;
2274 			putsp = 0;*/
2275 			put_chrs(lt, html - lt, put_chars, f);
2276 			html = skip_comment(html, eof);
2277 			goto set_lt;
2278 		}
2279 		if (*html != '<' || d_opt->plain || parse_element(html, eof, &name, &namelen, &attr, &end)) {
2280 			/*if (putsp == 1) goto put_sp;
2281 			putsp = 0;*/
2282 			html++;
2283 			continue;
2284 		}
2285 		element:
2286 		inv = *name == '/'; name += inv; namelen -= inv;
2287 		if (!inv && putsp == 1 && !html_top.invisible) goto put_sp;
2288 		put_chrs(lt, html - lt, put_chars, f);
2289 		if (par_format.align != AL_NO) if (!inv && !putsp) {
2290 			unsigned char *ee = end;
2291 			unsigned char *nm;
2292 			while (!parse_element(ee, eof, &nm, NULL, NULL, &ee))
2293 				if (*nm == '/') goto ng;
2294 			if (ee < eof && WHITECHAR(*ee)) {
2295 				/*putsp = -1;*/
2296 				put_chrs(" ", 1, put_chars, f);
2297 			}
2298 			ng:;
2299 		}
2300 		html = end;
2301 		for (ei = elements; ei->name; ei++) {
2302 			if (ei->name &&
2303 			   (strlen(ei->name) != (size_t)namelen || casecmp(ei->name, name, namelen)))
2304 				continue;
2305 			if (!inv) {
2306 				char *a;
2307 				ln_break(ei->linebreak, line_break, f);
2308 				if (ei->name && ((a = get_attr_val(attr, "id")))) {
2309 					special(f, SP_TAG, a);
2310 					mem_free(a);
2311 				}
2312 				if (!html_top.invisible) {
2313 					int a = par_format.align == AL_NO;
2314 					struct par_attrib pa = par_format;
2315 					if (ei->func == html_table && d_opt->tables && table_level < HTML_MAX_TABLE_LEVEL) {
2316 						format_table(attr, html, eof, &html, f);
2317 						ln_break(2, line_break, f);
2318 						goto set_lt;
2319 					}
2320 					if (ei->func == html_select) {
2321 						if (!do_html_select(attr, html, eof, &html, f))
2322 							goto set_lt;
2323 					}
2324 					if (ei->func == html_textarea) {
2325 						do_html_textarea(attr, html, eof, &html, f);
2326 						goto set_lt;
2327 					}
2328 					if (ei->nopair == 2 || ei->nopair == 3) {
2329 						struct html_element *e;
2330 						if (ei->nopair == 2) {
2331 							foreach(e, html_stack) {
2332 								if (e->dontkill) break;
2333 								if (e->linebreak || !ei->linebreak) break;
2334 							}
2335 						} else foreach(e, html_stack) {
2336 							if (e->linebreak && !ei->linebreak) break;
2337 							if (e->dontkill) break;
2338 							if (e->namelen == namelen && !casecmp(e->name, name, e->namelen)) break;
2339 						}
2340 						if (e->namelen == namelen && !casecmp(e->name, name, e->namelen)) {
2341 							while (e->prev != (void *)&html_stack) kill_html_stack_item(e->prev);
2342 							if (e->dontkill != 2) kill_html_stack_item(e);
2343 						}
2344 					}
2345 					if (ei->nopair != 1) {
2346 						html_stack_dup();
2347 						html_top.name = name;
2348 						html_top.namelen = namelen;
2349 						html_top.options = attr;
2350 						html_top.linebreak = ei->linebreak;
2351 					}
2352 					if (ei->func) ei->func(attr);
2353 					if (ei->func != html_br) was_br = 0;
2354 					if (a) par_format = pa;
2355 				}
2356 			} else {
2357 				struct html_element *e, *ff;
2358 				int lnb = 0;
2359 				int xxx = 0;
2360 				was_br = 0;
2361 				if (ei->nopair == 1 || ei->nopair == 3) break;
2362 				/*debug_stack();*/
2363 				foreach(e, html_stack) {
2364 					if (e->linebreak && !ei->linebreak && ei->name) xxx = 1;
2365 					if (e->namelen != namelen || casecmp(e->name, name, e->namelen)) {
2366 						if (e->dontkill) break;
2367 						else continue;
2368 					}
2369 					if (xxx) {
2370 						kill_html_stack_item(e);
2371 						break;
2372 					}
2373 					for (ff = e; ff != (void *)&html_stack; ff = ff->prev)
2374 						if (ff->linebreak > lnb) lnb = ff->linebreak;
2375 					ln_break(lnb, line_break, f);
2376 					while (e->prev != (void *)&html_stack) kill_html_stack_item(e->prev);
2377 					kill_html_stack_item(e);
2378 					break;
2379 				}
2380 				/*debug_stack();*/
2381 			}
2382 			goto set_lt;
2383 		}
2384 		goto set_lt; /* unreachable */
2385 	}
2386 	put_chrs(lt, html - lt, put_chars, f);
2387 	ln_break(1, line_break, f);
2388 	putsp = -1;
2389 	pos = 0;
2390 	/*line_breax = 1;*/
2391 	was_br = 0;
2392 }
2393 
get_image_map(unsigned char * head,unsigned char * s,unsigned char * eof,unsigned char * tag,struct menu_item ** menu,struct memory_list ** ml,unsigned char * href_base,unsigned char * target_base,int to,int def,int hdef)2394 int get_image_map(unsigned char *head, unsigned char *s, unsigned char *eof, unsigned char *tag, struct menu_item **menu, struct memory_list **ml, unsigned char *href_base, unsigned char *target_base, int to, int def, int hdef)
2395 {
2396 	unsigned char *name, *attr, *al, *label, *href, *target;
2397 	int namelen, lblen;
2398 	struct link_def *ld;
2399 	struct menu_item *nm;
2400 	int nmenu = 0;
2401 	int i;
2402 	unsigned char *hd = init_str();
2403 	int hdl = 0;
2404 	struct conv_table *ct;
2405 	if (head) add_to_str(&hd, &hdl, head);
2406 	scan_http_equiv(s, eof, &hd, &hdl, NULL);
2407 	ct = get_convert_table(hd, to, def, NULL, NULL, hdef);
2408 	mem_free(hd);
2409 	*menu = mem_alloc(sizeof(struct menu_item));
2410 	memset(*menu, 0, sizeof(struct menu_item));
2411 	/*(*menu)->text = NULL;*/
2412 	se:
2413 	while (s < eof && *s != '<') {
2414 		sp:
2415 		s++;
2416 	}
2417 	if (s >= eof) {
2418 		mem_free(*menu);
2419 		return -1;
2420 	}
2421 	if (eof - s >= 2 && (s[1] == '!' || s[1] == '?')) {
2422 		s = skip_comment(s, eof);
2423 		goto se;
2424 	}
2425 	if (parse_element(s, eof, &name, &namelen, &attr, &s)) goto sp;
2426 	if (namelen != 3 || casecmp(name, "MAP", 3)) goto se;
2427 	if (tag && *tag) {
2428 		if (!(al = get_attr_val(attr, "name"))) goto se;
2429 		if (strcasecmp(al, tag)) {
2430 			mem_free(al);
2431 			goto se;
2432 		}
2433 		mem_free(al);
2434 	}
2435 	*ml = getml(NULL);
2436 	se2:
2437 	while (s < eof && *s != '<') {
2438 		sp2:
2439 		s++;
2440 	}
2441 	if (s >= eof) {
2442 		freeml(*ml);
2443 		mem_free(*menu);
2444 		return -1;
2445 	}
2446 	if (eof - s >= 2 && (s[1] == '!' || s[1] == '?')) {
2447 		s = skip_comment(s, eof);
2448 		goto se2;
2449 	}
2450 	if (parse_element(s, eof, &name, &namelen, &attr, &s)) goto sp2;
2451 	if (namelen == 1 && !casecmp(name, "A", 1)) {
2452 		unsigned char *ss;
2453 		label = init_str();
2454 		lblen = 0;
2455 		se3:
2456 		ss = s;
2457 		se4:
2458 		while (ss < eof && *ss != '<') ss++;
2459 		if (ss >= eof) {
2460 			mem_free(label);
2461 			freeml(*ml);
2462 			mem_free(*menu);
2463 			return -1;
2464 		}
2465 		add_bytes_to_str(&label, &lblen, s, ss - s);
2466 		s = ss;
2467 		if (eof - s >= 2 && (s[1] == '!' || s[1] == '?')) {
2468 			s = skip_comment(s, eof);
2469 			goto se3;
2470 		}
2471 		if (parse_element(s, eof, NULL, NULL, NULL, &ss)) {
2472 			ss = s + 1;
2473 			goto se4;
2474 		}
2475 		if (!((namelen == 1 && !casecmp(name, "A", 1)) ||
2476 		      (namelen == 2 && !casecmp(name, "/A", 2)) ||
2477 		      (namelen == 3 && !casecmp(name, "MAP", 3)) ||
2478 		      (namelen == 4 && !casecmp(name, "/MAP", 4)) ||
2479 		      (namelen == 4 && !casecmp(name, "AREA", 4)) ||
2480 		      (namelen == 5 && !casecmp(name, "/AREA", 5)))) {
2481 				s = ss;
2482 				goto se3;
2483 		}
2484 	} else if (namelen == 4 && !casecmp(name, "AREA", 4)) {
2485 		unsigned char *l = get_attr_val(attr, "alt");
2486 		if (l) label = convert_string(ct, l, strlen(l)), mem_free(l);
2487 		else label = NULL;
2488 	} else if (namelen == 4 && !casecmp(name, "/MAP", 4)) goto done;
2489 	else goto se2;
2490 	if (!(href = get_url_val(attr, "href"))) {
2491 		if (label) mem_free(label);
2492 		goto se2;
2493 	}
2494 	if (!(target = get_target(attr)) && !(target = stracpy(target_base)))
2495 		target = stracpy("");
2496 	ld = mem_alloc(sizeof(struct link_def));
2497 	if (!(ld->link = join_urls(href_base, href))) {
2498 		mem_free(href);
2499 		mem_free(target);
2500 		f:
2501 		mem_free(ld);
2502 		if (label) mem_free(label);
2503 		goto se2;
2504 	}
2505 	mem_free(href);
2506 	ld->target = target;
2507 	for (i = 0; i < nmenu; i++) {
2508 		struct link_def *ll = (*menu)[i].data;
2509 		if (!strcmp(ll->link, ld->link) && !strcmp(ll->target, ld->target)) {
2510 			mem_free(ld->link);
2511 			mem_free(ld->target);
2512 			goto f;
2513 		}
2514 	}
2515 	if (label) clr_spaces(label);
2516 	if (label && !*label) mem_free(label), label = NULL;
2517 	if (!label) label = stracpy(ld->link);
2518 	if ((unsigned)nmenu > MAXINT / sizeof(struct menu_item) - 2) overalloc();
2519 	nm = mem_realloc(*menu, (nmenu + 2) * sizeof(struct menu_item));
2520 	*menu = nm;
2521 	memset(&nm[nmenu], 0, 2 * sizeof(struct menu_item));
2522 	nm[nmenu].text = label;
2523 	nm[nmenu].rtext = "";
2524 	nm[nmenu].hotkey = "";
2525 	nm[nmenu].func = MENU_FUNC map_selected;
2526 	nm[nmenu].data = ld;
2527 	nm[++nmenu].text = NULL;
2528 	add_to_ml(ml, ld, ld->link, ld->target, label, NULL);
2529 	goto se2;
2530 	done:
2531 	add_to_ml(ml, *menu, NULL);
2532 	return 0;
2533 }
2534 
scan_http_equiv(unsigned char * s,unsigned char * eof,unsigned char ** head,int * hdl,unsigned char ** title)2535 void scan_http_equiv(unsigned char *s, unsigned char *eof, unsigned char **head, int *hdl, unsigned char **title)
2536 {
2537 	unsigned char *name, *attr, *he, *c;
2538 	int namelen;
2539 	int tlen = 0;
2540 	if (title) *title = init_str();
2541 	add_chr_to_str(head, hdl, '\n');
2542 	se:
2543 	while (s < eof && *s != '<') sp:s++;
2544 	if (s >= eof) return;
2545 	if (eof - s >= 2 && (s[1] == '!' || s[1] == '?')) {
2546 		s = skip_comment(s, eof);
2547 		goto se;
2548 	}
2549 	if (parse_element(s, eof, &name, &namelen, &attr, &s)) goto sp;
2550 	ps:
2551 	if (namelen == 5 && !casecmp(name, "/HEAD", 5)) return;
2552 	if (title && !tlen && namelen == 5 && !casecmp(name, "TITLE", 5)) {
2553 		unsigned char *s1;
2554 		xse:
2555 		s1 = s;
2556 		while (s < eof && *s != '<') xsp:s++;
2557 		add_bytes_to_str(title, &tlen, s1, s - s1);
2558 		if (s >= eof) goto se;
2559 		if (eof - s >= 2 && (s[1] == '!' || s[1] == '?')) {
2560 			s = skip_comment(s, eof);
2561 			goto xse;
2562 		}
2563 		if (parse_element(s, eof, &name, &namelen, &attr, &s)) {
2564 			s1 = s;
2565 			goto xsp;
2566 		}
2567 		clr_spaces(*title);
2568 		goto ps;
2569 	}
2570 	if (namelen != 4 || casecmp(name, "META", 4)) goto se;
2571 	if ((he = get_attr_val(attr, "charset"))) {
2572 		add_to_str(head, hdl, "Charset: ");
2573 		add_to_str(head, hdl, he);
2574 		add_to_str(head, hdl, "\r\n");
2575 		mem_free(he);
2576 	}
2577 	if (!(he = get_attr_val(attr, "http-equiv"))) goto se;
2578 	c = get_attr_val(attr, "content");
2579 	add_to_str(head, hdl, he);
2580 	if (c) add_to_str(head, hdl, ": "), add_to_str(head, hdl, c), mem_free(c);
2581 	mem_free(he);
2582 	add_to_str(head, hdl, "\r\n");
2583 	goto se;
2584 }
2585 
2586