"Fossies" - the Fresh Open Source Software archive

Member "netrik-1.16.1/sgml.c" of archive netrik-1.16.1.tar.gz:


/*
   netrik -- The ANTRIK Internet Viewer
   Copyright (C) Olaf D. Buddenhagen AKA antrik, et al (see AUTHORS)
   Published under the GNU GPL; see LICENSE for details.
*/
/*
 * sgml.c -- modify syntax tree to cope with SGML
 *
 * (C) 2002 antrik
 *
 * The broken syntax trees created by missing end tags in SGML documents are
 * fixed retrospectively here.
 */
#ifndef XHTML_ONLY

#include "stdio.h"
#include "stdlib.h"

#include <syntax.h>

/* 
 * Fixes a broken syntax tree created by unclosed elements in SGML documents.
 *
 * Every element in the tree is checked against its ancestors to test whether
 * it should actually terminate the ancestor and be at same depth; if so, it's
 * lifted to that depth.
 */
void sgml_rework(tree_top)
struct Element	*tree_top;
{
   struct Element	*cur;    /* currently handled element in tree */
   struct Element	*prev;
   struct Element	*ancestor;    /* currently checked ancestor of "cur" */
   
   for(prev=tree_top, cur=tree_top->list_next; cur!=tree_top; prev=cur, cur=cur->list_next) {    /* all elements in tree (except top) */
      const enum Element_group	cur_group=element_table[cur->name.type].group;    /* element type group of "cur" */

      if(cur_group==GROUP_SINGLE)    /* single tag element -> just ensure it won't keep any children */
	 cur->closed=1;
      else if(cur_group!=GROUP_OBLIGATE) {    /* element with optional end tag -> test if needs to be lifted */
	 for(ancestor=cur->parent; ancestor!=tree_top; ancestor=ancestor->parent) {    /* all ancestors */
	    const enum Element_group	ancestor_group=element_table[ancestor->name.type].group;

	    if(ancestor_group==GROUP_OBLIGATE)    /* can't lift past elements with obligate end tag; stop scanning */
	       break;
	    if(ancestor_group==cur_group) {    /* ancestor should be terminated by current element -> lift current and following elements to it's level */
	       struct Element	*close;

	       /* close all elements in line between current element and the ancestor inclusive, so following elements won't stay inside the ancestor */
	       for(close=cur->parent; close!=ancestor->parent; close=close->parent) {
		  if(!close->closed) {    /* not already closed before */
		     if(cur->content!=NULL) {    /* lifted element has content -> need to create dummy element to save it */
			struct Element	*new;

			new=malloc(sizeof(struct Element));
			if(new==NULL) {
			   fprintf(stderr, "memory allocation error while fixing SGML\n");
			   exit(1);
			}

			new->name.type=EL_NO;
			new->attr_count=0;
			new->attr=NULL;

			/* take content of lifted element */
			new->content=cur->content;
			cur->content=NULL;

			/* insert into list (before "cur", inside closed element) */
			prev->list_next=new;
			new->list_next=cur;
			new->parent=close;
		     }    /* has content */

		     close->closed=1;
		  }    /* not already closed */
	       }    /* for all ancestors up to newly terminated one */

	       break;
	    }    /* lift */
	 }    /* for all ancestors */
      }    /* optional end tag */

      /* lift */
      while(cur->parent->closed)    /* parent is already closed -> lift element */
	 cur->parent=cur->parent->parent;
   }    /* all elements */
}

#endif    /* not XHTML_ONLY */