/****************************************************************************
 * 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"

/* Some part of this file is Copyright (C) 1996 - G.Dauphin
 * See the file "license.mMosaic" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include <time.h>
#ifndef VMS
struct timeval Tv;
struct timezone Tz;
#endif
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#include "HTMLmiscdefs.h"
#include "HTMLparse.h"
#include "HTMLP.h"
#include "HTMLPutil.h"
#include "../src/mosaic.h"

#include "HTMLform.h"
#include "HTMLframe.h"
#include "HTMLfont.h"

#include <Xm/Frame.h>
#include <Xm/DrawingA.h>
#include <Xm/Label.h>

#if defined(MULTINET) && defined(__DECC) && (__VMS_VER >= 70000000)
#define strdup  decc$strdup
#endif /* VMS V7, VRH, GEC, MPJZ */
extern char *strdup();

#include "../libnut/str-tools.h"

int appletSupportEnabled = 0;
int tableSupportEnabled;
extern int pushfont_count;

/* X context from GUI.C */
XtAppContext app_context;

#ifndef DISABLE_TRACE
int refreshTrace;
extern int htmlwTrace;
extern int reportBugs;
#endif

static MarkInfo NULL_ANCHOR = {
	M_ANCHOR,		/* MarkType */
	1,			/* is_end */
	NULL,			/* start */
	NULL,			/* text */
	0,			/* end */
	NULL,			/* next */
	NULL,			/* saved_aps */
	NULL,			/* anchor_name */
	NULL,			/* anchor_href */
	NULL			/* anchor_title */
};

MarkInfo *NULL_ANCHOR_PTR = &NULL_ANCHOR;

static AlignRec AlignBase;
static AlignRec *AlignStack;

DescRec BaseDesc;
DescRec *DescType;
static Boolean Strikeout;
static char *TitleText = NULL;
static XFontStruct *nonScriptFont;
static int InDocHead;
static int InDocBody;
static int header1_align;  /* Counters for header alignments */
static int header2_align;
static int header3_align;
static int header4_align;
static int header5_align;
static int header6_align;
static int header1;        /* Header counters */
static int header2;
static int header3;
static int header4;
static int header5;
static int header6;


/* Set the formatted element into the format list. */
struct ele_rec * CreateElement( HTMLWidget hw, int type, XFontStruct *fp,
	int x, int y, int width, int height, int baseline,
	PhotoComposeContext *pcc)
{
	struct ele_rec *eptr;

	if (pcc->cw_only)
		return NULL;

	/* There is not pre-allocated format list, or we have reached the
	 * end of the pre-allocated list.  Create a new element, and add it.
	 */
	if (!hw->html.formatted_elements) { /* the first element */
		eptr = (struct ele_rec *) malloc(sizeof(struct ele_rec));
		CHECK_OUT_OF_MEM(eptr);
		hw->html.formatted_elements = eptr;
		eptr->next = NULL;
		eptr->prev = NULL;
		hw->html.cur_elem_to_format = eptr;
		hw->html.last_formatted_elem = eptr;
	} 
	/* Work now with the current element to format */
	eptr = hw->html.cur_elem_to_format;
	if (!eptr) { /* No current element, add one at end */
		eptr = (struct ele_rec *) malloc(sizeof(struct ele_rec));
		CHECK_OUT_OF_MEM(eptr);
		eptr->next = NULL;
		eptr->prev = hw->html.last_formatted_elem;
		hw->html.last_formatted_elem->next = eptr;
		hw->html.last_formatted_elem = eptr;
	}
	hw->html.cur_elem_to_format = NULL;

	/* Now we work with 'eptr' and start the stuff */

	eptr->type = type;
	eptr->font = fp;
	eptr->x = x;
	eptr->y = y;
	eptr->width = width;
	eptr->height = height;
	eptr->baseline = baseline;
	eptr->ele_id = ++(pcc->element_id);

	eptr->pic_data = NULL;
	eptr->aps = NULL;
	eptr->widget_data = NULL;
	eptr->table_data = NULL;
	eptr->cell_data = NULL;
	eptr->ats = NULL;

	eptr->line_next = NULL;

	eptr->valignment = VALIGN_BOTTOM;
	eptr->halignment = HALIGN_LEFT;
	eptr->selected = False;
	eptr->indent_level = pcc->indent_level;
	eptr->start_pos = 0;
	eptr->end_pos = 0;
	eptr->bwidth = IMAGE_DEFAULT_BORDER;
	eptr->underline_number = pcc->underline_number;
	eptr->dashed_underline = pcc->dashed_underlines;
	eptr->strikeout = Strikeout;
	eptr->fg = pcc->fg;
	eptr->bg = pcc->bg;
        eptr->anchor_tag_ptr = pcc->anchor_tag_ptr; /* it's in struct markup */
        eptr->edata = NULL;
        eptr->edata_len = 0;
	eptr->is_in_form = 0;
	return eptr;
}

void AdjustBaseLine(HTMLWidget hw, struct ele_rec *eptr,
		PhotoComposeContext *pcc)
{
	int dbase;
	int supsubBaseline;
	int cur_baseline;

	cur_baseline = pcc->cur_baseline;

        if ((pcc->superscript > 0) || (pcc->subscript > 0)) {
		if (!nonScriptFont) {
			nonScriptFont = pcc->cur_font;
		}
		supsubBaseline = nonScriptFont->max_bounds.ascent;
		cur_baseline -= ((supsubBaseline * .4) * pcc->superscript);
		cur_baseline += ((supsubBaseline * .4) * pcc->subscript);
		cur_baseline += 2;
        }

	/* Adjust baseline of element */
	if (eptr->baseline <= cur_baseline) {
		if (eptr->type == E_TEXT) {
			eptr->height += cur_baseline - eptr->baseline;
			eptr->baseline = cur_baseline;
		}
		if (eptr->type == E_IMAGE) {
			if (pcc->cur_line_height < (eptr->height +
			    cur_baseline - eptr->baseline))
				pcc->cur_line_height = eptr->height +
				    cur_baseline - eptr->baseline;
			eptr->y += cur_baseline - eptr->baseline;
			eptr->baseline = cur_baseline;
		}
		if (pcc->cur_line_height < eptr->height)
			pcc->cur_line_height = eptr->height;
		return;
	}

	/* eptr->baseline > cur_baseline */
	dbase = eptr->baseline - cur_baseline;
	pcc->cur_baseline = eptr->baseline;
	pcc->cur_line_height += dbase;
	if (pcc->cur_line_height < eptr->height)
		pcc->cur_line_height = eptr->height; 
	while (eptr->prev) {	/* Adjust all ele baseline and height */
		eptr = eptr->prev;
		if (eptr->type == E_CR)
			break;
		eptr->baseline += dbase;
		if (eptr->type == E_IMAGE) {
			eptr->y += dbase;
		} else if (eptr->type == E_TEXT) {
			eptr->height += dbase;
		}
	}
}

static void PushAlign( DivAlignType align)
{
	AlignRec *aptr;

	aptr = (AlignRec *) malloc(sizeof(AlignRec));
	CHECK_OUT_OF_MEM(aptr);
	aptr->align = align;
	aptr->next = AlignStack;
	AlignStack = aptr;
}

static DivAlignType PopAlign()
{
	DivAlignType align;
	AlignRec *aptr;

	if (AlignStack->next) {
		aptr = AlignStack;
		AlignStack = AlignStack->next;
		align = aptr->align;
		free((char *)aptr);
	} else {
#ifndef DISABLE_TRACE
		if (htmlwTrace || reportBugs) 
			fprintf(stderr, "Warning: popping empty align stack!\n");
#endif
		align = AlignStack->align;
	}
	return(align);
}

/* Horrible code for the TEXTAREA element.  Escape '\' and ''' by
 * putting a '\' in front of them, then replace all '"' with '''.
 * This lets us safely put the resultant value between double quotes.
 */
char *TextAreaAddValue( char *value, char *text)
{
	int extra;
	char *buf;
	char *bptr;
	char *tptr;

	if (!text || (text[0] == '\0'))
		return(value);

	extra = 0;
	tptr = text;
	while (*tptr) {
		if ((*tptr == '\\') || (*tptr == '\''))
			extra++;
		tptr++;
	}

	buf = (char *)malloc(strlen(value) + strlen(text) + extra + 1);
	CHECK_OUT_OF_MEM(buf);
	strcpy(buf, value);

	tptr = text;
	bptr = (char *)(buf + strlen(value));
	while (*tptr) {
		if ((*tptr == '\\') || (*tptr == '\'')) {
			*bptr++ = '\\';
			*bptr++ = *tptr++;
		}
		else if (*tptr == '\"')
		{
			*bptr++ = '\'';
			tptr++;
		} else {
			*bptr++ = *tptr++;
		}
	}
	*bptr = '\0';
	free(value);
	return(buf);
}

/*
 * Make necessary changes to formatting, based on the type of the
 * parsed HTML text we are formatting.
 * Some calls create elements that are added to the formatted element list.
 */
