/****************************************************************************
 * NCSA Mosaic for the X Window System                                      *
 * Software Development Group                                               *
 * National Center for Supercomputing Applications                          *
 * University of Illinois at Urbana-Champaign                               *
 * 605 E. Springfield, Champaign IL 61820                                   *
 * mosaic@ncsa.uiuc.edu                                                     *
 *                                                                          *
 * Copyright (C) 1993, Board of Trustees of the University of Illinois      *
 *                                                                          *
 * NCSA Mosaic software, both binary and source (hereafter, Software) is    *
 * copyrighted by The Board of Trustees of the University of Illinois       *
 * (UI), and ownership remains with the UI.                                 *
 *                                                                          *
 * The UI grants you (hereafter, Licensee) a license to use the Software    *
 * for academic, research and internal business purposes only, without a    *
 * fee.  Licensee may distribute the binary and source code (if released)   *
 * to third parties provided that the copyright notice and this statement   *
 * appears on all copies and that no charge is associated with such         *
 * copies.                                                                  *
 *                                                                          *
 * Licensee may make derivative works.  However, if Licensee distributes    *
 * any derivative work based on or derived from the Software, then          *
 * Licensee will (1) notify NCSA regarding its distribution of the          *
 * derivative work, and (2) clearly notify users that such derivative       *
 * work is a modified version and not the original NCSA Mosaic              *
 * distributed by the UI.                                                   *
 *                                                                          *
 * Any Licensee wishing to make commercial use of the Software should       *
 * contact the UI, c/o NCSA, to negotiate an appropriate license for such   *
 * commercial use.  Commercial use includes (1) integration of all or       *
 * part of the source code into a product for sale or license by or on      *
 * behalf of Licensee to third parties, or (2) distribution of the binary   *
 * code or source code to third parties that need it to utilize a           *
 * commercial product sold or licensed by or on behalf of Licensee.         *
 *                                                                          *
 * UI MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR   *
 * ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED          *
 * WARRANTY.  THE UI SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE    *
 * USERS OF THIS SOFTWARE.                                                  *
 *                                                                          *
 * By using or copying this Software, Licensee agrees to abide by the       *
 * copyright law and all other applicable laws of the U.S. including, but   *
 * not limited to, export control laws, and the terms of this license.      *
 * UI shall have the right to terminate this license immediately by         *
 * written notice upon Licensee's breach of, or non-compliance with, any    *
 * of its terms.  Licensee may be held legally responsible for any          *
 * copyright infringement that is caused or encouraged by Licensee's        *
 * failure to abide by the terms of this license.                           *
 *                                                                          *
 * Comments and questions are welcome and can be sent to                    *
 * mosaic-x@ncsa.uiuc.edu.                                                  *
 ****************************************************************************/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include "HTML.h"

#ifndef DISABLE_TRACE
extern int htmlwTrace;
#endif

/*
 * Code to manage a linked list of parsed HTML objects generated
 * from a raw text file.
 * Also code to manage a linked list of formatted elements that
 * make up a page of a formatted document.
 */


/*
 * Free up the passed linked list of parsed elements, freeing
 * all memory associates with each element.
 */
void
FreeObjList(List)
	struct mark_up *List;
{
	struct mark_up *current;
	struct mark_up *mptr;

	current = List;
	while (current != NULL)
	{
		mptr = current;
		current = current->next;
		mptr->next = NULL;
		if (mptr->start != NULL)
		{
			free((char *)mptr->start);
		}
		if (mptr->text != NULL)
		{
			free((char *)mptr->text);
		}
		if (mptr->end != NULL)
		{
			free((char *)mptr->end);
		}
		free((char *)mptr);
	}
}


/*
 * Add an object to the parsed object list.
 * return a pointer to the current (end) position in the list.
 * If the object is a normal text object containing nothing but
 * white space, throw it out, unless we have been told to keep
 * white space.
 */
