iconv: Bail out of the loop when an illegal sequence of bytes occurs.
[elinks/elinks-j605.git] / src / document / html / frames.c
blob3c40db7f438daffe5cfdba0aab3992f5202fc5ea
1 /* HTML frames parser */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <errno.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
12 #include "elinks.h"
14 #include "cache/cache.h"
15 #include "document/document.h"
16 #include "document/html/frames.h"
17 #include "document/options.h"
18 #include "document/renderer.h"
19 #include "document/view.h"
20 #include "protocol/uri.h"
21 #include "session/session.h"
22 #include "terminal/draw.h"
23 #include "util/color.h"
24 #include "util/string.h"
25 #include "util/time.h"
28 void
29 add_frameset_entry(struct frameset_desc *frameset_desc,
30 struct frameset_desc *subframe,
31 unsigned char *name, unsigned char *url)
33 struct frame_desc *frame_desc;
34 int offset;
36 assert(frameset_desc);
37 if_assert_failed return;
39 /* FIXME: The following is triggered by
40 * http://www.thegoodlookingorganisation.co.uk/main.htm.
41 * There may exist a true fix for this... --Zas */
42 /* May the one truly fixing this notify'n'close bug 237 in the
43 * Bugzilla... --pasky */
44 if (frameset_desc->box.y >= frameset_desc->box.height)
45 return;
47 offset = frameset_desc->box.x
48 + frameset_desc->box.y * frameset_desc->box.width;
49 frame_desc = &frameset_desc->frame_desc[offset];
50 frame_desc->subframe = subframe;
51 frame_desc->name = null_or_stracpy(name);
52 frame_desc->uri = (url && *url) ? get_uri(url, 0) : NULL;
53 if (!frame_desc->uri)
54 frame_desc->uri = get_uri("about:blank", 0);
56 frameset_desc->box.x++;
57 if (frameset_desc->box.x >= frameset_desc->box.width) {
58 frameset_desc->box.x = 0;
59 frameset_desc->box.y++;
60 /* FIXME: check y here ? --Zas */
64 struct frameset_desc *
65 create_frameset(struct frameset_param *fp)
67 struct frameset_desc *fd;
68 unsigned int size;
70 assert(fp);
71 if_assert_failed return NULL;
73 assertm(fp->x > 0 && fp->y > 0,
74 "Bad size of frameset: x=%d y=%d", fp->x, fp->y);
75 if_assert_failed {
76 if (fp->x <= 0) fp->x = 1;
77 if (fp->y <= 0) fp->y = 1;
80 size = fp->x * fp->y;
81 /* size - 1 since one struct frame_desc is already reserved
82 * in struct frameset_desc. */
83 fd = mem_calloc(1, sizeof(*fd)
84 + (size - 1) * sizeof(fd->frame_desc[0]));
85 if (!fd) return NULL;
88 int i;
90 for (i = 0; i < size; i++) {
91 fd->frame_desc[i].width = fp->width[i % fp->x];
92 fd->frame_desc[i].height = fp->height[i / fp->x];
96 fd->n = size;
97 fd->box.width = fp->x;
98 fd->box.height = fp->y;
100 if (fp->parent)
101 add_frameset_entry(fp->parent, fd, NULL, NULL);
103 return fd;
106 static void
107 add_frame_to_list(struct session *ses, struct document_view *doc_view)
109 struct document_view *ses_doc_view;
111 assert(ses && doc_view);
112 if_assert_failed return;
114 foreach (ses_doc_view, ses->scrn_frames) {
115 int sx = ses_doc_view->box.x;
116 int sy = ses_doc_view->box.y;
117 int x = doc_view->box.x;
118 int y = doc_view->box.y;
120 if (sy > y || (sy == y && sx > x)) {
121 add_at_pos(ses_doc_view->prev, doc_view);
122 return;
126 add_to_list_end(ses->scrn_frames, doc_view);
129 static struct document_view *
130 find_fd(struct session *ses, unsigned char *name,
131 int depth, int x, int y)
133 struct document_view *doc_view;
135 assert(ses && name);
136 if_assert_failed return NULL;
138 foreachback (doc_view, ses->scrn_frames) {
139 if (doc_view->used) continue;
140 if (c_strcasecmp(doc_view->name, name)) continue;
142 doc_view->used = 1;
143 doc_view->depth = depth;
144 return doc_view;
147 doc_view = mem_calloc(1, sizeof(*doc_view));
148 if (!doc_view) return NULL;
150 doc_view->used = 1;
151 doc_view->name = stracpy(name);
152 if (!doc_view->name) {
153 mem_free(doc_view);
154 return NULL;
156 doc_view->depth = depth;
157 doc_view->session = ses;
158 doc_view->search_word = &ses->search_word;
159 set_box(&doc_view->box, x, y, 0, 0);
161 add_frame_to_list(ses, doc_view);
163 return doc_view;
166 static struct document_view *
167 format_frame(struct session *ses, struct frame_desc *frame_desc,
168 struct document_options *o, int depth)
170 struct view_state *vs;
171 struct document_view *doc_view;
172 int plain;
174 assert(ses && frame_desc && o);
175 if_assert_failed return NULL;
177 while (1) {
178 struct cache_entry *cached;
179 struct frame *frame = ses_find_frame(ses, frame_desc->name);
181 if (!frame) return NULL;
183 vs = &frame->vs;
184 cached = find_in_cache(vs->uri);
185 if (!cached) return NULL;
187 plain = o->plain;
188 if (vs->plain != -1) o->plain = vs->plain;
190 if (!cached->redirect || frame->redirect_cnt >= MAX_REDIRECTS)
191 break;
193 frame->redirect_cnt++;
194 done_uri(vs->uri);
195 vs->uri = get_uri_reference(cached->redirect);
196 #ifdef CONFIG_ECMASCRIPT
197 vs->ecmascript_fragile = 1;
198 #endif
199 o->plain = plain;
202 doc_view = find_fd(ses, frame_desc->name, depth, o->box.x, o->box.y);
203 if (doc_view) {
204 render_document(vs, doc_view, o);
205 assert(doc_view->document);
206 doc_view->document->frame = frame_desc;
208 o->plain = plain;
210 return doc_view;
213 void
214 format_frames(struct session *ses, struct frameset_desc *fsd,
215 struct document_options *op, int depth)
217 struct document_options o;
218 int j, n;
220 assert(ses && fsd && op);
221 if_assert_failed return;
223 if (depth > HTML_MAX_FRAME_DEPTH) {
224 return;
227 copy_struct(&o, op);
229 o.margin = !!o.margin;
231 n = 0;
232 for (j = 0; j < fsd->box.height; j++) {
233 int i;
235 o.box.x = op->box.x;
236 for (i = 0; i < fsd->box.width; i++) {
237 struct frame_desc *frame_desc = &fsd->frame_desc[n];
239 o.box.width = frame_desc->width;
240 o.box.height = frame_desc->height;
241 o.framename = frame_desc->name;
242 if (frame_desc->subframe) {
243 format_frames(ses, frame_desc->subframe, &o, depth + 1);
244 } else if (frame_desc->name) {
245 struct document_view *doc_view;
247 doc_view = format_frame(ses, frame_desc, &o, depth);
248 #ifdef CONFIG_DEBUG
249 do_not_optimize_here(&doc_view);
250 #endif
251 if (doc_view && document_has_frames(doc_view->document)) {
252 struct document *document = doc_view->document;
254 /* Make sure the frameset does not
255 * disappear during processing further
256 * frames - this can happen when we
257 * kick the same-frame-ids bug and
258 * replace view_state of an existing
259 * frame - if that frame is *this* one,
260 * it gets detached, losing the
261 * document and if we aren't lucky the
262 * document gets burned in
263 * shrink_format_cache(). Also note
264 * that the same bug replacing
265 * view_state also causes doc_view
266 * recyclation so we end up with a
267 * *different* document attached to it
268 * after the call, so we need to cache
269 * it. It's all fun to debug, really.
270 * --pasky */
271 object_lock(document);
272 format_frames(ses, document->frame_desc,
273 &o, depth + 1);
274 object_unlock(document);
277 o.box.x += o.box.width + 1;
278 n++;
279 #ifdef CONFIG_DEBUG
280 /* This makes this ugly loop actually at least remotely
281 * debuggable by gdb, otherwise the compiler happily
282 * randomly trashes or just blasts away *all* of these
283 * variables. */
284 do_not_optimize_here(&n);
285 do_not_optimize_here(&i);
286 do_not_optimize_here(&j);
287 do_not_optimize_here(&fsd);
288 do_not_optimize_here(&frame_desc);
289 #endif
291 o.box.y += o.box.height + 1;
296 /* Frame width computation tools */
298 /* Returns 0 on error. */
299 static int
300 distribute_rows_or_cols(int *val_, int max_value, int *values, int values_count)
302 int i;
303 int divisor = 0;
304 int tmp_val;
305 int val = *val_ - max_value;
307 for (i = 0; i < values_count; i++) {
308 if (values[i] < 1)
309 values[i] = 1;
311 divisor += values[i];
314 assert(divisor);
316 tmp_val = val;
317 for (i = 0; i < values_count; i++) {
318 int tmp;
320 /* SIGH! gcc 2.7.2.* has an optimizer bug! */
321 do_not_optimize_here_gcc_2_7(&divisor);
322 tmp = values[i] * (divisor - tmp_val) / divisor;
323 val -= values[i] - tmp;
324 values[i] = tmp;
327 while (val) {
328 int flag = 0;
330 for (i = 0; i < values_count; i++) {
331 if (val < 0) values[i]++, val++, flag = 1;
332 if (val > 0 && values[i] > 1) values[i]--, val--, flag = 1;
333 if (!val) break;
335 if (!flag) break;
338 *val_ = val;
339 return 1;
342 /* Returns 0 on error. */
343 static int
344 distribute_rows_or_cols_that_left(int *val_, int max_value, int *values, int values_count)
346 int i;
347 int val = *val_;
348 int *tmp_values;
349 int divisor = 0;
350 int tmp_val;
352 tmp_values = fmem_alloc(values_count * sizeof(*tmp_values));
353 if (!tmp_values) return 0;
354 memcpy(tmp_values, values, values_count * sizeof(*tmp_values));
356 for (i = 0; i < values_count; i++)
357 if (values[i] < 1)
358 values[i] = 1;
359 val = max_value - val;
361 for (i = 0; i < values_count; i++)
362 if (tmp_values[i] < 0)
363 divisor += -tmp_values[i];
364 assert(divisor);
366 tmp_val = val;
367 for (i = 0; i < values_count; i++)
368 if (tmp_values[i] < 0) {
369 int tmp = (-tmp_values[i] * tmp_val / divisor);
371 values[i] += tmp;
372 val -= tmp;
374 assertm(val >= 0, "distribute_rows_or_cols_that_left: val < 0");
375 if_assert_failed val = 0;
377 for (i = 0; i < values_count; i++)
378 if (tmp_values[i] < 0 && val)
379 values[i]++, val--;
381 assertm(val <= 0, "distribute_rows_or_cols_that_left: val > 0");
382 if_assert_failed val = 0;
384 fmem_free(tmp_values);
386 *val_ = val;
387 return 1;
390 /* Returns 0 on error. */
391 static int
392 extract_rows_or_cols_values(unsigned char *str, int max_value, int pixels_per_char,
393 int **new_values, int *new_values_count)
395 unsigned char *tmp_str;
396 int *values = NULL;
397 int values_count = 0;
399 while (1) {
400 unsigned char *end = str;
401 int *tmp_values;
402 unsigned long number;
403 int val = -1; /* Wildcard */
405 skip_space(str);
407 /* Some platforms (FreeBSD) set errno when the first char is
408 * not a digit others (GNU/Linux) don't so ignore errno. */
409 /* Extract number. */
410 number = strtoul(str, (char **) &end, 10);
411 if (end == str) {
412 number = 0;
413 } else {
414 str = end;
417 /* @number is an ulong, but @val is int,
418 * so check if @number is in a reasonable
419 * range to prevent bad things. */
420 if (number <= 0xffff) {
421 if (*str == '%') /* Percentage */
422 val = int_min((int) number, 100) * max_value / 100;
423 else if (*str != '*') /* Pixels */
424 val = (number + (pixels_per_char - 1) / 2) / pixels_per_char;
425 else if (number) /* Fraction, marked by negative value. */
426 val = -number;
429 /* Save value. */
430 tmp_values = mem_realloc(values, (values_count + 1) * sizeof(*tmp_values));
431 if (!tmp_values) return 0;
433 values = tmp_values;
434 values[values_count++] = val;
436 /* Check for next field if any. */
437 tmp_str = strchr(str, ',');
438 if (!tmp_str) break; /* It was the last field. */
440 str = tmp_str + 1;
443 *new_values = values;
444 *new_values_count = values_count;
446 return 1;
449 /* Parse rows and cols attribute values and calculate appropriated values for display.
450 * It handles things like:
451 * <frameset cols="140,260,160"> values in pixels
452 * <frameset cols="1*,2*,3*"> values in fractions
453 * <frameset cols="320,*"> wildcard
454 * <frameset cols="33%,33%,33%" rows="33%,33%,33%"> values in percentage
455 * */
456 void
457 parse_frame_widths(unsigned char *str, int max_value, int pixels_per_char,
458 int **new_values, int *new_values_count)
460 int val, ret;
461 int *values;
462 int values_count;
463 int i;
465 *new_values_count = 0;
467 ret = extract_rows_or_cols_values(str, max_value, pixels_per_char,
468 &values, &values_count);
469 if (!ret) return;
471 /* Here begins distribution between rows or cols. */
473 val = 2 * values_count - 1;
474 for (i = 0; i < values_count; i++)
475 if (values[i] > 0)
476 val += values[i] - 1;
478 if (val >= max_value) {
479 ret = distribute_rows_or_cols(&val, max_value, values, values_count);
480 } else {
481 int neg = 0;
483 for (i = 0; i < values_count; i++)
484 if (values[i] < 0)
485 neg = 1;
487 if (neg)
488 ret = distribute_rows_or_cols_that_left(&val, max_value, values, values_count);
489 else
490 ret = distribute_rows_or_cols(&val, max_value, values, values_count);
493 if (!ret) return;
495 for (i = 0; i < values_count; i++)
496 if (!values[i]) {
497 int j;
498 int maxval = 0;
499 int maxpos = 0;
501 for (j = 0; j < values_count; j++)
502 if (values[j] > maxval)
503 maxval = values[j], maxpos = j;
504 if (maxval)
505 values[i] = 1, values[maxpos]--;
508 *new_values = values;
509 *new_values_count = values_count;