void TriggerMarkChanges(HTMLWidget hw, MarkInfo **mptr,
		PhotoComposeContext *pcc, Boolean save_obj)
{
	MarkInfo *mark;
	XFontStruct *tmp_font;
	int type;
	MarkInfo mark_tmp;
	char *tptr;

	mark = *mptr;
	type = mark->type;

	/* If we are not in a tag that belongs in the HEAD, end the HEAD
	   section  - amb */
	if ((InDocHead == 1) && (type != M_TITLE) && (type != M_NONE) &&
	    (type != M_BASE) && (type != M_INDEX) && (type != M_COMMENT) &&
	    (type != M_META) && (type != M_LINK) && (type != M_STYLE) &&
	    (type != M_SCRIPT) && (type != M_UNKNOWN)) {
		pcc->ignore = 0;
		InDocHead = -1;
		/* Finish processing the title; too late for </title> */
		if (TitleText) {
			hw->html.title = TitleText;
			TitleText = NULL;
		}
	}
	/* If pcc->ignore is set, we ignore all further elements until we get
	 * to the end of the pcc->ignore
	 * Let text through so we can grab the title text.
	 * Let title through so we can hit the end title.
	 * Also used for SELECT parsing
	 * Let SELECT through so we can hit the end SELECT.
	 * Let OPTION through so we can hit the OPTIONs.
	 * Let TEXTAREA through so we can hit the TEXTAREAs.
	 */
	if (pcc->ignore && (InDocHead != 1) && (type != M_TITLE) &&
	    (type != M_NONE) &&
	    (type != M_SELECT) && (type != M_OPTION) &&
	    (type != M_TEXTAREA) && (type != M_DOC_HEAD))
		return;

	/* A block element ends current paragraph */
	if (pcc->in_paragraph && (type >= M_ADDRESS)) {
		ConditionalLineFeed(hw, 1, pcc);
		pcc->div = PopAlign();
		pcc->in_paragraph = False;
	}

	switch(type) {
	/*
	 * Place the text.  Different functions based on whether it
	 * is pre-formatted or not.
	 */
	case M_NONE:
		/* Ignore all text in scripts and style sheets */
		if (pcc->in_script || pcc->in_style)
			break;
		/* First translate any &quot characters */
		tptr = mark->text;
		while (*tptr) {
			if (*tptr == QUOT_CONST)
				*tptr = '\"';
			tptr++;
		}

		if (pcc->ignore && !pcc->current_select &&
		    !pcc->text_area_buf) {
			/* Did we finish it already ?*/
			if (!hw->html.title) {
				/* Did we start it yet? */
				if (!TitleText) {
					TitleText = (char *)
						malloc(strlen(mark->text) + 1);
					strcpy(TitleText, mark->text);
				} else {
					tptr = (char *) malloc(strlen(TitleText) +
						       strlen(mark->text) + 1);
					strcpy(tptr, TitleText);
					strcat(tptr, mark->text);
					free(TitleText);
					TitleText = tptr;
				}
			}
			break;
		}
		if (pcc->ignore && pcc->current_select) {
			if (pcc->current_select->option_buf) {
				tptr = (char *)malloc(strlen(
					pcc->current_select->option_buf) +
					       strlen(mark->text) + 1);
				strcpy(tptr, pcc->current_select->option_buf);
				strcat(tptr, mark->text);
				free(pcc->current_select->option_buf);
				pcc->current_select->option_buf = tptr;
			}
			break;
		}
		if (pcc->ignore && pcc->text_area_buf) {
			pcc->text_area_buf =
				TextAreaAddValue(pcc->text_area_buf,
					mark->text);
			break;
		}
		if (pcc->preformat) {
			PartOfPreTextPlace(hw, *mptr, pcc);
			break;
		} 
		PartOfTextPlace(hw, *mptr, pcc);
		break;
	case M_CENTER:
		if (mark->is_end) {
			ConditionalLineFeed(hw, 1, pcc);
			pcc->div = PopAlign();
		} else {
			ConditionalLineFeed(hw, 1, pcc);
			PushAlign(pcc->div);
			pcc->div = DIV_ALIGN_CENTER;
		}
		break;
	case M_DIV:
		if (mark->is_end) {
			ConditionalLineFeed(hw, 1, pcc);
			pcc->div = PopAlign();
		} else {
			ConditionalLineFeed(hw, 1, pcc);
			PushAlign(pcc->div);
			tptr = ParseMarkTag(mark->start, MT_DIV, "ALIGN");
			if (caseless_equal(tptr, "LEFT"))
				pcc->div = DIV_ALIGN_LEFT;
			if (caseless_equal(tptr, "CENTER"))
				pcc->div = DIV_ALIGN_CENTER;
			if (caseless_equal(tptr, "RIGHT"))
				pcc->div = DIV_ALIGN_RIGHT;
			if (tptr)
				free(tptr);
		}
		break;
	/*
	 * Just insert a linefeed, or ignore if this is preformatted
	 * text because the <P> will be followed by a linefeed.
	 * See above for additional end of paragraph processing.
	 */
	case M_PARAGRAPH:
		ConditionalLineFeed(hw, 1, pcc);
		/* Don't force linefeed at top of page or table cell */
		if (!pcc->at_top)
			ConditionalLineFeed(hw, 2, pcc);
		pcc->at_top = False;
		if (mark->is_end)
			break;
		PushAlign(pcc->div);
		pcc->in_paragraph = True;
		tptr = ParseMarkTag(mark->start, MT_PARAGRAPH, "ALIGN");
		if (caseless_equal(tptr, "LEFT"))
			pcc->div = DIV_ALIGN_LEFT;
		if (caseless_equal(tptr, "CENTER"))
			pcc->div = DIV_ALIGN_CENTER;
		if (caseless_equal(tptr, "RIGHT"))
			pcc->div = DIV_ALIGN_RIGHT;
		if (tptr)
			free(tptr);
		break;

	case M_HRULE:
		ConditionalLineFeed(hw, 1, pcc);
		HRulePlace(hw, *mptr, pcc);
		ConditionalLineFeed(hw, 1, pcc);
		break;
	/*
	 * Titles are just set into the widget for retrieval by
	 * XtGetValues().
	 */
	case M_TITLE:
		if (mark->is_end) {
		        if (InDocHead != 1)
			    pcc->ignore = 0;
			if (!hw->html.title)
				hw->html.title = TitleText;
		        TitleText = NULL;
		} else {
			pcc->ignore = 1;
			TitleText = NULL;
		}
		break;
	/*
	 * Formatting commands just change the current font.
	 */
	case M_CODE:
	case M_SAMPLE:
	case M_KEYBOARD:
	case M_FIXED:
		if (mark->is_end) {
			pcc->cur_font = PopFont(pcc);
		} else {
			PushFont(pcc);
			pcc->cur_font = hw->html.fixed_font;
			pcc->cur_font_type = FIXED_FONT;
		}
		break;
	case M_STRONG:
	case M_BOLD:
		if (mark->is_end) {
			pcc->cur_font = PopFont(pcc);
		} else {
			PushFont(pcc);
			if (pcc->cur_font == hw->html.fixed_font ||
			    pcc->cur_font == hw->html.fixeditalic_font) {
				pcc->cur_font = hw->html.fixedbold_font;
				pcc->cur_font_type = FIXEDBOLD_FONT;
			} else if (pcc->cur_font == hw->html.plain_font ||
				   pcc->cur_font == hw->html.plainitalic_font) {
				pcc->cur_font = hw->html.plainbold_font;
				pcc->cur_font_type = PLAINBOLD_FONT;
			} else if (pcc->cur_font == hw->html.italic_font) {
				pcc->cur_font = hw->html.bolditalic_font;
				pcc->cur_font_type = BOLDITALIC_FONT;
			} else {
				pcc->cur_font = hw->html.bold_font;
				pcc->cur_font_type = BOLD_FONT;
			}
		}
		break;
	case M_EMPHASIZED:
	case M_VARIABLE:
	case M_CITATION:
	case M_ITALIC:
	case M_DEFINE:
		if (mark->is_end) {
			pcc->cur_font = PopFont(pcc);
		} else {
			PushFont(pcc);
			if (pcc->cur_font == hw->html.fixed_font ||
			    pcc->cur_font == hw->html.fixedbold_font) {
				pcc->cur_font = hw->html.fixeditalic_font;
				pcc->cur_font_type = FIXEDITALIC_FONT;
			} else if (pcc->cur_font == hw->html.plain_font ||
				   pcc->cur_font == hw->html.plainbold_font) {
				pcc->cur_font = hw->html.plainitalic_font;
				pcc->cur_font_type = PLAINITALIC_FONT;
			} else if (pcc->cur_font == hw->html.bold_font) {
				pcc->cur_font = hw->html.bolditalic_font;
				pcc->cur_font_type = BOLDITALIC_FONT;
			} else {
				pcc->cur_font = hw->html.italic_font;
				pcc->cur_font_type = ITALIC_FONT;
			}
		}
		break;
	/*
	 * Strikeout means draw a line through the text.
	 * Right now we just set a boolean flag which gets shoved
	 * in the element record for all elements in the
	 * strikeout zone.
	 */
	case M_STRIKEOUT:
		Strikeout = True;
		if (mark->is_end)
			Strikeout = False;
		break;
        case M_SUP:

                if (mark->is_end) {
                       pcc->superscript--;
                       if ((pcc->superscript == 0) && (pcc->subscript == 0))
                             pcc->cur_font = PopFont(pcc);
                 } else {
                       pcc->superscript++;
                       if ((pcc->superscript == 1) && (pcc->subscript == 0)) {
                              nonScriptFont = pcc->cur_font;
                              PushFont(pcc);
                              pcc->cur_font = hw->html.supsub_font;
			      pcc->cur_font_type = SUPSUB_FONT;
                        }
                }
                break;
	case M_SUB:
		if (mark->is_end) {
                        pcc->subscript--;
                        if ((pcc->subscript == 0) && (pcc->superscript == 0))
                                pcc->cur_font = PopFont(pcc);
                } else {
                        pcc->subscript++;
                        if ((pcc->subscript == 1) && (pcc->superscript == 0)) {
                        	nonScriptFont = pcc->cur_font;
                        	PushFont(pcc);
                        	pcc->cur_font = hw->html.supsub_font;
			        pcc->cur_font_type = SUPSUB_FONT;
                        }
                }
                break;
	/* Ignore text inside a HEAD element */
	case M_DOC_HEAD:
		if (mark->is_end) {
		        InDocHead = -1;
			pcc->ignore = 0;
			/* Finish processing the title; too late for </title> */
			if (TitleText) {
				hw->html.title = TitleText;
				TitleText = NULL;
			}
		} else if (!InDocHead) {
			InDocHead = 1;
			pcc->ignore = 1;
		}
		break;
	case M_DOC_BODY:
		/* Allow additional body tags if first one was empty or
		 * nothing formatted yet */
		if (!mark->is_end && ((InDocBody < 2) ||
		    !hw->html.formatted_elements)) {
			static char *atts[] =
			    {"text", "bgcolor", "alink", "vlink", "link", NULL};
			char *tmp = NULL, *tmp_bgname = NULL;
			int i;

			InDocBody++;
			if (hw->html.body_colors) {
				for (i=0; atts[i]; i++) {
					tmp = ParseMarkTag(mark->start,
						MT_DOC_BODY, atts[i]);
					if (tmp) {
						hw_do_color(hw, atts[i],
							tmp, pcc);
						free(tmp);
						tmp = NULL;
						InDocBody++;
					}
				}
			}
			if (hw->html.body_images) {
				tmp_bgname = ParseMarkTag(mark->start,
					MT_DOC_BODY, "background");
				if (tmp_bgname) {
					hw_do_bg(hw, tmp_bgname, pcc);
					free(tmp_bgname);
					InDocBody++;
				}
			}
		        InDocHead = -1;   /* End <head> section */
			pcc->ignore = 0;
		}
		break;
	case M_UNDERLINED:
		pcc->underline_number = 1;
		pcc->in_underlined = 1;
		if (mark->is_end) {
			pcc->underline_number = 0;
			pcc->in_underlined = 0;
		}
		break;
	/*
	 * Headers are preceeded (except after list bullets) and followed
	 * by a linefeed.
	 */
	case M_HEADER_1:
		ConditionalLineFeed(hw, 1, pcc);
		if (mark->is_end && header1) {
			header1--;
			pcc->cur_font = PopFont(pcc);
			ConditionalLineFeed(hw, 2, pcc);
			if (header1_align) {
				header1_align--;
				pcc->div = PopAlign();
			}
		} else if (!mark->is_end) {
			header1++;
			if (!pcc->at_top)
				ConditionalLineFeed(hw, 2, pcc);
			pcc->at_top = False;
			PushFont(pcc);
			pcc->cur_font = hw->html.header1_font;
			pcc->cur_font_type = HEADER1_FONT;
			tptr = ParseMarkTag(mark->start, MT_HEADER_1, "ALIGN");
			if (tptr) {
				header1_align++;
				PushAlign(pcc->div);
				if (caseless_equal(tptr, "LEFT"))
					pcc->div = DIV_ALIGN_LEFT;
				else if (caseless_equal(tptr, "CENTER"))
					pcc->div = DIV_ALIGN_CENTER;
				else if (caseless_equal(tptr, "RIGHT"))
					pcc->div = DIV_ALIGN_RIGHT;
				else {
					header1_align--;
					pcc->div = PopAlign();
				}
				free(tptr);
			}
		}
		break;
	case M_HEADER_2:
		ConditionalLineFeed(hw, 1, pcc);
		if (mark->is_end && header2) {
			header2--;
			pcc->cur_font = PopFont(pcc);
			ConditionalLineFeed(hw, 2, pcc);
			if (header2_align) {
				header2_align--;
				pcc->div = PopAlign();
			}
		} else if (!mark->is_end) {
			header2++;
			if (!pcc->at_top)
				ConditionalLineFeed(hw, 2, pcc);
			pcc->at_top = False;
			PushFont(pcc);
			pcc->cur_font = hw->html.header2_font;
			pcc->cur_font_type = HEADER2_FONT;
			tptr = ParseMarkTag(mark->start, MT_HEADER_2, "ALIGN");
			if (tptr) {
				header2_align++;
				PushAlign(pcc->div);
				if (caseless_equal(tptr, "LEFT"))
					pcc->div = DIV_ALIGN_LEFT;
				else if (caseless_equal(tptr, "CENTER"))
					pcc->div = DIV_ALIGN_CENTER;
				else if (caseless_equal(tptr, "RIGHT"))
					pcc->div = DIV_ALIGN_RIGHT;
				else {
					header2_align--;
					pcc->div = PopAlign();
				}
				free(tptr);
			}
		}
		break;
	case M_HEADER_3:
		ConditionalLineFeed(hw, 1, pcc);
		if (mark->is_end && header3) {
			header3--;
			pcc->cur_font = PopFont(pcc);
			ConditionalLineFeed(hw, 2, pcc);
			if (header3_align) {
				header3_align--;
				pcc->div = PopAlign();
			}
		} else if (!mark->is_end) {
			header3++;
			if (!pcc->at_top)
				ConditionalLineFeed(hw, 2, pcc);
			pcc->at_top = False;
			PushFont(pcc);
			pcc->cur_font = hw->html.header3_font;
			pcc->cur_font_type = HEADER3_FONT;
			tptr = ParseMarkTag(mark->start, MT_HEADER_3, "ALIGN");
			if (tptr) {
				header3_align++;
				PushAlign(pcc->div);
				if (caseless_equal(tptr, "LEFT"))
					pcc->div = DIV_ALIGN_LEFT;
				else if (caseless_equal(tptr, "CENTER"))
					pcc->div = DIV_ALIGN_CENTER;
				else if (caseless_equal(tptr, "RIGHT"))
					pcc->div = DIV_ALIGN_RIGHT;
				else {
					header3_align--;
					pcc->div = PopAlign();
				}
				free(tptr);
			}
		}
		break;
	case M_HEADER_4:
		ConditionalLineFeed(hw, 1, pcc);
		if (mark->is_end && header4) {
			header4--;
			pcc->cur_font = PopFont(pcc);
			ConditionalLineFeed(hw, 2, pcc);
			if (header4_align) {
				header4_align--;
				pcc->div = PopAlign();
			}
		} else if (!mark->is_end) {
			header4++;
			if (!pcc->at_top)
				ConditionalLineFeed(hw, 2, pcc);
			pcc->at_top = False;
			PushFont(pcc);
			pcc->cur_font = hw->html.header4_font;
			pcc->cur_font_type = HEADER4_FONT;
			tptr = ParseMarkTag(mark->start, MT_HEADER_4, "ALIGN");
			if (tptr) {
				header4_align++;
				PushAlign(pcc->div);
				if (caseless_equal(tptr, "LEFT"))
					pcc->div = DIV_ALIGN_LEFT;
				else if (caseless_equal(tptr, "CENTER"))
					pcc->div = DIV_ALIGN_CENTER;
				else if (caseless_equal(tptr, "RIGHT"))
					pcc->div = DIV_ALIGN_RIGHT;
				else {
					header4_align--;
					pcc->div = PopAlign();
				}
				free(tptr);
			}
		}
		break;
	case M_HEADER_5:
		ConditionalLineFeed(hw, 1, pcc);
		if (mark->is_end && header5) {
			header5--;
			pcc->cur_font = PopFont(pcc);
			ConditionalLineFeed(hw, 2, pcc);
			if (header5_align) {
				header5_align--;
				pcc->div = PopAlign();
			}
		} else if (!mark->is_end) {
			header5++;
			if (!pcc->at_top)
				ConditionalLineFeed(hw, 2, pcc);
			pcc->at_top = False;
			PushFont(pcc);
			pcc->cur_font = hw->html.header5_font;
			pcc->cur_font_type = HEADER5_FONT;
			tptr = ParseMarkTag(mark->start, MT_HEADER_5, "ALIGN");
			if (tptr) {
				header5_align++;
				PushAlign(pcc->div);
				if (caseless_equal(tptr, "LEFT"))
					pcc->div = DIV_ALIGN_LEFT;
				else if (caseless_equal(tptr, "CENTER"))
					pcc->div = DIV_ALIGN_CENTER;
				else if (caseless_equal(tptr, "RIGHT"))
					pcc->div = DIV_ALIGN_RIGHT;
				else {
					header5_align--;
					pcc->div = PopAlign();
				}
				free(tptr);
			}
		}
		break;
	case M_HEADER_6:
		ConditionalLineFeed(hw, 1, pcc);
		if (mark->is_end && header6) {
			header6--;
			pcc->cur_font = PopFont(pcc);
			ConditionalLineFeed(hw, 2, pcc);
			if (header6_align) {
				header6_align--;
				pcc->div = PopAlign();
			}
		} else if (!mark->is_end) {
			header6++;
			if (!pcc->at_top)
				ConditionalLineFeed(hw, 2, pcc);
			pcc->at_top = False;
			PushFont(pcc);
			pcc->cur_font = hw->html.header6_font;
			pcc->cur_font_type = HEADER6_FONT;
			tptr = ParseMarkTag(mark->start, MT_HEADER_6, "ALIGN");
			if (tptr) {
				header6_align++;
				PushAlign(pcc->div);
				if (caseless_equal(tptr, "LEFT"))
					pcc->div = DIV_ALIGN_LEFT;
				else if (caseless_equal(tptr, "CENTER"))
					pcc->div = DIV_ALIGN_CENTER;
				else if (caseless_equal(tptr, "RIGHT"))
					pcc->div = DIV_ALIGN_RIGHT;
				else {
					header6_align--;
					pcc->div = PopAlign();
				}
				free(tptr);
			}
		}
		break;
	case M_FRAME:
	case M_FRAMESET:
	case M_NOFRAMES:
		FramePlace(hw, *mptr, pcc);
		break;
	/*
	 * Anchors change the text color, and may set
	 * underlining attributes.
	 * No linefeeds, so they can be imbedded anywhere.
	 */
	case M_ANCHOR:
		if (mark->is_end || pcc->in_anchor) {
			/* At end of anchor or at start of another without
			 * finding end of previous one */
			pcc->in_anchor = 0;
			pcc->fg = pcc->cur_font_color;
			pcc->underline_number = pcc->in_underlined;
			pcc->dashed_underlines = False;
			pcc->anchor_tag_ptr = NULL_ANCHOR_PTR;
			if (mark->is_end)
				break;
		}

		pcc->in_anchor = 1;
		/* Only change the color of anchors with HREF tags, 
		 * because other anchors are not active.
		 */
		pcc->anchor_tag_ptr = *mptr;
		tptr = mark->anc_href = ParseMarkTag(mark->start, 
			MT_ANCHOR, AT_HREF);
		if (tptr) { 
			pcc->anchor_start = 1;
		    	pcc->fg = hw->html.anchor_fg;
		    	pcc->underline_number = hw->html.num_anchor_underlines;
		    	pcc->dashed_underlines = hw->html.dashed_anchor_lines;
		        if (hw->html.previously_visited_test &&
			    ((*(visitTestProc)
			     (hw->html.previously_visited_test))
			     ((Widget)hw, tptr))) {
			        pcc->fg = hw->html.visitedAnchor_fg;
			        pcc->underline_number =
					hw->html.num_visitedAnchor_underlines;
			        pcc->dashed_underlines =
					hw->html.dashed_visitedAnchor_lines;
			    }
		}
		if (pcc->in_underlined) {
			pcc->dashed_underlines = False;
			if (!pcc->underline_number)
				pcc->underline_number = 1;
		}
		mark->anc_name = ParseMarkTag(mark->start, MT_ANCHOR, AT_NAME);
		/* Create anchor element to hold NAME attribute */ 
		if (mark->anc_name && !pcc->cw_only) {
			struct ele_rec *eptr;

			eptr = CreateElement(hw, E_ANCHOR, pcc->cur_font,
				pcc->x, pcc->y, 0, 0, 0, pcc);
		}
		mark->anc_title = ParseMarkTag(mark->start, 
			MT_ANCHOR, AT_TITLE);
		break;
	/* Blockquotes increase the margin width.  They cannot be nested. */
	case M_BLOCKQUOTE:
		if (mark->is_end && pcc->blockquote) {
			pcc->left_margin -= hw->html.margin_width;
			pcc->cur_line_width += hw->html.margin_width;
			ConditionalLineFeed(hw, 1, pcc);
			pcc->x = pcc->left_margin + pcc->eoffsetx;
			ConditionalLineFeed(hw, 2, pcc);
			pcc->blockquote--;
		} else {
			ConditionalLineFeed(hw, 1, pcc);
			pcc->left_margin += hw->html.margin_width;
			pcc->cur_line_width -= hw->html.margin_width;
			ConditionalLineFeed(hw, 2, pcc);
			pcc->x = pcc->left_margin + pcc->eoffsetx;
			pcc->blockquote++;
		}
		break;

	/* Can only be inside a SELECT tag. */
	case M_OPTION:
		if (mark->is_end)	/* No end mark on <option> */
			return;
		FormSelectOptionField(hw, mptr, pcc);
		break;

	/* Special INPUT tag. */
	case M_SELECT:
		if (mark->is_end) {
			FormSelectEnd(hw, mptr, pcc);
		} else {
			FormSelectBegin(hw, mptr, pcc);
		}
		break;

	case M_TEXTAREA:
		if (mark->is_end) {
			FormTextAreaEnd(hw, mptr, pcc);
		} else {
			FormTextAreaBegin(hw, mptr, pcc);
		}
		break;

	/* Just insert the widget. */
	case M_INPUT:
		if (mark->is_end)	/* No end mark on <input> */
			return;
		FormInputField(hw, mptr, pcc);
		break;

	/* Fillout forms.  Cannot be nested. */
	case M_FORM:
		/* Don't linefeed if in non-display HTML in table */
		if (pcc->in_table > -1) {
			ConditionalLineFeed(hw, 1, pcc);
		} else {
			/* Indicate LF needed at start of table */
			pcc->in_table = -2;
		}
		if (mark->is_end) {
			if (pcc->in_table > -1)
				ConditionalLineFeed(hw, 2, pcc);
			EndForm  (hw, mptr, pcc);
		} else {
			if (pcc->in_table > -1)
				ConditionalLineFeed(hw, 2, pcc);
			BeginForm(hw, mptr, pcc);
		}
		break;

	/*
	 * Addresses are just like headers.  A linefeed before and
	 * after, and change the font.
	 */
	case M_ADDRESS:
		ConditionalLineFeed(hw, 1, pcc);
		if (mark->is_end) {
			pcc->cur_font = PopFont(pcc);
		} else {
			PushFont(pcc);
			pcc->cur_font = hw->html.address_font;
			pcc->cur_font_type = ADDRESS_FONT;
		}
		break;
	/*
	 * Plain and listing text.  A single pre-formatted chunk of text
	 * in its own font.
	 */
	case M_PREFORMAT:
	case M_PLAIN_TEXT:
	case M_LISTING_TEXT:
	case M_PLAIN_FILE:
		if (mark->is_end) {
			/*
			 * Properly convert the Linefeed state
			 * variable from preformat to formatted
			 * state.
			 */
			if (pcc->pf_lf_state == 2) {
				pcc->pf_lf_state = 1;
			} else {
				pcc->pf_lf_state = 0;
			}
			pcc->preformat = 0;
			ConditionalLineFeed(hw, 1, pcc);
			pcc->cur_font = PopFont(pcc);
			ConditionalLineFeed(hw, 2, pcc);
		} else {
			ConditionalLineFeed(hw, 1, pcc);
			ConditionalLineFeed(hw, 2, pcc);
			pcc->preformat = 1;
			pcc->pf_lf_state = 2;
			PushFont(pcc);
			pcc->cur_font = hw->html.plain_font;
			pcc->cur_font_type = PLAIN_FONT;
			if (type == M_LISTING_TEXT) {
				pcc->cur_font = hw->html.listing_font;
				pcc->cur_font_type = LISTING_FONT;
			}
		}
		break;
	/*
	 * Numbered lists, Unnumbered lists and Menus.
	 * Currently also lump directory listings into this.
	 * Save state for each indent level.
	 * Change the value of the TxtIndent (can be nested)
	 * Linefeed at the end of the list.
	 */
	case M_NUM_LIST:
	case M_UNUM_LIST:
	case M_MENU:
	case M_DIRECTORY:
		ConditionalLineFeed(hw, 1, pcc);
		/*
		 * If this is the outermost level of indentation
		 * and we are not at top of page or table cell, then
		 * add another linefeed for more white space.
		 */
		if (!pcc->at_top && (!DescType->next ||
		    (mark->is_end && !DescType->next->next)))
			ConditionalLineFeed(hw, 2, pcc);
		pcc->at_top = False;
		if (mark->is_end) {
			/* Restore the old state if there is one */
			if (DescType->next) {
				DescRec *dptr;

				dptr = DescType;
				DescType = DescType->next;
				pcc->left_margin = dptr->save_left_margin;
				pcc->cur_line_width = dptr->save_cur_line_width;
				pcc->indent_level--;
				free((char *)dptr);
			}
		} else {
			DescRec *dptr;

			dptr = (DescRec *)malloc(sizeof(DescRec));
			/* Save the old state, and start a new */
			dptr->compact = 0;
			pcc->indent_level++;
			if (type == M_NUM_LIST) {
				dptr->type = D_OLIST;
				dptr->count = 1;
				dptr->style = '1';
				if (tptr = ParseMarkTag(mark->start,
				    MT_NUM_LIST, "TYPE")) {
					if (*tptr)
						dptr->style = tptr[0];
					free(tptr);
				}
				if (tptr = ParseMarkTag(mark->start,
				    MT_NUM_LIST, "START")) {
					if (*tptr)
						dptr->count = atoi(tptr);
					if (dptr->count < 1)
						dptr->count = 1;
					free(tptr);
				}
			} else {
				dptr->type = D_ULIST;
				dptr->count = 0;
			}
			if (DescType->next) {
				pcc->left_margin = DescType->indent_margin;
				pcc->cur_line_width = DescType->cur_line_width;
			}
			dptr->save_left_margin = pcc->left_margin;
			dptr->indent_margin = pcc->left_margin +
					D_INDENT_SPACES;
			dptr->save_cur_line_width = pcc->cur_line_width;
			dptr->cur_line_width = pcc->cur_line_width -
					D_INDENT_SPACES;
			if (dptr->cur_line_width <= 0) {
				dptr->cur_line_width = pcc->cur_line_width;
			}
			dptr->next = DescType;
			DescType = dptr;
			pcc->left_margin = DescType->indent_margin;
			pcc->cur_line_width = DescType->cur_line_width;
		}
		pcc->x = pcc->eoffsetx + pcc->left_margin;
		pcc->is_bol = 1;
		break;
	/* Place the bullet element at the beginning of this item. */
	case M_LIST_ITEM:
		if (!mark->is_end) {
			ConditionalLineFeed(hw, 1, pcc);
			/* If no type, than no current list, so do a hack */
			if (DescType->type == D_NONE) {
				pcc->x = pcc->eoffsetx + pcc->left_margin +
					pcc->cur_font->max_bounds.width;
			} else {
				pcc->left_margin = DescType->indent_margin;
				pcc->cur_line_width = DescType->cur_line_width;
				pcc->x = pcc->eoffsetx + pcc->left_margin;
			}
			pcc->is_bol = 1;
			/* Ordered lists have numbers instead of bullets. */
			if (DescType->type == D_OLIST) {
				if (tptr = ParseMarkTag(mark->start,
				    MT_LIST_ITEM, "VALUE")) {
					if (*tptr)
						DescType->count = atoi(tptr);
					if (DescType->count < 1)
						DescType->count = 1;
					free(tptr);
				}
				ListNumberPlace(hw, pcc, DescType->count,
					DescType->style);
				DescType->count++;
			} else {
				BulletPlace(hw, *mptr, pcc);
			}	
		}
		break;
	/* Description lists */
	case M_DESC_LIST:
		if (mark->is_end) {
			/* Restore the old state if there is one */
			if (DescType->next) {
				DescRec *dptr;

				dptr = DescType;
				DescType = DescType->next;
				pcc->left_margin = dptr->save_left_margin;
				pcc->cur_line_width = dptr->save_cur_line_width;
				ConditionalLineFeed(hw, 1, pcc);
				ConditionalLineFeed(hw, 2, pcc);
				pcc->indent_level--;
				free((char *)dptr);
			}
		} else {
			DescRec *dptr;

			ConditionalLineFeed(hw, 1, pcc);
			ConditionalLineFeed(hw, 2, pcc);
			pcc->indent_level++;
			dptr = (DescRec *)malloc(sizeof(DescRec));
			/* Check if this is a compact list */
			tptr = ParseMarkTag(mark->start, MT_DESC_LIST, "COMPACT");
			if (tptr) {
				free(tptr);
				dptr->compact = 1;
			} else {
				dptr->compact = 0;
			}
			if (DescType->next) {
				pcc->left_margin = DescType->indent_margin;
				pcc->cur_line_width = DescType->cur_line_width;
			}
			dptr->save_left_margin = pcc->left_margin;
			dptr->indent_margin = dptr->save_left_margin +
					D_INDENT_SPACES;
			dptr->save_cur_line_width = pcc->cur_line_width;
			dptr->cur_line_width = dptr->save_cur_line_width -
					D_INDENT_SPACES;
			if (dptr->cur_line_width <= 0) {
				dptr->cur_line_width = pcc->cur_line_width;
			}
			/* Save the old state, and start a new */
			dptr->type = D_DESC_LIST_START;
			dptr->next = DescType;
			DescType = dptr;
		}
		pcc->x = pcc->eoffsetx + pcc->left_margin;
		pcc->is_bol = 1;
		break;
	case M_DESC_TITLE:
		if (mark->is_end)
			break;
		ConditionalLineFeed(hw, 1, pcc);
		if (DescType->type != D_NONE) {
			pcc->left_margin = DescType->save_left_margin;
			pcc->cur_line_width = DescType->save_cur_line_width;
		}
		pcc->x = pcc->eoffsetx + pcc->left_margin;
		pcc->is_bol = 1;
		break;
	case M_DESC_TEXT:
		if (mark->is_end) {
			if (DescType->type != D_NONE) {
				pcc->left_margin = DescType->save_left_margin;
				pcc->cur_line_width = DescType->save_cur_line_width;
			}
			break;
		}
		/* For a compact list we want to stay on the same
		 * line if there is room and we are the first line
		 * after a title.
		 */
		if (!DescType->compact) {
			ConditionalLineFeed(hw, 1, pcc);
			pcc->have_space_after = 0;
			/* If no type, than no current list, so do a hack */
			if (DescType->type == D_NONE) {
				pcc->x = pcc->eoffsetx + pcc->left_margin +
					D_INDENT_SPACES;
			} else {
				pcc->left_margin = DescType->indent_margin;
				pcc->cur_line_width = DescType->cur_line_width;
				pcc->x = pcc->eoffsetx + pcc->left_margin;
			}
			pcc->is_bol = 1;
			break;
		} 
		if (DescType->type != D_NONE) {
			pcc->left_margin = DescType->indent_margin;
			pcc->cur_line_width = DescType->cur_line_width;
		}
		break;

	/*
	 * Now with forms, <INDEX> is the same as:
	 * <FORM>
	 * <HR>
	 * This is a searchable index.  Enter search keywords:
	 * <INPUT NAME="isindex">
	 * <HR>
	 * </FORM>
	 * Also, <INDEX> will take an ACTION tag to specify a
	 * different URL to submit the query to.
	 */
	case M_INDEX:
		hw->html.is_index = True;
		if (pcc->cur_form)
			break; /* No index inside a form */

		/* Start the form */
		ConditionalLineFeed(hw, 1, pcc);
		ConditionalLineFeed(hw, 2, pcc);
		pcc->cur_form = (FormInfo *)malloc(sizeof(FormInfo));
		pcc->cur_form->next = NULL;
		pcc->cur_form->hw = (Widget)hw;
		pcc->cur_form->action = NULL;
		pcc->cur_form->action = ParseMarkTag(mark->start, MT_INDEX,
					"ACTION");
		pcc->cur_form->method = ParseMarkTag(mark->start, MT_INDEX,
					"METHOD");
		pcc->cur_form->enctype = ParseMarkTag(mark->start, MT_INDEX,
					"ENCTYPE");
		pcc->cur_form->start = pcc->widget_id;
		pcc->cur_form->end = -1;
		pcc->cur_form->cw_only = pcc->cw_only;
		/* Horizontal rule */
		ConditionalLineFeed(hw, 1, pcc);
		HRulePlace(hw, *mptr ,pcc);
		ConditionalLineFeed(hw, 1, pcc);
		/*
		 * Text: "This is a searchable index.
		 *  Enter search keywords: "
		 */
		mark_tmp.text = (char *)malloc(strlen(
			"This is a searchable index.  Enter search keywords: ")
			 + 1);
		strcpy(mark_tmp.text,
			"This is a searchable index.  Enter search keywords: ");
		PartOfTextPlace(hw, &mark_tmp, pcc);
		/* Fake up the text INPUT tag.  */
		mark_tmp.start = (char *)malloc(strlen(
			"input SIZE=25 NAME=\"isindex\"") + 1);
		strcpy(mark_tmp.start, "input SIZE=25 NAME=\"isindex\"");
		WidgetPlace(hw, &mark_tmp, pcc);
		/*  Horizontal rule */
		ConditionalLineFeed(hw, 1, pcc);
		HRulePlace(hw, *mptr, pcc);
		ConditionalLineFeed(hw, 1, pcc);
		/* Close the form  */
		pcc->cur_form->end = pcc->widget_id;
		ConditionalLineFeed(hw, 2, pcc);
		AddNewForm(hw, pcc->cur_form);
		pcc->cur_form = NULL;
		break;

	case M_LINEBREAK:
		/* Force a hard linefeed */
		if (!pcc->cur_line_height) {
			pcc->cur_line_height = pcc->cur_font->ascent +
				pcc->cur_font->descent;
		}
		if (tptr = ParseMarkTag(mark->start, MT_LINEBREAK, "clear")) {
			if (!my_strcasecmp(tptr, "all")) {
				if (pcc->float_right)
					pcc->float_right = -1;
				if (pcc->float_left)
					pcc->float_left = -1;
			}
			if (!my_strcasecmp(tptr, "right") && pcc->float_right)
				pcc->float_right = -1;
			if (!my_strcasecmp(tptr, "left") && pcc->float_left)
				pcc->float_left = -1;
			free(tptr);
		}
		LinefeedPlace(hw, pcc);
		break;
	case M_NOBR:
		if (mark->is_end)
			pcc->nobr = 0;
		else
			pcc->nobr = 1;
			pcc->nobr_x = pcc->x;
		break;
	case M_BUGGY_TABLE:
		break;
	case M_TABLE:
		if (mark->is_end)
			break;
		PushAlign(pcc->div);
		if (tableSupportEnabled) {
			TablePlace(hw, mptr, pcc, save_obj);
		}
		pcc->div = PopAlign();
		break;
	case M_FIGURE:
	case M_IMAGE:
		if (mark->is_end)
			break;
		ImagePlace(hw, *mptr, pcc);
		break;
	case M_APPLET:
		if (mark->is_end)
			return;
		if (appletSupportEnabled) {
			AppletPlace(hw, mptr, pcc, save_obj);
		}
		break;
	case M_APROG:
		if (mark->is_end)
			return;
		AprogPlace(hw, mptr, pcc, save_obj);
		break;
	case M_SMALL:
		if (mark->is_end) {
			pcc->cur_font = PopFont(pcc);
			SetFontSize(hw, pcc, 0);
		} else {
			PushFont(pcc);
			if (pcc->cur_font_size > 2)
				pcc->cur_font_size = 2;
			else
				pcc->cur_font_size = 1;
			SetFontSize(hw, pcc, 0);
		}
		break;
	case M_BIG:
		if (mark->is_end) {
			pcc->cur_font = PopFont(pcc);
			SetFontSize(hw, pcc, 0);
		} else {
			PushFont(pcc);
			if (pcc->cur_font_size < 5)
				pcc->cur_font_size = 5;
			else if (pcc->cur_font_size = 5)
				pcc->cur_font_size = 6;
			else
				pcc->cur_font_size = 7;
			SetFontSize(hw, pcc, 0);
		}
		break;
	case M_FONT:
		if (mark->is_end) {
			pcc->cur_font = PopFont(pcc);
			SetFontSize(hw, pcc, 0);
		} else {
			FontRec *fptr;

			fptr = PushFont(pcc);
			if (hw->html.font_colors) {
				tptr = ParseMarkTag(mark->start, MT_FONT,
						"color");
				if (tptr) {
					hw_do_color(hw, "color", tptr, pcc);
					fptr->color_ch = 1;
					free(tptr);
				}
			}
			if (hw->html.font_sizes) {
				tptr = ParseMarkTag(mark->start, MT_FONT,
						"size");
				if (tptr) {
					int size;

					if (*tptr == '+') {
						size = atoi(tptr + 1);
						pcc->cur_font_size = size +
							pcc->cur_font_base;
						if (pcc->cur_font_size > 7)
							pcc->cur_font_size = 7;
					} else if (*tptr == '-') {
						size = atoi(tptr + 1);
						pcc->cur_font_size =
							pcc->cur_font_base -
							size;
						if (pcc->cur_font_size < 1)
							pcc->cur_font_size = 1;

					} else {
						size = atoi(tptr);
						if ((size > 0) && (size < 8))
							pcc->cur_font_size =
								size;
						else if (size > 7)
							pcc->cur_font_size = 7;
						else if (size < 1)
							pcc->cur_font_size = 1;
					}
					SetFontSize(hw, pcc, 0);
					free(tptr);
				}
				tptr = ParseMarkTag(mark->start, MT_FONT,
						"face");
				if (tptr) {
				    char *face;

				    pcc->cur_font_family = TIMES;
				    face = strtok(tptr, ", ");
				    while (face) {
					if (!my_strncasecmp(face, "Times", 5)) {
						pcc->cur_font_family = TIMES;
						break;
					} else if (!my_strncasecmp(face,
						   "Lucida", 6) ||
						   !my_strncasecmp(face,
						   "LucidaBright", 12)) {
						pcc->cur_font_family = LUCIDA;
						break;
					} else if (!my_strncasecmp(face,
						   "Helvetica", 9)) {
						pcc->cur_font_family = HELVETICA;
						break;
					} else if (!my_strncasecmp(face,
						   "New Century Schoolbook", 22)) {
						pcc->cur_font_family = CENTURY;
						break;
					}
					face = strtok(NULL, ", ");
				    }
				    SetFontSize(hw, pcc, 0);
				    free(tptr);
				}
			}
		}
		break;
	case M_BASEFONT:
		if (!mark->is_end && hw->html.font_sizes) {
			int size;

			tptr = ParseMarkTag(mark->start, MT_BASEFONT, "size");
			if (tptr) {
				size = atoi(tptr);
				if ((size > 0) && (size < 8))
					pcc->cur_font_base = size;
				free(tptr);
			}
		}
		break;

	case M_SCRIPT:
		if (mark->is_end)
			pcc->in_script = False;
		else
			pcc->in_script = True;
		break;
	case M_STYLE:
		if (mark->is_end)
			pcc->in_style = False;
		else
			pcc->in_style = True;
		break;

	case M_BASE:
		if (mark->is_end)
			break;
		tptr = ParseMarkTag(mark->start, MT_BASE, "href");
		if (tptr) {
			XtCallCallbackList((Widget)hw, hw->html.base_callback,
				(XtPointer)tptr);
			free(tptr);
		}
		break;
	case M_META:
		if (mark->is_end)
			break;
		if (tptr = ParseMarkTag(mark->start, MT_META, "http-equiv")) {
			if (!my_strcasecmp(tptr, "refresh")) {
				char *cptr, *sptr, *url;
				int seconds;
				RefreshInfo *rinfo;

				if (cptr = ParseMarkTag(mark->start, MT_META,
				    "content")) {
					if (!(sptr = strtok(cptr, ",; "))) {
						free(cptr);
						free(tptr);
						break;
					}
					seconds = atoi(sptr);
					if (!(sptr = strtok(NULL, " "))) {
						free(cptr);
						free(tptr);
						break;
					}
					if (!(url = ParseMarkTag(sptr, "",
								 "url"))) {
						url = strdup(sptr);
					}
#ifndef DISABLE_TRACE
					if (htmlwTrace || refreshTrace) {
						fprintf(stderr,
						       "Refresh= %d, URL= %s\n",
						       seconds, url);
					}
#endif
					rinfo = (RefreshInfo *)malloc(
						sizeof(RefreshInfo));
					rinfo->refresh = hw->html.refresh_count;
					rinfo->url = url;
					rinfo->hw = hw;
					XtAppAddTimeOut(app_context,
						seconds * 1000,
						(XtTimerCallbackProc)RefreshURL,
						(XtPointer)rinfo);
					free(cptr);
				}
			}
			free(tptr);
		}
		break;

	case M_MAP:			/* Done in HTMLParse */
	case M_AREA:

	case M_HTML:			/* Don't know what to do with */
	case M_COMMENT:
	case M_PARAM:			/* May be seen in APROG/APPLET */

	case M_TABLE_HEADER:		/* Handled in TablePlace */
	case M_TABLE_DATA:
	case M_TABLE_ROW:
	case M_CAPTION:

	case M_NOSCRIPT:		/* Just iqnore for now */
	case M_DOCTYPE:			/* unused */
	case M_LINK:			/* unused */
	case M_UNKNOWN:			/* Already reported error elsewhere */
		break;
	default:
#ifndef DISABLE_TRACE
		if (htmlwTrace || reportBugs) {
			fprintf(stderr,
				"[TriggerMarkChanges] Unknown marker %d\n",
				mark->type);
                        }
#endif
		break;
	}
} /* TriggerMarkChanges() */