struct mark_up *
AddObj(listp, current, mark, keep_wsp)
	struct mark_up **listp;
	struct mark_up *current;
	struct mark_up *mark;
	int keep_wsp;
{
	if (mark == NULL)
	{
		return(current);
	}

	/*
	 * Throw out normal text blocks that are only white space,
	 * unless keep_wsp is set.
	 */
	if ((mark->type == M_NONE)&&(!keep_wsp))
	{
		char *ptr;

		ptr = mark->text;
		if (ptr == NULL)
		{
			free((char *)mark);
			return(current);
		}

/*
 * No longer throw out whitespace, it is important to keep
 * white space between tags.
		while ((*ptr == ' ')||(*ptr == '\t')||(*ptr == '\n'))
		{
			ptr++;
		}
 *
 */

		if (*ptr == '\0')
		{
			free(mark->text);
			free((char *)mark);
			return(current);
		}
	}

	/*
	 * Add object to either the head of the list for a new list,
	 * or at the end after the current pointer.
	 */
	if (*listp == NULL)
	{
		*listp = mark;
		current = *listp;
	}
	else
	{
		current->next = mark;
		current = current->next;
	}

	current->next = NULL;

	return(current);
}


/*
 * Convert type number to a printed string for debug
 */
void
PrintType(type)
	int type;
{

#ifndef DISABLE_TRACE
	if (htmlwTrace) {

	switch(type)
	{
		case M_NONE:
			fprintf(stderr,"M_NONE");
			break;
		case M_TITLE:
			fprintf(stderr,"M_TITLE");
			break;
		case M_FIXED:
			fprintf(stderr,"M_FIXED");
			break;
		case M_BOLD:
			fprintf(stderr,"M_BOLD");
			break;
		case M_ITALIC:
			fprintf(stderr,"M_ITALIC");
			break;
		case M_EMPHASIZED:
			fprintf(stderr,"M_EMPHASIZED");
			break;
		case M_STRONG:
			fprintf(stderr,"M_STRONG");
			break;
		case M_CODE:
			fprintf(stderr,"M_CODE");
			break;
		case M_SAMPLE:
			fprintf(stderr,"M_SAMPLE");
			break;
		case M_KEYBOARD:
			fprintf(stderr,"M_KEYBOARD");
			break;
		case M_VARIABLE:
			fprintf(stderr,"M_VARIABLE");
			break;
		case M_CITATION:
			fprintf(stderr,"M_CITATION");
			break;
		case M_STRIKEOUT:
			fprintf(stderr,"M_STRIKEOUT");
			break;
		case M_HEADER_1:
			fprintf(stderr,"M_HEADER_1");
			break;
		case M_HEADER_2:
			fprintf(stderr,"M_HEADER_2");
			break;
		case M_HEADER_3:
			fprintf(stderr,"M_HEADER_3");
			break;
		case M_HEADER_4:
			fprintf(stderr,"M_HEADER_4");
			break;
		case M_HEADER_5:
			fprintf(stderr,"M_HEADER_5");
			break;
		case M_HEADER_6:
			fprintf(stderr,"M_HEADER_6");
			break;
		case M_ANCHOR:
			fprintf(stderr,"M_ANCHOR");
			break;
		case M_PARAGRAPH:
			fprintf(stderr,"M_PARAGRAPH");
			break;
		case M_ADDRESS:
			fprintf(stderr,"M_ADDRESS");
			break;
		case M_PLAIN_TEXT:
			fprintf(stderr,"M_PLAIN_TEXT");
			break;
		case M_LISTING_TEXT:
			fprintf(stderr,"M_LISTING_TEXT");
			break;
		case M_UNUM_LIST:
			fprintf(stderr,"M_UNUM_LIST");
			break;
		case M_NUM_LIST:
			fprintf(stderr,"M_NUM_LIST");
			break;
		case M_MENU:
			fprintf(stderr,"M_MENU");
			break;
		case M_DIRECTORY:
			fprintf(stderr,"M_DIRECTORY");
			break;
		case M_LIST_ITEM:
			fprintf(stderr,"M_LIST_ITEM");
			break;
		case M_DESC_LIST:
			fprintf(stderr,"M_DESC_LIST");
			break;
		case M_DESC_TITLE:
			fprintf(stderr,"M_DESC_TITLE");
			break;
		case M_DESC_TEXT:
			fprintf(stderr,"M_DESC_TEXT");
			break;
		case M_IMAGE:
			fprintf(stderr,"M_IMAGE");
			break;
		case M_SELECT:
			fprintf(stderr,"M_SELECT");
			break;
		case M_OPTION:
			fprintf(stderr,"M_OPTION");
			break;
		case M_INPUT:
			fprintf(stderr,"M_INPUT");
			break;
		case M_TEXTAREA:
			fprintf(stderr,"M_TEXTAREA");
			break;
		case M_FORM:
			fprintf(stderr,"M_FORM");
			break;
		case M_INDEX:
			fprintf(stderr,"M_INDEX");
			break;
		case M_HRULE:
			fprintf(stderr,"M_HRULE");
			break;
		case M_BASE:
			fprintf(stderr,"M_BASE");
			break;
		case M_LINEBREAK:
			fprintf(stderr,"M_LINEBREAK");
			break;
		case M_BLOCKQUOTE:
			fprintf(stderr,"M_BLOCKQUOTE");
			break;
		default:
			fprintf(stderr,"UNKNOWN %d", type);
			break;

	}
	}
#endif

}


