3484 lines
98 KiB
C
3484 lines
98 KiB
C
/* html.c
|
|
* (c) 2002 Mikulas Patocka
|
|
* This file is a part of the Links program, released under GPL.
|
|
*/
|
|
|
|
#include "links.h"
|
|
|
|
struct list_head html_stack = {&html_stack, &html_stack};
|
|
|
|
int html_format_changed = 0;
|
|
|
|
static inline int isA(unsigned char c)
|
|
{
|
|
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
|
|
}
|
|
|
|
static inline int atchr(unsigned char c)
|
|
{
|
|
return /*isA(c) ||*/ (c > ' ' && c != '=' && c != '<' && c != '>');
|
|
}
|
|
|
|
/* accepts one html element */
|
|
/* e is pointer to the begining of the element (*e must be '<') */
|
|
/* eof is pointer to the end of scanned area */
|
|
/* parsed element name is stored in name, it's length is namelen */
|
|
/* first attribute is stored in attr */
|
|
/* end points to first character behind the html element */
|
|
/* returns: -1 fail (returned values in pointers are invalid) */
|
|
/* 0 success */
|
|
int parse_element(unsigned char *e, unsigned char *eof, unsigned char **name, int *namelen, unsigned char **attr, unsigned char **end)
|
|
{
|
|
if (eof - e < 3 || *(e++) != '<') return -1;
|
|
if (name) *name = e;
|
|
if (*e == '/') {
|
|
e++;
|
|
if (*e == '>' || *e == '<') goto xx;
|
|
} else if (!isA(*e)) {
|
|
return -1;
|
|
}
|
|
while (isA(*e) || (*e >= '0' && *e <= '9') || *e == '_' || *e == '-' || *e == '=') {
|
|
e++;
|
|
if (e >= eof) return -1;
|
|
}
|
|
xx:
|
|
if (name && namelen) *namelen = (int)(e - *name);
|
|
while ((WHITECHAR(*e) || *e == '/' || *e == ':')) {
|
|
e++;
|
|
if (e >= eof) return -1;
|
|
}
|
|
if ((!atchr(*e) && *e != '>' && *e != '<')) return -1;
|
|
if (attr) *attr = e;
|
|
nextattr:
|
|
while (WHITECHAR(*e)) {
|
|
e++;
|
|
if (e >= eof) return -1;
|
|
}
|
|
if ((!atchr(*e) && *e != '>' && *e != '<')) return -1;
|
|
if (*e == '>' || *e == '<') goto en;
|
|
while (atchr(*e)) {
|
|
e++;
|
|
if (e >= eof) return -1;
|
|
}
|
|
while (WHITECHAR(*e)) {
|
|
e++;
|
|
if (e >= eof) return -1;
|
|
}
|
|
if (*e != '=') goto endattr;
|
|
if (1) goto x2;
|
|
while (WHITECHAR(*e)) {
|
|
x2:
|
|
e++;
|
|
if (e >= eof) return -1;
|
|
}
|
|
if (U(*e)) {
|
|
unsigned char uu = *e;
|
|
/*u:*/
|
|
if (1) goto x3;
|
|
while (e < eof && *e != uu && *e /*(WHITECHAR(*e) || *e > ' ')*/) {
|
|
x3:
|
|
e++;
|
|
if (e >= eof) return -1;
|
|
}
|
|
if (*e < ' ') return -1;
|
|
e++;
|
|
if (e >= eof /*|| (!WHITECHAR(*e) && *e != uu && *e != '>' && *e != '<')*/) return -1;
|
|
/*if (*e == uu) goto u;*/
|
|
} else {
|
|
while (!WHITECHAR(*e) && *e != '>' && *e != '<') {
|
|
e++;
|
|
if (e >= eof) return -1;
|
|
}
|
|
}
|
|
while (WHITECHAR(*e)) {
|
|
e++;
|
|
if (e >= eof) return -1;
|
|
}
|
|
endattr:
|
|
if (*e != '>' && *e != '<') goto nextattr;
|
|
en:
|
|
if (e[-1] == '\\') return -1;
|
|
if (end) *end = e + (*e == '>');
|
|
return 0;
|
|
}
|
|
|
|
#define add_chr(s, l, c) \
|
|
do { \
|
|
if (!((l) & (32 - 1))) { \
|
|
if ((unsigned)(l) > MAXINT - 32) overalloc(); \
|
|
(s) = mem_realloc((s), (l) + 32); \
|
|
} \
|
|
(s)[(l)++] = (c); \
|
|
} while (0)
|
|
|
|
int get_attr_val_nl = 0;
|
|
|
|
/* parses html element attributes */
|
|
/* e is attr pointer previously got from parse_element, DON'T PASS HERE ANY OTHER VALUE!!! */
|
|
/* name is searched attribute */
|
|
/* returns allocated string containing the attribute, or NULL on unsuccess */
|
|
unsigned char *get_attr_val(unsigned char *e, unsigned char *name)
|
|
{
|
|
unsigned char *n;
|
|
unsigned char *a = DUMMY;
|
|
int l = 0;
|
|
int f;
|
|
aa:
|
|
while (WHITECHAR(*e)) e++;
|
|
if (*e == '>' || *e == '<') return NULL;
|
|
n = name;
|
|
while (*n && upcase(*e) == upcase(*n)) e++, n++;
|
|
f = *n;
|
|
while (atchr(*e)) f = 1, e++;
|
|
while (WHITECHAR(*e)) e++;
|
|
if (*e != '=') goto ea;
|
|
e++;
|
|
while (WHITECHAR(*e)) e++;
|
|
if (!U(*e)) {
|
|
while (!WHITECHAR(*e) && *e != '>' && *e != '<') {
|
|
if (!f) add_chr(a, l, *e);
|
|
e++;
|
|
}
|
|
} else {
|
|
unsigned char uu = *e;
|
|
/*a:*/
|
|
e++;
|
|
while (*e != uu) {
|
|
if (!*e) {
|
|
mem_free(a);
|
|
return NULL;
|
|
}
|
|
if (!f) {
|
|
if (get_attr_val_nl == 2) goto exact;
|
|
if (*e != 13) {
|
|
if (*e != 9 && *e != 10) exact:add_chr(a, l, *e);
|
|
else if (!get_attr_val_nl) add_chr(a, l, ' ');
|
|
}
|
|
}
|
|
e++;
|
|
}
|
|
e++;
|
|
/*if (*e == uu) {
|
|
if (!f) add_chr(a, l, *e);
|
|
goto a;
|
|
}*/
|
|
}
|
|
ea:
|
|
if (!f) {
|
|
unsigned char *b;
|
|
add_chr(a, l, 0);
|
|
if (strchr(cast_const_char a, '&')) {
|
|
unsigned char *aa = a;
|
|
int c = d_opt->cp;
|
|
d_opt->cp = d_opt->real_cp;
|
|
a = convert_string(NULL, aa, (int)strlen(cast_const_char aa), d_opt);
|
|
d_opt->cp = c;
|
|
mem_free(aa);
|
|
}
|
|
while ((b = cast_uchar strchr(cast_const_char a, 1))) *b = ' ';
|
|
if (get_attr_val_nl != 2) {
|
|
for (b = a; *b == ' '; b++)
|
|
;
|
|
if (b != a) memmove(a, b, strlen(cast_const_char b) + 1);
|
|
for (b = a + strlen(cast_const_char a) - 1; b >= a && *b == ' '; b--) *b = 0;
|
|
}
|
|
set_mem_comment(a, name, (int)strlen(cast_const_char name));
|
|
return a;
|
|
}
|
|
goto aa;
|
|
}
|
|
|
|
int has_attr(unsigned char *e, unsigned char *name)
|
|
{
|
|
unsigned char *a;
|
|
if (!(a = get_attr_val(e, name))) return 0;
|
|
mem_free(a);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
static unsigned char *get_url_val(unsigned char *e, unsigned char *name)
|
|
{
|
|
int n = 0;
|
|
unsigned char *p, *q, *pp;
|
|
if (!(pp = get_attr_val(e, name))) return NULL;
|
|
p = pp; q = pp;
|
|
while (1) {
|
|
if (*p == '#') n = 1;
|
|
if ((*p = *q) != ' ' || n) p++;
|
|
if (!*q) break;
|
|
q++;
|
|
}
|
|
return pp;
|
|
}
|
|
*/
|
|
|
|
static unsigned char *get_url_val(unsigned char *e, unsigned char *name)
|
|
{
|
|
unsigned char *a, *p, *c;
|
|
int l;
|
|
get_attr_val_nl = 1;
|
|
a = get_attr_val(e, name);
|
|
get_attr_val_nl = 0;
|
|
if (!a)
|
|
return NULL;
|
|
if (d_opt->real_cp != utf8_table) {
|
|
if (url_non_ascii(a))
|
|
goto need_convert;
|
|
}
|
|
return a;
|
|
|
|
need_convert:
|
|
c = init_str();
|
|
l = 0;
|
|
for (p = a; *p; p++) {
|
|
int u = cp2u(*p, d_opt->real_cp);
|
|
unsigned char *us = encode_utf_8(u);
|
|
add_to_str(&c, &l, us);
|
|
}
|
|
mem_free(a);
|
|
return c;
|
|
}
|
|
|
|
static unsigned char *get_exact_attr_val(unsigned char *e, unsigned char *name)
|
|
{
|
|
unsigned char *a;
|
|
get_attr_val_nl = 2;
|
|
a = get_attr_val(e, name);
|
|
get_attr_val_nl = 0;
|
|
if (a) {
|
|
unsigned char *x1, *x2;
|
|
for (x1 = x2 = a; *x1; x1++, x2++) {
|
|
if (x1[0] == '\r') {
|
|
*x2 = '\n';
|
|
if (x1[1] == '\n') x1++;
|
|
} else {
|
|
*x2 = *x1;
|
|
}
|
|
}
|
|
*x2 = 0;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
static struct {
|
|
unsigned short int n;
|
|
char *s;
|
|
} roman_tbl[] = {
|
|
{1000, "m"},
|
|
{999, "im"},
|
|
/* {995, "vm"},*/
|
|
{990, "xm"},
|
|
/* {950, "lm"},*/
|
|
{900, "cm"},
|
|
{500, "d"},
|
|
{499, "id"},
|
|
/* {495, "vd"},*/
|
|
{490, "xd"},
|
|
/* {450, "ld"},*/
|
|
{400, "cd"},
|
|
{100, "c"},
|
|
{99, "ic"},
|
|
/* {95, "vc"},*/
|
|
{90, "xc"},
|
|
{50, "l"},
|
|
{49, "il"},
|
|
/* {45, "vl"},*/
|
|
{40, "xl"},
|
|
{10, "x"},
|
|
{9, "ix"},
|
|
{5, "v"},
|
|
{4, "iv"},
|
|
{1, "i"},
|
|
{0, NULL}
|
|
};
|
|
|
|
static void roman(unsigned char *p, unsigned n)
|
|
{
|
|
int i = 0;
|
|
if (n >= 4000) {
|
|
strcpy(cast_char p, "---");
|
|
return;
|
|
}
|
|
if (!n) {
|
|
strcpy(cast_char p, "o");
|
|
return;
|
|
}
|
|
p[0] = 0;
|
|
while (n) {
|
|
while (roman_tbl[i].n <= n) {
|
|
n -= roman_tbl[i].n;
|
|
strcat(cast_char p, cast_const_char roman_tbl[i].s);
|
|
}
|
|
i++;
|
|
if (n && !roman_tbl[i].n) {
|
|
internal_error("BUG in roman number convertor");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct color_spec {
|
|
char *name;
|
|
int rgb;
|
|
};
|
|
|
|
static_const struct color_spec color_specs[] = {
|
|
{"aliceblue", 0xF0F8FF},
|
|
{"antiquewhite", 0xFAEBD7},
|
|
{"aqua", 0x00FFFF},
|
|
{"aquamarine", 0x7FFFD4},
|
|
{"azure", 0xF0FFFF},
|
|
{"beige", 0xF5F5DC},
|
|
{"bisque", 0xFFE4C4},
|
|
{"black", 0x000000},
|
|
{"blanchedalmond", 0xFFEBCD},
|
|
{"blue", 0x0000FF},
|
|
{"blueviolet", 0x8A2BE2},
|
|
{"brown", 0xA52A2A},
|
|
{"burlywood", 0xDEB887},
|
|
{"cadetblue", 0x5F9EA0},
|
|
{"chartreuse", 0x7FFF00},
|
|
{"chocolate", 0xD2691E},
|
|
{"coral", 0xFF7F50},
|
|
{"cornflowerblue", 0x6495ED},
|
|
{"cornsilk", 0xFFF8DC},
|
|
{"crimson", 0xDC143C},
|
|
{"cyan", 0x00FFFF},
|
|
{"darkblue", 0x00008B},
|
|
{"darkcyan", 0x008B8B},
|
|
{"darkgoldenrod", 0xB8860B},
|
|
{"darkgray", 0xA9A9A9},
|
|
{"darkgreen", 0x006400},
|
|
{"darkkhaki", 0xBDB76B},
|
|
{"darkmagenta", 0x8B008B},
|
|
{"darkolivegreen", 0x556B2F},
|
|
{"darkorange", 0xFF8C00},
|
|
{"darkorchid", 0x9932CC},
|
|
{"darkred", 0x8B0000},
|
|
{"darksalmon", 0xE9967A},
|
|
{"darkseagreen", 0x8FBC8F},
|
|
{"darkslateblue", 0x483D8B},
|
|
{"darkslategray", 0x2F4F4F},
|
|
{"darkturquoise", 0x00CED1},
|
|
{"darkviolet", 0x9400D3},
|
|
{"deeppink", 0xFF1493},
|
|
{"deepskyblue", 0x00BFFF},
|
|
{"dimgray", 0x696969},
|
|
{"dodgerblue", 0x1E90FF},
|
|
{"firebrick", 0xB22222},
|
|
{"floralwhite", 0xFFFAF0},
|
|
{"forestgreen", 0x228B22},
|
|
{"fuchsia", 0xFF00FF},
|
|
{"gainsboro", 0xDCDCDC},
|
|
{"ghostwhite", 0xF8F8FF},
|
|
{"gold", 0xFFD700},
|
|
{"goldenrod", 0xDAA520},
|
|
{"gray", 0x808080},
|
|
{"green", 0x008000},
|
|
{"greenyellow", 0xADFF2F},
|
|
{"honeydew", 0xF0FFF0},
|
|
{"hotpink", 0xFF69B4},
|
|
{"indianred", 0xCD5C5C},
|
|
{"indigo", 0x4B0082},
|
|
{"ivory", 0xFFFFF0},
|
|
{"khaki", 0xF0E68C},
|
|
{"lavender", 0xE6E6FA},
|
|
{"lavenderblush", 0xFFF0F5},
|
|
{"lawngreen", 0x7CFC00},
|
|
{"lemonchiffon", 0xFFFACD},
|
|
{"lightblue", 0xADD8E6},
|
|
{"lightcoral", 0xF08080},
|
|
{"lightcyan", 0xE0FFFF},
|
|
{"lightgoldenrodyellow", 0xFAFAD2},
|
|
{"lightgreen", 0x90EE90},
|
|
{"lightgrey", 0xD3D3D3},
|
|
{"lightpink", 0xFFB6C1},
|
|
{"lightsalmon", 0xFFA07A},
|
|
{"lightseagreen", 0x20B2AA},
|
|
{"lightskyblue", 0x87CEFA},
|
|
{"lightslategray", 0x778899},
|
|
{"lightsteelblue", 0xB0C4DE},
|
|
{"lightyellow", 0xFFFFE0},
|
|
{"lime", 0x00FF00},
|
|
{"limegreen", 0x32CD32},
|
|
{"linen", 0xFAF0E6},
|
|
{"magenta", 0xFF00FF},
|
|
{"maroon", 0x800000},
|
|
{"mediumaquamarine", 0x66CDAA},
|
|
{"mediumblue", 0x0000CD},
|
|
{"mediumorchid", 0xBA55D3},
|
|
{"mediumpurple", 0x9370DB},
|
|
{"mediumseagreen", 0x3CB371},
|
|
{"mediumslateblue", 0x7B68EE},
|
|
{"mediumspringgreen", 0x00FA9A},
|
|
{"mediumturquoise", 0x48D1CC},
|
|
{"mediumvioletred", 0xC71585},
|
|
{"midnightblue", 0x191970},
|
|
{"mintcream", 0xF5FFFA},
|
|
{"mistyrose", 0xFFE4E1},
|
|
{"moccasin", 0xFFE4B5},
|
|
{"navajowhite", 0xFFDEAD},
|
|
{"navy", 0x000080},
|
|
{"oldlace", 0xFDF5E6},
|
|
{"olive", 0x808000},
|
|
{"olivedrab", 0x6B8E23},
|
|
{"orange", 0xFFA500},
|
|
{"orangered", 0xFF4500},
|
|
{"orchid", 0xDA70D6},
|
|
{"palegoldenrod", 0xEEE8AA},
|
|
{"palegreen", 0x98FB98},
|
|
{"paleturquoise", 0xAFEEEE},
|
|
{"palevioletred", 0xDB7093},
|
|
{"papayawhip", 0xFFEFD5},
|
|
{"peachpuff", 0xFFDAB9},
|
|
{"peru", 0xCD853F},
|
|
{"pink", 0xFFC0CB},
|
|
{"plum", 0xDDA0DD},
|
|
{"powderblue", 0xB0E0E6},
|
|
{"purple", 0x800080},
|
|
{"red", 0xFF0000},
|
|
{"rosybrown", 0xBC8F8F},
|
|
{"royalblue", 0x4169E1},
|
|
{"saddlebrown", 0x8B4513},
|
|
{"salmon", 0xFA8072},
|
|
{"sandybrown", 0xF4A460},
|
|
{"seagreen", 0x2E8B57},
|
|
{"seashell", 0xFFF5EE},
|
|
{"sienna", 0xA0522D},
|
|
{"silver", 0xC0C0C0},
|
|
{"skyblue", 0x87CEEB},
|
|
{"slateblue", 0x6A5ACD},
|
|
{"slategray", 0x708090},
|
|
{"snow", 0xFFFAFA},
|
|
{"springgreen", 0x00FF7F},
|
|
{"steelblue", 0x4682B4},
|
|
{"tan", 0xD2B48C},
|
|
{"teal", 0x008080},
|
|
{"thistle", 0xD8BFD8},
|
|
{"tomato", 0xFF6347},
|
|
{"turquoise", 0x40E0D0},
|
|
{"violet", 0xEE82EE},
|
|
{"wheat", 0xF5DEB3},
|
|
{"white", 0xFFFFFF},
|
|
{"whitesmoke", 0xF5F5F5},
|
|
{"yellow", 0xFFFF00},
|
|
{"yellowgreen", 0x9ACD32},
|
|
};
|
|
|
|
#define endof(T) ((T) + array_elements(T))
|
|
|
|
int decode_color(unsigned char *str, struct rgb *col)
|
|
{
|
|
unsigned long ch;
|
|
char *end;
|
|
if (*str != '#') {
|
|
const struct color_spec *cs;
|
|
for (cs = color_specs; cs < endof(color_specs); cs++)
|
|
if (!casestrcmp(cast_uchar cs->name, str)) {
|
|
ch = cs->rgb;
|
|
goto found;
|
|
}
|
|
} else {
|
|
str++;
|
|
}
|
|
if (strlen(cast_const_char str) == 6) {
|
|
ch = strtoul(cast_const_char str, &end, 16);
|
|
if (!*end && ch < 0x1000000) {
|
|
found:
|
|
memset(col, 0, sizeof(struct rgb));
|
|
col->r = (unsigned)ch / 0x10000;
|
|
col->g = (unsigned)ch / 0x100 % 0x100;
|
|
col->b = (unsigned)ch % 0x100;
|
|
return 0;
|
|
}
|
|
}
|
|
if (strlen(cast_const_char str) == 3) {
|
|
ch = strtoul(cast_const_char str, &end, 16);
|
|
if (!*end && ch < 0x1000) {
|
|
memset(col, 0, sizeof(struct rgb));
|
|
col->r = ((unsigned)ch / 0x100) * 0x11;
|
|
col->g = ((unsigned)ch / 0x10 % 0x10) * 0x11;
|
|
col->b = ((unsigned)ch % 0x10) * 0x11;
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int get_color(unsigned char *a, unsigned char *c, struct rgb *rgb)
|
|
{
|
|
unsigned char *at;
|
|
int r = -1;
|
|
if (d_opt->col >= 1) if ((at = get_attr_val(a, c))) {
|
|
r = decode_color(at, rgb);
|
|
mem_free(at);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
int get_bgcolor(unsigned char *a, struct rgb *rgb)
|
|
{
|
|
if (d_opt->col < 2) return -1;
|
|
return get_color(a, cast_uchar "bgcolor", rgb);
|
|
}
|
|
|
|
static unsigned char *get_target(unsigned char *a)
|
|
{
|
|
return get_attr_val(a, cast_uchar "target");
|
|
}
|
|
|
|
void kill_html_stack_item(struct html_element *e)
|
|
{
|
|
if (!e || (void *)e == &html_stack) {
|
|
internal_error("trying to free bad html element");
|
|
return;
|
|
}
|
|
if (e->dontkill == 2) {
|
|
internal_error("trying to kill unkillable element");
|
|
return;
|
|
}
|
|
html_format_changed = 1;
|
|
if (e->attr.fontface) mem_free(e->attr.fontface);
|
|
if (e->attr.link) mem_free(e->attr.link);
|
|
if (e->attr.target) mem_free(e->attr.target);
|
|
if (e->attr.image) mem_free(e->attr.image);
|
|
if (e->attr.href_base) mem_free(e->attr.href_base);
|
|
if (e->attr.target_base) mem_free(e->attr.target_base);
|
|
if (e->attr.select) mem_free(e->attr.select);
|
|
free_js_event_spec(e->attr.js_event);
|
|
del_from_list(e);
|
|
mem_free(e);
|
|
}
|
|
|
|
#if defined(DEBUG) && 0
|
|
void debug_stack(void)
|
|
{
|
|
struct html_element *e;
|
|
struct list_head *le;
|
|
printf("HTML stack debug: \n");
|
|
foreachback(struct html_element, e, le, html_stack) {
|
|
int i;
|
|
printf("\"");
|
|
for (i = 0; i < e->namelen; i++) printf("%c", e->name[i]);
|
|
printf("\"\n");
|
|
}
|
|
printf("%c", 7);
|
|
fflush(stdout);
|
|
portable_sleep(1000);
|
|
}
|
|
#endif
|
|
|
|
void html_stack_dup(void)
|
|
{
|
|
struct html_element *e;
|
|
struct html_element *ep;
|
|
html_format_changed = 1;
|
|
ep = &html_top;
|
|
e = mem_alloc(sizeof(struct html_element));
|
|
memcpy(e, ep, sizeof(struct html_element));
|
|
e->attr.fontface = stracpy(ep->attr.fontface);
|
|
e->attr.link = stracpy(ep->attr.link);
|
|
e->attr.target = stracpy(ep->attr.target);
|
|
e->attr.image = stracpy(ep->attr.image);
|
|
e->attr.href_base = stracpy(ep->attr.href_base);
|
|
e->attr.target_base = stracpy(ep->attr.target_base);
|
|
e->attr.select = stracpy(ep->attr.select);
|
|
copy_js_event_spec(&e->attr.js_event, ep->attr.js_event);
|
|
/*if (e->name) {
|
|
if (e->attr.link) set_mem_comment(e->attr.link, e->name, e->namelen);
|
|
if (e->attr.target) set_mem_comment(e->attr.target, e->name, e->namelen);
|
|
if (e->attr.image) set_mem_comment(e->attr.image, e->name, e->namelen);
|
|
if (e->attr.href_base) set_mem_comment(e->attr.href_base, e->name, e->namelen);
|
|
if (e->attr.target_base) set_mem_comment(e->attr.target_base, e->name, e->namelen);
|
|
if (e->attr.select) set_mem_comment(e->attr.select, e->name, e->namelen);
|
|
}*/
|
|
e->name = e->options = NULL;
|
|
e->namelen = 0;
|
|
e->dontkill = 0;
|
|
add_to_list(html_stack, e);
|
|
}
|
|
|
|
|
|
#ifdef JS
|
|
static void get_js_event(unsigned char *a, unsigned char *name, unsigned char **where)
|
|
{
|
|
unsigned char *v;
|
|
if ((v = get_attr_val(a, name))) {
|
|
if (*where) mem_free(*where);
|
|
*where = v;
|
|
}
|
|
}
|
|
|
|
static int get_js_events_x(struct js_event_spec **spec, unsigned char *a)
|
|
{
|
|
if (!has_attr(a, cast_uchar "onkeyup") && !has_attr(a, cast_uchar "onkeydown") && !has_attr(a,cast_uchar "onkeypress") && !has_attr(a,cast_uchar "onchange") && !has_attr(a, cast_uchar "onfocus") && !has_attr(a,cast_uchar "onblur") && !has_attr(a, cast_uchar "onclick") && !has_attr(a, cast_uchar "ondblclick") && !has_attr(a, cast_uchar "onmousedown") && !has_attr(a, cast_uchar "onmousemove") && !has_attr(a, cast_uchar "onmouseout") && !has_attr(a, cast_uchar "onmouseover") && !has_attr(a, cast_uchar "onmouseup")) return 0;
|
|
create_js_event_spec(spec);
|
|
get_js_event(a, cast_uchar "onclick", &(*spec)->click_code);
|
|
get_js_event(a, cast_uchar "ondblclick", &(*spec)->dbl_code);
|
|
get_js_event(a, cast_uchar "onmousedown", &(*spec)->down_code);
|
|
get_js_event(a, cast_uchar "onmouseup", &(*spec)->up_code);
|
|
get_js_event(a, cast_uchar "onmouseover", &(*spec)->over_code);
|
|
get_js_event(a, cast_uchar "onmouseout", &(*spec)->out_code);
|
|
get_js_event(a, cast_uchar "onmousemove", &(*spec)->move_code);
|
|
get_js_event(a, cast_uchar "onfocus", &(*spec)->focus_code);
|
|
get_js_event(a, cast_uchar "onblur", &(*spec)->blur_code);
|
|
get_js_event(a, cast_uchar "onchange", &(*spec)->change_code);
|
|
get_js_event(a, cast_uchar "onkeypress", &(*spec)->keypress_code);
|
|
get_js_event(a, cast_uchar "onkeyup", &(*spec)->keyup_code);
|
|
get_js_event(a, cast_uchar "onkeydown", &(*spec)->keydown_code);
|
|
return 1;
|
|
}
|
|
|
|
static int get_js_events(unsigned char *a)
|
|
{
|
|
return get_js_events_x(&format_.js_event, a);
|
|
}
|
|
#else
|
|
static int get_js_events_x(struct js_event_spec **spec, unsigned char *a)
|
|
{
|
|
return 0;
|
|
}
|
|
static int get_js_events(unsigned char *a)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
void *ff;
|
|
void (*put_chars_f)(void *, unsigned char *, int);
|
|
void (*line_break_f)(void *);
|
|
void *(*special_f)(void *, int, ...);
|
|
|
|
static unsigned char *eoff;
|
|
unsigned char *eofff;
|
|
unsigned char *startf;
|
|
|
|
int line_breax;
|
|
static int pos;
|
|
static int putsp;
|
|
|
|
static int was_br;
|
|
int table_level;
|
|
int empty_format;
|
|
|
|
static void ln_break(int n)
|
|
{
|
|
if (!n || html_top.invisible) return;
|
|
while (n > line_breax) line_breax++, line_break_f(ff);
|
|
pos = 0;
|
|
putsp = -1;
|
|
}
|
|
|
|
#define CH_BUF 256
|
|
#define BUF_RESERVE 6
|
|
|
|
static int put_chars_conv(unsigned char *c, int l)
|
|
{
|
|
static unsigned char buffer[CH_BUF];
|
|
int bp = 0;
|
|
int pp = 0;
|
|
int total = 0;
|
|
if (format_.attr & AT_GRAPHICS) {
|
|
put_chars_f(ff, c, l);
|
|
return l;
|
|
}
|
|
if (!l) put_chars_f(ff, NULL, 0);
|
|
while (pp < l) {
|
|
int sl;
|
|
unsigned char *e = NULL; /* against warning */
|
|
if (c[pp] < 128 && c[pp] != '&') {
|
|
put_c:
|
|
if (bp > CH_BUF - BUF_RESERVE && c[pp] >= 0xc0) goto flush;
|
|
if (!(buffer[bp++] = c[pp++])) buffer[bp - 1] = ' ';
|
|
if ((buffer[bp - 1] != ' ' || par_format.align == AL_NO || par_format.align == AL_NO_BREAKABLE) && bp < CH_BUF) continue;
|
|
goto flush;
|
|
}
|
|
if (c[pp] != '&') {
|
|
struct conv_table *t;
|
|
int i;
|
|
if (l - pp >= 3 && c[pp] == 0xef && c[pp + 1] == 0xbb && c[pp + 2] == 0xbf && d_opt->real_cp == utf8_table) {
|
|
pp += 3;
|
|
continue;
|
|
}
|
|
if ((d_opt->real_cp == d_opt->cp && d_opt->real_cp == utf8_table) || !convert_table) goto put_c;
|
|
t = convert_table;
|
|
i = pp;
|
|
decode:
|
|
if (!t[c[i]].t) {
|
|
e = t[c[i]].u.str;
|
|
} else {
|
|
t = t[c[i++]].u.tbl;
|
|
if (i >= l) goto put_c;
|
|
goto decode;
|
|
}
|
|
pp = i + 1;
|
|
} else {
|
|
int i = pp + 1;
|
|
if (d_opt->plain & 1) goto put_c;
|
|
while (i < l && !is_entity_terminator(c[i])) i++;
|
|
if (!(e = get_entity_string(&c[pp + 1], i - pp - 1, d_opt->cp))) goto put_c;
|
|
pp = i + (i < l && c[i] == ';');
|
|
}
|
|
if (!e[0]) continue;
|
|
if (!e[1]) {
|
|
buffer[bp++] = e[0];
|
|
if (bp < CH_BUF) continue;
|
|
flush:
|
|
e = cast_uchar "";
|
|
goto flush1;
|
|
}
|
|
sl = (int)strlen(cast_const_char e);
|
|
if (sl > BUF_RESERVE) {
|
|
e = cast_uchar "";
|
|
sl = 0;
|
|
}
|
|
if (bp + sl > CH_BUF) {
|
|
flush1:
|
|
put_chars_f(ff, buffer, bp);
|
|
if (d_opt->cp == utf8_table) {
|
|
while (bp) if ((buffer[--bp] & 0xc0) != 0x80) total++;
|
|
} else {
|
|
total += bp;
|
|
bp = 0;
|
|
}
|
|
}
|
|
while (*e) {
|
|
buffer[bp++] = *(e++);
|
|
}
|
|
if (bp == CH_BUF) goto flush;
|
|
}
|
|
if (bp) put_chars_f(ff, buffer, bp);
|
|
if (d_opt->cp == utf8_table) {
|
|
while (bp) if ((buffer[--bp] & 0xc0) != 0x80) total++;
|
|
} else {
|
|
total += bp;
|
|
}
|
|
return total;
|
|
}
|
|
|
|
static void put_chrs(unsigned char *start, int len)
|
|
{
|
|
if (par_format.align == AL_NO || par_format.align == AL_NO_BREAKABLE) putsp = 0;
|
|
if (!len || html_top.invisible) return;
|
|
if (putsp == 1) pos += put_chars_conv(cast_uchar " ", 1), putsp = -1;
|
|
if (putsp == -1) {
|
|
if (start[0] == ' ') start++, len--;
|
|
putsp = 0;
|
|
}
|
|
if (!len) {
|
|
putsp = -1;
|
|
if (par_format.align == AL_NO || par_format.align == AL_NO_BREAKABLE) putsp = 0;
|
|
return;
|
|
}
|
|
if (start[len - 1] == ' ') putsp = -1;
|
|
if (par_format.align == AL_NO || par_format.align == AL_NO_BREAKABLE) putsp = 0;
|
|
was_br = 0;
|
|
pos += put_chars_conv(start, len);
|
|
line_breax = 0;
|
|
}
|
|
|
|
static void kill_until(int ls, ...)
|
|
{
|
|
int l;
|
|
struct list_head *e = &html_top.list_entry;
|
|
if (ls) e = e->next;
|
|
while (e != &html_stack) {
|
|
struct html_element *he = list_struct(e, struct html_element);
|
|
int sk = 0;
|
|
va_list arg;
|
|
va_start(arg, ls);
|
|
while (1) {
|
|
unsigned char *s = va_arg(arg, unsigned char *);
|
|
if (!s) break;
|
|
if (!*s) sk++;
|
|
else if ((size_t)he->namelen == strlen(cast_const_char s) && !casecmp(he->name, s, strlen(cast_const_char s))) {
|
|
if (!sk) {
|
|
if (he->dontkill) break;
|
|
va_end(arg);
|
|
goto killll;
|
|
}
|
|
else if (sk == 1) {
|
|
va_end(arg);
|
|
goto killl;
|
|
} else break;
|
|
}
|
|
}
|
|
va_end(arg);
|
|
if (he->dontkill || (he->namelen == 5 && !casecmp(he->name, cast_uchar "TABLE", 5))) break;
|
|
if (he->namelen == 2 && upcase(he->name[0]) == 'T' && (upcase(he->name[1]) == 'D' || upcase(he->name[1]) == 'H' || upcase(he->name[1]) == 'R')) break;
|
|
e = e->next;
|
|
}
|
|
return;
|
|
killl:
|
|
e = e->prev;
|
|
killll:
|
|
l = 0;
|
|
while (e != &html_stack) {
|
|
struct html_element *he = list_struct(e, struct html_element);
|
|
if (ls && e == html_stack.next) break;
|
|
if (he->linebreak > l) l = he->linebreak;
|
|
e = e->prev;
|
|
kill_html_stack_item(he);
|
|
}
|
|
ln_break(l);
|
|
}
|
|
|
|
static inline unsigned char *top_href_base(void)
|
|
{
|
|
return list_struct(html_stack.prev, struct html_element)->attr.href_base;
|
|
}
|
|
|
|
int get_num(unsigned char *a, unsigned char *n)
|
|
{
|
|
unsigned char *al;
|
|
if ((al = get_attr_val(a, n))) {
|
|
char *end;
|
|
unsigned long s = strtoul(cast_const_char al, &end, 10);
|
|
if (!*al || *end || s > 10000) s = -1;
|
|
mem_free(al);
|
|
return (int)s;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* trunc somehow clips the maximum values. Use 0 to disable truncastion. */
|
|
static int parse_width(unsigned char *w, int trunc)
|
|
{
|
|
char *end;
|
|
int p = 0;
|
|
long s;
|
|
int l;
|
|
int limit = par_format.width - (par_format.leftmargin + par_format.rightmargin) * gf_val(1, G_HTML_MARGIN);
|
|
while (WHITECHAR(*w)) w++;
|
|
for (l = 0; w[l] && w[l] != ','; l++)
|
|
;
|
|
while (l && WHITECHAR(w[l - 1])) l--;
|
|
if (!l) return -1;
|
|
if (w[l - 1] == '%') l--, p = 1;
|
|
while (l && WHITECHAR(w[l - 1])) l--;
|
|
if (!l) return -1;
|
|
s = strtoul(cast_const_char w, &end, 10);
|
|
if ((unsigned char *)end - w < l || s < 0 || s > 10000) return -1;
|
|
if (p) {
|
|
if (trunc) {
|
|
#ifdef G
|
|
if (trunc == 3) {
|
|
return -1;
|
|
/*
|
|
limit = d_opt->yw - G_SCROLL_BAR_WIDTH;
|
|
if (limit < 0) limit = 0;
|
|
*/
|
|
}
|
|
#endif
|
|
s = s * limit / 100;
|
|
}
|
|
else return -1;
|
|
} else s = (s + (gf_val(HTML_CHAR_WIDTH, 1) - 1) / 2) / gf_val(HTML_CHAR_WIDTH, 1);
|
|
if (trunc == 1 && s > limit) s = limit;
|
|
if (s < 0) s = 0;
|
|
return (int)s;
|
|
}
|
|
|
|
/* trunc somehow clips the maximum values. Use 0 to disable truncastion. */
|
|
int get_width(unsigned char *a, unsigned char *n, int trunc)
|
|
{
|
|
int r;
|
|
unsigned char *w;
|
|
if (!(w = get_attr_val(a, n))) return -1;
|
|
r = parse_width(w, trunc);
|
|
mem_free(w);
|
|
return r;
|
|
}
|
|
|
|
static unsigned char *find_element_end(unsigned char *a)
|
|
{
|
|
unsigned char *p;
|
|
for (p = a - 1; *p != '<'; p--)
|
|
;
|
|
if (parse_element(p, eoff, NULL, NULL, NULL, &p)) {
|
|
internal_error("parse element failed");
|
|
return a;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
struct form form = { NULL, NULL, NULL, NULL, 0, 0 };
|
|
|
|
unsigned char *last_form_tag;
|
|
unsigned char *last_form_attr;
|
|
unsigned char *last_input_tag;
|
|
|
|
static inline void set_link_attr(void)
|
|
{
|
|
memcpy(!(format_.attr & AT_INVERT) ? &format_.fg : &format_.bg, &format_.clink, sizeof(struct rgb));
|
|
}
|
|
|
|
static void put_link_line(unsigned char *prefix, unsigned char *linkname, unsigned char *link, unsigned char *target)
|
|
{
|
|
if (!casecmp(link, cast_uchar "android-app:", 12))
|
|
return;
|
|
html_stack_dup();
|
|
ln_break(1);
|
|
if (format_.link) mem_free(format_.link), format_.link = NULL;
|
|
if (format_.target) mem_free(format_.target), format_.target = NULL;
|
|
format_.form = NULL;
|
|
put_chrs(prefix, (int)strlen(cast_const_char prefix));
|
|
html_format_changed = 1;
|
|
format_.link = join_urls(format_.href_base, link);
|
|
format_.target = stracpy(target);
|
|
set_link_attr();
|
|
put_chrs(linkname, (int)strlen(cast_const_char linkname));
|
|
ln_break(1);
|
|
kill_html_stack_item(&html_top);
|
|
}
|
|
|
|
static void html_span(unsigned char *a)
|
|
{
|
|
unsigned char *al;
|
|
if ((al = get_attr_val(a, cast_uchar "class"))) {
|
|
if (!strcmp(cast_const_char al, "line-number"))
|
|
ln_break(1);
|
|
if (!strcmp(cast_const_char al, "blob-code-inner")) { /* github hack */
|
|
ln_break(1);
|
|
format_.attr |= AT_FIXED;
|
|
par_format.align = AL_NO;
|
|
}
|
|
|
|
mem_free(al);
|
|
}
|
|
}
|
|
|
|
static void html_bold(unsigned char *a)
|
|
{
|
|
get_js_events(a);
|
|
format_.attr |= AT_BOLD;
|
|
}
|
|
|
|
static void html_italic(unsigned char *a)
|
|
{
|
|
get_js_events(a);
|
|
format_.attr |= AT_ITALIC;
|
|
}
|
|
|
|
static void html_underline(unsigned char *a)
|
|
{
|
|
get_js_events(a);
|
|
format_.attr |= AT_UNDERLINE;
|
|
}
|
|
|
|
static void html_strike(unsigned char *a)
|
|
{
|
|
get_js_events(a);
|
|
format_.attr |= AT_STRIKE;
|
|
}
|
|
|
|
static void html_fixed(unsigned char *a)
|
|
{
|
|
get_js_events(a);
|
|
format_.attr |= AT_FIXED;
|
|
}
|
|
|
|
static void html_invert(unsigned char *a)
|
|
{
|
|
struct rgb rgb;
|
|
get_js_events(a);
|
|
memcpy(&rgb, &format_.fg, sizeof(struct rgb));
|
|
memcpy(&format_.fg, &format_.bg, sizeof(struct rgb));
|
|
memcpy(&format_.bg, &rgb, sizeof(struct rgb));
|
|
format_.attr ^= AT_INVERT;
|
|
}
|
|
|
|
static void html_a(unsigned char *a)
|
|
{
|
|
unsigned char *al;
|
|
|
|
int ev = get_js_events(a);
|
|
|
|
if ((al = get_url_val(a, cast_uchar "href"))) {
|
|
unsigned char *all = al;
|
|
while (all[0] == ' ') all++;
|
|
while (all[0] && all[strlen(cast_const_char all) - 1] == ' ') all[strlen(cast_const_char all) - 1] = 0;
|
|
if (format_.link) mem_free(format_.link);
|
|
format_.link = join_urls(format_.href_base, all);
|
|
mem_free(al);
|
|
if ((al = get_target(a))) {
|
|
if (format_.target) mem_free(format_.target);
|
|
format_.target = al;
|
|
} else {
|
|
if (format_.target) mem_free(format_.target);
|
|
format_.target = stracpy(format_.target_base);
|
|
}
|
|
/*format_.attr ^= AT_BOLD;*/
|
|
set_link_attr();
|
|
} else if (!ev) kill_html_stack_item(&html_top);
|
|
if ((al = get_attr_val(a, cast_uchar "name"))) {
|
|
special_f(ff, SP_TAG, al);
|
|
mem_free(al);
|
|
}
|
|
}
|
|
|
|
static void html_a_special(unsigned char *a, unsigned char *next, unsigned char *eof)
|
|
{
|
|
unsigned char *t;
|
|
if (!format_.link) return;
|
|
while (next < eof && WHITECHAR(*next)) next++;
|
|
if (eof - next >= 4 && next[0] == '<' && next[1] == '/' && upcase(next[2]) == 'A' && next[3] == '>')
|
|
goto ok;
|
|
if (strstr(cast_const_char format_.link, "/raw/")) /* gitlab hack */
|
|
goto ok;
|
|
return;
|
|
|
|
ok:
|
|
if (!has_attr(a, cast_uchar "href")) return;
|
|
t = get_attr_val(a, cast_uchar "title");
|
|
if (!t) return;
|
|
put_chrs(t, (int)strlen(cast_const_char t));
|
|
mem_free(t);
|
|
}
|
|
|
|
static void html_sub(unsigned char *a)
|
|
{
|
|
if (!F) put_chrs(cast_uchar "_", 1);
|
|
get_js_events(a);
|
|
format_.fontsize = 1;
|
|
format_.baseline = -1;
|
|
}
|
|
|
|
static void html_sup(unsigned char *a)
|
|
{
|
|
if (!F) put_chrs(cast_uchar "^", 1);
|
|
get_js_events(a);
|
|
format_.fontsize = 1;
|
|
if (format_.baseline <= 0) format_.baseline = format_.fontsize;
|
|
}
|
|
|
|
|
|
static void html_font(unsigned char *a)
|
|
{
|
|
unsigned char *al;
|
|
if ((al = get_attr_val(a, cast_uchar "size"))) {
|
|
int p = 0;
|
|
unsigned long s;
|
|
unsigned char *nn = al;
|
|
char *end;
|
|
if (*al == '+') p = 1, nn++;
|
|
if (*al == '-') p = -1, nn++;
|
|
s = strtoul(cast_const_char nn, &end, 10);
|
|
if (*nn && !*end) {
|
|
if (s > 7) s = 7;
|
|
if (!p) format_.fontsize = (int)s;
|
|
else format_.fontsize += p * (int)s;
|
|
if (format_.fontsize < 1) format_.fontsize = 1;
|
|
if (format_.fontsize > 7) format_.fontsize = 7;
|
|
}
|
|
mem_free(al);
|
|
}
|
|
get_color(a, cast_uchar "color", &format_.fg);
|
|
}
|
|
|
|
static unsigned char *get_url_val_img(unsigned char *a, unsigned char *name)
|
|
{
|
|
unsigned char *v = get_url_val(a, name);
|
|
if (v && !v[strcspn(cast_const_char v, "./")]) {
|
|
mem_free(v);
|
|
v = NULL;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
static void html_img(unsigned char *a)
|
|
{
|
|
unsigned char *al;
|
|
unsigned char *s;
|
|
unsigned char *orig_link = NULL;
|
|
int ismap, usemap = 0;
|
|
/*put_chrs(cast_uchar " ", 1);*/
|
|
get_js_events(a);
|
|
if ((!F || !d_opt->display_images) && ((al = get_url_val(a, cast_uchar "usemap")))) {
|
|
unsigned char *u;
|
|
usemap = 1;
|
|
html_stack_dup();
|
|
if (format_.link) mem_free(format_.link);
|
|
if (format_.form) format_.form = NULL;
|
|
u = join_urls(*al == '#' ? top_href_base() : format_.href_base, al);
|
|
format_.link = stracpy(cast_uchar "MAP@");
|
|
add_to_strn(&format_.link, u);
|
|
format_.attr |= AT_BOLD;
|
|
mem_free(u);
|
|
mem_free(al);
|
|
}
|
|
ismap = format_.link && (F || !has_attr(a, cast_uchar "usemap")) && has_attr(a, cast_uchar "ismap");
|
|
if (format_.image) mem_free(format_.image), format_.image = NULL;
|
|
if (
|
|
(s = get_url_val_img(a, cast_uchar "data-defer-src")) ||
|
|
(s = get_url_val_img(a, cast_uchar "data-delay_url")) ||
|
|
(s = get_url_val_img(a, cast_uchar "data-full")) ||
|
|
(s = get_url_val_img(a, cast_uchar "data-lazy")) ||
|
|
(s = get_url_val_img(a, cast_uchar "data-lazy-src")) ||
|
|
(s = get_url_val_img(a, cast_uchar "data-li-src")) ||
|
|
(s = get_url_val_img(a, cast_uchar "data-normal")) ||
|
|
(s = get_url_val_img(a, cast_uchar "data-original")) ||
|
|
(s = get_url_val_img(a, cast_uchar "data-small")) ||
|
|
(s = get_url_val_img(a, cast_uchar "data-source")) ||
|
|
(s = get_url_val_img(a, cast_uchar "data-src")) ||
|
|
(s = get_url_val_img(a, cast_uchar "data-thumb")) ||
|
|
(s = get_url_val_img(a, cast_uchar "src")) ||
|
|
(s = get_url_val_img(a, cast_uchar "dynsrc")) ||
|
|
(s = get_url_val_img(a, cast_uchar "data")) ||
|
|
(s = get_url_val_img(a, cast_uchar "content")) ||
|
|
(s = get_url_val(a, cast_uchar "src"))
|
|
) {
|
|
if (!format_.link && d_opt->braille) goto skip_img;
|
|
if (!s[0]) goto skip_img;
|
|
format_.image = join_urls(format_.href_base, s);
|
|
skip_img:
|
|
orig_link = s;
|
|
}
|
|
if (!F || !d_opt->display_images) {
|
|
if ((!(al = get_attr_val(a, cast_uchar "alt")) && !(al = get_attr_val(a, cast_uchar "title"))) || !*al) {
|
|
if (al) mem_free(al);
|
|
if (!d_opt->images && !format_.link) goto ret;
|
|
if (d_opt->image_names && s) {
|
|
unsigned char *ss;
|
|
al = stracpy(cast_uchar "[");
|
|
if (!(ss = cast_uchar strrchr(cast_const_char s, '/'))) ss = s;
|
|
else ss++;
|
|
add_to_strn(&al, ss);
|
|
if ((ss = cast_uchar strchr(cast_const_char al, '?'))) *ss = 0;
|
|
if ((ss = cast_uchar strchr(cast_const_char al, '&'))) *ss = 0;
|
|
add_to_strn(&al, cast_uchar "]");
|
|
} else if (usemap) al = stracpy(cast_uchar "[USEMAP]");
|
|
else if (ismap) al = stracpy(cast_uchar "[ISMAP]");
|
|
else al = stracpy(cast_uchar "[IMG]");
|
|
}
|
|
if (al) {
|
|
if (ismap) {
|
|
unsigned char *h;
|
|
html_stack_dup();
|
|
h = stracpy(format_.link);
|
|
add_to_strn(&h, cast_uchar "?0,0");
|
|
mem_free(format_.link);
|
|
format_.link = h;
|
|
}
|
|
html_format_changed = 1;
|
|
put_chrs(al, (int)strlen(cast_const_char al));
|
|
if (ismap) kill_html_stack_item(&html_top);
|
|
}
|
|
mem_free(al);
|
|
#ifdef G
|
|
} else {
|
|
struct image_description i;
|
|
unsigned char *al;
|
|
unsigned char *u;
|
|
int aa = -1;
|
|
|
|
if ((al = get_attr_val(a, cast_uchar "align"))) {
|
|
if (!casestrcmp(al, cast_uchar "left")) aa = AL_LEFT;
|
|
if (!casestrcmp(al, cast_uchar "right")) aa = AL_RIGHT;
|
|
if (!casestrcmp(al, cast_uchar "center")) aa = AL_CENTER;
|
|
if (!casestrcmp(al, cast_uchar "bottom")) aa = AL_BOTTOM;
|
|
if (!casestrcmp(al, cast_uchar "middle")) aa = AL_MIDDLE;
|
|
if (!casestrcmp(al, cast_uchar "top")) aa = AL_TOP;
|
|
mem_free(al);
|
|
}
|
|
|
|
if (aa == AL_LEFT || aa == AL_RIGHT || aa == AL_CENTER) {
|
|
ln_break(1);
|
|
html_stack_dup();
|
|
par_format.align = aa;
|
|
}
|
|
|
|
memset(&i,0,sizeof(i));
|
|
if (ismap) {
|
|
unsigned char *h;
|
|
html_stack_dup();
|
|
h = stracpy(format_.link);
|
|
add_to_strn(&h, cast_uchar "?0,0");
|
|
mem_free(format_.link);
|
|
format_.link = h;
|
|
}
|
|
|
|
i.url = stracpy(format_.image);
|
|
|
|
i.src = orig_link, orig_link = NULL;
|
|
/*
|
|
i.xsize = get_num(a, cast_uchar "width");
|
|
i.ysize = get_num(a, cast_uchar "height");
|
|
*/
|
|
i.xsize = get_width(a, cast_uchar "width", 2);
|
|
i.ysize = get_width(a, cast_uchar "height", 3);
|
|
if (d_opt->porn_enable && i.xsize < 0 && i.ysize < 0 && d_opt->plain == 2) {
|
|
/* Strict checking for porn condition ;-) */
|
|
i.autoscale_x = d_opt->xw;
|
|
i.autoscale_y = d_opt->yw;
|
|
} else {
|
|
/* Turn off autoscale */
|
|
i.autoscale_x = 0;
|
|
i.autoscale_y = 0;
|
|
}
|
|
/*debug("%s, %s -> %d, %d", get_attr_val(a, cast_uchar "width"), get_attr_val(a, cast_uchar "height"), i.xsize, i.ysize);*/
|
|
i.hspace = get_num(a, cast_uchar "hspace");
|
|
i.vspace = get_num(a, cast_uchar "vspace");
|
|
i.border = get_num(a, cast_uchar "border");
|
|
i.align = aa;
|
|
i.name = get_attr_val(a, cast_uchar "id");
|
|
if (!i.name) i.name = get_attr_val(a, cast_uchar "name");
|
|
i.alt = get_attr_val(a, cast_uchar "title");
|
|
if (!i.alt) i.alt = get_attr_val(a, cast_uchar "alt");
|
|
i.insert_flag = !(format_.form);
|
|
i.ismap = ismap;
|
|
if ((u = get_url_val(a, cast_uchar "usemap"))) {
|
|
i.usemap = join_urls(*u == '#' ? top_href_base() : format_.href_base, u);
|
|
mem_free(u);
|
|
}
|
|
if (i.url) special_f(ff, SP_IMAGE, &i), mem_free(i.url);
|
|
if (i.usemap) mem_free(i.usemap);
|
|
if (i.name) mem_free(i.name);
|
|
if (i.alt) mem_free(i.alt);
|
|
if (i.src) mem_free(i.src);
|
|
line_breax = 0;
|
|
if (ismap) kill_html_stack_item(&html_top);
|
|
if (aa == AL_LEFT || aa == AL_RIGHT || aa == AL_CENTER) {
|
|
ln_break(1);
|
|
kill_html_stack_item(&html_top);
|
|
}
|
|
line_breax = 0;
|
|
was_br = 0;
|
|
#endif
|
|
}
|
|
ret:
|
|
if (format_.image) mem_free(format_.image), format_.image = NULL;
|
|
html_format_changed = 1;
|
|
if (usemap) kill_html_stack_item(&html_top);
|
|
/*put_chrs(cast_uchar " ", 1);*/
|
|
if (orig_link) mem_free(orig_link);
|
|
}
|
|
|
|
static void html_obj(unsigned char *a, int obj)
|
|
{
|
|
unsigned char *old_base = format_.href_base;
|
|
unsigned char *url;
|
|
unsigned char *type = get_attr_val(a, cast_uchar "type");
|
|
unsigned char *base;
|
|
if ((base = get_url_val(a, cast_uchar "codebase"))) format_.href_base = join_urls(format_.href_base, base);
|
|
if (!type) {
|
|
url = get_url_val(a, cast_uchar "src");
|
|
if (!url) url = get_url_val(a, cast_uchar "data");
|
|
if (url) {
|
|
unsigned char *ju = join_urls(format_.href_base, url);
|
|
type = get_content_type(NULL, ju);
|
|
mem_free(url);
|
|
mem_free(ju);
|
|
}
|
|
}
|
|
if (type && known_image_type(type)) {
|
|
html_img(a);
|
|
if (obj == 1) html_top.invisible = INVISIBLE;
|
|
goto ret;
|
|
}
|
|
url = get_url_val(a, cast_uchar "src");
|
|
if (!url) url = get_url_val(a, cast_uchar "data");
|
|
if (url) put_link_line(cast_uchar "", !obj ? cast_uchar "[EMBED]" : cast_uchar "[OBJ]", url, cast_uchar ""), mem_free(url);
|
|
ret:
|
|
if (base) mem_free(format_.href_base), format_.href_base = old_base, mem_free(base);
|
|
if (type) mem_free(type);
|
|
}
|
|
|
|
static void html_embed(unsigned char *a)
|
|
{
|
|
html_obj(a, 0);
|
|
}
|
|
|
|
static void html_object(unsigned char *a)
|
|
{
|
|
html_obj(a, 1);
|
|
}
|
|
|
|
static void html_body(unsigned char *a)
|
|
{
|
|
get_color(a, cast_uchar "text", &format_.fg);
|
|
get_color(a, cast_uchar "link", &format_.clink);
|
|
if (has_attr(a, cast_uchar "onload")) special_f(ff, SP_SCRIPT, NULL);
|
|
/*
|
|
get_bgcolor(a, &format_.bg);
|
|
get_bgcolor(a, &par_format.bgcolor);
|
|
*/
|
|
}
|
|
|
|
static void html_skip(unsigned char *a)
|
|
{
|
|
html_top.dontkill = 1;
|
|
html_top.invisible = INVISIBLE;
|
|
}
|
|
|
|
static void html_title(unsigned char *a)
|
|
{
|
|
if (a[0] == '>' && a[-1] == '/') return;
|
|
html_top.dontkill = 1;
|
|
html_top.invisible = INVISIBLE;
|
|
}
|
|
|
|
int should_skip_script(unsigned char *a)
|
|
{
|
|
return !has_attr(a, cast_uchar "/");
|
|
}
|
|
|
|
static void html_script(unsigned char *a)
|
|
{
|
|
unsigned char *s;
|
|
s = get_url_val(a, cast_uchar "src");
|
|
special_f(ff, SP_SCRIPT, s);
|
|
if (s) mem_free(s);
|
|
if (should_skip_script(a)) {
|
|
html_top.dontkill = 1;
|
|
html_top.invisible = INVISIBLE_SCRIPT;
|
|
}
|
|
}
|
|
|
|
static void html_style(unsigned char *a)
|
|
{
|
|
html_top.dontkill = 1;
|
|
html_top.invisible = INVISIBLE_STYLE;
|
|
}
|
|
|
|
static void html_noscript(unsigned char *a)
|
|
{
|
|
if (d_opt->js_enable) html_skip(a);
|
|
}
|
|
|
|
static void html_center(unsigned char *a)
|
|
{
|
|
par_format.align = AL_CENTER;
|
|
if (!table_level && !F) par_format.leftmargin = par_format.rightmargin = 0;
|
|
}
|
|
|
|
static void html_linebrk(unsigned char *a)
|
|
{
|
|
unsigned char *al;
|
|
if ((al = get_attr_val(a, cast_uchar "align"))) {
|
|
if (!casestrcmp(al, cast_uchar "left")) par_format.align = AL_LEFT;
|
|
if (!casestrcmp(al, cast_uchar "right")) par_format.align = AL_RIGHT;
|
|
if (!casestrcmp(al, cast_uchar "center")) {
|
|
par_format.align = AL_CENTER;
|
|
if (!table_level && !F) par_format.leftmargin = par_format.rightmargin = 0;
|
|
}
|
|
if (!casestrcmp(al, cast_uchar "justify")) par_format.align = AL_BLOCK;
|
|
mem_free(al);
|
|
}
|
|
}
|
|
|
|
static void html_br(unsigned char *a)
|
|
{
|
|
html_linebrk(a);
|
|
if (par_format.align != AL_NO && par_format.align != AL_NO_BREAKABLE) {
|
|
if (was_br) ln_break(2);
|
|
was_br = 1;
|
|
}
|
|
}
|
|
|
|
static void html_form(unsigned char *a)
|
|
{
|
|
was_br = 1;
|
|
}
|
|
|
|
static void html_p(unsigned char *a)
|
|
{
|
|
if (par_format.leftmargin < margin) par_format.leftmargin = margin;
|
|
if (par_format.rightmargin < margin) par_format.rightmargin = margin;
|
|
/*par_format.align = AL_LEFT;*/
|
|
html_linebrk(a);
|
|
}
|
|
|
|
static void html_address(unsigned char *a)
|
|
{
|
|
par_format.leftmargin += 1;
|
|
par_format.align = AL_LEFT;
|
|
}
|
|
|
|
static void html_blockquote(unsigned char *a)
|
|
{
|
|
par_format.leftmargin += 2;
|
|
par_format.align = AL_LEFT;
|
|
}
|
|
|
|
static void html_h(int h, unsigned char *a)
|
|
{
|
|
#if defined(__GNUC__) && defined(__arm__)
|
|
do_not_optimize_here(&h);
|
|
#endif
|
|
#ifdef G
|
|
if (F) {
|
|
html_linebrk(a);
|
|
format_.fontsize = 8 - h;
|
|
format_.attr |= AT_BOLD;
|
|
return;
|
|
}
|
|
#endif
|
|
par_format.align = AL_LEFT;
|
|
if (h == 1) {
|
|
html_center(a);
|
|
return;
|
|
}
|
|
html_linebrk(a);
|
|
switch (par_format.align) {
|
|
case AL_LEFT:
|
|
par_format.leftmargin = (h - 2) * 2;
|
|
par_format.rightmargin = 0;
|
|
break;
|
|
case AL_RIGHT:
|
|
par_format.leftmargin = 0;
|
|
par_format.rightmargin = (h - 2) * 2;
|
|
break;
|
|
case AL_CENTER:
|
|
par_format.leftmargin = par_format.rightmargin = 0;
|
|
break;
|
|
case AL_BLOCK:
|
|
par_format.leftmargin = par_format.rightmargin = (h - 2) * 2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void html_h1(unsigned char *a) { html_h(1, a); }
|
|
static void html_h2(unsigned char *a) { html_h(2, a); }
|
|
static void html_h3(unsigned char *a) { html_h(3, a); }
|
|
static void html_h4(unsigned char *a) { html_h(4, a); }
|
|
static void html_h5(unsigned char *a) { html_h(5, a); }
|
|
static void html_h6(unsigned char *a) { html_h(6, a); }
|
|
|
|
static void html_pre(unsigned char *a)
|
|
{
|
|
unsigned char *cl;
|
|
format_.attr |= AT_FIXED;
|
|
par_format.align = !par_format.implicit_pre_wrap ? AL_NO : AL_NO_BREAKABLE;
|
|
par_format.leftmargin = par_format.leftmargin > 1;
|
|
par_format.rightmargin = par_format.leftmargin;
|
|
if ((cl = get_attr_val(a, cast_uchar "class"))) {
|
|
if (strstr(cast_const_char cl, "bz_comment")) /* hack */
|
|
par_format.align = AL_NO_BREAKABLE;
|
|
mem_free(cl);
|
|
}
|
|
}
|
|
|
|
static void html_div(unsigned char *a)
|
|
{
|
|
unsigned char *al;
|
|
if ((al = get_attr_val(a, cast_uchar "class"))) {
|
|
if (!strcmp(cast_const_char al, "commit-msg") ||
|
|
!strcmp(cast_const_char al, "pre") /* sourceware hack */ ||
|
|
(!strncmp(cast_const_char al, "diff", 4) && casestrcmp(al, cast_uchar "diff-view") && strncmp(cast_const_char al, "diffbar", 7)) /* gitweb hack, github counter-hacks */ ||
|
|
0) {
|
|
format_.attr |= AT_FIXED;
|
|
par_format.align = AL_NO;
|
|
} else if (strstr(cast_const_char al, "plain-text-white-space")) {
|
|
format_.attr |= AT_FIXED;
|
|
par_format.align = AL_NO_BREAKABLE;
|
|
} else if (!strcmp(cast_const_char al, "bz_extra_private_groups")) {
|
|
html_top.invisible = INVISIBLE;
|
|
}
|
|
mem_free(al);
|
|
}
|
|
html_linebrk(a);
|
|
}
|
|
|
|
static void html_hr(unsigned char *a)
|
|
{
|
|
int i;
|
|
int q = get_num(a, cast_uchar "size");
|
|
html_stack_dup();
|
|
par_format.align = AL_CENTER;
|
|
if (format_.link) mem_free(format_.link), format_.link = NULL;
|
|
format_.form = NULL;
|
|
html_linebrk(a);
|
|
if (par_format.align == AL_BLOCK) par_format.align = AL_CENTER;
|
|
par_format.leftmargin = margin;
|
|
par_format.rightmargin = margin;
|
|
i = get_width(a, cast_uchar "width", 1);
|
|
if (!F) {
|
|
unsigned char r = 205;
|
|
if (q >= 0 && q < 2) r = 196;
|
|
if (i < 0) i = par_format.width - 2 * margin - 4;
|
|
format_.attr = AT_GRAPHICS;
|
|
special_f(ff, SP_NOWRAP, 1);
|
|
while (i-- > 0) put_chrs(&r, 1);
|
|
special_f(ff, SP_NOWRAP, 0);
|
|
}
|
|
#ifdef G
|
|
else {
|
|
struct hr_param hr;
|
|
if (q < 0) q = 2;
|
|
if (i < 0) i = par_format.width - 2 * margin * G_HTML_MARGIN - 6 * G_HTML_MARGIN;
|
|
hr.size = q;
|
|
hr.width = i;
|
|
if (i >= 0) special_f(ff, SP_HR, &hr);
|
|
}
|
|
#endif
|
|
ln_break(2);
|
|
kill_html_stack_item(&html_top);
|
|
}
|
|
|
|
static void html_table(unsigned char *a)
|
|
{
|
|
par_format.leftmargin = margin;
|
|
par_format.rightmargin = margin;
|
|
par_format.align = AL_LEFT;
|
|
html_linebrk(a);
|
|
format_.attr = 0;
|
|
}
|
|
|
|
static void html_tr(unsigned char *a)
|
|
{
|
|
html_linebrk(a);
|
|
}
|
|
|
|
static void html_th(unsigned char *a)
|
|
{
|
|
/*html_linebrk(a);*/
|
|
kill_until(1, cast_uchar "TD", cast_uchar "TH", cast_uchar "", cast_uchar "TR", cast_uchar "TABLE", NULL);
|
|
format_.attr |= AT_BOLD;
|
|
put_chrs(cast_uchar " ", 1);
|
|
}
|
|
|
|
static void html_td(unsigned char *a)
|
|
{
|
|
/*html_linebrk(a);*/
|
|
kill_until(1, cast_uchar "TD", cast_uchar "TH", cast_uchar "", cast_uchar "TR", cast_uchar "TABLE", NULL);
|
|
format_.attr &= ~AT_BOLD;
|
|
put_chrs(cast_uchar " ", 1);
|
|
}
|
|
|
|
static void html_base(unsigned char *a)
|
|
{
|
|
unsigned char *al;
|
|
if ((al = get_url_val(a, cast_uchar "href"))) {
|
|
if (format_.href_base) mem_free(format_.href_base);
|
|
format_.href_base = join_urls(top_href_base(), al);
|
|
special_f(ff, SP_SET_BASE, format_.href_base);
|
|
mem_free(al);
|
|
}
|
|
if ((al = get_target(a))) {
|
|
if (format_.target_base) mem_free(format_.target_base);
|
|
format_.target_base = al;
|
|
}
|
|
}
|
|
|
|
static void html_ul(unsigned char *a)
|
|
{
|
|
unsigned char *al;
|
|
/*debug_stack();*/
|
|
par_format.list_level++;
|
|
par_format.list_number = 0;
|
|
par_format.flags = P_STAR;
|
|
if ((al = get_attr_val(a, cast_uchar "type"))) {
|
|
if (!casestrcmp(al, cast_uchar "disc") ||
|
|
!casestrcmp(al, cast_uchar "circle")) par_format.flags = P_O;
|
|
if (!casestrcmp(al, cast_uchar "square")) par_format.flags = P_PLUS;
|
|
mem_free(al);
|
|
}
|
|
if ((par_format.leftmargin += 2 + (par_format.list_level > 1)) > par_format.width * 2 / 3 && !table_level)
|
|
par_format.leftmargin = par_format.width * 2 / 3;
|
|
par_format.align = AL_LEFT;
|
|
html_top.dontkill = 1;
|
|
}
|
|
|
|
static void html_ol(unsigned char *a)
|
|
{
|
|
unsigned char *al;
|
|
int st;
|
|
par_format.list_level++;
|
|
st = get_num(a, cast_uchar "start");
|
|
if (st == -1) st = 1;
|
|
par_format.list_number = st;
|
|
par_format.flags = P_NUMBER;
|
|
if ((al = get_attr_val(a, cast_uchar "type"))) {
|
|
if (!strcmp(cast_const_char al, "1")) par_format.flags = P_NUMBER;
|
|
if (!strcmp(cast_const_char al, "a")) par_format.flags = P_alpha;
|
|
if (!strcmp(cast_const_char al, "A")) par_format.flags = P_ALPHA;
|
|
if (!strcmp(cast_const_char al, "r")) par_format.flags = P_roman;
|
|
if (!strcmp(cast_const_char al, "R")) par_format.flags = P_ROMAN;
|
|
if (!strcmp(cast_const_char al, "i")) par_format.flags = P_roman;
|
|
if (!strcmp(cast_const_char al, "I")) par_format.flags = P_ROMAN;
|
|
mem_free(al);
|
|
}
|
|
if (!F) if ((par_format.leftmargin += (par_format.list_level > 1)) > par_format.width * 2 / 3 && !table_level)
|
|
par_format.leftmargin = par_format.width * 2 / 3;
|
|
par_format.align = AL_LEFT;
|
|
html_top.dontkill = 1;
|
|
}
|
|
|
|
static void html_li(unsigned char *a)
|
|
{
|
|
/*kill_until(0, cast_uchar "", cast_uchar "UL", cast_uchar "OL", NULL);*/
|
|
if (!par_format.list_number) {
|
|
unsigned char x[8] = "* ";
|
|
if ((par_format.flags & P_LISTMASK) == P_O) x[0] = 'o';
|
|
if ((par_format.flags & P_LISTMASK) == P_PLUS) x[0] = '+';
|
|
#ifdef G
|
|
if (F) par_format.leftmargin += 2;
|
|
#endif
|
|
put_chrs(x, 7);
|
|
if (!F) par_format.leftmargin += 2;
|
|
par_format.align = AL_LEFT;
|
|
putsp = -1;
|
|
} else {
|
|
unsigned char c = 0;
|
|
unsigned char n[32];
|
|
int t = par_format.flags & P_LISTMASK;
|
|
int s = get_num(a, cast_uchar "value");
|
|
#ifdef G
|
|
if (F) par_format.leftmargin += 4;
|
|
#endif
|
|
if (s != -1) par_format.list_number = s;
|
|
if ((t != P_roman && t != P_ROMAN && par_format.list_number < 10) || t == P_alpha || t == P_ALPHA) put_chrs(cast_uchar " ", 6), c = 1;
|
|
if (t == P_ALPHA || t == P_alpha) {
|
|
n[0] = par_format.list_number ? (par_format.list_number - 1) % 26 + (t == P_ALPHA ? 'A' : 'a') : 0;
|
|
n[1] = 0;
|
|
} else if (t == P_ROMAN || t == P_roman) {
|
|
roman(n, par_format.list_number);
|
|
if (t == P_ROMAN) {
|
|
unsigned char *x;
|
|
for (x = n; *x; x++) *x = upcase(*x);
|
|
}
|
|
} else sprintf(cast_char n, "%d", par_format.list_number);
|
|
put_chrs(n, (int)strlen(cast_const_char n));
|
|
put_chrs(cast_uchar ". ", 7);
|
|
if (!F) par_format.leftmargin += (int)strlen(cast_const_char n) + c + 2;
|
|
par_format.align = AL_LEFT;
|
|
list_struct(html_top.list_entry.next, struct html_element)->parattr.list_number = par_format.list_number + 1;
|
|
par_format.list_number = 0;
|
|
putsp = -1;
|
|
}
|
|
line_breax = 2;
|
|
}
|
|
|
|
static void html_dl(unsigned char *a)
|
|
{
|
|
par_format.flags &= ~P_COMPACT;
|
|
if (has_attr(a, cast_uchar "compact")) par_format.flags |= P_COMPACT;
|
|
if (par_format.list_level) par_format.leftmargin += 5;
|
|
par_format.list_level++;
|
|
par_format.list_number = 0;
|
|
par_format.align = AL_LEFT;
|
|
par_format.dd_margin = par_format.leftmargin;
|
|
html_top.dontkill = 1;
|
|
if (!(par_format.flags & P_COMPACT)) {
|
|
ln_break(2);
|
|
html_top.linebreak = 2;
|
|
}
|
|
}
|
|
|
|
static void html_dt(unsigned char *a)
|
|
{
|
|
kill_until(0, cast_uchar "", cast_uchar "DL", NULL);
|
|
par_format.align = AL_LEFT;
|
|
par_format.leftmargin = par_format.dd_margin;
|
|
if (!(par_format.flags & P_COMPACT) && !has_attr(a, cast_uchar "compact"))
|
|
ln_break(2);
|
|
}
|
|
|
|
static void html_dd(unsigned char *a)
|
|
{
|
|
kill_until(0, cast_uchar "", cast_uchar "DL", NULL);
|
|
if ((par_format.leftmargin = par_format.dd_margin + (table_level ? 3 : 8)) > par_format.width * 2 / 3 && !table_level)
|
|
par_format.leftmargin = par_format.width * 2 / 3;
|
|
par_format.align = AL_LEFT;
|
|
}
|
|
|
|
static void get_html_form(unsigned char *a, struct form *form)
|
|
{
|
|
unsigned char *al;
|
|
unsigned char *ch;
|
|
form->method = FM_GET;
|
|
if ((al = get_attr_val(a, cast_uchar "method"))) {
|
|
if (!casestrcmp(al, cast_uchar "post")) {
|
|
unsigned char *ax;
|
|
form->method = FM_POST;
|
|
if ((ax = get_attr_val(a, cast_uchar "enctype"))) {
|
|
if (!casestrcmp(ax, cast_uchar "multipart/form-data"))
|
|
form->method = FM_POST_MP;
|
|
mem_free(ax);
|
|
}
|
|
}
|
|
mem_free(al);
|
|
}
|
|
if ((al = get_url_val(a, cast_uchar "action"))) {
|
|
unsigned char *all = al;
|
|
while (all[0] == ' ') all++;
|
|
while (all[0] && all[strlen(cast_const_char all) - 1] == ' ') all[strlen(cast_const_char all) - 1] = 0;
|
|
form->action = join_urls(format_.href_base, all);
|
|
mem_free(al);
|
|
} else {
|
|
if ((ch = cast_uchar strchr(cast_const_char(form->action = stracpy(format_.href_base)), POST_CHAR))) *ch = 0;
|
|
if (form->method == FM_GET && (ch = cast_uchar strchr(cast_const_char form->action, '?'))) *ch = 0;
|
|
}
|
|
if ((al = get_target(a))) {
|
|
form->target = al;
|
|
} else {
|
|
form->target = stracpy(format_.target_base);
|
|
}
|
|
if ((al=get_attr_val(a,cast_uchar "name")))
|
|
{
|
|
form->form_name=al;
|
|
}
|
|
if ((al=get_attr_val(a,cast_uchar "onsubmit")))
|
|
{
|
|
form->onsubmit=al;
|
|
}
|
|
form->num = (int)(a - startf);
|
|
}
|
|
|
|
static void find_form_for_input(unsigned char *i)
|
|
{
|
|
unsigned char *s, *ss, *name, *attr, *lf, *la;
|
|
int namelen;
|
|
if (form.action) mem_free(form.action);
|
|
if (form.target) mem_free(form.target);
|
|
if (form.form_name) mem_free(form.form_name);
|
|
if (form.onsubmit) mem_free(form.onsubmit);
|
|
memset(&form, 0, sizeof(struct form));
|
|
if (!special_f(ff, SP_USED, NULL)) return;
|
|
if (last_form_tag && last_input_tag && i <= last_input_tag && i > last_form_tag) {
|
|
get_html_form(last_form_attr, &form);
|
|
return;
|
|
}
|
|
if (last_form_tag && last_input_tag && i > last_input_tag) {
|
|
if (parse_element(last_form_tag, i, &name, &namelen, &la, &s))
|
|
internal_error("couldn't parse already parsed tag");
|
|
lf = last_form_tag;
|
|
s = last_input_tag;
|
|
} else {
|
|
lf = NULL, la = NULL;
|
|
s = startf;
|
|
}
|
|
se:
|
|
while (s < i && *s != '<') sp:s++;
|
|
if (s >= i) goto end_parse;
|
|
if (eofff - s >= 2 && (s[1] == '!' || s[1] == '?')) {
|
|
s = skip_comment(s, i);
|
|
goto se;
|
|
}
|
|
ss = s;
|
|
if (parse_element(s, i, &name, &namelen, &attr, &s)) goto sp;
|
|
if (namelen != 4 || casecmp(name, cast_uchar "FORM", 4)) goto se;
|
|
lf = ss;
|
|
la = attr;
|
|
goto se;
|
|
|
|
end_parse:
|
|
if (lf) {
|
|
last_form_tag = lf;
|
|
last_form_attr = la;
|
|
last_input_tag = i;
|
|
get_html_form(la, &form);
|
|
} else {
|
|
last_form_tag = NULL;
|
|
}
|
|
}
|
|
|
|
static void html_button(unsigned char *a)
|
|
{
|
|
unsigned char *al;
|
|
struct form_control *fc;
|
|
|
|
if ((al = get_attr_val(a, cast_uchar "class"))) { /* Wikipedia hack */
|
|
if (strstr(cast_const_char al, "vector-toc-toggle") || strstr(cast_const_char al, "vector-pinnable-header-toggle-button")) {
|
|
html_top.invisible = INVISIBLE;
|
|
mem_free(al);
|
|
return;
|
|
}
|
|
mem_free(al);
|
|
}
|
|
|
|
find_form_for_input(a);
|
|
fc = mem_calloc(sizeof(struct form_control));
|
|
if (!(al = get_attr_val(a, cast_uchar "type"))) {
|
|
fc->type = FC_SUBMIT;
|
|
goto xxx;
|
|
}
|
|
if (!casestrcmp(al, cast_uchar "submit")) fc->type = FC_SUBMIT;
|
|
else if (!casestrcmp(al, cast_uchar "reset")) fc->type = FC_RESET;
|
|
else if (!casestrcmp(al, cast_uchar "button")) fc->type = FC_BUTTON;
|
|
else {
|
|
mem_free(al);
|
|
mem_free(fc);
|
|
return;
|
|
}
|
|
mem_free(al);
|
|
xxx:
|
|
get_js_events(a);
|
|
fc->form_num = last_form_tag ? (int)(last_form_tag - startf) : 0;
|
|
fc->ctrl_num = last_form_tag ? (int)(a - last_form_tag) : (int)(a - startf);
|
|
fc->position = (int)(a - startf);
|
|
fc->method = form.method;
|
|
fc->action = stracpy(form.action);
|
|
fc->form_name = stracpy(form.form_name);
|
|
fc->onsubmit = stracpy(form.onsubmit);
|
|
fc->name = get_attr_val(a, cast_uchar "name");
|
|
fc->default_value = get_exact_attr_val(a, cast_uchar "value");
|
|
fc->ro = has_attr(a, cast_uchar "disabled") ? 2 : has_attr(a, cast_uchar "readonly") ? 1 : 0;
|
|
if (fc->type == FC_SUBMIT && !fc->default_value) fc->default_value = stracpy(cast_uchar "Submit");
|
|
if (fc->type == FC_RESET && !fc->default_value) fc->default_value = stracpy(cast_uchar "Reset");
|
|
if (fc->type == FC_BUTTON && !fc->default_value) fc->default_value = stracpy(cast_uchar "BUTTON");
|
|
if (!fc->default_value) fc->default_value = stracpy(cast_uchar "");
|
|
special_f(ff, SP_CONTROL, fc);
|
|
format_.form = fc;
|
|
format_.attr |= AT_BOLD | AT_FIXED;
|
|
|
|
if (fc->type != FC_BUTTON) {
|
|
unsigned char *p, *name;
|
|
int namelen;
|
|
p = find_element_end(a);
|
|
p1:
|
|
while (p < eoff && WHITECHAR(*p)) p2:p++;
|
|
if (p == eoff) goto put_text;
|
|
if (*p != '<') return;
|
|
if (parse_element(p, eoff, &name, &namelen, NULL, &p)) goto p2;
|
|
if (namelen == 6 && !casecmp(name, cast_uchar "BUTTON", 6)) goto put_text;
|
|
if (namelen == 7 && !casecmp(name, cast_uchar "/BUTTON", 7)) goto put_text;
|
|
if (namelen == 3 && !casecmp(name, cast_uchar "IMG", 3)) return;
|
|
goto p1;
|
|
|
|
put_text:
|
|
put_chrs(cast_uchar "[ ", 7);
|
|
put_chrs(fc->default_value, (int)strlen(cast_const_char fc->default_value));
|
|
put_chrs(cast_uchar " ]", 7);
|
|
putsp = -1;
|
|
}
|
|
}
|
|
|
|
static void set_max_textarea_width(int *w)
|
|
{
|
|
int limit;
|
|
if (!table_level) {
|
|
limit = par_format.width - (par_format.leftmargin + par_format.rightmargin) * gf_val(1, G_HTML_MARGIN);
|
|
} else {
|
|
limit = gf_val(d_opt->xw - 2, d_opt->xw - G_SCROLL_BAR_WIDTH - 2 * G_HTML_MARGIN * d_opt->margin);
|
|
}
|
|
if (!F) {
|
|
if (*w > limit) {
|
|
*w = limit;
|
|
if (*w < HTML_MINIMAL_TEXTAREA_WIDTH) *w = HTML_MINIMAL_TEXTAREA_WIDTH;
|
|
}
|
|
#ifdef G
|
|
} else {
|
|
struct style *st = g_get_style(0, 0, d_opt->font_size, FF_MONOSPACED);
|
|
int uw = g_char_width(st, '_');
|
|
g_free_style(st);
|
|
if (uw && *w > limit / uw) {
|
|
*w = limit / uw;
|
|
if (*w < HTML_MINIMAL_TEXTAREA_WIDTH) *w = HTML_MINIMAL_TEXTAREA_WIDTH;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void html_input(unsigned char *a)
|
|
{
|
|
int i;
|
|
int size;
|
|
unsigned char *al;
|
|
struct form_control *fc;
|
|
find_form_for_input(a);
|
|
fc = mem_calloc(sizeof(struct form_control));
|
|
if (!(al = get_attr_val(a, cast_uchar "type"))) {
|
|
if (has_attr(a, cast_uchar "onclick")) fc->type = FC_BUTTON;
|
|
else fc->type = FC_TEXT;
|
|
goto xxx;
|
|
}
|
|
if (!casestrcmp(al, cast_uchar "text")) fc->type = FC_TEXT;
|
|
else if (!casestrcmp(al, cast_uchar "password")) fc->type = FC_PASSWORD;
|
|
else if (!casestrcmp(al, cast_uchar "checkbox")) fc->type = FC_CHECKBOX;
|
|
else if (!casestrcmp(al, cast_uchar "radio")) fc->type = FC_RADIO;
|
|
else if (!casestrcmp(al, cast_uchar "submit")) fc->type = FC_SUBMIT;
|
|
else if (!casestrcmp(al, cast_uchar "reset")) fc->type = FC_RESET;
|
|
else if (!casestrcmp(al, cast_uchar "file")) fc->type = FC_FILE_UPLOAD;
|
|
else if (!casestrcmp(al, cast_uchar "hidden")) fc->type = FC_HIDDEN;
|
|
else if (!casestrcmp(al, cast_uchar "image")) fc->type = FC_IMAGE;
|
|
else if (!casestrcmp(al, cast_uchar "button")) fc->type = FC_BUTTON;
|
|
else fc->type = FC_TEXT;
|
|
mem_free(al);
|
|
xxx:
|
|
fc->form_num = last_form_tag ? (int)(last_form_tag - startf) : 0;
|
|
fc->ctrl_num = last_form_tag ? (int)(a - last_form_tag) : (int)(a - startf);
|
|
fc->position = (int)(a - startf);
|
|
fc->method = form.method;
|
|
fc->action = stracpy(form.action);
|
|
fc->form_name = stracpy(form.form_name);
|
|
fc->onsubmit = stracpy(form.onsubmit);
|
|
fc->target = stracpy(form.target);
|
|
fc->name = get_attr_val(a, cast_uchar "name");
|
|
if (fc->type == FC_TEXT || fc->type == FC_PASSWORD) fc->default_value = get_attr_val(a, cast_uchar "value");
|
|
else if (fc->type != FC_FILE_UPLOAD) fc->default_value = get_exact_attr_val(a, cast_uchar "value");
|
|
if (fc->type == FC_CHECKBOX && !fc->default_value) fc->default_value = stracpy(cast_uchar "on");
|
|
if ((size = get_num(a, cast_uchar "size")) <= 1) size = HTML_DEFAULT_INPUT_SIZE;
|
|
size++;
|
|
if (size > HTML_MINIMAL_TEXTAREA_WIDTH) {
|
|
set_max_textarea_width(&size);
|
|
}
|
|
fc->size = size;
|
|
if ((fc->maxlength = get_num(a, cast_uchar "maxlength")) == -1) fc->maxlength = MAXINT / 4;
|
|
if (fc->type == FC_CHECKBOX || fc->type == FC_RADIO) fc->default_state = has_attr(a, cast_uchar "checked");
|
|
fc->ro = has_attr(a, cast_uchar "disabled") ? 2 : has_attr(a, cast_uchar "readonly") ? 1 : 0;
|
|
if (fc->type == FC_IMAGE) {
|
|
fc->alt = get_attr_val(a, cast_uchar "alt");
|
|
if (!fc->alt) fc->alt = get_attr_val(a, cast_uchar "title");
|
|
if (!fc->alt) fc->alt = get_attr_val(a, cast_uchar "name");
|
|
}
|
|
if (fc->type == FC_SUBMIT && !fc->default_value) fc->default_value = stracpy(cast_uchar "Submit");
|
|
if (fc->type == FC_RESET && !fc->default_value) fc->default_value = stracpy(cast_uchar "Reset");
|
|
if (!fc->default_value) fc->default_value = stracpy(cast_uchar "");
|
|
if (fc->type == FC_HIDDEN) goto hid;
|
|
put_chrs(cast_uchar " ", 1);
|
|
html_stack_dup();
|
|
format_.form = fc;
|
|
get_js_events(a);
|
|
switch (fc->type) {
|
|
case FC_TEXT:
|
|
case FC_PASSWORD:
|
|
case FC_FILE_UPLOAD:
|
|
format_.attr |= AT_BOLD | AT_FIXED;
|
|
format_.fontsize = 3;
|
|
for (i = 0; i < fc->size; i++) put_chrs(cast_uchar "_", 1);
|
|
break;
|
|
case FC_CHECKBOX:
|
|
format_.attr |= AT_BOLD | AT_FIXED;
|
|
format_.fontsize = 3;
|
|
put_chrs(cast_uchar "[ ]", 8);
|
|
break;
|
|
case FC_RADIO:
|
|
format_.attr |= AT_BOLD | AT_FIXED;
|
|
format_.fontsize = 3;
|
|
put_chrs(cast_uchar "[ ]", 8);
|
|
break;
|
|
case FC_IMAGE:
|
|
if (!F || !d_opt->display_images) {
|
|
if (format_.image) mem_free(format_.image), format_.image = NULL;
|
|
if ((al = get_url_val(a, cast_uchar "src")) || (al = get_url_val(a, cast_uchar "dynsrc"))) {
|
|
format_.image = join_urls(format_.href_base, al);
|
|
mem_free(al);
|
|
}
|
|
format_.attr |= AT_BOLD | AT_FIXED;
|
|
put_chrs(cast_uchar "[ ", 7);
|
|
if (fc->alt) put_chrs(fc->alt, (int)strlen(cast_const_char fc->alt));
|
|
else put_chrs(cast_uchar "Submit", 6);
|
|
put_chrs(cast_uchar " ]", 7);
|
|
} else html_img(a);
|
|
break;
|
|
case FC_SUBMIT:
|
|
case FC_RESET:
|
|
format_.attr |= AT_BOLD | AT_FIXED;
|
|
format_.fontsize = 3;
|
|
put_chrs(cast_uchar "[ ", 7);
|
|
if (fc->default_value) put_chrs(fc->default_value, (int)strlen(cast_const_char fc->default_value));
|
|
put_chrs(cast_uchar " ]", 7);
|
|
break;
|
|
case FC_BUTTON:
|
|
format_.attr |= AT_BOLD | AT_FIXED;
|
|
format_.fontsize = 3;
|
|
put_chrs(cast_uchar "[ ", 7);
|
|
if (fc->default_value) put_chrs(fc->default_value, (int)strlen(cast_const_char fc->default_value));
|
|
else put_chrs(cast_uchar "BUTTON", 6);
|
|
put_chrs(cast_uchar " ]", 7);
|
|
break;
|
|
default:
|
|
internal_error("bad control type");
|
|
}
|
|
kill_html_stack_item(&html_top);
|
|
put_chrs(cast_uchar " ", 1);
|
|
|
|
hid:
|
|
special_f(ff, SP_CONTROL, fc);
|
|
}
|
|
|
|
static void html_select(unsigned char *a)
|
|
{
|
|
unsigned char *al;
|
|
if (!(al = get_attr_val(a, cast_uchar "name"))) return;
|
|
html_top.dontkill = 1;
|
|
if (format_.select) mem_free(format_.select);
|
|
format_.select = al;
|
|
format_.select_disabled = 2 * has_attr(a, cast_uchar "disabled");
|
|
}
|
|
|
|
static void html_option(unsigned char *a)
|
|
{
|
|
struct form_control *fc;
|
|
unsigned char *val;
|
|
find_form_for_input(a);
|
|
if (!format_.select) return;
|
|
fc = mem_calloc(sizeof(struct form_control));
|
|
if (!(val = get_exact_attr_val(a, cast_uchar "value"))) {
|
|
unsigned char *p, *r;
|
|
unsigned char *name;
|
|
int namelen;
|
|
int l = 0;
|
|
val = init_str();
|
|
p = find_element_end(a);
|
|
rrrr:
|
|
while (p < eoff && WHITECHAR(*p)) p++;
|
|
while (p < eoff && !WHITECHAR(*p) && *p != '<') {
|
|
pppp:
|
|
add_chr_to_str(&val, &l, *p), p++;
|
|
}
|
|
r = p;
|
|
while (r < eoff && WHITECHAR(*r)) r++;
|
|
if (r >= eoff) goto x;
|
|
if (eoff - r >= 2 && (r[1] == '!' || r[1] == '?')) {
|
|
p = skip_comment(r, eoff);
|
|
goto rrrr;
|
|
}
|
|
if (parse_element(r, eoff, &name, &namelen, NULL, &p)) goto pppp;
|
|
if (!((namelen == 6 && !casecmp(name, cast_uchar "OPTION", 6)) ||
|
|
(namelen == 7 && !casecmp(name, cast_uchar "/OPTION", 7)) ||
|
|
(namelen == 6 && !casecmp(name, cast_uchar "SELECT", 6)) ||
|
|
(namelen == 7 && !casecmp(name, cast_uchar "/SELECT", 7)) ||
|
|
(namelen == 8 && !casecmp(name, cast_uchar "OPTGROUP", 8)) ||
|
|
(namelen == 9 && !casecmp(name, cast_uchar "/OPTGROUP", 9)))) goto rrrr;
|
|
}
|
|
x:
|
|
fc->form_num = last_form_tag ? (int)(last_form_tag - startf) : 0;
|
|
fc->ctrl_num = last_form_tag ? (int)(a - last_form_tag) : (int)(a - startf);
|
|
fc->position = (int)(a - startf);
|
|
fc->method = form.method;
|
|
fc->action = stracpy(form.action);
|
|
fc->form_name = stracpy(form.form_name);
|
|
fc->onsubmit = stracpy(form.onsubmit);
|
|
fc->type = FC_CHECKBOX;
|
|
fc->name = stracpy(format_.select);
|
|
fc->default_value = val;
|
|
fc->default_state = has_attr(a, cast_uchar "selected");
|
|
fc->ro = format_.select_disabled;
|
|
if (has_attr(a, cast_uchar "disabled")) fc->ro = 2;
|
|
put_chrs(cast_uchar " ", 1);
|
|
html_stack_dup();
|
|
format_.form = fc;
|
|
format_.attr |= AT_BOLD | AT_FIXED;
|
|
format_.fontsize = 3;
|
|
put_chrs(cast_uchar "[ ]", 3);
|
|
kill_html_stack_item(&html_top);
|
|
put_chrs(cast_uchar " ", 1);
|
|
special_f(ff, SP_CONTROL, fc);
|
|
}
|
|
|
|
void clr_white(unsigned char *name)
|
|
{
|
|
unsigned char *nm;
|
|
for (nm = name; *nm; nm++)
|
|
if (WHITECHAR(*nm) || *nm == 1) *nm = ' ';
|
|
}
|
|
|
|
void clr_spaces(unsigned char *name, int firstlast)
|
|
{
|
|
unsigned char *n1, *n2;
|
|
clr_white(name);
|
|
if (!strchr(cast_const_char name, ' ')) return;
|
|
for (n1 = name, n2 = name; *n1; n1++)
|
|
if (!(n1[0] == ' ' && ((firstlast && n2 == name) || n1[1] == ' ' || (firstlast && !n1[1]))))
|
|
*n2++ = *n1;
|
|
*n2 = 0;
|
|
}
|
|
|
|
static int menu_stack_size;
|
|
static struct menu_item **menu_stack;
|
|
|
|
static void new_menu_item(unsigned char *name, long data, int fullname)
|
|
/* name == NULL - up; data == -1 - down */
|
|
{
|
|
struct menu_item *top, *item, *nmenu = NULL; /* no uninitialized warnings */
|
|
if (name) {
|
|
clr_spaces(name, 1);
|
|
if (!name[0]) mem_free(name), name = stracpy(cast_uchar " ");
|
|
if (name[0] == 1) name[0] = ' ';
|
|
}
|
|
if (name && data == -1) {
|
|
nmenu = mem_calloc(sizeof(struct menu_item));
|
|
/*nmenu->text = cast_uchar "";*/
|
|
}
|
|
if (menu_stack_size && name) {
|
|
top = item = menu_stack[menu_stack_size - 1];
|
|
while (item->text) item++;
|
|
if ((size_t)((unsigned char *)(item + 2) - (unsigned char *)top) > MAXINT) overalloc();
|
|
top = mem_realloc(top, (unsigned char *)(item + 2) - (unsigned char *)top);
|
|
item = item - menu_stack[menu_stack_size - 1] + top;
|
|
menu_stack[menu_stack_size - 1] = top;
|
|
if (menu_stack_size >= 2) {
|
|
struct menu_item *below = menu_stack[menu_stack_size - 2];
|
|
while (below->text) below++;
|
|
below[-1].data = top;
|
|
}
|
|
item->text = name;
|
|
item->rtext = data == -1 ? cast_uchar ">" : cast_uchar "";
|
|
item->hotkey = fullname ? cast_uchar "\000\001" : cast_uchar "\000\000"; /* dirty */
|
|
item->func = data == -1 ? do_select_submenu : selected_item;
|
|
item->data = data == -1 ? nmenu : (void *)(my_intptr_t)data;
|
|
item->in_m = data == -1 ? 1 : 0;
|
|
item->free_i = 0;
|
|
item++;
|
|
memset(item, 0, sizeof(struct menu_item));
|
|
/*item->text = cast_uchar "";*/
|
|
} else if (name) mem_free(name);
|
|
if (name && data == -1) {
|
|
if ((unsigned)menu_stack_size > MAXINT / sizeof(struct menu_item *) - 1) overalloc();
|
|
menu_stack = mem_realloc(menu_stack, (menu_stack_size + 1) * sizeof(struct menu_item *));
|
|
menu_stack[menu_stack_size++] = nmenu;
|
|
}
|
|
if (!name) menu_stack_size--;
|
|
}
|
|
|
|
static void init_menu(void)
|
|
{
|
|
menu_stack_size = 0;
|
|
menu_stack = DUMMY;
|
|
new_menu_item(stracpy(cast_uchar ""), -1, 0);
|
|
}
|
|
|
|
void free_menu(struct menu_item *m) /* Grrr. Recursion */
|
|
{
|
|
struct menu_item *mm;
|
|
for (mm = m; mm->text; mm++) {
|
|
mem_free(mm->text);
|
|
if (mm->func == do_select_submenu) free_menu(mm->data);
|
|
}
|
|
mem_free(m);
|
|
}
|
|
|
|
static struct menu_item *detach_menu(void)
|
|
{
|
|
struct menu_item *i = NULL;
|
|
if (menu_stack) {
|
|
if (menu_stack_size) i = menu_stack[0];
|
|
mem_free(menu_stack);
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static void destroy_menu(void)
|
|
{
|
|
if (menu_stack && menu_stack != DUMMY) free_menu(menu_stack[0]);
|
|
detach_menu();
|
|
}
|
|
|
|
static void menu_labels(struct menu_item *m, unsigned char *base, unsigned char **lbls)
|
|
{
|
|
unsigned char *bs;
|
|
for (; m->text; m++) {
|
|
if (m->func == do_select_submenu) {
|
|
if ((bs = stracpy(base))) {
|
|
add_to_strn(&bs, m->text);
|
|
add_to_strn(&bs, cast_uchar " ");
|
|
menu_labels(m->data, bs, lbls);
|
|
mem_free(bs);
|
|
}
|
|
} else {
|
|
if ((bs = stracpy(m->hotkey[1] ? (unsigned char *)"" : base))) add_to_strn(&bs, m->text);
|
|
lbls[(my_intptr_t)m->data] = bs;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int menu_contains(struct menu_item *m, int f)
|
|
{
|
|
if (m->func != do_select_submenu) return (my_intptr_t)m->data == f;
|
|
for (m = m->data; m->text; m++) if (menu_contains(m, f)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
void do_select_submenu(struct terminal *term, void *menu_, void *ses_)
|
|
{
|
|
struct menu_item *menu = (struct menu_item *)menu_;
|
|
struct session *ses = (struct session *)ses_;
|
|
struct menu_item *m;
|
|
int def = get_current_state(ses);
|
|
int sel = 0;
|
|
if (def < 0) def = 0;
|
|
for (m = menu; m->text; m++, sel++) if (menu_contains(m, def)) goto f;
|
|
sel = 0;
|
|
f:
|
|
do_menu_selected(term, menu, ses, sel, NULL, NULL);
|
|
}
|
|
|
|
static int do_html_select(unsigned char *attr, unsigned char *html, unsigned char *eof, unsigned char **end)
|
|
{
|
|
struct form_control *fc;
|
|
unsigned char *t_name, *t_attr, *en;
|
|
int t_namelen;
|
|
unsigned char *lbl;
|
|
int lbl_l;
|
|
unsigned char *vlbl;
|
|
int vlbl_l;
|
|
int nnmi = 0;
|
|
struct conv_table *ct = special_f(ff, SP_TABLE, NULL);
|
|
unsigned char **val, **lbls;
|
|
int order, preselect, group;
|
|
int i, mw;
|
|
if (has_attr(attr, cast_uchar "multiple") || dmp) return 1;
|
|
find_form_for_input(attr);
|
|
lbl = NULL;
|
|
lbl_l = 0;
|
|
vlbl = NULL;
|
|
vlbl_l = 0;
|
|
val = DUMMY;
|
|
order = 0, group = 0, preselect = -1;
|
|
init_menu();
|
|
se:
|
|
en = html;
|
|
see:
|
|
html = en;
|
|
while (html < eof && *html != '<') html++;
|
|
if (html >= eof) {
|
|
int i;
|
|
abort:
|
|
*end = html;
|
|
if (lbl) mem_free(lbl);
|
|
if (vlbl) mem_free(vlbl);
|
|
for (i = 0; i < order; i++) if (val[i]) mem_free(val[i]);
|
|
mem_free(val);
|
|
destroy_menu();
|
|
*end = en;
|
|
return 0;
|
|
}
|
|
if (lbl) {
|
|
unsigned char *q, *s = en;
|
|
int l = (int)(html - en);
|
|
while (l && WHITECHAR(s[0])) s++, l--;
|
|
while (l && WHITECHAR(s[l-1])) l--;
|
|
q = convert_string(ct, s, l, d_opt);
|
|
if (q) add_to_str(&lbl, &lbl_l, q), mem_free(q);
|
|
add_bytes_to_str(&vlbl, &vlbl_l, s, l);
|
|
}
|
|
if (eof - html >= 2 && (html[1] == '!' || html[1] == '?')) {
|
|
html = skip_comment(html, eof);
|
|
goto se;
|
|
}
|
|
if (parse_element(html, eof, &t_name, &t_namelen, &t_attr, &en)) {
|
|
html++;
|
|
goto se;
|
|
}
|
|
if (t_namelen == 7 && !casecmp(t_name, cast_uchar "/SELECT", 7)) {
|
|
if (lbl) {
|
|
if (!val[order - 1]) val[order - 1] = stracpy(vlbl);
|
|
if (!nnmi) new_menu_item(lbl, order - 1, 1), lbl = NULL;
|
|
else mem_free(lbl), lbl = NULL;
|
|
mem_free(vlbl), vlbl = NULL;
|
|
}
|
|
goto end_parse;
|
|
}
|
|
if (t_namelen == 7 && !casecmp(t_name, cast_uchar "/OPTION", 7)) {
|
|
if (lbl) {
|
|
if (!val[order - 1]) val[order - 1] = stracpy(vlbl);
|
|
if (!nnmi) new_menu_item(lbl, order - 1, 1), lbl = NULL;
|
|
else mem_free(lbl), lbl = NULL;
|
|
mem_free(vlbl), vlbl = NULL;
|
|
}
|
|
goto see;
|
|
}
|
|
if (t_namelen == 6 && !casecmp(t_name, cast_uchar "OPTION", 6)) {
|
|
unsigned char *v, *vx;
|
|
if (lbl) {
|
|
if (!val[order - 1]) val[order - 1] = stracpy(vlbl);
|
|
if (!nnmi) new_menu_item(lbl, order - 1, 1), lbl = NULL;
|
|
else mem_free(lbl), lbl = NULL;
|
|
mem_free(vlbl), vlbl = NULL;
|
|
}
|
|
if (has_attr(t_attr, cast_uchar "disabled")) goto see;
|
|
if (preselect == -1 && has_attr(t_attr, cast_uchar "selected")) preselect = order;
|
|
v = get_exact_attr_val(t_attr, cast_uchar "value");
|
|
if (!(order & (ALLOC_GR - 1))) {
|
|
if ((unsigned)order > MAXINT / sizeof(unsigned char *) - ALLOC_GR) overalloc();
|
|
val = mem_realloc(val, (order + ALLOC_GR) * sizeof(unsigned char *));
|
|
}
|
|
val[order++] = v;
|
|
if ((vx = get_attr_val(t_attr, cast_uchar "label"))) {
|
|
new_menu_item(convert_string(ct, vx, (int)strlen(cast_const_char vx), d_opt), order - 1, 0);
|
|
mem_free(vx);
|
|
}
|
|
if (!v || !vx) {
|
|
lbl = init_str(), lbl_l = 0;
|
|
vlbl = init_str(), vlbl_l = 0;
|
|
nnmi = !!vx;
|
|
}
|
|
goto see;
|
|
}
|
|
if ((t_namelen == 8 && !casecmp(t_name, cast_uchar "OPTGROUP", 8)) || (t_namelen == 9 && !casecmp(t_name, cast_uchar "/OPTGROUP", 9))) {
|
|
if (lbl) {
|
|
if (!val[order - 1]) val[order - 1] = stracpy(vlbl);
|
|
if (!nnmi) new_menu_item(lbl, order - 1, 1), lbl = NULL;
|
|
else mem_free(lbl), lbl = NULL;
|
|
mem_free(vlbl), vlbl = NULL;
|
|
}
|
|
if (group) new_menu_item(NULL, -1, 0), group = 0;
|
|
}
|
|
if (t_namelen == 8 && !casecmp(t_name, cast_uchar "OPTGROUP", 8)) {
|
|
unsigned char *la;
|
|
if (!(la = get_attr_val(t_attr, cast_uchar "label"))) la = stracpy(cast_uchar "");
|
|
new_menu_item(convert_string(ct, la, (int)strlen(cast_const_char la), d_opt), -1, 0);
|
|
mem_free(la);
|
|
group = 1;
|
|
}
|
|
goto see;
|
|
|
|
end_parse:
|
|
*end = en;
|
|
if (!order) goto abort;
|
|
fc = mem_calloc(sizeof(struct form_control));
|
|
if ((unsigned)order > (unsigned)MAXINT / sizeof(unsigned char *)) overalloc();
|
|
lbls = mem_calloc(order * sizeof(unsigned char *));
|
|
fc->form_num = last_form_tag ? (int)(last_form_tag - startf) : 0;
|
|
fc->ctrl_num = last_form_tag ? (int)(attr - last_form_tag) : (int)(attr - startf);
|
|
fc->position = (int)(attr - startf);
|
|
fc->method = form.method;
|
|
fc->action = stracpy(form.action);
|
|
fc->form_name= stracpy(form.form_name);
|
|
fc->onsubmit= stracpy(form.onsubmit);
|
|
fc->name = get_attr_val(attr, cast_uchar "name");
|
|
fc->type = FC_SELECT;
|
|
fc->default_state = preselect < 0 ? 0 : preselect;
|
|
fc->default_value = order ? stracpy(val[fc->default_state]) : stracpy(cast_uchar "");
|
|
fc->ro = has_attr(attr, cast_uchar "disabled") ? 2 : has_attr(attr, cast_uchar "readonly") ? 1 : 0;
|
|
fc->nvalues = order;
|
|
fc->values = val;
|
|
fc->menu = detach_menu();
|
|
fc->labels = lbls;
|
|
menu_labels(fc->menu, cast_uchar "", lbls);
|
|
html_stack_dup();
|
|
format_.attr |= AT_FIXED;
|
|
format_.fontsize = 3;
|
|
put_chrs(cast_uchar "[", 1);
|
|
html_stack_dup();
|
|
get_js_events(attr);
|
|
format_.form = fc;
|
|
format_.attr |= AT_BOLD | AT_FIXED;
|
|
format_.fontsize = 3;
|
|
mw = 0;
|
|
for (i = 0; i < order; i++) if (lbls[i] && cp_len(d_opt->cp, lbls[i]) > mw) mw = cp_len(d_opt->cp, lbls[i]);
|
|
for (i = 0; i < mw; i++) put_chrs(cast_uchar "_", 1);
|
|
kill_html_stack_item(&html_top);
|
|
put_chrs(cast_uchar "]", 1);
|
|
kill_html_stack_item(&html_top);
|
|
special_f(ff, SP_CONTROL, fc);
|
|
return 0;
|
|
}
|
|
|
|
static void html_textarea(unsigned char *a)
|
|
{
|
|
internal_error("This should be never called");
|
|
}
|
|
|
|
static void do_html_textarea(unsigned char *attr, unsigned char *html, unsigned char *eof, unsigned char **end)
|
|
{
|
|
struct form_control *fc;
|
|
unsigned char *p, *t_name, *w;
|
|
int t_namelen;
|
|
int cols, rows;
|
|
int i;
|
|
find_form_for_input(attr);
|
|
while (html < eof && (*html == '\n' || *html == '\r')) html++;
|
|
p = html;
|
|
while (p < eof && *p != '<') {
|
|
pp:
|
|
p++;
|
|
}
|
|
if (p >= eof) {
|
|
*end = eof;
|
|
return;
|
|
}
|
|
if (parse_element(p, eof, &t_name, &t_namelen, NULL, end)) goto pp;
|
|
if (t_namelen != 9 || casecmp(t_name, cast_uchar "/TEXTAREA", 9)) goto pp;
|
|
fc = mem_calloc(sizeof(struct form_control));
|
|
fc->form_num = last_form_tag ? (int)(last_form_tag - startf) : 0;
|
|
fc->ctrl_num = last_form_tag ? (int)(attr - last_form_tag) : (int)(attr - startf);
|
|
fc->position = (int)(attr - startf);
|
|
fc->method = form.method;
|
|
fc->action = stracpy(form.action);
|
|
fc->form_name = stracpy(form.form_name);
|
|
fc->onsubmit = stracpy(form.onsubmit);
|
|
fc->name = get_attr_val(attr, cast_uchar "name");
|
|
fc->type = FC_TEXTAREA;;
|
|
fc->ro = has_attr(attr, cast_uchar "disabled") ? 2 : has_attr(attr, cast_uchar "readonly") ? 1 : 0;
|
|
fc->default_value = memacpy(html, p - html);
|
|
if ((cols = get_num(attr, cast_uchar "cols")) < HTML_MINIMAL_TEXTAREA_WIDTH) cols = HTML_DEFAULT_TEXTAREA_WIDTH;
|
|
cols++;
|
|
set_max_textarea_width(&cols);
|
|
if ((rows = get_num(attr, cast_uchar "rows")) <= 0) rows = HTML_DEFAULT_TEXTAREA_HEIGHT;
|
|
if (!F) {
|
|
if (rows > d_opt->yw) {
|
|
rows = d_opt->yw;
|
|
if (rows <= 0) rows = 1;
|
|
}
|
|
#ifdef G
|
|
} else {
|
|
if (d_opt->font_size && rows > (int)(d_opt->yw - G_SCROLL_BAR_WIDTH) / (int)d_opt->font_size) {
|
|
rows = (int)(d_opt->yw - G_SCROLL_BAR_WIDTH) / (int)d_opt->font_size;
|
|
if (rows <= 0) rows = 1;
|
|
}
|
|
#endif
|
|
}
|
|
fc->cols = cols;
|
|
fc->rows = rows;
|
|
fc->wrap = 1;
|
|
if ((w = get_attr_val(attr, cast_uchar "wrap"))) {
|
|
if (!casestrcmp(w, cast_uchar "hard") || !casestrcmp(w, cast_uchar "physical")) fc->wrap = 2;
|
|
else if (!casestrcmp(w, cast_uchar "off")) fc->wrap = 0;
|
|
mem_free(w);
|
|
}
|
|
if ((fc->maxlength = get_num(attr, cast_uchar "maxlength")) == -1) fc->maxlength = MAXINT / 4;
|
|
if (rows > 1) {
|
|
ln_break(1);
|
|
special_f(ff, SP_FORCE_BREAK);
|
|
} else {
|
|
put_chrs(cast_uchar " ", 1);
|
|
}
|
|
html_stack_dup();
|
|
get_js_events(attr);
|
|
format_.form = fc;
|
|
format_.attr = AT_BOLD | AT_FIXED;
|
|
#ifdef G
|
|
if (F) format_.attr &= ~AT_BOLD;
|
|
#endif
|
|
format_.fontsize = 3;
|
|
for (i = 0; i < rows; i++) {
|
|
int j;
|
|
for (j = 0; j < cols; j++) put_chrs(cast_uchar "_", 1);
|
|
if (i < rows - 1) ln_break(1);
|
|
}
|
|
kill_html_stack_item(&html_top);
|
|
if (rows > 1) ln_break(1);
|
|
else put_chrs(cast_uchar " ", 1);
|
|
special_f(ff, SP_CONTROL, fc);
|
|
}
|
|
|
|
static void html_iframe(unsigned char *a)
|
|
{
|
|
unsigned char *name, *url;
|
|
if (!(url = get_url_val(a, cast_uchar "src"))) return;
|
|
if (!*url) goto free_url_ret;
|
|
if (!(name = get_attr_val(a, cast_uchar "name"))) name = stracpy(cast_uchar "");
|
|
if (*name) put_link_line(cast_uchar "IFrame: ", name, url, d_opt->framename);
|
|
else put_link_line(cast_uchar "", cast_uchar "IFrame", url, d_opt->framename);
|
|
mem_free(name);
|
|
free_url_ret:
|
|
mem_free(url);
|
|
}
|
|
|
|
static void html_noframes(unsigned char *a)
|
|
{
|
|
if (d_opt->frames) html_skip(a);
|
|
}
|
|
|
|
static void html_frame(unsigned char *a)
|
|
{
|
|
unsigned char *name, *u2, *url;
|
|
if (!(u2 = get_url_val(a, cast_uchar "src"))) {
|
|
url = stracpy(cast_uchar "");
|
|
} else {
|
|
url = join_urls(format_.href_base, u2);
|
|
mem_free(u2);
|
|
}
|
|
name = get_attr_val(a, cast_uchar "name");
|
|
if (name && !name[0])
|
|
mem_free(name), name = NULL;
|
|
if (!name) {
|
|
name = get_attr_val(a, cast_uchar "src");
|
|
if (!name)
|
|
name = stracpy(cast_uchar "Frame");
|
|
}
|
|
if (!d_opt->frames || !html_top.frameset) put_link_line(cast_uchar "Frame: ", name, url, cast_uchar "");
|
|
else {
|
|
struct frame_param fp;
|
|
unsigned char *scroll = get_attr_val(a, cast_uchar "scrolling");
|
|
fp.name = name;
|
|
fp.url = url;
|
|
fp.parent = html_top.frameset;
|
|
fp.marginwidth = get_num(a, cast_uchar "marginwidth");
|
|
fp.marginheight = get_num(a, cast_uchar "marginheight");
|
|
fp.scrolling = SCROLLING_AUTO;
|
|
if (scroll) {
|
|
if (!casestrcmp(scroll, cast_uchar "no"))
|
|
fp.scrolling = SCROLLING_NO;
|
|
else if (!casestrcmp(scroll, cast_uchar "yes"))
|
|
fp.scrolling = SCROLLING_YES;
|
|
mem_free(scroll);
|
|
}
|
|
if (special_f(ff, SP_USED, NULL)) special_f(ff, SP_FRAME, &fp);
|
|
}
|
|
mem_free(name);
|
|
mem_free(url);
|
|
}
|
|
|
|
static void parse_frame_widths(unsigned char *a, int ww, int www, int **op, int *olp)
|
|
{
|
|
unsigned char *aa;
|
|
char *end;
|
|
int q, qq, i, d, nn;
|
|
unsigned long n;
|
|
int *oo, *o;
|
|
int ol;
|
|
ol = 0;
|
|
o = DUMMY;
|
|
new_ch:
|
|
while (WHITECHAR(*a)) a++;
|
|
n = strtoul(cast_const_char a, &end, 10);
|
|
a = cast_uchar end;
|
|
if (n > 10000) n = 10000;
|
|
q = (int)n;
|
|
if (*a == '%') q = q * ww / 100;
|
|
else if (*a != '*') q = (q + (www - 1) / 2) / (www ? www : 1);
|
|
else if (!(q = -q)) q = -1;
|
|
if ((unsigned)ol > MAXINT / sizeof(int) - 1) overalloc();
|
|
o = mem_realloc(o, (ol + 1) * sizeof(int));
|
|
o[ol++] = q;
|
|
if ((aa = cast_uchar strchr(cast_const_char a, ','))) {
|
|
a = aa + 1;
|
|
goto new_ch;
|
|
}
|
|
*op = o;
|
|
*olp = ol;
|
|
q = gf_val(2 * ol - 1, ol);
|
|
for (i = 0; i < ol; i++) if (o[i] > 0) q += o[i] - 1;
|
|
if (q >= ww) {
|
|
distribute:
|
|
for (i = 0; i < ol; i++) if (o[i] < 1) o[i] = 1;
|
|
q -= ww;
|
|
d = 0;
|
|
for (i = 0; i < ol; i++) d += o[i];
|
|
qq = q;
|
|
for (i = 0; i < ol; i++) {
|
|
q -= o[i] - o[i] * (d - qq) / (d ? d : 1);
|
|
#ifdef __GNUC__
|
|
#if __GNUC__ == 2
|
|
do_not_optimize_here(&d);
|
|
#endif
|
|
#endif
|
|
/* SIGH! gcc 2.7.2.* has an optimizer bug! */
|
|
o[i] = o[i] * (d - qq) / (d ? d : 1);
|
|
}
|
|
while (q) {
|
|
nn = 0;
|
|
for (i = 0; i < ol; i++) {
|
|
if (q < 0) o[i]++, q++, nn = 1;
|
|
if (q > 0 && o[i] > 1) o[i]--, q--, nn = 1;
|
|
if (!q) break;
|
|
}
|
|
if (!nn) break;
|
|
}
|
|
} else {
|
|
int nn = 0;
|
|
for (i = 0; i < ol; i++) if (o[i] < 0) nn = 1;
|
|
if (!nn) goto distribute;
|
|
if ((unsigned)ol > MAXINT / sizeof(int)) overalloc();
|
|
oo = mem_alloc(ol * sizeof(int));
|
|
memcpy(oo, o, ol * sizeof(int));
|
|
for (i = 0; i < ol; i++) if (o[i] < 1) o[i] = 1;
|
|
q = ww - q;
|
|
d = 0;
|
|
for (i = 0; i < ol; i++) if (oo[i] < 0) d += -oo[i];
|
|
qq = q;
|
|
for (i = 0; i < ol; i++) if (oo[i] < 0) {
|
|
o[i] += (-oo[i] * qq / (d ? d : 1));
|
|
q -= (-oo[i] * qq / (d ? d : 1));
|
|
}
|
|
if (q < 0) {
|
|
q = 0;
|
|
/*internal_error("parse_frame_widths: q < 0"); may happen when page contains too big values */
|
|
}
|
|
for (i = 0; i < ol; i++) if (oo[i] < 0) {
|
|
if (q) o[i]++, q--;
|
|
}
|
|
if (q > 0) {
|
|
q = 0;
|
|
/*internal_error("parse_frame_widths: q > 0"); may happen when page contains too big values */
|
|
}
|
|
mem_free(oo);
|
|
}
|
|
for (i = 0; i < ol; i++) if (!o[i]) {
|
|
int j;
|
|
int m = 0;
|
|
int mj = 0;
|
|
for (j = 0; j < ol; j++) if (o[j] > m) m = o[j], mj = j;
|
|
if (m) o[i] = 1, o[mj]--;
|
|
}
|
|
}
|
|
|
|
static void html_frameset(unsigned char *a)
|
|
{
|
|
int x = 0, y = 0; /* against warning */
|
|
struct frameset_param fp;
|
|
unsigned char *c, *d;
|
|
if (!d_opt->frames || !special_f(ff, SP_USED, NULL)) return;
|
|
if (!(c = get_attr_val(a, cast_uchar "cols"))) c = stracpy(cast_uchar "100%");
|
|
if (!(d = get_attr_val(a, cast_uchar "rows"))) d = stracpy(cast_uchar "100%");
|
|
if (!html_top.frameset) {
|
|
x = d_opt->xw;
|
|
y = d_opt->yw;
|
|
} else {
|
|
struct frameset_desc *f = html_top.frameset;
|
|
if (f->yp >= f->y) goto free_cd;
|
|
x = f->f[f->xp + f->yp * f->x].xw;
|
|
y = f->f[f->xp + f->yp * f->x].yw;
|
|
}
|
|
parse_frame_widths(c, x, gf_val(HTML_FRAME_CHAR_WIDTH, 1), &fp.xw, &fp.x);
|
|
parse_frame_widths(d, y, gf_val(HTML_FRAME_CHAR_HEIGHT, 1), &fp.yw, &fp.y);
|
|
fp.parent = html_top.frameset;
|
|
if (fp.x && fp.y) {
|
|
html_top.frameset = special_f(ff, SP_FRAMESET, &fp);
|
|
#ifdef JS
|
|
if (html_top.frameset)html_top.frameset->onload_code=get_attr_val(a,cast_uchar "onload");
|
|
#endif
|
|
}
|
|
mem_free(fp.xw);
|
|
mem_free(fp.yw);
|
|
free_cd:
|
|
mem_free(c);
|
|
mem_free(d);
|
|
}
|
|
|
|
/*static void html_frameset(unsigned char *a)
|
|
{
|
|
int w;
|
|
int horiz = 0;
|
|
struct frameset_param *fp;
|
|
unsigned char *c, *d;
|
|
if (!d_opt->frames || !special_f(ff, SP_USED, NULL)) return;
|
|
if (!(c = get_attr_val(a, cast_uchar "cols"))) {
|
|
horiz = 1;
|
|
if (!(c = get_attr_val(a, cast_uchar "rows"))) return;
|
|
}
|
|
fp = mem_alloc(sizeof(struct frameset_param));
|
|
fp->n = 0;
|
|
fp->horiz = horiz;
|
|
par_format.leftmargin = par_format.rightmargin = 0;
|
|
d = c;
|
|
while (1) {
|
|
while (WHITECHAR(*d)) d++;
|
|
if (!*d) break;
|
|
if (*d == ',') {
|
|
d++;
|
|
continue;
|
|
}
|
|
if ((w = parse_width(d, 1)) != -1) {
|
|
if ((unsigned)fp->n > (MAXINT - sizeof(struct frameset_param)) / sizeof(int) - 1) overalloc();
|
|
fp = mem_realloc(fp, sizeof(struct frameset_param) + (fp->n + 1) * sizeof(int));
|
|
fp->width[fp->n++] = w;
|
|
}
|
|
if (!(d = cast_uchar strchr(cast_const_char d, ','))) break;
|
|
d++;
|
|
}
|
|
fp->parent = html_top.frameset;
|
|
if (fp->n) html_top.frameset = special_f(ff, SP_FRAMESET, fp);
|
|
mem_free(fp);
|
|
f:
|
|
mem_free(c);
|
|
}*/
|
|
|
|
static void html_meta(unsigned char *a)
|
|
{
|
|
unsigned char *prop;
|
|
if ((prop = get_attr_val(a, cast_uchar "property"))) {
|
|
if (!strcmp(cast_const_char prop, "og:image")) {
|
|
unsigned char *host = get_host_name(format_.href_base);
|
|
if (host) {
|
|
if (casestrstr(host, cast_uchar "facebook.") ||
|
|
casestrstr(host, cast_uchar "flickr.") ||
|
|
casestrstr(host, cast_uchar "imgur.") ||
|
|
casestrstr(host, cast_uchar "instagram.") ||
|
|
casestrstr(host, cast_uchar "mastodon.") ||
|
|
casestrstr(host, cast_uchar "pinterest.") ||
|
|
casestrstr(host, cast_uchar "twitter."))
|
|
html_img(a);
|
|
mem_free(host);
|
|
}
|
|
}
|
|
mem_free(prop);
|
|
}
|
|
}
|
|
|
|
static void html_link(unsigned char *a)
|
|
{
|
|
unsigned char *name, *url, *title;
|
|
if ((name = get_attr_val(a, cast_uchar "type"))) {
|
|
if (casestrcmp(name, cast_uchar "text/html")) {
|
|
mem_free(name);
|
|
return;
|
|
}
|
|
mem_free(name);
|
|
}
|
|
if (!(url = get_url_val(a, cast_uchar "href"))) return;
|
|
if (!(name = get_attr_val(a, cast_uchar "rel")))
|
|
if (!(name = get_attr_val(a, cast_uchar "rev")))
|
|
name = get_attr_val(a, cast_uchar "ref");
|
|
if (name) {
|
|
unsigned char *lang;
|
|
if ((lang = get_attr_val(a, cast_uchar "hreflang"))) {
|
|
add_to_strn(&name, cast_uchar " ");
|
|
add_to_strn(&name, lang);
|
|
mem_free(lang);
|
|
}
|
|
}
|
|
if (!name) {
|
|
if (has_attr(a, cast_uchar "title"))
|
|
name = stracpy(cast_uchar "");
|
|
else {
|
|
name = get_attr_val(a, cast_uchar "href");
|
|
if (!name)
|
|
name = stracpy(cast_uchar "Link");
|
|
}
|
|
}
|
|
if (
|
|
!casecmp(name, cast_uchar "schema", 6) ||
|
|
!casecmp(name, cast_uchar "mw-", 3) ||
|
|
!casestrcmp(name, cast_uchar "Edit-Time-Data") ||
|
|
!casestrcmp(name, cast_uchar "File-List") ||
|
|
!casestrcmp(name, cast_uchar "alternate stylesheet") ||
|
|
!casestrcmp(name, cast_uchar "generator-home") ||
|
|
!casestrcmp(name, cast_uchar "https://api.w.org/") ||
|
|
!casestrcmp(name, cast_uchar "https://github.com/WP-API/WP-API") ||
|
|
!casestrcmp(name, cast_uchar "made") ||
|
|
!casestrcmp(name, cast_uchar "manifest") ||
|
|
!casestrcmp(name, cast_uchar "meta") ||
|
|
!casestrcmp(name, cast_uchar "pingback") ||
|
|
!casestrcmp(name, cast_uchar "preconnect") ||
|
|
!casestrcmp(name, cast_uchar "stylesheet") ||
|
|
casestrstr(name, cast_uchar "icon") ||
|
|
0) goto skip;
|
|
if (!casestrcmp(name, cast_uchar "prefetch") ||
|
|
!casestrcmp(name, cast_uchar "prerender") ||
|
|
!casestrcmp(name, cast_uchar "preload")) {
|
|
unsigned char *pre_url = join_urls(format_.href_base, url);
|
|
if (!dmp) load_url(pre_url, format_.href_base, NULL, PRI_PRELOAD, NC_ALWAYS_CACHE, 0, 0, 0, 0);
|
|
mem_free(pre_url);
|
|
goto skip;
|
|
}
|
|
if (!casestrcmp(name, cast_uchar "dns-prefetch")) {
|
|
unsigned char *pre_url, *host;
|
|
if (dmp || *proxies.socks_proxy || proxies.only_proxies) {
|
|
goto skip;
|
|
}
|
|
pre_url = join_urls(format_.href_base, url);
|
|
if (get_proxy_string(pre_url) || is_noproxy_url(pre_url)) {
|
|
mem_free(pre_url);
|
|
goto skip;
|
|
}
|
|
host = get_host_name(pre_url);
|
|
mem_free(pre_url);
|
|
dns_prefetch(host);
|
|
mem_free(host);
|
|
goto skip;
|
|
}
|
|
if ((title = get_attr_val(a, cast_uchar "title"))) {
|
|
if (*name) add_to_strn(&name, cast_uchar ": ");
|
|
add_to_strn(&name, title);
|
|
mem_free(title);
|
|
}
|
|
put_link_line(cast_uchar "Link: ", name, url, format_.target_base);
|
|
skip:
|
|
mem_free(name);
|
|
mem_free(url);
|
|
}
|
|
|
|
struct element_info {
|
|
char *name;
|
|
void (*func)(unsigned char *);
|
|
int linebreak;
|
|
int nopair; /* Somehow relates to paired elements */
|
|
};
|
|
|
|
static struct element_info elements[] = {
|
|
{"SPAN", html_span, 0, 0},
|
|
{"B", html_bold, 0, 0},
|
|
{"STRONG", html_bold, 0, 0},
|
|
{"DFN", html_bold, 0, 0},
|
|
{"I", html_italic, 0, 0},
|
|
{"Q", html_italic, 0, 0},
|
|
{"CITE", html_italic, 0, 0},
|
|
{"EM", html_italic, 0, 0},
|
|
{"ABBR", html_italic, 0, 0},
|
|
{"U", html_underline, 0, 0},
|
|
{"INS", html_underline, 0, 0},
|
|
{"S", html_strike, 0, 0},
|
|
{"DEL", html_strike, 0, 0},
|
|
{"STRIKE", html_strike, 0, 0},
|
|
{"FIXED", html_fixed, 0, 0},
|
|
{"CODE", html_fixed, 0, 0},
|
|
{"TT", html_fixed, 0, 0},
|
|
{"SAMP", html_fixed, 0, 0},
|
|
{"SUB", html_sub, 0, 0},
|
|
{"SUP", html_sup, 0, 0},
|
|
{"FONT", html_font, 0, 0},
|
|
{"INVERT", html_invert, 0, 0},
|
|
{"A", html_a, 0, 2},
|
|
{"IMG", html_img, 0, 1},
|
|
{"IMAGE", html_img, 0, 1},
|
|
{"OBJECT", html_object, 0, 0},
|
|
{"EMBED", html_embed, 0, 1},
|
|
|
|
{"BASE", html_base, 0, 1},
|
|
{"BASEFONT", html_font, 0, 1},
|
|
|
|
{"BODY", html_body, 0, 0},
|
|
|
|
/* {"HEAD", html_skip, 0, 0},*/
|
|
{"TITLE", html_title, 0, 0},
|
|
{"SCRIPT", html_script, 0, 0},
|
|
{"NOSCRIPT", html_noscript, 0, 0},
|
|
{"STYLE", html_style, 0, 0},
|
|
{"NOEMBED", html_skip, 0, 0},
|
|
|
|
{"BR", html_br, 1, 1},
|
|
{"DIV", html_div, 1, 0},
|
|
{"CENTER", html_center, 1, 0},
|
|
{"CAPTION", html_center, 1, 0},
|
|
{"P", html_p, 2, 2},
|
|
{"HR", html_hr, 2, 1},
|
|
{"H1", html_h1, 2, 2},
|
|
{"H2", html_h2, 2, 2},
|
|
{"H3", html_h3, 2, 2},
|
|
{"H4", html_h4, 2, 2},
|
|
{"H5", html_h5, 2, 2},
|
|
{"H6", html_h6, 2, 2},
|
|
{"BLOCKQUOTE", html_blockquote,2, 0},
|
|
{"ADDRESS", html_address, 2, 0},
|
|
{"PRE", html_pre, 2, 0},
|
|
{"LISTING", html_pre, 2, 0},
|
|
|
|
{"UL", html_ul, 1, 0},
|
|
{"DIR", html_ul, 1, 0},
|
|
{"MENU", html_ul, 1, 0},
|
|
{"OL", html_ol, 1, 0},
|
|
{"LI", html_li, 1, 3},
|
|
{"DL", html_dl, 1, 0},
|
|
{"DT", html_dt, 1, 1},
|
|
{"DD", html_dd, 1, 1},
|
|
|
|
{"TABLE", html_table, 2, 0},
|
|
{"TR", html_tr, 1, 0},
|
|
{"TD", html_td, 0, 0},
|
|
{"TH", html_th, 0, 0},
|
|
|
|
{"FORM", html_form, 1, 0},
|
|
{"INPUT", html_input, 0, 1},
|
|
{"TEXTAREA", html_textarea, 0, 1},
|
|
{"SELECT", html_select, 0, 0},
|
|
{"OPTION", html_option, 1, 1},
|
|
{"BUTTON", html_button, 0, 0},
|
|
|
|
{"META", html_meta, 0, 1},
|
|
{"LINK", html_link, 0, 1},
|
|
{"IFRAME", html_iframe, 1, 1},
|
|
{"FRAME", html_frame, 1, 1},
|
|
{"FRAMESET", html_frameset, 1, 0},
|
|
{"NOFRAMES", html_noframes, 0, 0},
|
|
};
|
|
|
|
unsigned char *skip_comment(unsigned char *html, unsigned char *eof)
|
|
{
|
|
int comm = eof - html >= 4 && html[2] == '-' && html[3] == '-';
|
|
html += comm ? 4 : 2;
|
|
while (html < eof) {
|
|
if (!comm && html[0] == '>') return html + 1;
|
|
if (comm && eof - html >= 2 && html[0] == '-' && html[1] == '-') {
|
|
html += 2;
|
|
while (html < eof && (*html == '-' || *html == '!')) html++;
|
|
while (html < eof && WHITECHAR(*html)) html++;
|
|
if (html >= eof) return eof;
|
|
if (*html == '>') return html + 1;
|
|
continue;
|
|
}
|
|
html++;
|
|
}
|
|
return eof;
|
|
}
|
|
|
|
static void process_head(unsigned char *head)
|
|
{
|
|
unsigned char *r, *p;
|
|
struct refresh_param rp;
|
|
if ((r = parse_http_header(head, cast_uchar "Refresh", NULL))) {
|
|
if (!d_opt->auto_refresh) {
|
|
if ((p = parse_header_param(r, cast_uchar "URL", 0)) || (p = parse_header_param(r, cast_uchar "", 0))) {
|
|
put_link_line(cast_uchar "Refresh: ", p, p, d_opt->framename);
|
|
mem_free(p);
|
|
}
|
|
} else {
|
|
rp.url = parse_header_param(r, cast_uchar "URL", 0);
|
|
if (!rp.url) rp.url = parse_header_param(r, cast_uchar "", 0);
|
|
rp.time = atoi(cast_const_char r);
|
|
if (rp.time < 1) rp.time = 1;
|
|
special_f(ff, SP_REFRESH, &rp);
|
|
if (rp.url) mem_free(rp.url);
|
|
}
|
|
mem_free(r);
|
|
}
|
|
}
|
|
|
|
static int qd(unsigned char *html, unsigned char *eof, int *len)
|
|
{
|
|
int l;
|
|
*len = 1;
|
|
if (html >= eof) {
|
|
internal_error("qd: out of data, html == %p, eof == %p", html, eof);
|
|
return -1;
|
|
}
|
|
if (html[0] != '&' || d_opt->plain & 1) return html[0];
|
|
if (eof - html >= 5 && !memcmp(html + 1, "Tab;", 4)) {
|
|
*len = 5;
|
|
return 9;
|
|
}
|
|
if (eof - html <= 1) return -1;
|
|
if (html[1] != '#') return -1;
|
|
for (l = 2; l < 10 && eof - html > l; l++) if (html[l] == ';') {
|
|
int n = get_entity_number(html + 2, l - 2);
|
|
if (n >= 0) {
|
|
*len = l + 1;
|
|
return n;
|
|
}
|
|
break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void parse_html(unsigned char *html, unsigned char *eof, void (*put_chars)(void *, unsigned char *, int), void (*line_break)(void *), void *(*special)(void *, int, ...), void *f, unsigned char *head)
|
|
{
|
|
unsigned char *lt;
|
|
|
|
html_format_changed = 1;
|
|
putsp = -1;
|
|
line_breax = table_level ? 2 : 1;
|
|
pos = 0;
|
|
was_br = 0;
|
|
|
|
#define set_globals \
|
|
do { \
|
|
put_chars_f = put_chars; \
|
|
line_break_f = line_break; \
|
|
special_f = special; \
|
|
ff = f; \
|
|
eoff = eof; \
|
|
} while (0)
|
|
|
|
set_globals;
|
|
|
|
if (head) process_head(head);
|
|
|
|
set_lt:
|
|
|
|
/*set_globals;*/
|
|
|
|
lt = html;
|
|
while (html < eof) {
|
|
unsigned char *name, *attr, *end;
|
|
unsigned char *a;
|
|
int namelen;
|
|
struct element_info *ei;
|
|
int inv;
|
|
if (par_format.align != AL_NO && par_format.align != AL_NO_BREAKABLE && WHITECHAR(*html)) {
|
|
unsigned char *h = html;
|
|
/*if (putsp == -1) {
|
|
while (html < eof && WHITECHAR(*html)) html++;
|
|
goto set_lt;
|
|
}
|
|
putsp = 0;*/
|
|
while (h < eof && WHITECHAR(*h)) h++;
|
|
if (eof - h > 1 && h[0] == '<' && h[1] == '/') {
|
|
if (!parse_element(h, eof, &name, &namelen, &attr, &end)) {
|
|
put_chrs(lt, (int)(html - lt));
|
|
lt = html = h;
|
|
if (!html_top.invisible) putsp = 1;
|
|
goto element;
|
|
}
|
|
}
|
|
html++;
|
|
if (!(pos + (html-lt-1))) goto skip_w; /* ??? */
|
|
if (*(html - 1) == ' ') {
|
|
if (html < eof && !WHITECHAR(*html)) continue; /* BIG performance win; not sure if it doesn't cause any bug */
|
|
put_chrs(lt, (int)(html - lt));
|
|
} else {
|
|
put_chrs(lt, (int)(html - 1 - lt));
|
|
put_chrs(cast_uchar " ", 1);
|
|
}
|
|
skip_w:
|
|
while (html < eof && WHITECHAR(*html)) html++;
|
|
/*putsp = -1;*/
|
|
goto set_lt;
|
|
}
|
|
if (0) {
|
|
put_sp:
|
|
put_chrs(cast_uchar " ", 1);
|
|
/*putsp = -1;*/
|
|
}
|
|
if ((par_format.align == AL_NO || par_format.align == AL_NO_BREAKABLE) && (*html < 32 || *html == '&')) {
|
|
int l;
|
|
int q = qd(html, eof, &l);
|
|
putsp = 0;
|
|
if (q == 9) {
|
|
put_chrs(lt, (int)(html - lt));
|
|
put_chrs(cast_uchar " ", 8 - pos % 8);
|
|
html += l;
|
|
goto set_lt;
|
|
} else if (q == 13 || q == 10) {
|
|
put_chrs(lt, (int)(html - lt));
|
|
next_break:
|
|
html += l;
|
|
if (q == 13 && eof - html > 1 && qd(html, eof, &l) == 10) html += l;
|
|
ln_break(1);
|
|
if (html >= eof) goto set_lt;
|
|
q = qd(html, eof, &l);
|
|
if (q == 13 || q == 10) {
|
|
line_breax = 0;
|
|
goto next_break;
|
|
}
|
|
goto set_lt;
|
|
}
|
|
}
|
|
if (*html < ' ') {
|
|
int xl;
|
|
put_chrs(lt, (int)(html - lt));
|
|
xl = 1;
|
|
while (xl < 240 && eof - html > xl && html[xl] < ' ' && html[xl] != 9 && html[xl] != 10 && html[xl] != 13) xl++;
|
|
put_chrs(cast_uchar "................................................................................................................................................................................................................................................", xl);
|
|
html += xl;
|
|
goto set_lt;
|
|
}
|
|
if (eof - html >= 2 && html[0] == '<' && (html[1] == '!' || html[1] == '?') && !(d_opt->plain & 1) && html_top.invisible != INVISIBLE_STYLE) {
|
|
/*if (putsp == 1) goto put_sp;
|
|
putsp = 0;*/
|
|
put_chrs(lt, (int)(html - lt));
|
|
html = skip_comment(html, eof);
|
|
goto set_lt;
|
|
}
|
|
if (*html != '<' || d_opt->plain & 1 || parse_element(html, eof, &name, &namelen, &attr, &end)) {
|
|
/*if (putsp == 1) goto put_sp;
|
|
putsp = 0;*/
|
|
html++;
|
|
continue;
|
|
}
|
|
element:
|
|
html_format_changed = 1;
|
|
inv = *name == '/'; name += inv; namelen -= inv;
|
|
if (html_top.invisible == INVISIBLE_SCRIPT && !(inv && namelen == 6 && !casecmp(name, cast_uchar "SCRIPT", 6))) {
|
|
html++;
|
|
continue;
|
|
}
|
|
if (html_top.invisible == INVISIBLE_STYLE && !(inv && namelen == 5 && !casecmp(name, cast_uchar "STYLE", 5))) {
|
|
html++;
|
|
continue;
|
|
}
|
|
if (!inv && putsp == 1 && !html_top.invisible) goto put_sp;
|
|
put_chrs(lt, (int)(html - lt));
|
|
if (par_format.align != AL_NO && par_format.align != AL_NO_BREAKABLE) if (!inv && !putsp) {
|
|
unsigned char *ee = end;
|
|
unsigned char *nm;
|
|
while (!parse_element(ee, eof, &nm, NULL, NULL, &ee))
|
|
if (*nm == '/') goto ng;
|
|
if (ee < eof && WHITECHAR(*ee)) {
|
|
/*putsp = -1;*/
|
|
put_chrs(cast_uchar " ", 1);
|
|
}
|
|
ng:;
|
|
}
|
|
html = end;
|
|
for (ei = elements; ei != endof(elements); ei++) {
|
|
if (strlen(cast_const_char ei->name) != (size_t)namelen || casecmp(cast_uchar ei->name, name, namelen))
|
|
continue;
|
|
if (ei - elements > 4) {
|
|
struct element_info e = *ei;
|
|
memmove(elements + 1, elements, (ei - elements) * sizeof(struct element_info));
|
|
elements[0] = e;
|
|
ei = &elements[0];
|
|
}
|
|
if (!inv) {
|
|
int display_none = 0;
|
|
int noskip = 0;
|
|
/* treat <br> literally in <pre> (fixes source code viewer on github) */
|
|
if ((par_format.align == AL_NO || par_format.align == AL_NO_BREAKABLE) && !casestrcmp(cast_uchar ei->name, cast_uchar "BR"))
|
|
line_breax = 0;
|
|
ln_break(ei->linebreak);
|
|
if ((a = get_attr_val(attr, cast_uchar "id"))) {
|
|
special(f, SP_TAG, a);
|
|
mem_free(a);
|
|
}
|
|
if ((a = get_attr_val(attr, cast_uchar "style"))) {
|
|
unsigned char *d, *s;
|
|
|
|
if (!casestrcmp(cast_uchar ei->name, cast_uchar "INPUT")) {
|
|
unsigned char *aa = get_attr_val(attr, cast_uchar "type");
|
|
if (aa) {
|
|
if (!casestrcmp(aa, cast_uchar "hidden"))
|
|
noskip = 1;
|
|
mem_free(aa);
|
|
}
|
|
}
|
|
|
|
for (d = s = a; *s; s++) if (*s > ' ') *d++ = *s;
|
|
*d = 0;
|
|
display_none |= !casecmp(a, cast_uchar "display:none", 12) && !noskip;
|
|
mem_free(a);
|
|
}
|
|
if (display_none) {
|
|
if (ei->nopair == 1) goto set_lt;
|
|
html_stack_dup();
|
|
html_top.name = name;
|
|
html_top.namelen = namelen;
|
|
html_top.options = attr;
|
|
html_top.linebreak = 0;
|
|
html_top.invisible = INVISIBLE;
|
|
goto set_lt;
|
|
}
|
|
if (!html_top.invisible) {
|
|
int a = par_format.align == AL_NO || par_format.align == AL_NO_BREAKABLE;
|
|
struct par_attrib pa = par_format;
|
|
if (ei->func == html_table && d_opt->tables && table_level < HTML_MAX_TABLE_LEVEL) {
|
|
format_table(attr, html, eof, &html, f);
|
|
set_globals;
|
|
ln_break(2);
|
|
goto set_lt;
|
|
}
|
|
if (ei->func == html_select) {
|
|
if (!do_html_select(attr, html, eof, &html))
|
|
goto set_lt;
|
|
}
|
|
if (ei->func == html_textarea) {
|
|
do_html_textarea(attr, html, eof, &html);
|
|
goto set_lt;
|
|
}
|
|
if (ei->nopair == 2 || ei->nopair == 3) {
|
|
struct html_element *e = NULL; /* against warning */
|
|
struct list_head *le;
|
|
if (ei->nopair == 2) {
|
|
foreach(struct html_element, e, le, html_stack) {
|
|
if (e->dontkill) break;
|
|
if (e->linebreak || !ei->linebreak) break;
|
|
}
|
|
} else foreach(struct html_element, e, le, html_stack) {
|
|
if (e->linebreak && !ei->linebreak) break;
|
|
if (e->dontkill) break;
|
|
if (e->namelen == namelen && !casecmp(e->name, name, e->namelen)) break;
|
|
}
|
|
if (e->namelen == namelen && !casecmp(e->name, name, e->namelen)) {
|
|
while (e->list_entry.prev != &html_stack) kill_html_stack_item(list_struct(e->list_entry.prev, struct html_element));
|
|
if (e->dontkill != 2) kill_html_stack_item(e);
|
|
}
|
|
}
|
|
if (ei->nopair != 1) {
|
|
html_stack_dup();
|
|
html_top.name = name;
|
|
html_top.namelen = namelen;
|
|
html_top.options = attr;
|
|
html_top.linebreak = ei->linebreak;
|
|
}
|
|
if (ei->func) ei->func(attr);
|
|
if (ei->func == html_a) html_a_special(attr, html, eof);
|
|
if (ei->func != html_br) was_br = 0;
|
|
if (a) par_format = pa;
|
|
} else {
|
|
if (!ei->nopair) {
|
|
html_stack_dup();
|
|
html_top.name = name;
|
|
html_top.namelen = namelen;
|
|
html_top.options = attr;
|
|
html_top.linebreak = 0;
|
|
html_top.invisible = INVISIBLE;
|
|
}
|
|
}
|
|
} else {
|
|
struct html_element *e, *fx;
|
|
struct list_head *le, *lfx;
|
|
int lnb = 0;
|
|
int xxx = 0;
|
|
was_br = 0;
|
|
if (ei->nopair == 1 || ei->nopair == 3) break;
|
|
/*debug_stack();*/
|
|
foreach(struct html_element, e, le, html_stack) {
|
|
if (e->linebreak && !ei->linebreak) xxx = 1;
|
|
if (e->namelen != namelen || casecmp(e->name, name, e->namelen)) {
|
|
if (e->dontkill) break;
|
|
else continue;
|
|
}
|
|
if (xxx) {
|
|
kill_html_stack_item(e);
|
|
break;
|
|
}
|
|
foreachbackfrom(struct html_element, fx, lfx, html_stack, le)
|
|
if (fx->linebreak > lnb) lnb = fx->linebreak;
|
|
format_.fontsize = list_struct(e->list_entry.next, struct html_element)->attr.fontsize;
|
|
ln_break(lnb);
|
|
while (e->list_entry.prev != &html_stack) kill_html_stack_item(list_struct(e->list_entry.prev, struct html_element));
|
|
if (e->namelen == 4 && !casecmp(e->name, cast_uchar "SPAN", 4)) { /* Wikipedia hack */
|
|
unsigned char *al;
|
|
if ((al = get_attr_val(e->options, cast_uchar "class"))) {
|
|
if (!strcmp(cast_const_char al, "vector-toc-numb"))
|
|
putsp = 1;
|
|
mem_free(al);
|
|
}
|
|
}
|
|
if (e->namelen == 2 && (!casecmp(e->name, cast_uchar "UL", 2) || !casecmp(e->name, cast_uchar "OL", 2))) { /* Wikipedia hack */
|
|
while (html < eof && WHITECHAR(*html))
|
|
html++;
|
|
}
|
|
kill_html_stack_item(e);
|
|
break;
|
|
}
|
|
/*debug_stack();*/
|
|
}
|
|
goto set_lt;
|
|
}
|
|
if (!inv) {
|
|
if ((a = get_attr_val(attr, cast_uchar "id"))) {
|
|
special(f, SP_TAG, a);
|
|
mem_free(a);
|
|
}
|
|
}
|
|
goto set_lt;
|
|
}
|
|
put_chrs(lt, (int)(html - lt));
|
|
ln_break(1);
|
|
putsp = -1;
|
|
pos = 0;
|
|
/*line_breax = 1;*/
|
|
was_br = 0;
|
|
#undef set_globals
|
|
}
|
|
|
|
static void scan_area_tag(unsigned char *attr, unsigned char *name, unsigned char **ptr, struct memory_list **ml)
|
|
{
|
|
unsigned char *v;
|
|
if ((v = get_attr_val(attr, name))) {
|
|
*ptr = v;
|
|
add_to_ml(ml, v, NULL);
|
|
}
|
|
}
|
|
|
|
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, int gfx)
|
|
{
|
|
unsigned char *name, *attr, *al, *label, *href, *target;
|
|
int namelen, lblen;
|
|
struct link_def *ld;
|
|
struct menu_item *nm;
|
|
int nmenu = 0;
|
|
int i;
|
|
unsigned char *hd = init_str();
|
|
int hdl = 0;
|
|
struct conv_table *ct;
|
|
if (head) add_to_str(&hd, &hdl, head);
|
|
scan_http_equiv(s, eof, &hd, &hdl, NULL, NULL, NULL, NULL, NULL);
|
|
if (!gfx) ct = get_convert_table(hd, to, def, NULL, NULL, hdef);
|
|
else ct = convert_table;
|
|
mem_free(hd);
|
|
*menu = mem_calloc(sizeof(struct menu_item));
|
|
se:
|
|
while (s < eof && *s != '<') {
|
|
sp:
|
|
s++;
|
|
}
|
|
if (s >= eof) {
|
|
mem_free(*menu);
|
|
return -1;
|
|
}
|
|
if (eof - s >= 2 && (s[1] == '!' || s[1] == '?')) {
|
|
s = skip_comment(s, eof);
|
|
goto se;
|
|
}
|
|
if (parse_element(s, eof, &name, &namelen, &attr, &s)) goto sp;
|
|
if (namelen != 3 || casecmp(name, cast_uchar "MAP", 3)) goto se;
|
|
if (tag && *tag) {
|
|
if (!(al = get_attr_val(attr, cast_uchar "name"))) goto se;
|
|
if (casestrcmp(al, tag)) {
|
|
mem_free(al);
|
|
goto se;
|
|
}
|
|
mem_free(al);
|
|
}
|
|
*ml = getml(NULL);
|
|
se2:
|
|
while (s < eof && *s != '<') {
|
|
sp2:
|
|
s++;
|
|
}
|
|
if (s >= eof) {
|
|
freeml(*ml);
|
|
mem_free(*menu);
|
|
return -1;
|
|
}
|
|
if (eof - s >= 2 && (s[1] == '!' || s[1] == '?')) {
|
|
s = skip_comment(s, eof);
|
|
goto se2;
|
|
}
|
|
if (parse_element(s, eof, &name, &namelen, &attr, &s)) goto sp2;
|
|
if (namelen == 1 && !casecmp(name, cast_uchar "A", 1)) {
|
|
unsigned char *ss;
|
|
label = init_str();
|
|
lblen = 0;
|
|
se3:
|
|
ss = s;
|
|
se4:
|
|
while (ss < eof && *ss != '<') ss++;
|
|
if (ss >= eof) {
|
|
mem_free(label);
|
|
freeml(*ml);
|
|
mem_free(*menu);
|
|
return -1;
|
|
}
|
|
add_bytes_to_str(&label, &lblen, s, ss - s);
|
|
s = ss;
|
|
if (eof - s >= 2 && (s[1] == '!' || s[1] == '?')) {
|
|
s = skip_comment(s, eof);
|
|
goto se3;
|
|
}
|
|
if (parse_element(s, eof, NULL, NULL, NULL, &ss)) {
|
|
ss = s + 1;
|
|
goto se4;
|
|
}
|
|
if (!((namelen == 1 && !casecmp(name, cast_uchar "A", 1)) ||
|
|
(namelen == 2 && !casecmp(name, cast_uchar "/A", 2)) ||
|
|
(namelen == 3 && !casecmp(name, cast_uchar "MAP", 3)) ||
|
|
(namelen == 4 && !casecmp(name, cast_uchar "/MAP", 4)) ||
|
|
(namelen == 4 && !casecmp(name, cast_uchar "AREA", 4)) ||
|
|
(namelen == 5 && !casecmp(name, cast_uchar "/AREA", 5)))) {
|
|
s = ss;
|
|
goto se3;
|
|
}
|
|
} else if (namelen == 4 && !casecmp(name, cast_uchar "AREA", 4)) {
|
|
unsigned char *l = get_attr_val(attr, cast_uchar "alt");
|
|
if (l) label = !gfx ? convert_string(ct, l, (int)strlen(cast_const_char l), d_opt) : stracpy(l), mem_free(l);
|
|
else label = NULL;
|
|
} else if (namelen == 4 && !casecmp(name, cast_uchar "/MAP", 4)) goto done;
|
|
else goto se2;
|
|
href = get_url_val(attr, cast_uchar "href");
|
|
if (!(target = get_target(attr)) && !(target = stracpy(target_base)))
|
|
target = stracpy(cast_uchar "");
|
|
ld = mem_calloc(sizeof(struct link_def));
|
|
if (href) {
|
|
ld->link = join_urls(href_base, href);
|
|
mem_free(href);
|
|
}
|
|
ld->target = target;
|
|
|
|
add_to_ml(ml, ld, ld->target, NULL);
|
|
if (ld->link) add_to_ml(ml, ld->link, NULL);
|
|
scan_area_tag(attr, cast_uchar "shape", &ld->shape, ml);
|
|
scan_area_tag(attr, cast_uchar "coords", &ld->coords, ml);
|
|
scan_area_tag(attr, cast_uchar "onclick", &ld->onclick, ml);
|
|
scan_area_tag(attr, cast_uchar "ondblclick", &ld->ondblclick, ml);
|
|
scan_area_tag(attr, cast_uchar "onmousedown", &ld->onmousedown, ml);
|
|
scan_area_tag(attr, cast_uchar "onmouseup", &ld->onmouseup, ml);
|
|
scan_area_tag(attr, cast_uchar "onmouseover", &ld->onmouseover, ml);
|
|
scan_area_tag(attr, cast_uchar "onmouseout", &ld->onmouseout, ml);
|
|
scan_area_tag(attr, cast_uchar "onmousemove", &ld->onmousemove, ml);
|
|
|
|
if (label) clr_spaces(label, 1);
|
|
if (label && !*label) mem_free(label), label = NULL;
|
|
ld->label = label;
|
|
if (!label) label = stracpy(ld->link);
|
|
if (label && !*label) mem_free(label), label = NULL;
|
|
if (!label) label = stracpy(ld->onclick);
|
|
if (label && !*label) mem_free(label), label = NULL;
|
|
if (!label && !gfx) goto se2;
|
|
if (!label) label = stracpy(ld->onmousedown);
|
|
if (label && !*label) mem_free(label), label = NULL;
|
|
if (!label) label = stracpy(ld->onmouseup);
|
|
if (label && !*label) mem_free(label), label = NULL;
|
|
if (!label) label = stracpy(ld->ondblclick);
|
|
if (label && !*label) mem_free(label), label = NULL;
|
|
if (!label) label = stracpy(ld->onmouseover);
|
|
if (label && !*label) mem_free(label), label = NULL;
|
|
if (!label) label = stracpy(ld->onmouseout);
|
|
if (label && !*label) mem_free(label), label = NULL;
|
|
if (!label) label = stracpy(ld->onmousemove);
|
|
if (label && !*label) mem_free(label), label = NULL;
|
|
if (!label) goto se2;
|
|
add_to_ml(ml, label, NULL);
|
|
|
|
if (!gfx) for (i = 0; i < nmenu; i++) {
|
|
struct link_def *ll = (*menu)[i].data;
|
|
if (!xstrcmp(ll->link, ld->link) && !xstrcmp(ll->target, ld->target) && !xstrcmp(ll->onclick, ld->onclick)) {
|
|
goto se2;
|
|
}
|
|
}
|
|
if ((unsigned)nmenu > MAXINT / sizeof(struct menu_item) - 2) overalloc();
|
|
nm = mem_realloc(*menu, (nmenu + 2) * sizeof(struct menu_item));
|
|
*menu = nm;
|
|
memset(&nm[nmenu], 0, 2 * sizeof(struct menu_item));
|
|
nm[nmenu].text = label;
|
|
nm[nmenu].rtext = cast_uchar "";
|
|
nm[nmenu].hotkey = cast_uchar "";
|
|
nm[nmenu].func = map_selected;
|
|
nm[nmenu].data = ld;
|
|
nm[++nmenu].text = NULL;
|
|
goto se2;
|
|
done:
|
|
add_to_ml(ml, *menu, NULL);
|
|
return 0;
|
|
}
|
|
|
|
void scan_http_equiv(unsigned char *s, unsigned char *eof, unsigned char **head, int *hdl, unsigned char **title, unsigned char **background, unsigned char **bgcolor, int *pre_wrap, struct js_event_spec **j)
|
|
{
|
|
unsigned char *name, *attr, *he, *c;
|
|
int namelen;
|
|
int tlen = 0;
|
|
if (background) *background = NULL;
|
|
if (bgcolor) *bgcolor = NULL;
|
|
if (pre_wrap) *pre_wrap = 0;
|
|
if (title) *title = init_str();
|
|
add_chr_to_str(head, hdl, '\n');
|
|
se:
|
|
while (s < eof && *s != '<') sp:s++;
|
|
if (s >= eof) return;
|
|
if (eof - s >= 2 && (s[1] == '!' || s[1] == '?')) {
|
|
s = skip_comment(s, eof);
|
|
goto se;
|
|
}
|
|
if (parse_element(s, eof, &name, &namelen, &attr, &s)) goto sp;
|
|
ps:
|
|
if (namelen == 6 && !casecmp(name, cast_uchar "SCRIPT", 6)) {
|
|
if (should_skip_script(attr)) {
|
|
s = skip_element(s, eof, cast_uchar "SCRIPT", 0);
|
|
goto se;
|
|
}
|
|
}
|
|
if (namelen == 4 && !casecmp(name, cast_uchar "BODY", 4)) {
|
|
if (background) *background = get_attr_val(attr, cast_uchar "background"), background = NULL;
|
|
if (bgcolor) *bgcolor = get_attr_val(attr, cast_uchar "bgcolor"), bgcolor = NULL;
|
|
if (j) get_js_events_x(j, attr);
|
|
/*return;*/
|
|
}
|
|
if (title && !tlen && namelen == 5 && !casecmp(name, cast_uchar "TITLE", 5)) {
|
|
unsigned char *s1;
|
|
xse:
|
|
s1 = s;
|
|
while (s < eof && *s != '<') xsp:s++;
|
|
add_bytes_to_str(title, &tlen, s1, s - s1);
|
|
if (s >= eof) goto se;
|
|
if (eof - s >= 2 && (s[1] == '!' || s[1] == '?')) {
|
|
s = skip_comment(s, eof);
|
|
goto xse;
|
|
}
|
|
if (parse_element(s, eof, &name, &namelen, &attr, &s)) {
|
|
s1 = s;
|
|
goto xsp;
|
|
}
|
|
clr_spaces(*title, 1);
|
|
goto ps;
|
|
}
|
|
if (namelen == 5 && !casecmp(name, cast_uchar "STYLE", 5)) {
|
|
while (s < eof && *s != '<') {
|
|
if (*s == 'p' && eof - s >= 8 && !strncmp(cast_const_char s, "pre-wrap", 8)) {
|
|
if (pre_wrap) *pre_wrap = 1;
|
|
}
|
|
s++;
|
|
}
|
|
goto se;
|
|
}
|
|
if (namelen != 4 || casecmp(name, cast_uchar "META", 4)) goto se;
|
|
if ((he = get_attr_val(attr, cast_uchar "charset"))) {
|
|
add_to_str(head, hdl, cast_uchar "Charset: ");
|
|
add_to_str(head, hdl, he);
|
|
add_to_str(head, hdl, cast_uchar "\r\n");
|
|
mem_free(he);
|
|
}
|
|
if (!(he = get_attr_val(attr, cast_uchar "http-equiv"))) goto se;
|
|
c = get_attr_val(attr, cast_uchar "content");
|
|
add_to_str(head, hdl, he);
|
|
if (c) add_to_str(head, hdl, cast_uchar ": "), add_to_str(head, hdl, c), mem_free(c);
|
|
mem_free(he);
|
|
add_to_str(head, hdl, cast_uchar "\r\n");
|
|
goto se;
|
|
}
|