/* Format all the objects in the passed Widget's parsed object list to fit
 * the locally global Width.  Passes in the x,y coords of where to start 
 * placing the formatted text.  Returns the ending x,y in same variables.
 * Title objects are ignored, and not formatted.
 * The locally global variables are assumed to have been initialized
 * before this function was called.
 * FormatChunk also builds an internal 'struct ele_rec' that contain
 * information for later X-Window code and placement.  So it needs to
 * be recursive.  For example a table can contain another table.
 */
void FormatChunk( HTMLWidget hw, MarkInfo *start_mark, MarkInfo *end_mark,
	PhotoComposeContext *pcc, Boolean save_obj)
{
	MarkInfo *mptr;

	mptr = start_mark;
	while (mptr) {
		TriggerMarkChanges(hw, &mptr, pcc, save_obj);
		if (mptr == end_mark)
			break;
		if (mptr)
			mptr = mptr->next;
	}
	/* Now clean up any outstanding floating alignment */
	if (pcc->float_left || pcc->float_right) {
		if (pcc->float_right)
			pcc->float_right = -1;
		if (pcc->float_left)
			pcc->float_left = -1;
		LinefeedPlace(hw, pcc);
	}
}

/* GD: added PhotoComposeContext struct */
/*
 * Called by the widget to format all the objects in the
 * parsed object list to fit its current window size.
 * Returns the max_height of the entire document.
 * Title objects are ignored, and not formatted.
 */