/*
 * Print the contents of a parsed object list, for debug
 */
void
PrintList(list)
	struct mark_up *list;
{
	struct mark_up *mptr;

#ifndef DISABLE_TRACE
	if (htmlwTrace) {

	mptr = list;
	while (mptr != NULL)
	{
		PrintType(mptr->type);
		if (mptr->is_end)
		{
			fprintf(stderr," END");
		}
		else
		{
			fprintf(stderr," START");
		}
		if (mptr->text != NULL)
		{
			fprintf(stderr,"\n{\n\t");
			fprintf(stderr,"%s", mptr->text);
			fprintf(stderr,"}\n");
		}
		else
		{
			fprintf(stderr,"\n");
		}
		mptr = mptr->next;
	}

	}
#endif
}


/*
 * Used to find the longest line (in characters) in a collection
 * of text blocks.  cnt is the running count of characters, and
 * txt is the pointer to the current text block.  Since we are
 * finding line widths, a newline resets the width count.
 */
char *
MaxTextWidth(txt, cnt)
	char *txt;
	int *cnt;
{
	char *start;
	char *end;
	int width;

	if (txt == NULL)
	{
		return(NULL);
	}

	width = *cnt;
	start = txt;

	/*
	 * If this blocks starts with a newline, reset the width
	 * count, and skip the newline.
	 */
	if (*start == '\n')
	{
		width = 0;
		start++;
	}

	end = start;

	/*
	 * count characters, stoping either at a newline, or at the
	 * end of this text block.  Expand tabs.
	 */
	while ((*end != '\0')&&(*end != '\n'))
	{
		if (*end == '\t')
		{
			width = ((width / 8) + 1) * 8;
		}
		else
		{
			width++;
		}
		end++;
	}

	*cnt = width;
	return(end);
}


/*
 * Free up the passed linked list of formatted elements, freeing
 * all memory associates with each element.
 */
void
FreeLineList(list)
	struct ele_rec *list;
{
	struct ele_rec *current;
	struct ele_rec *eptr;

	current = list;
	while (current != NULL)
	{
		eptr = current;
		current = current->next;
		eptr->next = NULL;
		if (eptr->edata != NULL)
		{
			free((char *)eptr->edata);
		}
		if (eptr->anchorHRef != NULL)
		{
			free((char *)eptr->anchorHRef);
		}
		if (eptr->anchorName != NULL)
		{
			free((char *)eptr->anchorName);
		}
		if (eptr->anchorSubject != NULL)
		{
			free((char *)eptr->anchorSubject);
		}
		free((char *)eptr);
	}
}


/*
 * Add an element to the linked list of formatted elements.
 * return a pointer to the current (end) position in the list.
 */
struct ele_rec *
AddEle(elistp, current, eptr)
	struct ele_rec **elistp;
	struct ele_rec *current;
	struct ele_rec *eptr;
{
	if (eptr == NULL)
	{
		return(current);
	}

	/*
	 * Add object to either the head of the list for a new list,
	 * or at the end after the current pointer.
	 */
	if (*elistp == NULL)
	{
		*elistp = eptr;
		(*elistp)->next = NULL;
		(*elistp)->prev = NULL;
		current = *elistp;
	}
	else
	{
		current->next = eptr;
		eptr->prev = current;
		current = current->next;
		current->next = NULL;
	}
	return(current);
}


