1 /*
2 netrik -- The ANTRIK Internet Viewer
3 Copyright (C) Olaf D. Buddenhagen AKA antrik, et al (see AUTHORS)
4 Published under the GNU GPL; see LICENSE for details.
5 */
6 /*
7 * parse-struct.c -- this one takes the syntax parsing tree generated by
8 * parse_syntax() and creates another structure called item_tree().
9 *
10 * (C) 2001, 2002, 2003 antrik
11 * 2002 Patrice Neff
12 *
13 * This item tree containes items as they are visible on the screen: text
14 * blocks, blank lines, boxes aligning several other items etc.
15 */
16 #include <ctype.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20
21 #include "colors.h"
22 #include "forms.h"
23 #include "items.h"
24 #include "syntax.h"
25
26 /* all information specific for every parse tree depth */
27 struct State {
28 int visible; /* content inside this element will be displayed in output page */
29
30 enum Text_mode text_mode; /* content encountered now will be stored in this mode */
31 int high; /* highlighted text (additional to "text_mode") */
32
33 int list_depth; /* nesting depth of item lists */
34
35 struct Item *first_item; /* first item at this tree depth */
36 struct Item *last_item; /* presently last item at this tree depth */
37
38 enum Form_control link_type; /* indicates type of link/form element (-1: no link) */
39 char *link_value; /* initial value of form element */
40 int form_enabled; /* form element may be submitted */
41 char *select_name; /* name assigned to all options inside a <select> */
42 enum Form_control select_type; /* type (FORM_OPTION or FORM_MULTIOPTION) for all <option> elements in current select */
43
44 int link_start; /* starting positon of link/anchor (string end position at time of entering this depth) */
45 struct Item *link_item; /* string item where link started */
46 int id_attr; /* position of the "id" (or "name") attribute of an anchor (or form control) */
47 };
48
49 static int fingerprint(char *start, char *end); /* create checksum */
50 static struct State *push_state(void); /* save state when descending in parse tree */
51 static struct State *pop_state(void); /* recall state when ascending in parse tree */
52 struct Item *add_item(int virtual); /* create new item in structure tree */
53 void add_text(char *text, int color); /* append text to current string */
54 int find_attr(struct Element *search_el, enum Attr_type attr); /* find attribute of specified type in specified element */
55
56 static struct State *state; /* (dynamic) array (stack) of states for all parse tree depths */
57
58 static struct State *cur_state; /* pointer to currently active state in state stack */
59 static int depth; /* depth in parse tree/state stack (-1=uninitialized state stack) */
60
61 static struct Item *cur_item; /* most recently added item */
62 static struct Item *string_item; /* text item containing currently active string (any new text will be added to this one) */
63 static struct Item *first_item; /* first item in structure tree */
64
65 static int para_blank; /* 1: blank line necessary before next item; -1: already items in this box (blank line necessary, if next item creates paragraph) */
66 static int blank_depth; /* tree depth of last "para_blank" request */
67
68
69 /* create a simple checksum of the desired string part */
fingerprint(start,end)70 static int fingerprint(start, end)
71 char *start, *end; /* handled memory area (string part) */
72 {
73 char *chr;
74 int sum;
75
76 sum=0;
77 for(chr=start; chr<end; ++chr)
78 sum^=(sum<<8)^*chr;
79
80 return sum;
81 }
82
83 /* save state when descending in parse tree;
84 * increment depth counter, resize state stack, copy old state, and return pointer to new state */
push_state(void)85 static struct State *push_state(void)
86 {
87 /* resize state stack to (new) tree depth */
88 state=realloc(state, (++depth+1)*sizeof(struct State));
89 if(state==NULL) {
90 fprintf(stderr, "memory allocation error while parsing structure (in function push_state)\n");
91 exit(1);
92 }
93
94 /* init new state */
95 if(depth) { /* normal push -> copy old state */
96 state[depth]=state[depth-1];
97 state[depth].last_item=state[depth].first_item=NULL; /* don't inherit item list */
98 state[depth].id_attr=-1; /* don't inherit anchors */
99 state[depth].link_type=-1; /* don't inherit links */
100 } else { /* initialization of first entry -> default values */
101 state[0].visible=1;
102 state[0].text_mode=TM_NORMAL;
103 state[0].high=0;
104 state[0].list_depth=0;
105 state[0].first_item=state[0].last_item=NULL;
106 state[0].link_type=-1;
107 state[0].form_enabled=0;
108 state[0].select_name=NULL;
109 state[0].id_attr=-1;
110 }
111
112 return &state[depth]; /* pointer to new state */
113 }
114
115 /* restore state when ascending in parse tree */
pop_state(void)116 static struct State *pop_state(void)
117 {
118 return &state[--depth]; /* previous state */
119 }
120
121 /*
122 * Create new item and insert into item list of current parse tree depth.
123 *
124 * If last item was a string item, it is invalidated. If pending "para_blank",
125 * first add blank line; after adding item, "para_blank" is set to -1 (=may
126 * need blank line before next item).
127 */
128
add_item(virtual)129 struct Item *add_item(virtual)
130 int virtual; /* don't change "para_blank" status nor break text blocks */
131 {
132 struct Item *new_item; /* newly created item */
133
134 if(para_blank==1 && !virtual) { /* pending blank line -> first create&insert blank line into structure tree */
135 /* alloc item */
136 new_item=malloc(sizeof(struct Item));
137 if(new_item==NULL) {
138 fprintf(stderr, "memory allocation error while parsing structure (in function create_item)\n");
139 exit(1);
140 }
141
142 /* insert into tree */
143 cur_item->list_next=new_item;
144 cur_item->next=new_item; /* same level as last item */
145 new_item->next=NULL; /* last item at this tree depth */
146 new_item->first_child=NULL; /* blank line never has children... */
147 state[blank_depth].last_item=new_item; /* insert into item list of tree depth at which request was generated */
148
149 new_item->type=ITEM_BLANK;
150
151 new_item->center=0; /* not really neccesary, but looks nicer... */
152
153 cur_item=new_item; /* now this one is newest... */
154 }
155
156 /* alloc item */
157 new_item=malloc(sizeof(struct Item));
158 if(new_item==NULL) {
159 fprintf(stderr, "memory allocation error while parsing structure (in function create_item)\n");
160 exit(1);
161 }
162
163 /* insert into tree */
164 if(cur_item!=NULL) /* any items present yet -> insert as next */
165 cur_item->list_next=new_item;
166 else /* no items yet -> first one */
167 first_item=new_item;
168 new_item->list_next=NULL; /* last item for now */
169 new_item->first_child=NULL; /* no children yet */
170
171 /* insert into item list of current tree depth */
172 if(cur_state->first_item!=NULL) { /* alredy items at this tree depth -> insert as last */
173 cur_state->last_item->next=new_item;
174 cur_state->last_item=new_item;
175 } else { /* no items at this tree depth yet -> insert as only one */
176 cur_state->last_item=cur_state->first_item=new_item;
177 cur_state->first_item->parent=NULL; /* indicates that list not inserted into tree yet */
178 }
179 new_item->next=NULL; /* last item at this tree depth */
180
181 new_item->center=0; /* centering not handled at creation time */
182
183 if(!virtual) {
184 string_item=NULL; /* if last item was a text item, it is no longer valid to add text to it */
185
186 para_blank=-1; /* now have some item in this box (blank line may be necessary before next item) */
187 }
188 blank_depth=depth; /* need to know in which box to add next blank line... */
189
190 return new_item;
191 }
192
193 /*
194 * Append text to current string.
195 *
196 * If currently not in a text block, create new text item and new string to add
197 * text to.
198 *
199 * If called with text==NULL, don't append anything; force creation of new div on
200 * next invocation instead, even if color is same as in last div.
201 *
202 * color==-1 means to keep the last one. (For adding newlines.)
203 */
204
add_text(text,color)205 void add_text(text, color)
206 char *text; /* appended text */
207 int color; /* text color */
208 {
209 int old_len, new_len; /* string len before and after concatenating the new text */
210 static struct String *string; /* currently active string */
211 static int force_div=0; /* force creation of new div for next text part, even if same color as previous one */
212
213 if(text==NULL) {
214 force_div=1;
215 return;
216 }
217
218 if(string_item==NULL) { /* no open text item -> create new one */
219
220 /* add new string item */
221 cur_item=string_item=add_item(0); string_item->type=ITEM_TEXT;
222
223 /* alloc string structure */
224 string=string_item->data.string=malloc(sizeof(struct String));
225 if(string==NULL) {
226 fprintf(stderr, "memory allocation error while parsing structure (in function add_text)\n");
227 exit(1);
228 }
229
230 /* init */
231 string->text=NULL;
232 string->div_count=0;
233 string->div=NULL;
234 string->link_count=0;
235 string->link=NULL;
236
237 old_len=0; /* no text yet */
238
239 if(text[0]==' ') /* skip blank at beginning of text block */
240 ++text;
241 } else
242 old_len=string->div[string->div_count-1].end; /* text len is equal to end position of last div */
243
244 if(string->div==NULL /* no divs yet */
245 || (color!=-1 && string->div[string->div_count-1].color!=color /* new color */
246 && *text) /* any text to add */
247 || force_div
248 ) { /* new div */
249
250 if(color==-1) { /* no color supplied, but div forced -> need to set some */
251 if(string->div_count) /* already have divs -> keep color of last */
252 color=string->div[string->div_count-1].color;
253 else /* no divs yet -> use default color */
254 color=7;
255 }
256
257 /* alloc new div */
258 string->div=realloc(string->div, ++string->div_count*sizeof(struct Div));
259 if(string->div==NULL) {
260 fprintf(stderr, "memory allocation error while parsing structure (in function add_text)\n");
261 exit(1);
262 }
263
264 string->div[string->div_count-1].color=color; /* set color of new div */
265 }
266
267 new_len=old_len+strlen(text); /* add len of new text */
268 string->div[string->div_count-1].end=new_len; /* set/adjust len of last div */
269
270 /* adjust text block size */
271 string->text=realloc(string->text, new_len+1);
272 if(string->text==NULL) {
273 fprintf(stderr, "memory allocation error while parsing structure (in function add_text)\n");
274 exit(1);
275 }
276
277 strcpy(&string->text[old_len], text); /* concatenate new text */
278
279 force_div=0; /* next text part will be added normally again */
280 }
281
282 /* find attribute of specified type in specified element */
find_attr(search_el,attr)283 int find_attr(search_el, attr)
284 struct Element *search_el; /* element in whose attribute list to search */
285 enum Attr_type attr; /* attribute type to search */
286 {
287 int cur_attr;
288
289 for(cur_attr=0; cur_attr<search_el->attr_count; ++cur_attr) /* all attributes of this tag */
290 if(search_el->attr[cur_attr].name.type==attr) /* found desired attribute type */
291 return cur_attr; /* -> return attr num */
292
293 return -1; /* not found */
294 }
295
296 /* create a structure tree containing all the elements (boxes, text) of a document, and their dependencies */
parse_struct(syntax_tree)297 struct Item *parse_struct(syntax_tree)
298 struct Element *syntax_tree; /* top of syntax parse tree */
299 {
300 struct Element *cur_el; /* current element in parse tree */
301 struct Element *new_el; /* current element while ascending */
302
303 para_blank=0; /* no blank line before global item */
304 string_item=cur_item=NULL; /* no items yet */
305
306 /* init state stack */
307 depth=-1; state=NULL; /* no state stack yet */
308 cur_state=push_state();
309
310 /* all elements in syntax parse tree */
311 cur_el=syntax_tree;
312 do { /* until cur_el->parent==NULL (wrapped back to global element) */
313
314 if(cur_el->content!=NULL) /* some content before element -> add to current text block */
315 if(cur_state->visible) /* ignore content inside <head> and some other elements */
316 add_text(cur_el->content, color_map[cur_state->text_mode]^(cur_state->high<<3)); /* color from colormap, intensity bit toggled by "high" mode */
317
318 if(element_table[cur_el->name.type].breaks) { /* element breaks lines */
319
320 string_item=NULL; /* start new text block */
321
322 if(para_blank==-1) { /* already items in this box, but no blank line request yet -> look if need one */
323 if(element_table[cur_el->name.type].breaks==2) /* element creates paragraph */
324 para_blank=1; /* -> needs blank line to seperate from previous paragraphs (inserted later, before next item) */
325 else if(element_table[cur_el->name.type].force_box) /* element creates no paragraph, but own box -> no blank line before next paragraph */
326 para_blank=0; /* no items in new box yet */
327 }
328
329 }
330
331 cur_state=push_state(); /* descend (depth is below depth of cur_el afterwards) */
332
333 if(!element_table[cur_el->name.type].visible)
334 cur_state->visible=0;
335
336 /* anchors */
337 {
338 int id_attr=find_attr(cur_el, ATTR_NAME);
339
340 if(id_attr<0) /* no "name" -> try "id" */
341 id_attr=find_attr(cur_el, ATTR_ID);
342
343 if(id_attr>=0) /* has id (or name) -> create anchor */
344 cur_state->id_attr=id_attr; /* store for use when leaving element; also indicates existance of anchor */
345 }
346
347 /* save current position in text string for links/form controls/inline anchors */
348 cur_state->link_item=string_item;
349 if(string_item!=NULL)
350 cur_state->link_start=string_item->data.string->div[string_item->data.string->div_count-1].end;
351
352 switch(cur_el->name.type) {
353 case EL_H1:
354 case EL_H2:
355 case EL_H3:
356 case EL_H4:
357 case EL_H5:
358 case EL_H6:
359 cur_state->high=1; /* headings simply highlighted for now */
360 break;
361 case EL_EM:
362 case EL_I:
363 if(cur_state->text_mode<TM_ITALIC) /* not already in higher priority mode -> set mode */
364 cur_state->text_mode=TM_ITALIC;
365 break;
366 case EL_STRONG:
367 case EL_B:
368 cur_state->high=1; /* highlight (color itself isn't changed) */
369 break;
370 case EL_A: {
371 const int href_attr=find_attr(cur_el, ATTR_HREF);
372
373 if(href_attr>=0) { /* has href => link */
374 cur_state->link_type=FORM_NO; /* normal link, not form */
375 cur_state->link_value=cur_el->attr[href_attr].value.str;
376
377 add_text(NULL, 0); /* force new div */
378
379 cur_state->text_mode=TM_LINK;
380 if(cur_el->attr[href_attr].value.str[0]=='#') /* local anchor -> print marker */
381 add_text("->", color_map[TM_SYS]^(cur_state->high<<3));
382 }
383 break;
384 }
385 case EL_BR:
386 if(string_item!=NULL) { /* can't insert line break outside text block... */
387 const struct String *string=string_item->data.string;
388
389 if(*string->text && string->text[string->div[string->div_count-1].end-1]!='\n') /* last char isn't newline (never insert more than one '\n') */
390 add_text("\n", -1); /* add newline, don't change color */
391 }
392 break;
393 case EL_TR:
394 add_text("-", color_map[TM_SYS]); /* put indicator in front of every table row */
395 break;
396 case EL_TH:
397 cur_state->high=1; /* header cells highlighted */
398 /* fallthrough */
399 case EL_TD:
400 add_text("|", color_map[TM_SYS]); /* put indicator in front of every table cell */
401 break;
402 case EL_UL:
403 case EL_OL:
404 ++cur_state->list_depth;
405 break;
406 case EL_LI: {
407 int i;
408
409 /* indent for all nesting depths but present one */
410 for(i=0; i<cur_state->list_depth-1; ++i)
411 add_text("\xa0\xa0", color_map[TM_SYS]^(cur_state->high<<3));
412
413 add_text("*\xa0", color_map[TM_SYS]^(cur_state->high<<3)); /* put list item indicator */
414 break;
415 }
416 /* Patrice, antrik --> */
417 case EL_DL:
418 ++cur_state->list_depth;
419 break;
420 case EL_DT: {
421 int i;
422
423 /* indent for all nesting depths but present one */
424 for(i=0; i<cur_state->list_depth-1; ++i)
425 add_text("\xa0\xa0", color_map[TM_SYS]^(cur_state->high<<3));
426
427 add_text("*\xa0", color_map[TM_SYS]^(cur_state->high<<3)); /* put definition term indicator */
428 break;
429 }
430 /* <-- Patrice, antrik */
431 case EL_HR:
432 add_text("---", color_map[TM_SYS]^(cur_state->high<<3));
433 break;
434 case EL_INS:
435 add_text("[+", color_map[TM_SYS]^(cur_state->high<<3));
436 break;
437 case EL_DEL:
438 case EL_S:
439 case EL_STRIKE:
440 add_text("[-", color_map[TM_SYS]^(cur_state->high<<3));
441 break;
442 case EL_U:
443 cur_state->high=1;
444 break;
445 case EL_FORM:
446 add_text("<", color_map[TM_FORM]^(cur_state->high<<3));
447 break;
448 /* Patrice, antrik --> */
449 case EL_INPUT: {
450 const int type_attr=find_attr(cur_el, ATTR_TYPE);
451 const char *type=cur_el->attr[type_attr].value.str;
452
453 const int value_attr=find_attr(cur_el, ATTR_VALUE);
454 cur_state->link_value= value_attr>=0 ? cur_el->attr[value_attr].value.str : "";
455
456 add_text(NULL, 0); /* force new div */
457
458 if(strcasecmp(type, "submit")==0) {
459 cur_state->link_type=FORM_SUBMIT;
460
461 add_text("[", color_map[TM_FORM]^(cur_state->high<<3));
462 if(value_attr>=0)
463 add_text(cur_el->attr[value_attr].value.str, color_map[TM_LINK]^(cur_state->high<<3));
464 else
465 add_text("SUBMIT", color_map[TM_LINK]^(cur_state->high<<3));
466 add_text("]", color_map[TM_FORM]^(cur_state->high<<3));
467 } else if(strcasecmp(type, "reset")==0) {
468 add_text("(", color_map[TM_FORM]^(cur_state->high<<3));
469 if(value_attr>=0)
470 add_text(cur_el->attr[value_attr].value.str, color_map[TM_FORM]^(cur_state->high<<3));
471 else
472 add_text("RESET", color_map[TM_FORM]^(cur_state->high<<3));
473 add_text(")", color_map[TM_FORM]^(cur_state->high<<3));
474 } else if(strcasecmp(type, "button")==0) {
475 add_text("(", color_map[TM_FORM]^(cur_state->high<<3));
476 if(value_attr>=0)
477 add_text(cur_el->attr[value_attr].value.str, color_map[TM_FORM]^(cur_state->high<<3));
478 else
479 add_text("*", color_map[TM_FORM]^(cur_state->high<<3));
480 add_text(")", color_map[TM_FORM]^(cur_state->high<<3));
481 } else if(strcasecmp(type, "image")==0) {
482 cur_state->link_type=FORM_SUBMIT;
483
484 add_text("[", color_map[TM_FORM]^(cur_state->high<<3));
485 { /* store image */
486 const int alt_attr=find_attr(cur_el, ATTR_ALT);
487
488 if(alt_attr>=0) {
489 add_text("[", color_map[TM_IMG]^(cur_state->high<<3));
490 add_text(cur_el->attr[alt_attr].value.str, color_map[TM_LINK]^(cur_state->high<<3));
491 add_text("]", color_map[TM_IMG]^(cur_state->high<<3));
492 } else {
493 add_text("(", color_map[TM_IMG]^(cur_state->high<<3));
494 add_text("*", color_map[TM_LINK]^(cur_state->high<<3));
495 add_text(")", color_map[TM_IMG]^(cur_state->high<<3));
496 }
497 }
498 add_text("]", color_map[TM_FORM]^(cur_state->high<<3));
499 } else if(strcasecmp(type, "checkbox")==0) {
500 const int checked_attr=find_attr(cur_el, ATTR_CHECKED);
501
502 cur_state->link_type=FORM_CHECKBOX;
503 if(checked_attr>=0)
504 cur_state->form_enabled=1;
505
506 add_text("[", color_map[TM_FORM]^(cur_state->high<<3));
507 add_text("?", color_map[TM_NORMAL]^(cur_state->high<<3)); /* (will be overwritten...) */
508 add_text("]", color_map[TM_FORM]^(cur_state->high<<3));
509 } else if(strcasecmp(type, "radio")==0) {
510 const int checked_attr=find_attr(cur_el, ATTR_CHECKED);
511
512 cur_state->link_type=FORM_RADIO;
513 if(checked_attr>=0)
514 cur_state->form_enabled=1;
515
516 add_text("(", color_map[TM_FORM]^(cur_state->high<<3));
517 add_text("?", color_map[TM_NORMAL]^(cur_state->high<<3)); /* (will be overwritten...) */
518 add_text(")", color_map[TM_FORM]^(cur_state->high<<3));
519 } else if(strcasecmp(type, "password")==0 || strcasecmp(type, "text")==0 || strcasecmp(type, "file")==0 || strcasecmp(type, "hidden")==0) {
520 const int size_attr=find_attr(cur_el, ATTR_SIZE);
521 const int size= (size_attr>=0 && cur_el->attr[size_attr].value.num>0 && type[0]!='h') ? cur_el->attr[size_attr].value.num : 10;
522
523 switch(tolower(type[0])) {
524 case 'p': cur_state->link_type=FORM_PASS; break;
525 case 't': cur_state->link_type=FORM_TEXT; break;
526 case 'f': cur_state->link_type=FORM_FILE; break;
527 case 'h': cur_state->link_type=FORM_HIDDEN; break;
528 }
529 cur_state->form_enabled=1;
530
531 if(cur_state->link_type==FORM_FILE)
532 cur_state->link_value=NULL; /* don't allow default values for file upload! (prevent uploading files not explicitely requested by user) */
533
534 add_text(cur_state->link_type==FORM_FILE ? "[/" : "[", color_map[TM_FORM]^(cur_state->high<<3));
535 {
536 char line_buf[size+1];
537 memset(line_buf, '\xa0', size);
538 line_buf[size]=0;
539 add_text(line_buf, color_map[cur_state->link_type!=FORM_HIDDEN?TM_NORMAL:TM_DIM]^(cur_state->high<<3));
540 }
541 add_text("]", color_map[TM_FORM]^(cur_state->high<<3));
542 }
543
544 break;
545 }
546 /* <-- Patrice, antrik */
547 case EL_SELECT: {
548 const int multi_attr=find_attr(cur_el, ATTR_MULTIPLE);
549 cur_state->select_type= multi_attr>=0 ? FORM_MULTIOPTION : FORM_OPTION;
550
551 /* save name for all <option> subelements */
552 if(cur_state->id_attr >= 0)
553 cur_state->select_name=cur_el->attr[cur_state->id_attr].value.str;
554
555 if(cur_state->select_type==FORM_MULTIOPTION)
556 add_text("{", color_map[TM_FORM]^(cur_state->high<<3));
557 else
558 add_text("(", color_map[TM_FORM]^(cur_state->high<<3));
559
560 break;
561 }
562 case EL_OPTION: {
563 const int selected_attr=find_attr(cur_el, ATTR_SELECTED);
564 const int value_attr=find_attr(cur_el, ATTR_VALUE);
565
566 cur_state->link_type=cur_state->select_type; /* inherit option type (FORM_OPTION or FORM_MULTIOPTION) from <select> element */
567
568 /* get "value" */
569 if(value_attr>=0) /* has "value" attribute */
570 cur_state->link_value=cur_el->attr[value_attr].value.str;
571 else if(cur_el->list_next!=NULL && cur_el->list_next->content!=NULL) { /* no "value" attribute, but content */
572 char *content=cur_el->list_next->content;
573 if(*content==' ') /* skip space at beginning (workaround incorrect space handling) */
574 ++content;
575 cur_state->link_value=content;
576 } else /* no "value" nor content */
577 cur_state->link_value="";
578
579 if(selected_attr>=0) /* initially on */
580 cur_state->form_enabled=1;
581
582 add_text(NULL, 0); /* force new div */
583 add_text("?", color_map[TM_FORM]^(cur_state->high<<3)); /* (will be overwritten...) */
584 break;
585 }
586 case EL_BUTTON: {
587 const int type_attr=find_attr(cur_el, ATTR_TYPE);
588 const char *type= type_attr>=0 ? cur_el->attr[type_attr].value.str : NULL;
589
590 if(type==NULL || !strcasecmp(type, "submit")) { /* submit button (default) */
591 const int value_attr=find_attr(cur_el, ATTR_VALUE);
592
593 cur_state->link_type=FORM_SUBMIT;
594 cur_state->link_value= value_attr>=0 ? cur_el->attr[value_attr].value.str :"";
595
596 add_text(NULL, 0); /* force new div */
597 add_text("[", color_map[TM_FORM]^(cur_state->high<<3));
598 cur_state->text_mode=TM_LINK;
599 } else { /* reset or push button -> no action */
600 add_text("(", color_map[TM_FORM]^(cur_state->high<<3));
601 cur_state->text_mode=TM_FORM;
602 }
603 break;
604 }
605 case EL_TEXTAREA: {
606 const int cols_attr=find_attr(cur_el, ATTR_COLS);
607 const int size= (cols_attr >= 0 && cur_el->attr[cols_attr].value.num > 0) ? cur_el->attr[cols_attr].value.num : 50;
608
609 /* gather initial value form content (stored in subelements) */
610 {
611 char *value=NULL;
612 int value_len=0;
613
614 struct Element *sub_el;
615
616 for(sub_el=cur_el->list_next; sub_el->parent==cur_el; sub_el=sub_el->list_next) { /* all direct children */
617 if(sub_el->content) {
618 const int new_len=value_len+strlen(sub_el->content);
619
620 value=realloc(value, new_len+1);
621 if(value==NULL) {
622 fprintf(stderr, "Memory allocation error while parsing structure (in function parse_struct)\n");
623 exit(1);
624 }
625 strcpy(&value[value_len], sub_el->content);
626
627 value_len=new_len;
628 } /* has content */
629 } /* for all direct children */
630 cur_state->link_value=value;
631 }
632
633 cur_state->link_type=FORM_TEXTAREA;
634 cur_state->form_enabled=1;
635
636 add_text("[[", color_map[TM_FORM]^(cur_state->high<<3));
637 {
638 char line_buf[size+1];
639 memset(line_buf, '\xa0', size);
640 line_buf[size]=0;
641 add_text(line_buf, color_map[TM_NORMAL]^(cur_state->high<<3));
642 }
643 add_text("]]", color_map[TM_FORM]^(cur_state->high<<3));
644
645 break;
646 }
647 case EL_IMG: {
648 const int alt_attr=find_attr(cur_el, ATTR_ALT);
649
650 if(alt_attr>=0) {
651 add_text("[", color_map[TM_IMG]^(cur_state->high<<3));
652 add_text(cur_el->attr[alt_attr].value.str, color_map[cur_state->text_mode==TM_LINK?TM_LINK:TM_IMG]^(cur_state->high<<3));
653 add_text("]", color_map[TM_IMG]^(cur_state->high<<3));
654 } else {
655 add_text("(", color_map[TM_IMG]^(cur_state->high<<3));
656 if(cur_state->text_mode==TM_LINK)
657 add_text("*", color_map[TM_LINK]^(cur_state->high<<3));
658 add_text(")", color_map[TM_IMG]^(cur_state->high<<3));
659 }
660 break;
661 }
662 default: /* html, body, p, center, pre, table, dd, div, span, no, global */
663 break;
664 } /* switch element type */
665
666 /* ascend to depth of next element (no ascend if next element is child of current (keep the descend), ascend once if at same level (undo the descend), ascend more than once if above current) */
667 for(new_el=cur_el; new_el!=cur_el->list_next->parent; new_el=new_el->parent) { /* ascend until parent of next element found */
668 struct State *old_state; /* state before ascending */
669
670 switch(new_el->name.type) {
671 case EL_TR:
672 add_text("-", color_map[TM_SYS]); /* put indicator after every table row */
673 break;
674 case EL_TD:
675 case EL_TH: {
676 const int span_attr=find_attr(new_el, ATTR_COLSPAN);
677 int i;
678
679 if(span_attr>=0) /* has colspan */
680 for(i=1; i<atoi(new_el->attr[span_attr].value.str); ++i) /* all but first spanned cell */
681 add_text("(|)", color_map[TM_SYS]); /* put indicator for cells left out due to the span */
682 break;
683 }
684 case EL_DL:
685 --cur_state->list_depth;
686 break;
687 case EL_DT:
688 add_text(":", color_map[TM_SYS]^(cur_state->high<<3)); /* put definition indicator after definiton term */
689 break;
690 case EL_INS:
691 add_text("+]", color_map[TM_SYS]^(cur_state->high<<3));
692 break;
693 case EL_DEL:
694 case EL_S:
695 case EL_STRIKE:
696 add_text("-]", color_map[TM_SYS]^(cur_state->high<<3));
697 break;
698 case EL_FORM:
699 add_text(">", color_map[TM_FORM]^(cur_state->high<<3));
700 break;
701 case EL_SELECT:
702 if(cur_state->select_type==FORM_MULTIOPTION)
703 add_text("}", color_map[TM_FORM]^(cur_state->high<<3));
704 else
705 add_text(")", color_map[TM_FORM]^(cur_state->high<<3));
706 break;
707 case EL_BUTTON:
708 if((signed)cur_state->link_type >= 0) /* active button (submit) */
709 add_text("]", color_map[TM_FORM]^(cur_state->high<<3));
710 else /* reset or push button */
711 add_text(")", color_map[TM_FORM]^(cur_state->high<<3));
712 break;
713 default: /* html, body, h1-h6, p, em, i, strong, b, center, a, br, pre, table, ul, li, ol, dl, dd, hr, form, input, option, div, span, no, global */
714 break;
715 } /* switch element type */
716
717 /* links */
718 if((signed)cur_state->link_type>=0 && (string_item!=NULL || cur_state->link_item!=NULL)) { /* has link, and valid (inside text block) -> add link */
719 struct String *string; /* string where link will be stored */
720 int link_end; /* position inside string where link ends */
721 int new_link; /* link number in string's link table */
722
723 if(cur_state->link_item!=NULL) /* link began inside some string -> store in this one */
724 string=cur_state->link_item->data.string;
725 else { /* link began outside string -> store in current string (that's the best we can do -- normally, this is the one where link begins...) */
726 string=string_item->data.string;
727 cur_state->link_start=0; /* start at string start */
728 }
729 link_end=string->div[string->div_count-1].end; /* string end (normally that's the current string position; if link started in other string, truncates to end of that string) */
730
731 new_link=string->link_count;
732 /* nested links workaround */
733 if(string->link_count && cur_state->link_start <= string->link[string->link_count-1].start) { /* nested links (link not first, and starts before last link) */
734 /* find position where link belongs (after last link starting before current) */
735 for(new_link=string->link_count; new_link>0; --new_link)
736 if(cur_state->link_start > string->link[new_link-1].start) /* found */
737 break;
738
739 link_end=string->link[new_link].start; /* truncate to beginning of next link */
740 }
741
742 /* workaround incorrect space handling so activated links will be highlighted (more or less) correctly */
743 if(string->text[cur_state->link_start]==' ') { /* ' ' at link start -> move start behind the ' ' */
744 if(cur_state->link_start>0 && !(string->link_count && string->link[string->link_count-1].end==cur_state->link_start)) { /* can't handle links starting at string start or immediately following other links */
745 int start_div; /* first div of link */
746
747 /* also move div start (if not already moved by some other (nested) link...) */
748 for(start_div=1; start_div<string->div_count; ++start_div) { /* find first div of link */
749 if(string->div[start_div-1].end==cur_state->link_start) { /* found */
750 ++string->div[start_div-1].end;
751 break;
752 }
753 }
754
755 ++cur_state->link_start;
756 } /* possible */
757 } /* move link start past ' ' */
758
759 if(link_end > cur_state->link_start) { /* link not empty -> store */
760 add_text(NULL, 0); /* force new div */
761
762 /* resize link table of string */
763 string->link=realloc(string->link, ++string->link_count*sizeof(struct Link));
764 if(string->link==NULL) {
765 fprintf(stderr, "memory allocation error while parsing structure (in function parse_struct)\n");
766 exit(1);
767 }
768
769 /* nested links workaround */
770 if(new_link < string->link_count-1) { /* not inserted as last -> move all links after new_link one position behind */
771 int link;
772 for(link=string->link_count-1; link>new_link; --link)
773 string->link[link]=string->link[link-1];
774 }
775
776 /* store link */
777 string->link[new_link].start=cur_state->link_start; /* starting position saved before */
778 string->link[new_link].end=link_end;
779 string->link[new_link].form=cur_state->link_type;
780 string->link[new_link].enabled=cur_state->form_enabled;
781 string->link[new_link].name=NULL;
782
783 if(cur_state->link_value==NULL)
784 cur_state->link_value="";
785 string->link[new_link].value.size=strlen(cur_state->link_value);
786 string->link[new_link].value.data=strdup(cur_state->link_value);
787 if(string->link[new_link].value.data==NULL) {
788 fprintf(stderr, "memory allocation error while parsing structure (in function parse_struct)\n");
789 exit(1);
790 }
791
792 if(cur_state->link_type!=FORM_NO) { /* form element */
793 set_form(string, &string->link[new_link]); /* display initial value */
794
795 if(cur_state->id_attr >= 0) { /* has "name"/"id" -> store that (currently used by form handling for simplicity) */
796 string->link[new_link].name=strdup(new_el->attr[cur_state->id_attr].value.str);
797 if(string->link[new_link].name==NULL) {
798 fprintf(stderr, "memory allocation error while parsing structure (in function parse_struct)\n");
799 exit(1);
800 }
801 } else if(cur_state->select_name!=NULL) { /* no "name"/"id", but name inherited from <select> */
802 string->link[new_link].name=strdup(cur_state->select_name);
803 if(string->link[new_link].name==NULL) {
804 fprintf(stderr, "memory allocation error while parsing structure (in function parse_struct)\n");
805 exit(1);
806 }
807 } else /* no name */
808 string->link[new_link].name=NULL;
809 } /* form element */
810
811 /* fingerprint link (for retrival of correct link when revisiting a page that changed) */
812 string->link[new_link].text_fingerprint=fingerprint(&string->text[cur_state->link_start], &string->text[link_end]);
813 string->link[new_link].url_fingerprint=fingerprint(cur_state->link_value, strchr(cur_state->link_value, 0));
814 } /* link not empty */
815 } /* has link */
816
817 /* ascend ("depth" is equal to depth of "new_el" afterwards) */
818 old_state=cur_state;
819 cur_state=pop_state();
820
821 if(element_table[new_el->name.type].force_box) { /* element creates own box */
822 struct Item *item; /* currently handled child */
823
824 if(depth<blank_depth) /* last blank line request was generated inside this box -> discard it */
825 para_blank=0;
826
827 /* create new box item */
828 cur_item=add_item(0);
829 cur_item->type=ITEM_BOX;
830
831 /* form box */
832 if(new_el->name.type==EL_FORM) { /* box created by "form" element */
833 const int action_attr=find_attr(new_el, ATTR_ACTION);
834 const int method_attr=find_attr(new_el, ATTR_METHOD);
835
836 cur_item->type=ITEM_FORM;
837
838 cur_item->data.form=malloc(sizeof(struct Form));
839 if(cur_item->data.form==NULL) {
840 fprintf(stderr, "memory allocation error while parsing structure\n");
841 exit(1);
842 }
843
844 if(action_attr>=0) { /* has "action" -> store URL */
845 cur_item->data.form->url=strdup(new_el->attr[action_attr].value.str);
846 if(cur_item->data.form->url==NULL) {
847 fprintf(stderr, "memory allocation error while parsing structure\n");
848 exit(1);
849 }
850 } else /* no "action" */
851 cur_item->data.form->url=NULL;
852
853 if(method_attr>=0 && strcasecmp(new_el->attr[method_attr].value.str, "POST")==0) {
854 const int enctype_attr=find_attr(new_el, ATTR_ENCTYPE);
855
856 if(enctype_attr>=0 && strcasecmp(new_el->attr[enctype_attr].value.str, "multipart/form-data")==0)
857 cur_item->data.form->method=METHOD_POST_MIMEENC;
858 else
859 cur_item->data.form->method=METHOD_POST_URLENC;
860 } else
861 cur_item->data.form->method=METHOD_GET;
862 } /* form */
863
864 /* set parent of all items inside box */
865 for(item=old_state->first_item; item!=NULL; item=item->next)
866 item->parent=cur_item;
867
868 cur_item->first_child=old_state->first_item;
869
870 } else { /* no box necessary */
871
872 /* handle <center> element or attribute */
873 {
874 const int center_attr=find_attr(new_el, ATTR_ALIGN);
875
876 if(new_el->name.type==EL_CENTER || (center_attr>=0 && strcasecmp(new_el->attr[center_attr].value.str, "center")==0)) { /* leaving <center> element or any elment containing align="center" -> center all items created inside it */
877 struct Item *item; /* currently handled child */
878
879 for(item=old_state->first_item; item!=NULL; item=item->next)
880 item->center=1;
881 }
882 }
883
884 /* lift all waiting items to new depth */
885 if(old_state->first_item!=NULL) { /* any items to lift */
886 if(cur_state->first_item!=NULL) /* already items at new depth */
887 cur_state->last_item->next=old_state->first_item; /* -> add to end of list */
888 else /* no items in new depth yet */
889 cur_state->first_item=old_state->first_item; /* -> copy list */
890 cur_state->last_item=old_state->last_item; /* new depth's item list now ends with old depth's last item */
891 }
892
893 /* also lift pending blank line */
894 if(depth<blank_depth) /* last blank line request was generated inside this element */
895 blank_depth=depth;
896
897 } /* no box */
898
899 if(element_table[new_el->name.type].breaks) { /* element breaks lines */
900 string_item=NULL; /* start new text block */
901
902 if(element_table[new_el->name.type].breaks==2 && para_blank==-1) /* element creates paragraph and there are already items in this box */
903 para_blank=1; /* -> may need blank line after it to seperate from next paragraph (inserted later, before next item) */
904 }
905
906 /* anchors */
907 if(old_state->id_attr>=0) { /* has anchor -> create anchor item */
908 cur_item=add_item(1); /* add a virtual item */
909
910 if(string_item!=NULL) { /* inside string => inline anchor */
911 struct Inline_anchor *anchor;
912
913 cur_item->type=ITEM_INLINE_ANCHOR;
914
915 if(old_state->link_item!=string_item) /* link began in other string */
916 old_state->link_start=0; /* start at string start (this way at least the last part of the anchor will be stored correctly) */
917
918 /* create data block */
919 anchor=cur_item->data.inline_anchor=malloc(sizeof(struct Inline_anchor));
920 if(anchor==NULL) {
921 fprintf(stderr, "memory allocation error while parsing structure (in function parse_struct)\n");
922 exit(1);
923 }
924
925 /* store anchor data */
926 anchor->id=strdup(new_el->attr[old_state->id_attr].value.str);
927 if(anchor->id==NULL) {
928 fprintf(stderr, "memory allocation error while parsing structure (in function parse_struct)\n");
929 exit(1);
930 }
931 anchor->virtual_parent=string_item;
932 anchor->start=old_state->link_start; /* starting position saved before */
933 anchor->end=string_item->data.string->div[string_item->data.string->div_count-1].end; /* current string end */
934
935 /* workaround incorrect space handling so anchors will be set (more or less) correctly */
936 if(string_item->data.string->text[anchor->start]==' ') /* blank at anchor start -> skip */
937 ++anchor->start;
938 } else { /* not inside string => block anchor */
939 struct Block_anchor *anchor;
940
941 cur_item->type=ITEM_BLOCK_ANCHOR;
942
943 /* create data block */
944 anchor=cur_item->data.block_anchor=malloc(sizeof(struct Block_anchor));
945 if(anchor==NULL) {
946 fprintf(stderr, "memory allocation error while parsing structure (in function parse_struct)\n");
947 exit(1);
948 }
949
950 /* store anchor data */
951 anchor->id=strdup(new_el->attr[old_state->id_attr].value.str);
952 if(anchor->id==NULL) {
953 fprintf(stderr, "memory allocation error while parsing structure (in function parse_struct)\n");
954 exit(1);
955 }
956 if(old_state->first_item!=NULL) { /* any items inside the anchor element */
957 if(old_state->first_item->parent!=NULL) /* items inside the anchor element have gotten some parent -> store only the parent in virtual box */
958 anchor->virtual_child=old_state->first_item->parent;
959 else /* items have no parent -> store them directly */
960 anchor->virtual_child=old_state->first_item; /* first item in virtual box */
961 } else /* no items inside anchor element -> point back to itself */
962 anchor->virtual_child=cur_item;
963 } /* block anchor */
964 } /* has anchor */
965
966 } /* for new_el (ascend to depth of next element) */
967
968 cur_el=cur_el->list_next;
969 } while(cur_el->parent!=NULL); /* until wrapped back to global element (all elements processed) */
970
971 cur_item->parent=first_item; /* save reference to first item */
972 cur_item->next=cur_item; /* top points back to itself */
973
974 free(state);
975
976 return cur_item;
977 }
978
979 /* unallocate whole item tree;
980 * traverses whole tree (list), and frees every node after freeing its data */
free_items(item_tree)981 void free_items(item_tree)
982 struct Item *item_tree;
983 {
984 struct Item *cur_item; /* item to be deleted */
985 struct Item *next_item; /* need to save "list_next", as won't be available after deleting "cur_item" */
986
987 /* all items */
988 next_item=item_tree->parent; /* start with first item */
989 do {
990 cur_item=next_item;
991 next_item=cur_item->list_next; /* save "list_next" for later */
992
993 switch(cur_item->type) {
994
995 case ITEM_TEXT:
996 if(cur_item->data.string!=NULL) { /* has "String" -> free it */
997 free(cur_item->data.string->text);
998 free(cur_item->data.string->div);
999 free(cur_item->data.string->line_table);
1000 {
1001 int link;
1002 for(link=0; link<cur_item->data.string->link_count; ++link) {
1003 free(cur_item->data.string->link[link].value.data);
1004 if(cur_item->data.string->link[link].name!=NULL)
1005 free(cur_item->data.string->link[link].name);
1006 }
1007 }
1008 free(cur_item->data.string->link);
1009 free(cur_item->data.string);
1010 }
1011 break;
1012
1013 case ITEM_BLOCK_ANCHOR:
1014 free(cur_item->data.block_anchor->id);
1015 free(cur_item->data.block_anchor);
1016 break;
1017
1018 case ITEM_INLINE_ANCHOR:
1019 free(cur_item->data.inline_anchor->id);
1020 free(cur_item->data.inline_anchor);
1021 break;
1022
1023 case ITEM_FORM:
1024 free(cur_item->data.form->url);
1025 free(cur_item->data.form);
1026 break;
1027
1028 case ITEM_BOX:
1029 case ITEM_BLANK:
1030 break;
1031 } /* switch item type */
1032
1033 free(cur_item);
1034 } while(next_item!=NULL); /* for all items (until list end) */
1035 }
1036
1037 /* create list of all links in the page;
1038 * traverses the whole item tree, and for every text item stores all its links
1039 * to the list */
make_link_list(item_tree)1040 struct Link_list *make_link_list(item_tree)
1041 struct Item *item_tree;
1042 {
1043 struct Item *cur_item;
1044 struct Link_list *list;
1045
1046 /* create list */
1047 list=malloc(sizeof(struct Link_list));
1048 if(list==NULL) {
1049 fprintf(stderr, "memory allocation error while creating link list\n");
1050 exit(1);
1051 }
1052 list->count=0;
1053 list->link=NULL;
1054
1055 /* find and insert links */
1056 for(cur_item=item_tree->parent; cur_item!=NULL; cur_item=cur_item->list_next) { /* all items (starting from bottom) */
1057 if(cur_item->type==ITEM_TEXT) {
1058 int link;
1059
1060 for(link=0; link<cur_item->data.string->link_count; ++link) { /* all links in this text block */
1061 if(cur_item->data.string->link[link].form!=FORM_HIDDEN) { /* active link -> add to link list */
1062 list->link=realloc(list->link, ++list->count*sizeof(struct Link_ptr)); /* new entry in link list */
1063 if(list->link==NULL) {
1064 fprintf(stderr, "memory allocation error while creating link list\n");
1065 exit(1);
1066 }
1067
1068 list->link[list->count-1].item=cur_item;
1069 list->link[list->count-1].num=link;
1070 } /* active link */
1071 } /* for all links in text block */
1072 } /* ITEM_TEXT */
1073 } /* for all items */
1074
1075 return list;
1076 }
1077
free_links(list)1078 void free_links(list)
1079 struct Link_list *list;
1080 {
1081 free(list->link);
1082 free(list);
1083 }
1084
1085 /* create list of all anchors in the page;
1086 * traverses the whole item tree, and store every encountered anchor
1087 * to the list */
make_anchor_list(item_tree)1088 struct Anchor_list *make_anchor_list(item_tree)
1089 struct Item *item_tree;
1090 {
1091 struct Item *cur_item;
1092 struct Anchor_list *list;
1093
1094 /* create list */
1095 list=malloc(sizeof(struct Anchor_list));
1096 if(list==NULL) {
1097 fprintf(stderr, "memory allocation error while creating link list\n");
1098 exit(1);
1099 }
1100 list->count=0;
1101 list->anchor_item=NULL;
1102
1103 /* find all anchors and add to list */
1104 for(cur_item=item_tree->parent; cur_item!=NULL; cur_item=cur_item->list_next) { /* all items (starting from bottom) */
1105 if(cur_item->type==ITEM_BLOCK_ANCHOR || cur_item->type==ITEM_INLINE_ANCHOR) {
1106 list->anchor_item=realloc(list->anchor_item, ++list->count*sizeof(struct Item *)); /* new entry in anchor list */
1107 if(list->anchor_item==NULL) {
1108 fprintf(stderr, "memory allocation error while creating link list\n");
1109 exit(1);
1110 }
1111
1112 list->anchor_item[list->count-1]=cur_item;
1113 } /* anchor item */
1114 } /* for all items */
1115
1116 return list;
1117 }
1118
free_anchors(list)1119 void free_anchors(list)
1120 struct Anchor_list *list;
1121 {
1122 free(list->anchor_item);
1123 free(list);
1124 }
1125