int FormatAll(HTMLWidget hw, int *Fwidth, Boolean save_obj)
{
	int saved_width;
	PhotoComposeContext pcc;
	int WidthOfViewablePart;

#ifndef DISABLE_TRACE
	if (htmlwTrace) {
#ifndef VMS
		gettimeofday(&Tv, &Tz);
		fprintf(stderr, "FormatAll enter (%d.%d)\n", Tv.tv_sec,
			Tv.tv_usec);
#else
		time_t clock = time(NULL);
                fprintf(stderr, "FormatAll enter (%s)\n",
			asctime(localtime(&clock)));
#endif
	}
#endif
	/* Stop any current animations */
	hw->html.draw_count++;

	/* Stop any outstanding refresh timers */
	hw->html.refresh_count++;

	saved_width = *Fwidth;
	hw->html.is_index = False;	 /* Clear the is_index flag */

	WidthOfViewablePart = *Fwidth;	/* hw->core.width - swidth - (2 * st) */

	pcc.width_of_viewable_part = WidthOfViewablePart; /* never change */
					/* during computation */
	pcc.right_margin = hw->html.margin_width; /* initial margin */
	pcc.left_margin = hw->html.margin_width;
	pcc.eoffsetx = 0;	/* I am the master element */
	pcc.cur_line_width = WidthOfViewablePart - 
		pcc.right_margin - pcc.left_margin;
	pcc.x = pcc.left_margin;
	pcc.y = hw->html.margin_height;
	pcc.margin_height = hw->html.margin_height;
	pcc.cur_baseline = 0;	/* All objects in a line must have the same */
				/* baseline.  If baseline changes then adjust */
				/* pcc.y, pcc.cur_line_height */
				/* and the y value in each element of line */
	pcc.cur_line_height = 0;
	pcc.element_id = 0;	/* to get unique number */
	pcc.is_bol = True;	/* we are at begin of line */
	pcc.have_space_after = False;	/* Does word have a space after? */
	pcc.cur_font = hw->html.font;
	pcc.cur_font_base = hw->html.font_base;
	pcc.cur_font_size = hw->html.font_base;
	pcc.cur_font_type = FONT;
	pcc.cur_font_family = hw->html.font_family;
	pcc.anchor_tag_ptr = NULL_ANCHOR_PTR;	/* Are we in anchor? */
	pcc.max_width_return = 0;
				/* we compute the MaxWidth of hyper text to */
				/* adjust scrollbar */
				/* initial value is WidthOfViewablePart */
	pcc.pf_lf_state = 2;	/* Linefeed state is at start of line */
	pcc.preformat = 0;
	pcc.div = DIV_ALIGN_NONE;
	pcc.valign = VALIGN_BOTTOM;
	pcc.fg = pcc.cur_font_color = hw->manager.foreground;
	pcc.bg = hw->core.background_pixel;
	pcc.anchor_start = 0;
	pcc.underline_number = 0;
	pcc.in_underlined = 0;
	pcc.dashed_underlines = False;
	pcc.cw_only = False;
	pcc.computed_min_x = 0;
	pcc.computed_max_x = 0;
	pcc.cur_form = NULL;
	pcc.in_form = False;
	pcc.widget_id = 0;
	pcc.aprog_id = 0;
	pcc.applet_id = 0;
        pcc.superscript = 0;
        pcc.subscript = 0;
	pcc.indent_level = 0;
	pcc.text_area_buf = NULL;
	pcc.ignore = 0;
	pcc.current_select = NULL;
	pcc.in_paragraph = False;
	pcc.in_script = False;
	pcc.in_select = False;
	pcc.in_style = False;
	pcc.in_table = False;
	pcc.at_top = True;
	pcc.last_progressive_ele = NULL;
	pcc.in_anchor = 0;
	pcc.float_left = 0;
	pcc.float_right = 0;
	pcc.nobr = 0;
	pcc.blockquote = 0;
	pcc.ignore_float = 0;
	pcc.frameset = 0;
	pcc.noframes = 0;

	header1_align = 0;
	header2_align = 0;
	header3_align = 0;
	header4_align = 0;
	header5_align = 0;
	header6_align = 0;
	header1 = 0;
	header2 = 0;
	header3 = 0;
	header4 = 0;
	header5 = 0;
	header6 = 0;

	/* Initialize local variables, some from the widget */
	Strikeout = False;
	DescType = &BaseDesc;
	DescType->type = D_NONE;
	DescType->count = 0;
	DescType->compact = 0;
	DescType->next = NULL;
	DescType->save_left_margin = pcc.left_margin;
	DescType->indent_margin = pcc.left_margin;
	DescType->save_cur_line_width = pcc.cur_line_width;
	DescType->cur_line_width = pcc.cur_line_width;
	InDocHead = 0;
	InDocBody = 0;
	if (hw->html.title) { /* Free the old title, if there is one. */
		free(hw->html.title);
		hw->html.title = NULL;
	}
	/* Free any partially processed title */
	if (TitleText)
		free(TitleText);
	TitleText = NULL;

	/* Free up previously formatted elements */
	FreeLineList(hw->html.formatted_elements, hw);

	/* Start a null element list, to be filled in as we go. */
	hw->html.cur_elem_to_format = NULL;
        hw->html.last_formatted_elem = NULL;
	hw->html.formatted_elements = NULL;

	/* Clear any previous selections */
	hw->html.select_start = NULL;
	hw->html.select_end = NULL;
	hw->html.new_start = NULL;
	hw->html.new_end = NULL;

	/* Get correct font in html.font and pcc.cur_font */ 
	SetFontSize(hw, &pcc, 0);

	/* Set up font and alignment stacks */
	InitFontStack(hw);
	AlignStack = &AlignBase;
	AlignStack->align = DIV_ALIGN_NONE;

	PushFont(&pcc);
	/*
	 * If we have parsed special header text, fill it in now.
	 */
	if (hw->html.html_header_objects) {
		FormatChunk(hw,	hw->html.html_header_objects,
			NULL, &pcc, save_obj);
		LinefeedPlace(hw, &pcc);
	}

	/* Format the main text */
	FormatChunk(hw, hw->html.html_objects, NULL, &pcc, save_obj);
	pcc.cur_font = PopFont(&pcc);

	/*
	 * If we have parsed special footer text, fill it in now.
	 */
	if (hw->html.html_footer_objects) {
		pcc.preformat = 0;
		pcc.pf_lf_state = 0;
		LinefeedPlace(hw, &pcc);
		FormatChunk(hw, hw->html.html_footer_objects,
			NULL, &pcc, save_obj);
	}

	/* Ensure a linefeed after the final element. */
	LinefeedPlace(hw, &pcc);

	/* Add the bottom margin to the max height. */
	pcc.y = pcc.y + hw->html.margin_height;

	/* If the passed in MaxWidth was wrong, correct it. */
	if (pcc.max_width_return > saved_width)
		*Fwidth = pcc.max_width_return;

	/* If height is too high, tell the widget to use the vbar */
	if (pcc.y > (hw->core.height - HbarHeight(hw))) {
		hw->html.use_vbar = True;
	} else {
		hw->html.use_vbar = False;
	}
	
	return(pcc.y);
}