/*
 * Contruct and return an array of pointers into the element list that
 * indexes the elements by line number.
 * Note, lines containing only while space will have NULL pointers
 * into the element list.
 */
struct ele_rec **
MakeLineList(elist, max_line)
	struct ele_rec *elist;
	int max_line;
{
	int i;
	struct ele_rec *eptr;
	struct ele_rec **ll;

	/*
	 * malloc index array
	 */
	ll = (struct ele_rec **)malloc(sizeof(struct ele_rec *) * max_line);
	if (ll == NULL)
	{
		fprintf(stderr, "cannot allocate space for line list\n");
		exit(1);
	}

	/*
	 * zero the index array
	 */
	for (i=0; i<max_line; i++)
	{
		ll[i] = NULL;
	}

	/*
	 * fill in pointers to beginning of the lines
	 */
	eptr = elist;
	while (eptr != NULL)
	{
		if (eptr->line_number > max_line)
		{
			break;
		}

		if (ll[eptr->line_number - 1] == NULL)
		{
			ll[eptr->line_number - 1] = eptr;
		}

		eptr = eptr->next;
	}
	return(ll);
}


/*
 * Passed in 2 element pointers, and element positions.
 * Function should return 1 if if start occurs before end.
 * Otherwise return 0.
 */
int
ElementLessThan(start, end, start_pos, end_pos)
	struct ele_rec *start;
	struct ele_rec *end;
	int start_pos, end_pos;
{
	struct ele_rec *current;

	/*
	 * Deal with start or end being NULL
	 */
	if ((start == NULL)&&(end == NULL))
	{
		return(0);
	}
	else if ((start == NULL)&&(end != NULL))
	{
		return(1);
	}
	else if ((start != NULL)&&(end == NULL))
	{
		return(0);
	}

	/*
	 * Deal with easy identical case
	 */
	if (start == end)
	{
		if (start_pos < end_pos)
		{
			return(1);
		}
		else
		{
			return(0);
		}
	}

	/*
	 * We know element Ids are always equal or increasing within a
	 * list.
	 */
	if (start->ele_id < end->ele_id)
	{
		return(1);
	}
	else if (start->ele_id == end->ele_id)
	{
		current = start;
		while (current != NULL)
		{
			if (current->ele_id != start->ele_id)
			{
				break;
			}
			else if (current == end)
			{
				break;
			}
			current = current->next;
		}
		if (current == end)
		{
			return(1);
		}
		else
		{
			return(0);
		}
	}
	else
	{
		return(0);
	}
}


/*
 * Passed in 2 element pointers, and element positions.
 * Function should return 1 if they need to be swapped in order for then
 * to proceed left to right and top to bottom in the text.
 * Otherwise return 0.
 */
int
SwapElements(start, end, start_pos, end_pos)
	struct ele_rec *start;
	struct ele_rec *end;
	int start_pos, end_pos;
{
	struct ele_rec *current;

	/*
	 * Deal with start or end being NULL
	 */
	if ((start == NULL)&&(end == NULL))
	{
		return(0);
	}
	else if ((start == NULL)&&(end != NULL))
	{
		return(1);
	}
	else if ((start != NULL)&&(end == NULL))
	{
		return(0);
	}

	/*
	 * Deal with easy identical case
	 */
	if (start == end)
	{
		if (start_pos > end_pos)
		{
			return(1);
		}
		else
		{
			return(0);
		}
	}

	/*
	 * We know element Ids are always equal or increasing within a
	 * list.
	 */
	if (start->ele_id < end->ele_id)
	{
		return(0);
	}
	else if (start->ele_id == end->ele_id)
	{
		current = start;
		while (current != NULL)
		{
			if (current->ele_id != start->ele_id)
			{
				break;
			}
			else if (current == end)
			{
				break;
			}
			current = current->next;
		}
		if (current == end)
		{
			return(0);
		}
		else
		{
			return(1);
		}
	}
	else
	{
		return(1);
	}
}


/*
 * Free up the allocated list of internal hrefs.
 */
