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