/*
 * Refresh an element (or table cell contents) into the widget's window
 */
struct ele_rec * RefreshElement(HTMLWidget hw, struct ele_rec *eptr)
{
#ifndef DISABLE_TRACE
    if (refreshTrace) {
	fprintf(stderr, "RefreshElement type %d at ", eptr->type);
	fprintf(stderr, "x = %d, y = %d, ", eptr->x, eptr->y);
	fprintf(stderr, "baseline = %d, sx = %d, sy = %d\n",
		eptr->baseline, hw->html.scroll_x, hw->html.scroll_y);
    }
#endif
    switch (eptr->type) {
	case E_TEXT:
	        TextRefresh(hw, eptr, 0, (eptr->edata_len - 2));
		break;
	case E_BULLET:
		BulletRefresh(hw, eptr);
		break;
	case E_HRULE:
		HRuleRefresh(hw, eptr);
		break;
	case E_CR:
		hw->html.underline_yoffset = -1;
		break;
	case E_LINEFEED:
	        if (!hw->html.bg_image)
			LinefeedRefresh(hw, eptr); 
		break;
	case E_IMAGE:
		ImageRefresh(hw, eptr, NULL);
		break;
	case E_WIDGET:
		WidgetRefresh(hw, eptr);
		break;
	case E_TABLE:
		/* Do table borders */
		TableRefresh(hw, eptr);
		break;
	case E_CELL_TABLE:
		/* Do table cell contents */
		hw->html.underline_yoffset = -1;
		eptr = CellRefresh(hw, eptr);
		break;
	case E_ANCHOR:
		break;
	case E_APROG:
		AprogRefresh(hw, eptr);
		break;
	case E_APPLET:
		AppletRefresh(hw, eptr);
		break;
	default:
#ifndef DISABLE_TRACE
    		if (reportBugs) {
			fprintf(stderr,
				"Unknown Element type %d during refresh\n",
				eptr->type);
		}
#endif
		break;
	}

