1 /* html_gr.c
2  * HTML parser in graphics mode
3  * (c) 2002 Mikulas Patocka
4  * This file is a part of the Links program, released under GPL.
5  */
6 
7 #include "cfg.h"
8 
9 #ifdef G
10 
11 #include "links.h"
12 
13 static int g_nobreak;
14 
get_real_font_size(int size)15 static int get_real_font_size(int size)
16 {
17 	int fs=d_opt->font_size;
18 
19 	if (size < 1) size = 1;
20 	if (size > 7) size = 7;
21 	switch (size) {
22 		case 1:	return (14*fs)>>4;
23 		case 2: return (15*fs)>>4;
24 		case 3: return (16*fs)>>4;
25 		case 4: return (19*fs)>>4;
26 		case 5: return (22*fs)>>4;
27 		case 6: return (25*fs)>>4;
28 		case 7: return (28*fs)>>4;
29 	}
30 	return 0;
31 }
32 
g_get_background(unsigned char * bg,unsigned char * bgcolor)33 struct background *g_get_background(unsigned char *bg, unsigned char *bgcolor)
34 {
35 	struct background *b;
36 	struct rgb r;
37 	b = mem_alloc(sizeof(struct background));
38 	b->color = 0;
39 	b->gamma_stamp = gamma_stamp - 1;
40 	if (bgcolor && !decode_color(bgcolor, &r)) {
41 		b->sRGB = (r.r << 16) + (r.g << 8) + r.b;
42 	} else {
43 		b->sRGB = (d_opt->default_bg.r << 16) + (d_opt->default_bg.g << 8) + d_opt->default_bg.b;
44 	}
45 	return b;
46 }
47 
g_release_background(struct background * bg)48 void g_release_background(struct background *bg)
49 {
50 	mem_free(bg);
51 }
52 
g_draw_background(struct graphics_device * dev,struct background * bg,int x,int y,int xw,int yw)53 void g_draw_background(struct graphics_device *dev, struct background *bg, int x, int y, int xw, int yw)
54 {
55 	int x2, y2;
56 	long color;
57 	if (bg->gamma_stamp == gamma_stamp) {
58 		color = bg->color;
59 	} else {
60 		bg->gamma_stamp = gamma_stamp;
61 		color = bg->color = dip_get_color_sRGB(bg->sRGB);
62 	}
63 	if (test_int_overflow(x, xw, &x2)) x2 = MAXINT;
64 	if (test_int_overflow(y, yw, &y2)) y2 = MAXINT;
65 	drv->fill_area(dev, x, y, x2, y2, color);
66 }
67 
68 static void g_put_chars(void *, unsigned char *, int);
69 
70 /* Returns 0 to 2550 */
gray(int r,int g,int b)71 static int gray (int r, int g, int b)
72 {
73 	return r*3+g*6+b;
74 }
75 
76 /* Tells if two colors are too near to be legible one on another */
77 /* 1=too near 0=not too near */
too_near(int r1,int g1,int b1,int r2,int g2,int b2)78 static int too_near(int r1, int g1, int b1, int r2, int g2, int b2)
79 {
80 	int diff = abs(r1-r2) * 3 + abs(g1-g2) * 6 + abs(b1-b2);
81 
82 	return diff < 0x17f;
83 }
84 
85 /* Fixes foreground based on background */
separate_fg_bg(int * fgr,int * fgg,int * fgb,int bgr,int bgg,int bgb)86 static void separate_fg_bg(int *fgr, int *fgg, int *fgb
87 	, int bgr, int bgg, int bgb)
88 {
89 	if (too_near(*fgr, *fgg, *fgb, bgr, bgg, bgb)) {
90 		*fgr = 255 - bgr;
91 		*fgg = 255 - bgg;
92 		*fgb = 255 - bgb;
93 	} else return;
94 	if (too_near(*fgr, *fgg, *fgb, bgr, bgg, bgb)) {
95 		if (gray(bgr, bgg, bgb) <= 1275)
96 			*fgr = *fgg = *fgb = 255;
97 		else
98 			*fgr = *fgg = *fgb = 0;
99 	}
100 }
101 
get_style_by_ta(struct text_attrib * ta)102 static struct style *get_style_by_ta(struct text_attrib *ta)
103 {
104 	int fg_r, fg_g, fg_b; /* sRGB 0-255 values */
105 	int fs = get_real_font_size(ta->fontsize);
106 	struct style *stl;
107 	unsigned fflags;
108 
109 	fg_r = ta->fg.r;
110 	fg_g = ta->fg.g;
111 	fg_b = ta->fg.b;
112 	separate_fg_bg(&fg_r, &fg_g, &fg_b, ta->bg.r, ta->bg.g, ta->bg.b);
113 
114 	fflags = 0;
115 	if (ta->attr & AT_UNDERLINE) fflags |= FF_UNDERLINE;
116 	if (ta->attr & AT_BOLD) fflags |= FF_BOLD;
117 	if (ta->attr & AT_ITALIC) fflags |= FF_ITALIC;
118 	if (ta->attr & AT_FIXED) fflags |= FF_MONOSPACED;
119 
120 	stl = g_get_style((fg_r << 16) + (fg_g << 8) + fg_b, (ta->bg.r << 16) +
121 			(ta->bg.g << 8) + ta->bg.b, fs, fflags);
122 	return stl;
123 }
124 
125 #define rm(x) ((x).width - (x).rightmargin * G_HTML_MARGIN > 0 ? (x).width - (x).rightmargin * G_HTML_MARGIN : 0)
126 
flush_pending_line_to_obj(struct g_part * p,int minheight)127 void flush_pending_line_to_obj(struct g_part *p, int minheight)
128 {
129 	int i, pp, pos, w, lbl;
130 	struct g_object_line *l = p->line;
131 	struct g_object_area *a;
132 	if (!l) {
133 		return;
134 	}
135 	for (i = l->n_entries - 1; i >= 0; i--) {
136 		struct g_object *go = l->entries[i];
137 		if (go->draw == g_text_draw) {
138 			struct g_object_text *got = get_struct(go, struct g_object_text, goti.go);
139 			int l = (int)strlen(cast_const_char got->text);
140 			while (l && got->text[l - 1] == ' ') got->text[--l] = 0, got->goti.go.xw -= g_char_width(got->style, ' ');
141 			if (got->goti.go.xw < 0) internal_error("xw(%d) < 0", got->goti.go.xw);
142 		}
143 		if (!go->xw) continue;
144 		break;
145 	}
146 	scan_again:
147 	pp = 0;
148 	w = minheight;
149 	lbl = 0;
150 	for (i = 0; i < l->n_entries; i++) {
151 		int yy = l->entries[i]->y;
152 		if (yy >= G_OBJ_ALIGN_SPECIAL) yy = 0;
153 		pp = safe_add(pp, l->entries[i]->xw);
154 		if (l->entries[i]->xw && safe_add(l->entries[i]->yw, yy) > w) w = safe_add(l->entries[i]->yw, yy);
155 		if (yy < lbl) lbl = yy;
156 	}
157 	if (lbl < 0) {
158 		for (i = 0; i < l->n_entries; i++) {
159 			if (l->entries[i]->y < G_OBJ_ALIGN_SPECIAL) l->entries[i]->y = safe_add(l->entries[i]->y, -lbl);
160 		}
161 		goto scan_again;
162 	}
163 	if (par_format.align == AL_CENTER) pos = (safe_add(rm(par_format), par_format.leftmargin * G_HTML_MARGIN) - pp) / 2;
164 	else if (par_format.align == AL_RIGHT) pos = rm(par_format) - pp;
165 	else pos = par_format.leftmargin * G_HTML_MARGIN;
166 	if (pos < par_format.leftmargin * G_HTML_MARGIN) pos = par_format.leftmargin * G_HTML_MARGIN;
167 	pp = pos;
168 	for (i = 0; i < l->n_entries; i++) {
169 		l->entries[i]->x = pp;
170 		pp = safe_add(pp, l->entries[i]->xw);
171 		if (l->entries[i]->y < G_OBJ_ALIGN_SPECIAL) {
172 			l->entries[i]->y = w - l->entries[i]->yw - l->entries[i]->y;
173 		} else if (l->entries[i]->y == G_OBJ_ALIGN_TOP) {
174 			l->entries[i]->y = 0;
175 		} else if (l->entries[i]->y == G_OBJ_ALIGN_MIDDLE) {
176 			l->entries[i]->y = (w - safe_add(l->entries[i]->yw, 1)) / 2;
177 		}
178 	}
179 	l->go.x = 0;
180 	l->go.xw = par_format.width;
181 	l->go.yw = w;
182 	l->go.y = p->cy;
183 	a = p->root;
184 	if (l->go.xw > a->go.xw) a->go.xw = l->go.xw;
185 	p->cy = safe_add(p->cy, w);
186 	a->go.yw = p->cy;
187 	if (!(a->n_lines & (a->n_lines + 1))) {
188 		if ((unsigned)a->n_lines > ((MAXINT - sizeof(struct g_object_area)) / sizeof(struct g_object_text *) - 1) / 2) overalloc();
189 		a = mem_realloc(a, sizeof(struct g_object_area) + sizeof(struct g_object_text *) * (a->n_lines * 2 + 1));
190 		p->root = a;
191 	}
192 	a->lines[a->n_lines++] = l;
193 	p->line = NULL;
194 	p->w.pos = 0;
195 	p->w.last_wrap = NULL;
196 	p->w.last_wrap_obj = NULL;
197 }
198 
add_object_to_line(struct g_part * pp,struct g_object_line ** lp,struct g_object * go)199 void add_object_to_line(struct g_part *pp, struct g_object_line **lp, struct g_object *go)
200 {
201 	struct g_object_line *l;
202 	if (go && (go->xw < 0 || go->yw < 0)) {
203 		internal_error("object has negative size: %d,%d", go->xw, go->yw);
204 		return;
205 	}
206 	if (!*lp) {
207 		l = mem_calloc(sizeof(struct g_object_line) + sizeof(struct g_object_text *));
208 		l->go.mouse_event = g_line_mouse;
209 		l->go.draw = g_line_draw;
210 		l->go.destruct = g_line_destruct;
211 		l->go.get_list = g_line_get_list;
212 		/*l->go.x = 0;
213 		l->go.y = 0;
214 		l->go.xw = 0;
215 		l->go.yw = 0;*/
216 		l->bg = pp->root->bg;
217 		if (!go) {
218 			*lp = l;
219 			return;
220 		}
221 		l->n_entries = 1;
222 	} else {
223 		if (!go) return;
224 		(*lp)->n_entries++;
225 		if ((unsigned)(*lp)->n_entries > (MAXINT - sizeof(struct g_object_line)) / sizeof(struct g_object *)) overalloc();
226 		l = mem_realloc(*lp, sizeof(struct g_object_line) + sizeof(struct g_object *) * (*lp)->n_entries);
227 		*lp = l;
228 	}
229 	l->entries[l->n_entries - 1] = go;
230 	*lp = l;
231 	if (pp->cx == -1) pp->cx = par_format.leftmargin * G_HTML_MARGIN;
232 	if (go->xw) {
233 		pp->cx = safe_add(pp->cx, pp->cx_w);
234 		pp->cx_w = 0;
235 		pp->cx = safe_add(pp->cx, go->xw);
236 	}
237 }
238 
flush_pending_text_to_line(struct g_part * p)239 void flush_pending_text_to_line(struct g_part *p)
240 {
241 	struct g_object_text *t = p->text;
242 	if (!t) return;
243 	add_object_to_line(p, &p->line, &t->goti.go);
244 	p->text = NULL;
245 }
246 
split_line_object(struct g_part * p,struct g_object * text_go,unsigned char * ptr)247 static void split_line_object(struct g_part *p, struct g_object *text_go, unsigned char *ptr)
248 {
249 	struct g_object_text *text_t = NULL;
250 	struct g_object_text *t2;
251 	struct g_object_line *l2;
252 	size_t sl;
253 	int n;
254 	if (!ptr) {
255 		if (p->line && p->line->n_entries && text_go == p->line->entries[p->line->n_entries - 1]) {
256 			flush_pending_line_to_obj(p, 0);
257 			goto wwww;
258 		}
259 		t2 = NULL;
260 		goto nt2;
261 	}
262 	text_t = get_struct(text_go, struct g_object_text, goti.go);
263 	if (par_format.align == AL_NO_BREAKABLE && text_t == p->text && !ptr[strspn(cast_const_char ptr, cast_const_char " ")]) {
264 		return;
265 	}
266 #ifdef DEBUG
267 	if (ptr < text_t->text || ptr >= text_t->text + strlen(cast_const_char text_t->text))
268 		internal_error("split_line_object: split point (%p) pointing out of object (%p,%lx)", ptr, text_t->text, (unsigned long)strlen(cast_const_char text_t->text));
269 #endif
270 	sl = strlen(cast_const_char ptr);
271 	if (sl > MAXINT - sizeof(struct g_object_text)) overalloc();
272 	t2 = mem_calloc(sizeof(struct g_object_text) + sl);
273 	t2->goti.go.mouse_event = g_text_mouse;
274 	t2->goti.go.draw = g_text_draw;
275 	t2->goti.go.destruct = g_text_destruct;
276 	t2->goti.go.get_list = NULL;
277 	if (*ptr == ' ') {
278 		memcpy(t2->text, ptr + 1, sl);
279 		*ptr = 0;
280 		/*debug("split: (%s)(%s)", text_t->text, ptr + 1);*/
281 	} else {
282 		memcpy(t2->text, ptr, sl + 1);
283 		ptr[0] = '-';
284 		ptr[1] = 0;
285 	}
286 	t2->goti.go.y = text_t->goti.go.y;
287 	t2->style = g_clone_style(text_t->style);
288 	t2->goti.link_num = text_t->goti.link_num;
289 	t2->goti.link_order = text_t->goti.link_order;
290 	text_t->goti.go.xw = g_text_width(text_t->style, text_t->text);
291 	nt2:
292 	if (p->line) for (n = 0; n < p->line->n_entries; n++) if (p->line->entries[n] == text_go) goto found;
293 	if (!text_t || text_t != p->text) {
294 		internal_error("split_line_object: bad wrap");
295 		return;
296 	}
297 	if (0) {
298 		int nn;
299 		found:
300 		n = safe_add(n, !ptr);
301 		l2 = mem_calloc(sizeof(struct g_object_line) + (p->line->n_entries - n) * sizeof(struct g_object_text *));
302 		l2->go.mouse_event = g_line_mouse;
303 		l2->go.draw = g_line_draw;
304 		l2->go.destruct = g_line_destruct;
305 		l2->go.get_list = g_line_get_list;
306 		l2->bg = p->root->bg;
307 		l2->n_entries = p->line->n_entries - n;
308 		if (ptr)
309 			l2->entries[0] = &t2->goti.go;
310 		memcpy(&l2->entries[!!ptr], p->line->entries + safe_add(n, !!ptr), (l2->n_entries - !!ptr) * sizeof(struct g_object_text *));
311 		p->line->n_entries = safe_add(n, !!ptr);
312 		flush_pending_line_to_obj(p, 0);
313 		p->line = l2;
314 		if (ptr) {
315 			t2->goti.go.xw = g_text_width(t2->style, t2->text);
316 			t2->goti.go.yw = text_t->goti.go.yw;
317 			p->w.pos = 0;
318 		}
319 		for (nn = 0; nn < l2->n_entries; nn++) {
320 			p->w.pos = safe_add(p->w.pos, l2->entries[nn]->xw);	/* !!! FIXME: nastav last_wrap */
321 			/*debug("a1: %d (%s)", l2->entries[nn]->xw, tt->text);*/
322 		}
323 		wwww:
324 		if (p->text) {
325 			int qw = g_text_width(p->text->style, p->text->text);
326 			p->w.pos = safe_add(p->w.pos, qw);
327 		}
328 		/*debug("%d", p->w.pos);*/
329 	} else {
330 		flush_pending_text_to_line(p);
331 		flush_pending_line_to_obj(p, 0);
332 		p->line = NULL;
333 		t2->goti.go.xw = g_text_width(t2->style, t2->text);
334 		t2->goti.go.yw = text_t->goti.go.yw;
335 		p->text = t2;
336 		p->pending_text_len = -1;
337 		p->w.pos = t2->goti.go.xw;
338 		p->cx_w = g_char_width(t2->style, ' ');
339 	}
340 	p->w.last_wrap = NULL;
341 	p->w.last_wrap_obj = NULL;
342 	t2 = p->text;
343 	if (t2) {
344 		int sl = (int)strlen(cast_const_char t2->text);
345 		if (sl >= 1 && t2->text[sl - 1] == ' ') {
346 			p->w.last_wrap = &t2->text[sl - 1];
347 			p->w.last_wrap_obj = &t2->goti.go;
348 		} else if (sl >= 2 && t2->text[sl - 2] == 0xc2 && t2->text[sl - 1] == 0xad) {
349 			p->w.last_wrap = &t2->text[sl - 2];
350 			p->w.last_wrap_obj = &t2->goti.go;
351 		}
352 	}
353 }
354 
add_object(struct g_part * p,struct g_object * o)355 void add_object(struct g_part *p, struct g_object *o)
356 {
357 	g_nobreak = 0;
358 	flush_pending_text_to_line(p);
359 	p->w.width = rm(par_format) - par_format.leftmargin * G_HTML_MARGIN;
360 	if (safe_add(p->w.pos, o->xw) > p->w.width) flush_pending_line_to_obj(p, 0);
361 	add_object_to_line(p, &p->line, o);
362 	p->w.last_wrap = NULL;
363 	p->w.last_wrap_obj = o;
364 	p->w.pos = safe_add(p->w.pos, o->xw);
365 }
366 
g_line_break(void * p_)367 static void g_line_break(void *p_)
368 {
369 	struct g_part *p = p_;
370 	if (g_nobreak) {
371 		g_nobreak = 0;
372 		return;
373 	}
374 	flush_pending_text_to_line(p);
375 	if (!p->line || par_format.align == AL_NO || par_format.align == AL_NO_BREAKABLE) {
376 		add_object_to_line(p, &p->line, NULL);
377 		empty_line:
378 		flush_pending_line_to_obj(p, get_real_font_size(format_.fontsize));
379 	} else {
380 		int i;
381 		for (i = 0; i < p->line->n_entries; i++) {
382 			struct g_object *go = p->line->entries[i];
383 			if (go->destruct != g_tag_destruct)
384 				goto flush;
385 		}
386 		goto empty_line;
387 		flush:
388 		flush_pending_line_to_obj(p, 0);
389 	}
390 	if (p->cx > p->xmax) p->xmax = p->cx;
391 	p->cx = -1;
392 	p->cx_w = 0;
393 }
394 
395 /* SHADOWED IN html_form_control */
g_html_form_control(struct g_part * p,struct form_control * fc)396 static void g_html_form_control(struct g_part *p, struct form_control *fc)
397 {
398 	if (!p->data) {
399 		/*destroy_fc(fc);
400 		mem_free(fc);*/
401 		add_to_list(p->uf, fc);
402 		return;
403 	}
404 	fc->g_ctrl_num = g_ctrl_num;
405 	g_ctrl_num = safe_add(g_ctrl_num, 1);
406 	if (fc->type == FC_TEXT || fc->type == FC_PASSWORD || fc->type == FC_TEXTAREA) {
407 		unsigned char *dv = convert_string(convert_table, fc->default_value, (int)strlen(cast_const_char fc->default_value), d_opt);
408 		if (dv) {
409 			mem_free(fc->default_value);
410 			fc->default_value = dv;
411 		}
412 		/*
413 		for (i = 0; i < fc->nvalues; i++) if ((dv = convert_string(convert_table, fc->values[i], strlen(cast_const_char fc->values[i]), d_opt))) {
414 			mem_free(fc->values[i]);
415 			fc->values[i] = dv;
416 		}
417 		*/
418 	}
419 	if (fc->type == FC_TEXTAREA) {
420 		unsigned char *p;
421 		for (p = fc->default_value; p[0]; p++) if (p[0] == '\r') {
422 			if (p[1] == '\n') memmove(p, p + 1, strlen(cast_const_char p)), p--;
423 			else p[0] = '\n';
424 		}
425 	}
426 	add_to_list(p->data->forms, fc);
427 }
428 
429 static struct link **putchars_link_ptr = NULL;
430 
431 /* Probably releases clickable map */
release_image_map(struct image_map * map)432 void release_image_map(struct image_map *map)
433 {
434 	int i;
435 	if (!map) return;
436 	for (i = 0; i < map->n_areas; i++) mem_free(map->area[i].coords);
437 	mem_free(map);
438 }
439 
is_in_area(struct map_area * a,int x,int y)440 int is_in_area(struct map_area *a, int x, int y)
441 {
442 	int i;
443 	int over;
444 	switch (a->shape) {
445 		case SHAPE_DEFAULT:
446 			return 1;
447 		case SHAPE_RECT:
448 			return a->ncoords >= 4 && x >= a->coords[0] && y >= a->coords[1] && x < a->coords[2] && y < a->coords[3];
449 		case SHAPE_CIRCLE:
450 			return a->ncoords >= 3 && (a->coords[0]-x)*(a->coords[0]-x)+(a->coords[1]-y)*(a->coords[1]-y) <= a->coords[2]*a->coords[2];
451 		case SHAPE_POLY:
452 			over = 0;
453 			if (a->ncoords >= 4) for (i = 0; a->ncoords - i > 1; i += 2) {
454 				int x1, x2, y1, y2;
455 				x1 = a->coords[i];
456 				y1 = a->coords[i + 1];
457 				x2 = a->coords[0];
458 				y2 = a->coords[1];
459 				if (a->ncoords - i > 3) {
460 					x2 = a->coords[i + 2];
461 					y2 = a->coords[i + 3];
462 				}
463 				if (y1 > y2) {
464 					int sw;
465 					sw = x1; x1 = x2; x2 = sw;
466 					sw = y1; y1 = y2; y2 = sw;
467 				}
468 				if (y >= y1 && y < y2) {
469 					int po = 10000 * (y - y1) / (y2 - y1);
470 					int xs = x1 + (x2 - x1) * po / 10000;
471 					if (xs >= x) over++;
472 				}
473 			}
474 			return over & 1;
475 		default:
476 			internal_error("is_in_area: bad shape: %d", a->shape);
477 	}
478 	return 0;
479 }
480 
481 /* The size is requested in im->xsize and im->ysize. <0 means
482  * not specified. Autoscale is requested in im->autoscale.
483  * If autoscale is specified, im->xsize and im->ysize must
484  * be >0. */
do_image(struct g_part * p,struct image_description * im)485 static void do_image(struct g_part *p, struct image_description *im)
486 {
487 	struct g_object_image *io;
488 	struct link *link;
489 	link = NULL;
490 	putchars_link_ptr = &link;
491 	g_put_chars(p, NULL, 0);
492 	putchars_link_ptr = NULL;
493 	if (!link) im->link_num = -1;
494 	else {
495 		im->link_num = (int)(link - p->data->links);
496 		im->link_order = link->obj_order;
497 		link->obj_order = safe_add(link->obj_order, 1);
498 		if (link->img_alt) mem_free(link->img_alt);
499 		link->img_alt = stracpy(im->alt);
500 	}
501 	io = insert_image(p, im);
502 	if (!io) goto ab;
503 	io->goti.ismap = im->ismap;
504 	add_object(p, &io->goti.go);
505 	if (im->usemap && p->data) {
506 		unsigned char *tag = extract_position(im->usemap);
507 		struct additional_file *af = request_additional_file(current_f_data, im->usemap);
508 		af->need_reparse = 1;
509 		if (af->rq && (af->rq->state == O_LOADING || af->rq->state == O_INCOMPLETE || af->rq->state == O_OK) && af->rq->ce) {
510 			struct memory_list *ml;
511 			struct menu_item *menu;
512 			struct cache_entry *ce = af->rq->ce;
513 			unsigned char *start;
514 			size_t len;
515 			int i;
516 			struct image_map *map;
517 			if (get_file(af->rq, &start, &len)) goto ft;
518 			if (!len) goto ft;
519 			if (len > MAXINT) len = MAXINT;
520 			if (get_image_map(ce->head, start, start + len, tag, &menu, &ml, format_.href_base, format_.target_base, 0, 0, 0, 1)) goto ft;
521 			map = mem_alloc(sizeof(struct image_map));
522 			map->n_areas = 0;
523 			for (i = 0; menu[i].text; i++) {
524 				struct link_def *ld = menu[i].data;
525 				struct map_area *a;
526 				struct link *link;
527 				int shape =
528 		!ld->shape || !*ld->shape ? SHAPE_RECT :
529 		!casestrcmp(ld->shape, cast_uchar "default") ? SHAPE_DEFAULT :
530 		!casestrcmp(ld->shape, cast_uchar "rect") ? SHAPE_RECT :
531 		!casestrcmp(ld->shape, cast_uchar "circle") ? SHAPE_CIRCLE :
532 		!casestrcmp(ld->shape, cast_uchar "poly") ||
533 		!casestrcmp(ld->shape, cast_uchar "polygon") ? SHAPE_POLY : -1;
534 				if (shape == -1) continue;
535 				if ((unsigned)map->n_areas > (MAXINT - sizeof(struct image_map)) / sizeof(struct map_area) - 1) overalloc();
536 				map = mem_realloc(map, sizeof(struct image_map) + (map->n_areas + 1) * sizeof(struct map_area));
537 				a = &map->area[map->n_areas++];
538 				a->shape = shape;
539 				a->coords = DUMMY;
540 				a->ncoords = 0;
541 				if (ld->coords) {
542 					unsigned char *p = ld->coords;
543 					int num;
544 					next_coord:
545 					num = 0;
546 					while (*p && (*p < '0' || *p > '9')) p++;
547 					if (!*p) goto noc;
548 					while (*p >= '0' && *p <= '9' && num < 10000000) num = num * 10 + *p - '0', p++;
549 					if (*p == '.') {
550 						p++;
551 						while (*p >= '0' && *p <= '9') p++;
552 					}
553 					if (*p == '%' && num < 1000) {
554 						int m = io->goti.go.xw < io->goti.go.yw ? io->goti.go.xw : io->goti.go.yw;
555 						num = num * m / 100;
556 						p++;
557 					} else num = num * d_opt->image_scale / 100;
558 					if ((unsigned)a->ncoords > MAXINT / sizeof(int) - 1) overalloc();
559 					a->coords = mem_realloc(a->coords, (a->ncoords + 1) * sizeof(int));
560 					a->coords[a->ncoords++] = num;
561 					goto next_coord;
562 				}
563 				noc:
564 				if (!(link = new_link(p->data))) a->link_num = -1;
565 				else {
566 					link->pos = DUMMY;
567 					link->type = L_LINK;
568 					link->where = stracpy(ld->link);
569 					link->target = stracpy(ld->target);
570 					link->img_alt = stracpy(ld->label);
571 					link->where_img = stracpy(im->url);
572 #ifdef JS
573 					if (ld->onclick || ld->ondblclick || ld->onmousedown || ld->onmouseup || ld->onmouseover || ld->onmouseout || ld->onmousemove) {
574 						create_js_event_spec(&link->js_event);
575 						link->js_event->click_code = stracpy(ld->onclick);
576 						link->js_event->dbl_code = stracpy(ld->ondblclick);
577 						link->js_event->down_code = stracpy(ld->onmousedown);
578 						link->js_event->up_code = stracpy(ld->onmouseup);
579 						link->js_event->over_code = stracpy(ld->onmouseover);
580 						link->js_event->out_code = stracpy(ld->onmouseout);
581 						link->js_event->move_code = stracpy(ld->onmousemove);
582 					}
583 #endif
584 					a->link_num = (int)(link - p->data->links);
585 				}
586 				if (last_link) mem_free(last_link), last_link = NULL;
587 			}
588 			io->goti.map = map;
589 			freeml(ml);
590 			ft:;
591 		}
592 		if (tag) mem_free(tag);
593 	}
594 	ab:;
595 }
596 
g_hr(struct g_part * gp,struct hr_param * hr)597 static void g_hr(struct g_part *gp, struct hr_param *hr)
598 {
599 	unsigned char bgstr[8];
600 	struct g_object_line *o;
601 	o = mem_calloc(sizeof(struct g_object_line));
602 	o->go.mouse_event = g_line_mouse;
603 	o->go.draw = g_line_draw;
604 	o->go.destruct = g_line_bg_destruct;
605 	o->go.get_list = g_line_get_list;
606 	/*o->go.x = 0;
607 	o->go.y = 0;*/
608 	o->go.xw = hr->width;
609 	o->go.yw = hr->size;
610 	table_bg(&format_, bgstr);
611 	o->bg = g_get_background(NULL, bgstr);
612 	o->n_entries = 0;
613 	flush_pending_text_to_line(gp);
614 	add_object_to_line(gp, &gp->line, &o->go);
615 	line_breax = 0;
616 	gp->cx = -1;
617 	gp->cx_w = 0;
618 }
619 
620 
g_html_special(void * p_,int c,...)621 static void *g_html_special(void *p_, int c, ...)
622 {
623 	struct g_part *p = p_;
624 	va_list l;
625 	unsigned char *t;
626 	size_t sl;
627 	struct form_control *fc;
628 	struct frameset_param *fsp;
629 	struct frame_param *fp;
630 	struct image_description *im;
631 	struct g_object_tag *tag;
632 	struct refresh_param *rp;
633 	struct hr_param *hr;
634 	va_start(l, c);
635 	switch (c) {
636 		case SP_TAG:
637 			t = va_arg(l, unsigned char *);
638 			va_end(l);
639 			/* not needed to convert %AB here because html_tag will be called anyway */
640 			sl = strlen(cast_const_char t);
641 			if (sl > MAXINT - sizeof(struct g_object_tag)) overalloc();
642 			tag = mem_calloc(sizeof(struct g_object_tag) + sl);
643 			tag->go.mouse_event = g_dummy_mouse;
644 			tag->go.draw = g_dummy_draw;
645 			tag->go.destruct = g_tag_destruct;
646 			memcpy(tag->name, t, sl + 1);
647 			flush_pending_text_to_line(p);
648 			add_object_to_line(p, &p->line, &tag->go);
649 			break;
650 		case SP_CONTROL:
651 			fc = va_arg(l, struct form_control *);
652 			va_end(l);
653 			g_html_form_control(p, fc);
654 			break;
655 		case SP_TABLE:
656 			va_end(l);
657 			return convert_table;
658 		case SP_USED:
659 			va_end(l);
660 			return (void *)(my_intptr_t)!!p->data;
661 		case SP_FRAMESET:
662 			fsp = va_arg(l, struct frameset_param *);
663 			va_end(l);
664 			return create_frameset(p->data, fsp);
665 		case SP_FRAME:
666 			fp = va_arg(l, struct frame_param *);
667 			va_end(l);
668 			create_frame(fp);
669 			break;
670 		case SP_SCRIPT:
671 			t = va_arg(l, unsigned char *);
672 			va_end(l);
673 			if (p->data) process_script(p->data, t);
674 			break;
675 		case SP_IMAGE:
676 			im = va_arg(l, struct image_description *);
677 			va_end(l);
678 			do_image(p, im);
679 			break;
680 		case SP_NOWRAP:
681 			va_end(l);
682 			break;
683 		case SP_REFRESH:
684 			rp = va_arg(l, struct refresh_param *);
685 			va_end(l);
686 			html_process_refresh(p->data, rp->url, rp->time);
687 			break;
688 		case SP_SET_BASE:
689 			t = va_arg(l, unsigned char *);
690 			va_end(l);
691 			if (p->data) set_base(p->data, t);
692 			break;
693 		case SP_HR:
694 			hr = va_arg(l, struct hr_param *);
695 			va_end(l);
696 			g_hr(p, hr);
697 			break;
698 		case SP_FORCE_BREAK:
699 			if (p->cx > par_format.leftmargin * G_HTML_MARGIN) {
700 				g_nobreak = 0;
701 				g_line_break(p);
702 			}
703 			break;
704 		default:
705 			va_end(l);
706 			internal_error("html_special: unknown code %d", c);
707 	}
708 	return NULL;
709 }
710 
711 static const unsigned char to_je_ale_prasarna_[] = "";
712 #define to_je_ale_prasarna	(cast_uchar to_je_ale_prasarna_)
713 
714 static unsigned char *cached_font_face = to_je_ale_prasarna;
715 static struct text_attrib_beginning ta_cache = { -1, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, 0, 0 };
716 
g_put_chars(void * p_,unsigned char * s,int l)717 static void g_put_chars(void *p_, unsigned char *s, int l)
718 {
719 	struct g_part *p = p_;
720 	struct g_object_text *t;
721 	struct link *link;
722 	int ptl;
723 	unsigned char *sh;
724 	int qw;
725 
726 	if (l < 0) overalloc();
727 
728 	while (l > 2 && (sh = memchr(s + 1, 0xad, l - 2)) && sh[-1] == 0xc2) {
729 		sh++;
730 		g_put_chars(p_, s, (int)(sh - s));
731 		l -= (int)(sh - s);
732 		s = sh;
733 	}
734 	while (par_format.align != AL_NO && l >= 2 && (sh = memchr(s, ' ', l - 1))) {
735 		sh++;
736 		g_put_chars(p_, s, (int)(sh - s));
737 		l -= (int)(sh - s);
738 		s = sh;
739 	}
740 
741 	/*fprintf(stderr, "%d: '%.*s'\n", l, l, s);*/
742 
743 	t = NULL;
744 
745 	if (putchars_link_ptr) {
746 		link = NULL;
747 		goto check_link;
748 	}
749 	while (par_format.align != AL_NO && par_format.align != AL_NO_BREAKABLE && p->cx == -1 && l && *s == ' ') s++, l--;
750 	if (!l) return;
751 	g_nobreak = 0;
752 	if (p->cx < par_format.leftmargin * G_HTML_MARGIN) p->cx = par_format.leftmargin * G_HTML_MARGIN;
753 	if (html_format_changed) {
754 		if (memcmp(&ta_cache, &format_, sizeof(struct text_attrib_beginning)) || xstrcmp(cached_font_face, format_.fontface) || cached_font_face == to_je_ale_prasarna ||
755 	xstrcmp(format_.link, last_link) || xstrcmp(format_.target, last_target) ||
756 	    xstrcmp(format_.image, last_image) || format_.form != last_form
757 	    || ((format_.js_event || last_js_event) && compare_js_event_spec(format_.js_event, last_js_event))	) {
758 			/*if (!html_format_changed) internal_error("html_format_changed not set");*/
759 			flush_pending_text_to_line(p);
760 			if (xstrcmp(cached_font_face, format_.fontface) || cached_font_face == to_je_ale_prasarna) {
761 				if (cached_font_face && cached_font_face != to_je_ale_prasarna) mem_free(cached_font_face);
762 				cached_font_face = stracpy(format_.fontface);
763 			}
764 			memcpy(&ta_cache, &format_, sizeof(struct text_attrib_beginning));
765 			if (p->current_style) g_free_style(p->current_style);
766 			p->current_style = get_style_by_ta(&format_);
767 		}
768 		html_format_changed = 0;
769 	}
770 	/*if (p->cx <= par_format.leftmargin * G_HTML_MARGIN && *s == ' ' && par_format.align != AL_NO && par_format.align != AL_NO_BREAKABLE) s++, l--;*/
771 	if (!p->text) {
772 		link = NULL;
773 		t = mem_calloc(sizeof(struct g_object_text) + ALLOC_GR);
774 		t->goti.go.mouse_event = g_text_mouse;
775 		t->goti.go.draw = g_text_draw;
776 		t->goti.go.destruct = g_text_destruct;
777 		t->goti.go.get_list = NULL;
778 		t->style = g_clone_style(p->current_style);
779 		t->goti.go.yw = t->style->height;
780 		/*t->goti.go.xw = 0;
781 		t->goti.go.y = 0;*/
782 		if (format_.baseline) {
783 			if (format_.baseline < 0) t->goti.go.y = -(t->style->height / 3);
784 			if (format_.baseline > 0) t->goti.go.y = get_real_font_size(format_.baseline) - (t->style->height / 2);
785 		}
786 		check_link:
787 		if (last_link || last_image || last_form || format_.link || format_.image || format_.form
788 		|| format_.js_event || last_js_event
789 		) goto process_link;
790 		back_link:
791 		if (putchars_link_ptr) {
792 			*putchars_link_ptr = link;
793 			return;
794 		}
795 
796 		if (!link) t->goti.link_num = -1;
797 		else {
798 			t->goti.link_num = (int)(link - p->data->links);
799 			t->goti.link_order = link->obj_order++;
800 		}
801 
802 		t->text[0] = 0;
803 		p->pending_text_len = 0;
804 		p->text = t;
805 	}
806 	if (p->pending_text_len == -1) {
807 		p->pending_text_len = (int)strlen(cast_const_char p->text->text);
808 		ptl = p->pending_text_len;
809 		if (!ptl) ptl = 1;
810 		goto a1;
811 	}
812 	ptl = p->pending_text_len;
813 	if (!ptl) ptl = 1;
814 	safe_add(safe_add(ptl, l), ALLOC_GR);
815 	if (((ptl + ALLOC_GR - 1) & ~(ALLOC_GR - 1)) != ((ptl + l + ALLOC_GR - 1) & ~(ALLOC_GR - 1))) a1: {
816 		struct g_object_text *t;
817 		if ((unsigned)l > MAXINT) overalloc();
818 		if ((unsigned)ptl + (unsigned)l > MAXINT - ALLOC_GR) overalloc();
819 		t = mem_realloc(p->text, sizeof(struct g_object_text) + ((ptl + l + ALLOC_GR - 1) & ~(ALLOC_GR - 1)));
820 		if (p->w.last_wrap >= p->text->text && p->w.last_wrap < p->text->text + p->pending_text_len) p->w.last_wrap = p->w.last_wrap + ((unsigned char *)t - (unsigned char *)p->text);
821 		if (p->w.last_wrap_obj == &p->text->goti.go) p->w.last_wrap_obj = &t->goti.go;
822 		p->text = t;
823 	}
824 	memcpy(p->text->text + p->pending_text_len, s, l), p->text->text[p->pending_text_len = safe_add(p->pending_text_len, l)] = 0;
825 	qw = g_text_width(p->text->style, p->text->text + p->pending_text_len - l);
826 	p->text->goti.go.xw = safe_add(p->text->goti.go.xw, qw); /* !!! FIXME: move to g_wrap_text */
827 	if (par_format.align != AL_NO /*&& par_format.align != AL_NO_BREAKABLE*/) {
828 		p->w.text = p->text->text + p->pending_text_len - l;
829 		p->w.style = p->text->style;
830 		p->w.obj = &p->text->goti.go;
831 		p->w.width = rm(par_format) - par_format.leftmargin * G_HTML_MARGIN;
832 		p->w.force_break = 0;
833 		if (p->w.width < 0) p->w.width = 0;
834 		if (!g_wrap_text(&p->w)) {
835 			split_line_object(p, p->w.last_wrap_obj, p->w.last_wrap);
836 		}
837 	}
838 	return;
839 
840 	/* !!! WARNING: THE FOLLOWING CODE IS SHADOWED IN HTML_R.C */
841 
842 	process_link:
843 	if ((last_link /*|| last_target*/ || last_image || last_form) &&
844 	    !putchars_link_ptr &&
845 	    !xstrcmp(format_.link, last_link) && !xstrcmp(format_.target, last_target) &&
846 	    !xstrcmp(format_.image, last_image) && format_.form == last_form
847 	    && ((!format_.js_event && !last_js_event) || !compare_js_event_spec(format_.js_event, last_js_event))) {
848 		if (!p->data) goto back_link;
849 		if (!p->data->nlinks) {
850 			internal_error("no link");
851 			goto back_link;
852 		}
853 		link = &p->data->links[p->data->nlinks - 1];
854 		goto back_link;
855 	} else {
856 		if (last_link) mem_free(last_link);
857 		if (last_target) mem_free(last_target);
858 		if (last_image) mem_free(last_image);
859 		free_js_event_spec(last_js_event);
860 		last_link = last_target = last_image = NULL;
861 		last_form = NULL;
862 		last_js_event = NULL;
863 		if (!(format_.link || format_.image || format_.form || format_.js_event)) goto back_link;
864 		/*if (d_opt->num_links) {
865 			unsigned char s[64];
866 			unsigned char *fl = format_.link, *ft = format_.target, *fi = format_.image;
867 			struct form_control *ff = format_.form;
868 			struct js_event_spec *js = format_.js_event;
869 			format_.link = format_.target = format_.image = NULL;
870 			format_.form = NULL;
871 			format_.js_event = NULL;
872 			s[0] = '[';
873 			snzprint(s + 1, 62, p->link_num);
874 			strcat(cast_char s, "]");
875 			g_put_chars(p, s, strlen(cast_const_char s));
876 			if (ff && ff->type == FC_TEXTAREA) g_line_break(p);
877 			if (p->cx < par_format.leftmargin * G_HTML_MARGIN) p->cx = par_format.leftmargin * G_HTML_MARGIN;
878 			format_.link = fl, format_.target = ft, format_.image = fi;
879 			format_.form = ff;
880 			format_.js_event = js;
881 		}*/
882 		p->link_num++;
883 		last_link = stracpy(format_.link);
884 		last_target = stracpy(format_.target);
885 		last_image = stracpy(format_.image);
886 		last_form = format_.form;
887 		copy_js_event_spec(&last_js_event, format_.js_event);
888 		if (!p->data) goto back_link;
889 		if (!(link = new_link(p->data))) goto back_link;
890 		link->num = p->link_num - 1;
891 		link->pos = DUMMY;
892 		copy_js_event_spec(&link->js_event, format_.js_event);
893 		if (!last_form) {
894 			link->type = L_LINK;
895 			link->where = stracpy(last_link);
896 			link->target = stracpy(last_target);
897 		} else {
898 			link->type = last_form->type == FC_TEXT || last_form->type == FC_PASSWORD || last_form->type == FC_FILE_UPLOAD ? L_FIELD : last_form->type == FC_TEXTAREA ? L_AREA : last_form->type == FC_CHECKBOX || last_form->type == FC_RADIO ? L_CHECKBOX : last_form->type == FC_SELECT ? L_SELECT : L_BUTTON;
899 			link->form = last_form;
900 			link->target = stracpy(last_form->target);
901 		}
902 		link->where_img = stracpy(last_image);
903 		link->sel_color = 0;
904 		link->n = 0;
905 	}
906 	goto back_link;
907 }
908 
g_do_format(unsigned char * start,unsigned char * end,struct g_part * part,unsigned char * head)909 static void g_do_format(unsigned char *start, unsigned char *end, struct g_part *part, unsigned char *head)
910 {
911 	pr(
912 	parse_html(start, end, g_put_chars, g_line_break, g_html_special, part, head);
913 	flush_pending_text_to_line(part);
914 	flush_pending_line_to_obj(part, 0);
915 	) {};
916 }
917 
g_format_html_part(unsigned char * start,unsigned char * end,int align,int m,int width,unsigned char * head,int link_num,unsigned char * bg,unsigned char * bgcolor,struct f_data * f_d)918 struct g_part *g_format_html_part(unsigned char *start, unsigned char *end, int align, int m, int width, unsigned char *head, int link_num, unsigned char *bg, unsigned char *bgcolor, struct f_data *f_d)
919 {
920 	int wa;
921 	struct g_part *p;
922 	struct html_element *e;
923 	struct form_control *fc;
924 	struct list_head *lfc;
925 	int lm = margin;
926 
927 	if (par_format.implicit_pre_wrap) {
928 		int limit = d_opt->xw - G_SCROLL_BAR_WIDTH;
929 		if (table_level) limit -= 2 * G_HTML_MARGIN * d_opt->margin;
930 		if (limit < 0) limit = d_opt->xw;
931 		if (width > limit)
932 			width = limit;
933 	}
934 
935 	if (!f_d) {
936 		p = find_table_cache_entry(start, end, align, m, width, 0, link_num);
937 		if (p) return p;
938 	}
939 	margin = m;
940 
941 	/*d_opt->tables = 0;*/
942 
943 	if (last_link) mem_free(last_link);
944 	if (last_image) mem_free(last_image);
945 	if (last_target) mem_free(last_target);
946 	free_js_event_spec(last_js_event);
947 	last_link = last_image = last_target = NULL;
948 	last_form = NULL;
949 	last_js_event = NULL;
950 
951 	cached_font_face = to_je_ale_prasarna;
952 	p = mem_calloc(sizeof(struct g_part));
953 	{
954 		struct g_object_area *a;
955 		a = mem_calloc(sizeof(struct g_object_area));
956 		a->bg = g_get_background(bg, bgcolor);
957 		if (bgcolor) decode_color(bgcolor, &format_.bg);
958 		if (bgcolor) decode_color(bgcolor, &par_format.bgcolor);
959 		a->go.mouse_event = g_area_mouse;
960 		a->go.draw = g_area_draw;
961 		a->go.destruct = g_area_destruct;
962 		a->go.get_list = g_area_get_list;
963 		/*a->n_lines = 0;*/
964 		p->root = a;
965 		init_list(p->uf);
966 	}
967 	p->data = f_d;
968 	p->x = p->y = 0;
969 	p->xmax = 0;
970 	html_stack_dup();
971 	e = &html_top;
972 	html_top.dontkill = 2;
973 	html_top.namelen = 0;
974 	par_format.align = align;
975 	par_format.leftmargin = m;
976 	par_format.rightmargin = m;
977 	par_format.width = width;
978 	par_format.list_level = 0;
979 	par_format.list_number = 0;
980 	par_format.dd_margin = 0;
981 	if (align == AL_NO || align == AL_NO_BREAKABLE)
982 		format_.attr |= AT_FIXED;
983 	p->cx = -1;
984 	p->cx_w = 0;
985 	g_nobreak = align != AL_NO && par_format.align != AL_NO_BREAKABLE;
986 	g_do_format(start, end, p, head);
987 	g_nobreak = 0;
988 	line_breax = 1;
989 	while (&html_top != e) {
990 		kill_html_stack_item(&html_top);
991 		if (!&html_top || (void *)&html_top == (void *)&html_stack) {
992 			internal_error("html stack trashed");
993 			break;
994 		}
995 	}
996 	html_top.dontkill = 0;
997 
998 	wa = g_get_area_width(p->root);
999 	if (wa > p->x) p->x = wa;
1000 	g_x_extend_area(p->root, p->x, 0, align);
1001 	if (p->x > p->xmax) p->xmax = p->x;
1002 	p->y = p->root->go.yw;
1003 	/*debug("WIDTH: obj (%d, %d), p (%d %d)", p->root->xw, p->root->yw, p->x, p->y);*/
1004 
1005 	kill_html_stack_item(&html_top);
1006 	if (!f_d) g_release_part(p), p->root = NULL;
1007 	if (cached_font_face && cached_font_face != to_je_ale_prasarna) mem_free(cached_font_face);
1008 	cached_font_face = to_je_ale_prasarna;
1009 
1010 	foreach(struct form_control, fc, lfc, p->uf) destroy_fc(fc);
1011 	free_list(struct form_control, p->uf);
1012 
1013 	margin = lm;
1014 
1015 	if (last_link) mem_free(last_link);
1016 	if (last_image) mem_free(last_image);
1017 	if (last_target) mem_free(last_target);
1018 	free_js_event_spec(last_js_event);
1019 	last_link = last_image = last_target = NULL;
1020 	last_form = NULL;
1021 	last_js_event = NULL;
1022 
1023 	if (table_level > 1 && !f_d) {
1024 		add_table_cache_entry(start, end, align, m, width, 0, link_num, p);
1025 	}
1026 
1027 	return p;
1028 }
1029 
g_release_part(struct g_part * p)1030 void g_release_part(struct g_part *p)
1031 {
1032 	if (p->text) p->text->goti.go.destruct(&p->text->goti.go);
1033 	if (p->line) p->line->go.destruct(&p->line->go);
1034 	if (p->root) p->root->go.destruct(&p->root->go);
1035 	if (p->current_style) g_free_style(p->current_style);
1036 }
1037 
g_scan_lines(struct g_object_line ** o,int n,int * w)1038 static void g_scan_lines(struct g_object_line **o, int n, int *w)
1039 {
1040 	while (n--) {
1041 		if ((*o)->n_entries) {
1042 			struct g_object *oo = (*o)->entries[(*o)->n_entries - 1];
1043 			if (safe_add(safe_add((*o)->go.x, oo->x), oo->xw) > *w)
1044 				*w = (*o)->go.x + oo->x + oo->xw;
1045 		}
1046 		o++;
1047 	}
1048 }
1049 
g_get_area_width(struct g_object_area * a)1050 int g_get_area_width(struct g_object_area *a)
1051 {
1052 	int w = 0;
1053 	g_scan_lines(a->lines, a->n_lines, &w);
1054 	return w;
1055 }
1056 
g_x_extend_area(struct g_object_area * a,int width,int height,int align)1057 void g_x_extend_area(struct g_object_area *a, int width, int height, int align)
1058 {
1059 	struct g_object_line *l;
1060 	int i;
1061 	a->go.xw = width;
1062 	for (i = 0; i < a->n_lines; i++) {
1063 		a->lines[i]->go.xw = width;
1064 	}
1065 	if (align != AL_NO && par_format.align != AL_NO_BREAKABLE) for (i = a->n_lines - 1; i >= 0; i--) {
1066 		l = a->lines[i];
1067 		if (!l->n_entries) {
1068 			a->go.yw -= l->go.yw;
1069 			l->go.destruct(&l->go);
1070 			a->n_lines--;
1071 			continue;
1072 		}
1073 		break;
1074 	}
1075 	if (a->go.yw >= height) return;
1076 	l = mem_calloc(sizeof(struct g_object_line));
1077 	l->go.mouse_event = g_line_mouse;
1078 	l->go.draw = g_line_draw;
1079 	l->go.destruct = g_line_destruct;
1080 	l->go.get_list = g_line_get_list;
1081 	l->go.x = 0;
1082 	l->go.y = a->go.yw;
1083 	l->go.xw = width;
1084 	l->go.yw = height - a->go.yw;
1085 	l->bg = a->bg;
1086 	l->n_entries = 0;
1087 	a->lines[a->n_lines] = l;
1088 	a->n_lines++;
1089 }
1090 
1091 #endif
1092