void
FreeHRefs(list)
	struct ref_rec *list;
{
	struct ref_rec *hptr;
	struct ref_rec *tptr;

	hptr = list;
	while (hptr != NULL)
	{
		tptr = hptr;
		hptr = hptr->next;
		if (tptr->anchorHRef != NULL)
		{
			free((char *)tptr->anchorHRef);
		}
		free((char *)tptr);
	}
}


/*
 * Find an element in the linked list of Internal HREFS.
 * return a pointer to the element, or NULL if not found.
 */
struct ref_rec *
FindHRef(list, href)
	struct ref_rec *list;
	char *href;
{
	struct ref_rec *hptr;

	if (href == NULL)
	{
		return(NULL);
	}

	hptr = list;
	while (hptr != NULL)
	{
		if (strcmp(hptr->anchorHRef, href) == 0)
		{
			break;
		}
		hptr = hptr->next;
	}
	return(hptr);
}


/*
 * Add an element to the linked list of Internal HREFS we
 * have visited before.
 * return a pointer to the head of the new list.
 */
struct ref_rec *
AddHRef(list, href)
	struct ref_rec *list;
	char *href;
{
	struct ref_rec *hptr;

	if (href == NULL)
	{
		return(list);
	}

	hptr = FindHRef(list, href);

	if (hptr == NULL)
	{
		hptr = (struct ref_rec *)malloc(sizeof(struct ref_rec));
		if (hptr == NULL)
		{
#ifndef DISABLE_TRACE
			if (htmlwTrace) {
				fprintf(stderr, "cannot extend internal href list\n");
			}
#endif

			return(list);
		}
		hptr->anchorHRef = (char *)malloc(strlen(href) + 1);
		if (hptr->anchorHRef == NULL)
		{
			free((char *)hptr);
#ifndef DISABLE_TRACE
			if (htmlwTrace) {
				fprintf(stderr, "cannot extend internal href list\n");
			}
#endif

			return(list);
		}
		strcpy(hptr->anchorHRef, href);
		hptr->next = list;
		list = hptr;
	}

	return(list);
}


/*
 * Free up the allocated list of visited delayed images
 */
void
FreeDelayedImages(list)
	struct delay_rec *list;
{
	struct delay_rec *iptr;
	struct delay_rec *tptr;

	iptr = list;
	while (iptr != NULL)
	{
		tptr = iptr;
		iptr = iptr->next;
		if (tptr->src != NULL)
		{
			free((char *)tptr->src);
		}
		free((char *)tptr);
	}
}


/*
 * Find an element in the linked list of visited delayed images.
 * return a pointer to the element, or NULL if not found.
 */
struct delay_rec *
FindDelayedImage(list, src)
	struct delay_rec *list;
	char *src;
{
	struct delay_rec *iptr;

	if (src == NULL)
	{
		return(NULL);
	}

	iptr = list;
	while (iptr != NULL)
	{
		if (strcmp(iptr->src, src) == 0)
		{
			break;
		}
		iptr = iptr->next;
	}
	return(iptr);
}


/*
 * Add an element to the linked list of visited delayed images.
 * return a pointer to the head of the new list.
 */
struct delay_rec *
AddDelayedImage(list, src)
	struct delay_rec *list;
	char *src;
{
	struct delay_rec *iptr;

	if (src == NULL)
	{
		return(list);
	}

	iptr = FindDelayedImage(list, src);

	if (iptr == NULL)
	{
		iptr = (struct delay_rec *)malloc(sizeof(struct delay_rec));
		if (iptr == NULL)
		{
#ifndef DISABLE_TRACE
			if (htmlwTrace) {
				fprintf(stderr, "cannot extend visited delayed images list\n");
			}
#endif

			return(list);
		}
		iptr->src = (char *)malloc(strlen(src) + 1);
		if (iptr->src == NULL)
		{
			free((char *)iptr);
#ifndef DISABLE_TRACE
			if (htmlwTrace) {
				fprintf(stderr, "cannot extend visited delayed images list\n");
			}
#endif

			return(list);
		}
		strcpy(iptr->src, src);
		iptr->next = list;
		list = iptr;
	}

	return(list);
}