	return eptr;
}

/*
 * Locate the element (if any) that is at the passed location
 * in the widget.  If there is no corresponding element, return
 * NULL.  If an element is found, return the position of the character
 * you are at in the pos pointer passed.
 */
struct ele_rec * LocateElement( HTMLWidget hw, int x, int y, int *pos)
{
	struct ele_rec *eptr;
	struct ele_rec *rptr;
	int tx1, tx2, ty1, ty2;

	x = x + hw->html.scroll_x;
	y = y + hw->html.scroll_y;


	/* Search element by element, for now we only search
	 * text elements and images.
	 */
	eptr = hw->html.formatted_elements;

	rptr = NULL;
	while (eptr) {
		ty1 = eptr->y;
		ty2 = eptr->y + eptr->height;
		tx1 = eptr->x;
		tx2 = eptr->x + eptr->width;

		switch (eptr->type) {
		case E_TEXT:
			if ((x >= tx1) && (x <= tx2) && (y >= ty1) &&
			    (y <= ty2)) {
				rptr = eptr;
			}
			break;
		case E_IMAGE:
			/* Ignore if a blank image */
			if (eptr->pic_data->fetched ||
			    (eptr->pic_data->internal &&
			     (eptr->pic_data->internal != 3))) {
				if ((x >= tx1) && (x <= tx2) && (y >= ty1) &&
				    (y <= ty2)) {
					rptr = eptr;
				}
			}
			break;
		case E_CR:
		case E_LINEFEED:
			break;
		}

		if (rptr)
			break;
		eptr = eptr->next;
	}

	/*
	 * If we found an element, locate the exact character position within
	 * that element.
	 */
	if (rptr && rptr->type == E_TEXT) {
		int dir, ascent, descent;
		XCharStruct all;
		int epos;

		/*
		 * Start assuming fixed width font.  The real position should
		 * always be <= to this, but just in case, start at the end
		 * of the string if it is not.
		 */
		epos = ((x - rptr->x) / rptr->font->max_bounds.width) + 1;
		if (epos >= rptr->edata_len - 1)
			epos = rptr->edata_len - 2;
		XTextExtents(rptr->font, (char *)rptr->edata,
				(epos + 1), &dir, &ascent, &descent, &all);
		if (x > (int)(rptr->x + all.width)) {
			epos = rptr->edata_len - 3;
		} else {
			epos--;
		}

		while (epos >= 0) {
			XTextExtents(rptr->font, (char *)rptr->edata,
				(epos + 1), &dir, &ascent, &descent, &all);
			if ((int)(rptr->x + all.width) <= x)
				break;
			epos--;
		}
		epos++;
		*pos = epos;
	}
	return(rptr);
}

/*
 * Used by ParseTextToPrettyString to let it be sloppy about its
 * string creation, and never overflow the buffer.
 * It concatenates the passed string to the current string, managing
 * both the current string length, and the total buffer length.
 */
void strcpy_or_grow( char **str, int *slen, int *blen, char *add)
{
	int newlen;
	int addlen;
	char *buf;

	/*
	 * If necessary, initialize this string buffer
	 */
	if (!*str) {
		*str = (char *)malloc(1024 * sizeof(char));
		CHECK_OUT_OF_MEM(*str);
		*blen = 1024;
		strcpy(*str, "");
		*slen = 0;
	}
	buf = *str;
	if (!buf || !add)
		return;
	addlen = strlen(add);
	newlen = *slen + addlen;
	if (newlen >= *blen) {
		newlen = ((newlen / 1024) + 1) * 1024;
		buf = (char *)malloc(newlen * sizeof(char));
		CHECK_OUT_OF_MEM(buf);
		memcpy(buf, *str, *blen);
		free((char *)*str);
		*str = buf;
		*blen = newlen;
	}
	memcpy((char *)(buf + *slen), add, addlen + 1);
	*slen = *slen + addlen;
}

/*
 * Parse all the formatted text elements from start to end
 * into an ascii text string, and return it.
 * space_width and lmargin tell us how many spaces
 * to indent lines.
 */
char * ParseTextToString( struct ele_rec *elist,
	struct ele_rec *startp, struct ele_rec *endp,
	int start_pos, int end_pos,
	int space_width, int lmargin)
{
	int newline;
	int epos;
	char *text;
	int t_slen, t_blen;
	struct ele_rec *eptr;
	struct ele_rec *start;
	struct ele_rec *end;

	if (!startp)
		return(NULL);

	if (SwapElements(startp, endp, start_pos, end_pos)) {
		start = endp;
		end = startp;
		epos = start_pos;
		start_pos = end_pos;
		end_pos = epos;
	} else {
		start = startp;
		end = endp;
	}
	text = NULL;
	newline = 0;
	eptr = start;
	while (eptr && (eptr != end)) {
		if (eptr->type == E_TEXT) {
			int i, spaces;
			char *tptr;

			if (eptr == start) {
				tptr = (char *)(eptr->edata + start_pos);
			} else {
				tptr = (char *)eptr->edata;
			}

			if (newline) {
				spaces = (eptr->x - lmargin) / space_width;
				if (spaces < 0)
					spaces = 0;
				for (i=0; i<spaces; i++) {
					strcpy_or_grow(&text, &t_slen, &t_blen,
						" ");
				}
			}
			strcpy_or_grow(&text, &t_slen, &t_blen, tptr);
			newline = 0;
		}
		else if (eptr->type == E_LINEFEED) {
			strcpy_or_grow(&text, &t_slen, &t_blen, "\n");
			newline = 1;
		}
		eptr = eptr->next;
	}
	if (eptr) {
		if (eptr->type == E_TEXT) {
			int i, spaces;
			char *tptr;
			char *tend, tchar;

			if (eptr == start) {
				tptr = (char *)(eptr->edata + start_pos);
			} else {
				tptr = (char *)eptr->edata;
			}

			if (eptr == end) {
				tend = (char *)(eptr->edata + end_pos + 1);
				tchar = *tend;
				*tend = '\0';
			}

			if (newline) {
				spaces = (eptr->x - lmargin) / space_width;
				if (spaces < 0)
					spaces = 0;
				for (i=0; i<spaces; i++) 
					strcpy_or_grow(&text, &t_slen,
						&t_blen, " ");
			}
			strcpy_or_grow(&text, &t_slen, &t_blen, tptr);
			newline = 0;
			if (eptr == end)
				*tend = tchar;
		} else if (eptr->type == E_LINEFEED) {
			strcpy_or_grow(&text, &t_slen, &t_blen, "\n");
			newline = 1;
		}
	}
	return(text);
}

/*
 * Parse all the formatted text elements from start to end
 * into an ascii text string, and return it.
 * Very like ParseTextToString() except the text is prettied up
 * to show headers and the like.
 * space_width and lmargin tell us how many spaces to indent lines.
 */
char * ParseTextToPrettyString( HTMLWidget hw, struct ele_rec *elist,
	struct ele_rec *startp, struct ele_rec *endp,
	int start_pos, int end_pos,
	int space_width, int lmargin)
{
	int newline;
	int lead_spaces;
	int epos;
	char *text;
	int t_slen, t_blen;
	char *line_buf;
	int l_slen, l_blen;
	char lchar;
	struct ele_rec *eptr;
	struct ele_rec *start;
	struct ele_rec *end;
	struct ele_rec *last;

	if (!startp)
		return(NULL);

	if (SwapElements(startp, endp, start_pos, end_pos)) {
		start = endp;
		end = startp;
		epos = start_pos;
		start_pos = end_pos;
		end_pos = epos;
	} else {
		start = startp;
		end = endp;
	}

	text = NULL;
	line_buf = NULL;

/* We need to know if we should consider the indentation or bullet
 * that might be just before the first selected element to also be
 * selected.  This current hack looks to see if they selected the
 * Whole line, and assumes if they did, they also wanted the beginning.
 *
 * If we are at the beginning of the list, or the beginning of
 * a line, or just behind a bullet, assume this is the start of
 * a line that we may want to include the indent for.
 */
	if ((start_pos == 0) && 
	    ((start->prev == NULL) || (start->prev->type == E_BULLET))) {
		eptr = start;
		while (eptr && (eptr != end) && (eptr->type != E_LINEFEED))
			eptr = eptr->next;
		if (eptr && (eptr->type == E_LINEFEED)) {
			newline = 1;
			if (start->prev && (start->prev->type == E_BULLET))
				start = start->prev;
		} else {
			newline = 0;
		}
	} else {
		newline = 0;
	}
	lead_spaces = 0;
	last = start;
	eptr = start;
	while (eptr && (eptr != end)) {
		if (eptr->type == E_BULLET) {
			int i, spaces;

			if (newline) {
				spaces = (eptr->x - lmargin) / space_width;
				spaces -= 2;
				if (spaces < 0)
					spaces = 0;
				lead_spaces = spaces;
				for (i=0; i<spaces; i++) {
					strcpy_or_grow(&line_buf, &l_slen, 
							&l_blen, " ");
				}
			}
			newline = 0;
			strcpy_or_grow(&line_buf, &l_slen, &l_blen, "o ");
			lead_spaces += 2;
		}
		else if (eptr->type == E_TEXT) {
			int i, spaces;
			char *tptr;

			if (eptr == start) {
				tptr = (char *)(eptr->edata + start_pos);
			} else {
				tptr = (char *)eptr->edata;
			}

			if (newline) {
				spaces = (eptr->x - lmargin) / space_width;
				if (spaces < 0)
					spaces = 0;
				lead_spaces = spaces;
				for (i=0; i<spaces; i++) {
					strcpy_or_grow(&line_buf,
						&l_slen, &l_blen, " ");
				}
			}
			strcpy_or_grow(&line_buf, &l_slen, &l_blen, tptr);
			newline = 0;
		}
		else if (eptr->type == E_LINEFEED) {
			strcpy_or_grow(&text, &t_slen, &t_blen, line_buf);
			strcpy_or_grow(&text, &t_slen, &t_blen, "\n");
			newline = 1;
			lchar = '\0';
			if (eptr->font == hw->html.header1_font) {
				lchar = '*';
			}
			else if (eptr->font == hw->html.header2_font) {
				lchar = '=';
			}
			else if (eptr->font == hw->html.header3_font) {
				lchar = '+';
			}
			else if (eptr->font == hw->html.header4_font) {
				lchar = '-';
			}
			else if (eptr->font == hw->html.header5_font) {
				lchar = '~';
			}
			else if (eptr->font == hw->html.header6_font) {
				lchar = '.';
			}
			if (lchar) {
				char *ptr;
				int cnt;

				cnt = 0;
				ptr = line_buf;
				while (ptr && *ptr) {
					cnt++;
					if (cnt > lead_spaces)
						*ptr = lchar;
					ptr++;
				}
				strcpy_or_grow(&text,&t_slen,&t_blen, line_buf);
				strcpy_or_grow(&text, &t_slen, &t_blen, "\n");
			}
			if (line_buf) {
				free(line_buf);
				line_buf = NULL;
			}
		}
		last = eptr;
		eptr = eptr->next;
	} /* while */
	if (eptr) {
		if (eptr->type == E_BULLET) {
			int i, spaces;

			if (newline) {
				spaces = (eptr->x - lmargin) / space_width;
				spaces -= 2;
				if (spaces < 0)
					spaces = 0;
				lead_spaces = spaces;
				for (i=0; i<spaces; i++) {
					strcpy_or_grow(&line_buf,
						&l_slen, &l_blen, " ");
				}
			}
			newline = 0;

			strcpy_or_grow(&line_buf, &l_slen, &l_blen, "o ");
			lead_spaces += 2;
		}
		else if (eptr->type == E_TEXT) {
			int i, spaces;
			char *tptr;
			char *tend, tchar;

			if (eptr == start) {
				tptr = (char *)(eptr->edata + start_pos);
			} else {
				tptr = (char *)eptr->edata;
			}

			if (eptr == end) {
				tend = (char *)(eptr->edata + end_pos + 1);
				tchar = *tend;
				*tend = '\0';
			}

			if (newline) {
				spaces = (eptr->x - lmargin) / space_width;
				if (spaces < 0)
					spaces = 0;
				lead_spaces = spaces;
				for (i=0; i<spaces; i++) {
					strcpy_or_grow(&line_buf,
						&l_slen, &l_blen, " ");
				}
			}
			strcpy_or_grow(&line_buf, &l_slen, &l_blen, tptr);
			newline = 0;
			if (eptr == end)
				*tend = tchar;
		}
		else if (eptr->type == E_LINEFEED) {
			strcpy_or_grow(&text, &t_slen, &t_blen, line_buf);
			strcpy_or_grow(&text, &t_slen, &t_blen, "\n");
			newline = 1;
			lchar = '\0';
			if (eptr->font == hw->html.header1_font) {
				lchar = '*';
			}
			else if (eptr->font == hw->html.header2_font) {
				lchar = '=';
			}
			else if (eptr->font == hw->html.header3_font) {
				lchar = '+';
			}
			else if (eptr->font == hw->html.header4_font) {
				lchar = '-';
			}
			else if (eptr->font == hw->html.header5_font) {
				lchar = '~';
			}
			else if (eptr->font == hw->html.header6_font) {
				lchar = '.';
			}
			if (lchar) {
				char *ptr;
				int cnt;

				cnt = 0;
				ptr = line_buf;
				while (ptr && *ptr) {
					cnt++;
					if (cnt > lead_spaces) {
						*ptr = lchar;
					}
					ptr++;
				}
				strcpy_or_grow(&text,&t_slen,&t_blen, line_buf);
				strcpy_or_grow(&text, &t_slen, &t_blen, "\n");
			}
			if (line_buf) {
				free(line_buf);
				line_buf = NULL;
			}
		}
		last = eptr;
	}
	if (line_buf) {
		strcpy_or_grow(&text, &t_slen, &t_blen, line_buf);
		lchar = '\0';
		if (last->font == hw->html.header1_font) {
			lchar = '*';
		}
		else if (last->font == hw->html.header2_font) {
			lchar = '=';
		}
		else if (last->font == hw->html.header3_font) {
			lchar = '+';
		}
		else if (last->font == hw->html.header4_font) {
			lchar = '-';
		}
		else if (last->font == hw->html.header5_font) {
			lchar = '~';
		}
		else if (last->font == hw->html.header6_font) {
			lchar = '.';
		}
		if (lchar) {
			char *ptr;
			int cnt;

			cnt = 0;
			ptr = line_buf;
			while (ptr && *ptr) {
				cnt++;
				if (cnt > lead_spaces)
					*ptr = lchar;
				ptr++;
			}
			strcpy_or_grow(&text, &t_slen, &t_blen, "\n");
			strcpy_or_grow(&text, &t_slen, &t_blen, line_buf);
		}
	}
	if (line_buf) {
		free(line_buf);
		line_buf = NULL;
	}
	return(text);
}

/* Find the preferred width of a parsed HTML document
 * Currently unformatted plain text, unformatted listing text, plain files
 * and preformatted text require special width.
 * Preferred width = (width of longest plain text line in document) *
 * 	(width of that text's font)
 */
int DocumentWidth(HTMLWidget hw, MarkInfo *list)
{
	MarkInfo *mptr;
	int plain_text;
	int listing_text;
	int pcnt, lcnt, pwidth, lwidth;
	int width;
	char *ptr;

	width = 0;
	pwidth = 0;
	lwidth = 0;
	plain_text = 0;
	listing_text = 0;
	mptr = list;

	/* Loop through object list looking at the plain, preformatted text */
	while (mptr) {
		/* All text blocks between the starting and ending
		 * plain and pre text markers are plain text blocks.
		 * Manipulate flags so we recognize these blocks.
		 */
		if ((mptr->type == M_PLAIN_TEXT) ||
		    (mptr->type == M_PLAIN_FILE) ||
		    (mptr->type == M_PREFORMAT)) {
			if (mptr->is_end) {
				plain_text--;
				if (plain_text < 0)
					plain_text = 0;
			} else {
				plain_text++;
			}
			pcnt = 0;
			lcnt = 0;
		}
		/*
		 * All text blocks between the starting and ending
		 * listing markers are listing text blocks.
		 */
		else if (mptr->type == M_LISTING_TEXT) {
			if (mptr->is_end) {
				listing_text--;
				if (listing_text < 0)
					listing_text = 0;
			} else {
				listing_text++;
			}
			lcnt = 0;
			pcnt = 0;
		}
		/* If this is a plain text block, add to line length.
		 * Find the Max of all line lengths.
		 */
		else if (plain_text && (mptr->type == M_NONE)) {
			ptr = mptr->text;
			while (ptr && *ptr) {
				ptr = MaxTextWidth(ptr, &pcnt);
				if (pcnt > pwidth)
					pwidth = pcnt;
			}
		}
		/*
		 * If this is a listing text block, add to line length.
		 * Find the Max of all line lengths.
		 */
		else if (listing_text && (mptr->type == M_NONE)) {
			ptr = mptr->text;
			while (ptr && *ptr) {
				ptr = MaxTextWidth(ptr, &lcnt);
				if (lcnt > lwidth)
					lwidth = lcnt;
			}
		}
		mptr = mptr->next;
	} /* while */

	width = pwidth * hw->html.plain_font->max_bounds.width;
	lwidth = lwidth * hw->html.listing_font->max_bounds.width;
	if (lwidth > width)
		width = lwidth;
	return(width);
}
