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

#ifdef TIMING
#include <sys/time.h>
struct timeval Tv;
struct timezone Tz;
#endif

#include <stdio.h>
#include <ctype.h>

#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include "HTMLP.h"


/*
 * I need my own is ispunct function because I need a closing paren
 * immediately after a word to act like punctuation.
 */
#define	MY_ISPUNCT(val)	(ispunct((int)(val)) || ((val) == ')'))

#define INDENT_SPACES	2
#define IMAGE_BORDER	2

#define D_NONE		0
#define D_TITLE		1
#define D_TEXT		2
#define D_OLIST		3
#define D_ULIST		4

#define ALIGN_BOTTOM	0
#define ALIGN_MIDDLE	1
#define ALIGN_TOP	2


extern struct ele_rec *AddEle ();
extern void FreeLineList ();
extern void FreeObjList ();
extern int SwapElements ();
extern struct ele_rec **MakeLineList ();
extern char *ParseMarkTag ();
extern char *MaxTextWidth ();
extern char *IsMapForm ();
extern char *DelayedHRef ();
extern int IsDelayedHRef ();
extern int AnchoredHeight ();
extern struct mark_up *HTMLParse ();
extern struct ref_rec *FindHRef ();
extern struct delay_rec *FindDelayedImage ();
extern ImageInfo *NoImageData ();
extern ImageInfo *DelayedImageData ();
extern Pixmap NoImage ();
extern Pixmap DelayedImage ();
extern Pixmap InfoToImage ();
extern void clean_white_space ();
extern void WidgetRefresh ();
extern WidgetInfo *MakeWidget ();
extern XFontStruct *GetWidgetFont ();
extern void AddNewForm ();
extern void PrepareFormEnd ();
extern char *ComposeCommaList ();
extern void FreeCommaList ();


/*
 * To allow arbitrary nesting of lists
 */
typedef struct dtype_rec
{
  int type;			/* D_NONE, D_TITLE, D_TEXT, D_OLIST, D_ULIST */
  int count;
  int compact;
  struct dtype_rec *next;
}
DescRec;


/*
 * To allow arbitrary nesting of font changes
 */
typedef struct font_rec
{
  XFontStruct *font;
  struct font_rec *next;
}
FontRec;

static DescRec BaseDesc;
static DescRec *DescType;
static DescRec *ListData;
static FontRec FontBase;
static FontRec *FontStack;
static XFontStruct *currentFont;
static XFontStruct *saveFont;
static unsigned long Fg;
static unsigned long Bg;
static int Width;
static int MaxWidth;
static int ElementId;
static int WidgetId;
static int LineNumber;
static int LineHeight;
static int LineBottom;
static int BaseLine;
static int TextIndent;
static int MarginW;
static int Ignore;
static int Preformat;
static int PF_LF_State;		/* Pre-formatted linefeed state.  Hack for bad HTMLs */
static int NeedSpace;
static Boolean Internal;
static Boolean DashedUnderlines;
static Boolean Strikeout;
static int Underlines;
static int CharsInLine;
static int IndentLevel;
static struct ele_rec *Current;
static char *AnchorText;
static char *TitleText;
static char *TextAreaBuf;
static struct mark_up *Last;
static FormInfo *CurrentForm;
static SelectInfo *CurrentSelect;

/*
 * Turned out we were taking WAY too much time mallocing and freeing
 * memory when composing the lines into elements.  So this ineligent
 * method minimizes all that.
 */
#define COMP_LINE_BUF_LEN	1024
static char *CompLine = NULL;
static int CompLineLen = 0;
static char *CompWord = NULL;
static int CompWordLen = 0;

/*
 * Create a formatted element
 */
struct ele_rec *
CreateElement (hw, type, fp, x, y, edata)
HTMLWidget hw;
int type;
XFontStruct *fp;
int x, y;
char *edata;
{
  struct ele_rec *eptr;
  int baseline;

  if (fp != NULL)
  {
    baseline = fp->max_bounds.ascent;
  }
  else
  {
    baseline = LineHeight;
  }

  eptr = (struct ele_rec *)XtMalloc(sizeof(struct ele_rec));
  eptr->type = type;
  eptr->pic_data = NULL;
  eptr->widget_data = NULL;
  eptr->font = fp;
  eptr->alignment = ALIGN_BOTTOM;
  eptr->selected = False;
  eptr->internal = Internal;
  eptr->strikeout = Strikeout;
  eptr->x = x;
  eptr->y = y;
  eptr->y_offset = 0;
  eptr->width = 0;
  eptr->line_number = LineNumber;
  eptr->line_height = LineHeight;
  eptr->fg = Fg;
  eptr->bg = Bg;
  eptr->underline_number = Underlines;
  eptr->dashed_underline = DashedUnderlines;
  eptr->indent_level = IndentLevel;

  switch (type)
  {
  case E_TEXT:
    /*
     * get a unique element id
     */
    ElementId++;
    eptr->ele_id = ElementId;

    eptr->y_offset = 0;

    eptr->edata_len = strlen (edata) + 1;
    eptr->edata = XtNewString (edata);

    /*
     * if this is an anchor, puts its href value into
     * the element.
     */
    if (AnchorText != NULL)
    {
      eptr->anchorHRef = ParseMarkTag (AnchorText, MT_ANCHOR, AT_HREF);
      eptr->anchorName = ParseMarkTag (AnchorText, MT_ANCHOR, AT_NAME);
    }
    else
    {
      eptr->anchorHRef = NULL;
      eptr->anchorName = NULL;
    }
    break;
  case E_BULLET:
    eptr->ele_id = ElementId;

    if (BaseLine == -100)
    {
      BaseLine = baseline;
      if (LineBottom == 0)
      {
	LineBottom = LineHeight - baseline;
      }
      else
      {
	/*
	 * It is possible (with the first item
	 * in a line being a top aligned image)
	 * for LineBottom to have already been
	 * set. It now needs to be
	 * corrected as we set a real
	 * BaseLine
	 */
	if ((LineHeight - baseline) > (LineBottom - baseline))
	{
	  LineBottom = LineHeight - baseline;
	}
	else
	{
	  LineBottom = LineBottom - baseline;
	}
      }
    }
    else if (baseline < BaseLine)
    {
      eptr->y_offset = BaseLine - baseline;
    }

    /*
     * Bullets can't be underlined!
     */
    eptr->underline_number = 0;

    eptr->edata = NULL;
    eptr->edata_len = 0;
    eptr->anchorHRef = NULL;
    eptr->anchorName = NULL;
    break;
  case E_HRULE:
    /*
     * get a unique element id
     */
    ElementId++;
    eptr->ele_id = ElementId;

    if (BaseLine == -100)
    {
      BaseLine = baseline;
      if (LineBottom == 0)
      {
	LineBottom = LineHeight - baseline;
      }
      else
      {
	/*
	 * It is possible (with the first item
	 * in a line being a top aligned image)
	 * for LineBottom to have already been
	 * set. It now needs to be
	 * corrected as we set a real
	 * BaseLine
	 */
	if ((LineHeight - baseline) > (LineBottom - baseline))
	{
	  LineBottom = LineHeight - baseline;
	}
	else
	{
	  LineBottom = LineBottom - baseline;
	}
      }
    }
    else if (baseline < BaseLine)
    {
      eptr->y_offset = BaseLine - baseline;
    }

    /*
     * Rules can't be underlined!
     */
    eptr->underline_number = 0;

    eptr->edata = NULL;
    eptr->edata_len = 0;
    eptr->anchorHRef = NULL;
    eptr->anchorName = NULL;
    break;
  case E_LINEFEED:
    eptr->ele_id = ElementId;

    eptr->y_offset = 0;

    if (BaseLine == -100)
    {
      BaseLine = baseline;
      if (LineBottom == 0)
      {
	LineBottom = LineHeight - baseline;
      }
      else
      {
	/*
	 * It is possible (with the first item
	 * in a line being a top aligned image)
	 * for LineBottom to have already been
	 * set. It now needs to be
	 * corrected as we set a real
	 * BaseLine
	 */
	if ((LineHeight - baseline) > (LineBottom - baseline))
	{
	  LineBottom = LineHeight - baseline;
	}
	else
	{
	  LineBottom = LineBottom - baseline;
	}
      }
    }

    /*
     * Linefeeds have to use the maximum line height.
     * Deal with bad Lucidia descents.
     */
#ifdef NO_EXTRA_FILLS
    eptr->line_height = eptr->font->ascent +
      eptr->font->descent;
#else
    eptr->line_height = LineHeight;
#endif /* NO_EXTRA_FILLS */
    if ((BaseLine + LineBottom) > eptr->line_height)
    {
      eptr->line_height = BaseLine + LineBottom;
    }

    /*
     * Linefeeds can't be underlined!
     */
    eptr->underline_number = 0;

    eptr->edata = NULL;
    eptr->edata_len = 0;
    /*
     * if this linefeed is part of a broken anchor put
     * its href value into the element so we can reconnect
     * it when activated.
     * If it at the beginning of an anchor, don't put
     * the href in, and change the color back.
     */
    if (AnchorText != NULL)
    {
      char *tptr;

      tptr = ParseMarkTag (AnchorText, MT_ANCHOR, AT_HREF);
      if ((Current != NULL) &&
	  ((Current->anchorHRef == NULL) ||
	   (tptr == NULL) ||
	   (strcasecmp (Current->anchorHRef, tptr) != 0)))
      {
	if (tptr) XtFree(tptr);
	eptr->anchorHRef = NULL;
	eptr->anchorName = NULL;
	eptr->fg = hw->html.foreground;
      }
      else
      {
	eptr->anchorHRef = tptr;
	eptr->anchorName = ParseMarkTag (AnchorText, MT_ANCHOR, AT_NAME);
      }
    }
    else
    {
      eptr->anchorHRef = NULL;
      eptr->anchorName = NULL;
    }
    break;
  case E_IMAGE:
    /*
     * get a unique element id
     */
    ElementId++;
    eptr->ele_id = ElementId;

    /*
     * Images can't be underlined!
     */
    eptr->underline_number = 0;

    if (edata != NULL)
    {
      eptr->edata_len = strlen (edata) + 1;
      eptr->edata = XtNewString (edata);
    }
    else
    {
      eptr->edata_len = 0;
      eptr->edata = NULL;
    }

    /*
     * if this image is part of an anchor put
     * its href and name values into the element
     * so we can reconnect it when activated.
     */
    if (AnchorText != NULL)
    {
      eptr->anchorHRef = ParseMarkTag (AnchorText,
				       MT_ANCHOR, AT_HREF);
      eptr->anchorName = ParseMarkTag (AnchorText,
				       MT_ANCHOR, AT_NAME);
    }
    else
    {
      eptr->anchorHRef = NULL;
      eptr->anchorName = NULL;
    }

    /*
     * Picture stuff
     */
    /*
     * if we have an image resolver, use it.
     */
    if (hw->html.resolveImage != NULL)
    {
      int internal;

      /*
       * See if this is a special internal image
       */
      if ((edata != NULL) &&
	  (strncasecmp (edata, INTERNAL_IMAGE,
			strlen (INTERNAL_IMAGE)) == 0))
      {
	internal = 1;
      }
      else
      {
	internal = 0;
      }

      /*
       * if we delay image fetching
       * internal images are not delayed.
       */
      if ((hw->html.delay_images == True) &&
	  (!internal))
      {
	/*
	 * see if already cached.
	 */
	eptr->pic_data = (*(resolveImageProc)
			  (hw->html.resolveImage)) (hw, edata, 1);
	if (eptr->pic_data != NULL)
	{
	  eptr->pic_data->delayed = 0;
	  /*
	   * Mark images we have sucessfully
	   * loaded at least once
	   */
	  if (eptr->pic_data->image_data != NULL)
	  {
	    eptr->pic_data->fetched = 1;
	  }
	}
	/*
	 * else, not cached.
	 */
	else
	{
	  /*
	   * just image
	   */
	  if (eptr->anchorHRef == NULL)
	  {
	    eptr->pic_data = DelayedImageData (hw, False);
	    eptr->pic_data->delayed = 1;
	    eptr->anchorHRef = DelayedHRef (hw);
	    eptr->fg = hw->html.anchor_fg;
	  }
	  /*
	   * else anchor and image
	   */
	  else
	  {
	    eptr->pic_data = DelayedImageData (hw, True);
	    eptr->pic_data->delayed = 1;
	  }
	}
      }
      else
      {
	eptr->pic_data = (*(resolveImageProc)
			  (hw->html.resolveImage)) (hw, edata, 0);
	if (eptr->pic_data != NULL)
	{
	  eptr->pic_data->delayed = 0;
	  /*
	   * Mark images we have sucessfully
	   * loaded at least once
	   */
	  if (eptr->pic_data->image_data != NULL)
	  {
	    eptr->pic_data->fetched = 1;
	  }
	}
      }
      if (eptr->pic_data != NULL)
      {
	eptr->pic_data->internal = internal;
      }
    }
    if (eptr->pic_data == NULL)
    {
      eptr->pic_data = NoImageData (hw);
      eptr->pic_data->delayed = 0;
      eptr->pic_data->internal = 0;
    }

    break;
  case E_WIDGET:
    /*
     * get a unique element id
     */
    WidgetId++;
    ElementId++;
    eptr->ele_id = ElementId;

    /*
     * Widgets can't be underlined!
     */
    eptr->underline_number = 0;

    eptr->edata = NULL;
    eptr->edata_len = 0;

    /*
     * if this widget is part of an anchor put
     * its href and name values into the element
     * so we can reconnect it when activated.
     */
    if (AnchorText != NULL)
    {
      eptr->anchorHRef = ParseMarkTag (AnchorText,
				       MT_ANCHOR, AT_HREF);
      eptr->anchorName = ParseMarkTag (AnchorText,
				       MT_ANCHOR, AT_NAME);
    }
    else
    {
      eptr->anchorHRef = NULL;
      eptr->anchorName = NULL;
    }

    /*
     * Widget stuff
     */
    eptr->widget_data = MakeWidget (hw, edata,
				    (x + IMAGE_BORDER), (y + IMAGE_BORDER),
				    WidgetId, CurrentForm);

    /*
     * I have no idea what to do if we can't create the
     * widget.  It probably means we are so messed up we
     * will soon be crashing.
     */
    if (eptr->widget_data == NULL)
    {
    }

    break;
  default:
    fprintf (stderr, "CreateElement:  Unknown type %d\n", type);
    eptr->ele_id = ElementId;

    eptr->edata = NULL;
    eptr->edata_len = 0;
    eptr->anchorHRef = NULL;
    eptr->anchorName = NULL;
    break;
  }
  return (eptr);
}


/*
 * Set the formatted element into the format list.  Use a pre-allocated
 * list position if possible, otherwise allocate a new list position.
 */
void
SetElement (hw, type, fp, x, y, edata)
HTMLWidget hw;
int type;
XFontStruct *fp;
int x, y;
char *edata;
{
  struct ele_rec *eptr;
  int len;
  int baseline;

  if (fp != NULL) baseline = fp->max_bounds.ascent;
  else baseline = LineHeight;

  /*
   * 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 == NULL) ||
      ((Current != NULL) && (Current->next == NULL)))
  {
    eptr = CreateElement (hw, type, fp, x, y, edata);
    Current = AddEle (&(hw->html.formatted_elements), Current, eptr);
    return;
  }

  /*
   * If current is null, but we have a pre-allocated format list, then
   * this is the first SetElement() call for this formated text, and
   * we must set current to the head of the formatted list.  Otherwise
   * we move current to the next pre-allocated list position.
   */
  if (Current == NULL) Current = hw->html.formatted_elements;
  else Current = Current->next;

  eptr = Current;
  if (eptr == NULL)
  {
    fprintf (stderr, "SetElement: Error, setting a null element\n");
    exit (1);
  }

  eptr->type = type;
  eptr->pic_data = NULL;
  eptr->widget_data = NULL;
  eptr->font = fp;
  eptr->alignment = ALIGN_BOTTOM;
  eptr->selected = False;
  eptr->internal = Internal;
  eptr->strikeout = Strikeout;
  eptr->x = x;
  eptr->y = y;
  eptr->y_offset = 0;
  eptr->width = 0;
  eptr->line_number = LineNumber;
  eptr->line_height = LineHeight;
  eptr->fg = Fg;
  eptr->bg = Bg;
  eptr->underline_number = Underlines;
  eptr->dashed_underline = DashedUnderlines;
  eptr->indent_level = IndentLevel;

  switch (type)
  {
  case E_TEXT:
    /*
     * get a unique element id
     */
    ElementId++;
    eptr->ele_id = ElementId;

    eptr->y_offset = 0;

    len = strlen (edata) + 1;
    if (len > eptr->edata_len)
    {
      if (eptr->edata != NULL) XtFree((char *) eptr->edata);
      eptr->edata = (char *)XtMalloc(len);
    }
    eptr->edata_len = len;
    strcpy (eptr->edata, edata);

    /*
     * if this is an anchor, puts its href and name
     * values into the element.
     */
    if (eptr->anchorHRef != NULL)
    {
      XtFree((char *) eptr->anchorHRef);
      eptr->anchorHRef = NULL;
    }
    if (eptr->anchorName != NULL)
    {
      XtFree((char *) eptr->anchorName);
      eptr->anchorName = NULL;
    }
    if (AnchorText != NULL)
    {
      eptr->anchorHRef = ParseMarkTag (AnchorText, MT_ANCHOR, AT_HREF);
      eptr->anchorName = ParseMarkTag (AnchorText, MT_ANCHOR, AT_NAME);
    }
    break;
  case E_BULLET:
    eptr->ele_id = ElementId;

    if (BaseLine == -100)
    {
      BaseLine = baseline;
      if (LineBottom == 0)
      {
	LineBottom = LineHeight - baseline;
      }
      else
      {
	/*
	 * It is possible (with the first item
	 * in a line being a top aligned image)
	 * for LineBottom to have already been
	 * set. It now needs to be
	 * corrected as we set a real
	 * BaseLine
	 */
	if ((LineHeight - baseline) > (LineBottom - baseline))
	{
	  LineBottom = LineHeight - baseline;
	}
	else
	{
	  LineBottom = LineBottom - baseline;
	}
      }
    }
    else if (baseline < BaseLine)
    {
      eptr->y_offset = BaseLine - baseline;
    }

    /*
     * Bullets can't be underlined!
     */
    eptr->underline_number = 0;
    eptr->edata_len = 0;

    if (eptr->edata != NULL)
    {
      XtFree((char *) eptr->edata);
      eptr->edata = NULL;
    }
    if (eptr->anchorHRef != NULL)
    {
      XtFree((char *) eptr->anchorHRef);
      eptr->anchorHRef = NULL;
    }
    if (eptr->anchorName != NULL)
    {
      XtFree((char *) eptr->anchorName);
      eptr->anchorName = NULL;
    }
    break;
  case E_HRULE:
    /*
     * get a unique element id
     */
    ElementId++;
    eptr->ele_id = ElementId;

    if (BaseLine == -100)
    {
      BaseLine = baseline;
      if (LineBottom == 0)
      {
	LineBottom = LineHeight - baseline;
      }
      else
      {
	/*
	 * It is possible (with the first item
	 * in a line being a top aligned image)
	 * for LineBottom to have already been
	 * set. It now needs to be
	 * corrected as we set a real
	 * BaseLine
	 */
	if ((LineHeight - baseline) > (LineBottom - baseline))
	{
	  LineBottom = LineHeight - baseline;
	}
	else
	{
	  LineBottom = LineBottom - baseline;
	}
      }
    }
    else if (baseline < BaseLine)
    {
      eptr->y_offset = BaseLine - baseline;
    }

    /*
     * Rules can't be underlined!
     */
    eptr->underline_number = 0;
    eptr->edata_len = 0;

    if (eptr->edata != NULL)
    {
      XtFree((char *) eptr->edata);
      eptr->edata = NULL;
    }
    if (eptr->anchorHRef != NULL)
    {
      XtFree((char *) eptr->anchorHRef);
      eptr->anchorHRef = NULL;
    }
    if (eptr->anchorName != NULL)
    {
      XtFree((char *) eptr->anchorName);
      eptr->anchorName = NULL;
    }
    break;
  case E_LINEFEED:
    eptr->ele_id = ElementId;

    eptr->y_offset = 0;

    if (BaseLine == -100)
    {
      BaseLine = baseline;
      if (LineBottom == 0)
      {
	LineBottom = LineHeight - baseline;
      }
      else
      {
	/*
	 * It is possible (with the first item
	 * in a line being a top aligned image)
	 * for LineBottom to have already been
	 * set. It now needs to be
	 * corrected as we set a real
	 * BaseLine
	 */
	if ((LineHeight - baseline) > (LineBottom - baseline))
	{
	  LineBottom = LineHeight - baseline;
	}
	else
	{
	  LineBottom = LineBottom - baseline;
	}
      }
    }

    /*
     * Linefeeds have to use the maximum line height.
     * Deal with bad Lucidia descents.
     */
#ifdef NO_EXTRA_FILLS
    eptr->line_height = eptr->font->ascent + eptr->font->descent;
#else
    eptr->line_height = LineHeight;
#endif /* NO_EXTRA_FILLS */
    if ((BaseLine + LineBottom) > eptr->line_height)
    {
      eptr->line_height = (BaseLine + LineBottom);
    }

    /*
     * Linefeeds can't be underlined!
     */
    eptr->underline_number = 0;
    eptr->edata_len = 0;

    if (eptr->edata != NULL)
    {
      XtFree((char *) eptr->edata);
      eptr->edata = NULL;
    }
    /*
     * if this linefeed is part of a broken anchor put
     * its href and name values into the element
     * so we can reconnect it when activated.
     * If it at the beginning of an anchor, don't put
     * the href in and change the color back.
     */
    if (eptr->anchorHRef != NULL)
    {
      XtFree((char *) eptr->anchorHRef);
      eptr->anchorHRef = NULL;
    }
    if (eptr->anchorName != NULL)
    {
      XtFree((char *) eptr->anchorName);
      eptr->anchorName = NULL;
    }
    if (AnchorText != NULL)
    {
      char *tptr;

      tptr = ParseMarkTag (AnchorText, MT_ANCHOR, AT_HREF);
      if ((eptr->prev != NULL) &&
	  ((eptr->prev->anchorHRef == NULL) ||
	   (tptr == NULL) ||
	   (strcasecmp (eptr->prev->anchorHRef, tptr) != 0)))
      {
	if (tptr) XtFree(tptr);
	eptr->anchorHRef = NULL;
	eptr->anchorName = NULL;
	eptr->fg = hw->html.foreground;
      }
      else
      {
	eptr->anchorHRef = tptr;
	eptr->anchorName = ParseMarkTag (AnchorText, MT_ANCHOR, AT_NAME);
      }
    }
    break;
  case E_IMAGE:
    /*
     * get a unique element id
     */
    ElementId++;
    eptr->ele_id = ElementId;

    /*
     * Images can't be underlined!
     */
    eptr->underline_number = 0;

    if (edata != NULL)
    {
      len = strlen (edata) + 1;
      if (len > eptr->edata_len)
      {
	if (eptr->edata != NULL) XtFree((char *) eptr->edata);
	eptr->edata = (char *)XtMalloc(len);
      }
      eptr->edata_len = len;
      strcpy (eptr->edata, edata);
    }
    else
    {
      eptr->edata_len = 0;
      if (eptr->edata != NULL)
      {
	XtFree((char *) eptr->edata);
	eptr->edata = NULL;
      }
    }

    /*
     * if this image is part of an anchor put
     * its href and name values into the element
     * so we can reconnect it when activated.
     */
    if (eptr->anchorHRef != NULL)
    {
      XtFree((char *) eptr->anchorHRef);
      eptr->anchorHRef = NULL;
    }
    if (eptr->anchorName != NULL)
    {
      XtFree((char *) eptr->anchorName);
      eptr->anchorName = NULL;
    }
    if (AnchorText != NULL)
    {
      eptr->anchorHRef = ParseMarkTag (AnchorText, MT_ANCHOR, AT_HREF);
      eptr->anchorName = ParseMarkTag (AnchorText, MT_ANCHOR, AT_NAME);
    }

    /*
     * Picture stuff
     */
    /*
     * if we have an image resolver, use it.
     */
    if (hw->html.resolveImage != NULL)
    {
      int internal;

      /*
       * See if this is a special internal image
       */
      if ((edata != NULL) &&
	  (strncasecmp (edata, INTERNAL_IMAGE,
			strlen (INTERNAL_IMAGE)) == 0))
      {
	internal = 1;
      }
      else
      {
	internal = 0;
      }

      /*
       * if we delay image fetching
       * internal images are not delayed.
       */
      if ((hw->html.delay_images == True) &&
	  (!internal))
      {
	/*
	 * see if already cached.
	 */
	eptr->pic_data = (*(resolveImageProc)
			  (hw->html.resolveImage)) (hw, edata, 1);
	if (eptr->pic_data != NULL)
	{
	  eptr->pic_data->delayed = 0;
	  /*
	   * Mark images we have sucessfully
	   * loaded at least once
	   */
	  if (eptr->pic_data->image_data != NULL)
	  {
	    eptr->pic_data->fetched = 1;
	  }
	}
	/*
	 * else, not cached.
	 */
	else
	{
	  /*
	   * just image
	   */
	  if (eptr->anchorHRef == NULL)
	  {
	    eptr->pic_data = DelayedImageData (hw, False);
	    eptr->pic_data->delayed = 1;
	    eptr->anchorHRef = DelayedHRef (hw);
	    eptr->fg = hw->html.anchor_fg;
	  }
	  /*
	   * else anchor and image
	   */
	  else
	  {
	    eptr->pic_data = DelayedImageData (hw, True);
	    eptr->pic_data->delayed = 1;
	  }
	}
      }
      else
      {
	eptr->pic_data = (*(resolveImageProc)
			  (hw->html.resolveImage)) (hw, edata, 0);
	if (eptr->pic_data != NULL)
	{
	  eptr->pic_data->delayed = 0;
	  /*
	   * Mark images we have sucessfully
	   * loaded at least once
	   */
	  if (eptr->pic_data->image_data != NULL)
	  {
	    eptr->pic_data->fetched = 1;
	  }
	}
      }
      if (eptr->pic_data != NULL)
      {
	eptr->pic_data->internal = internal;
      }
    }
    if (eptr->pic_data == NULL)
    {
      eptr->pic_data = NoImageData (hw);
      eptr->pic_data->delayed = 0;
      eptr->pic_data->internal = 0;
    }

    break;
  case E_WIDGET:
    /*
     * get a unique element id
     */
    WidgetId++;
    ElementId++;
    eptr->ele_id = ElementId;

    /*
     * Widgets can't be underlined!
     */
    eptr->underline_number = 0;
    eptr->edata_len = 0;

    if (eptr->edata != NULL)
    {
      XtFree((char *) eptr->edata);
      eptr->edata = NULL;
    }

    /*
     * if this widget is part of an anchor put
     * its href and name values into the element
     * so we can reconnect it when activated.
     */
    if (eptr->anchorHRef != NULL)
    {
      XtFree((char *) eptr->anchorHRef);
      eptr->anchorHRef = NULL;
    }
    if (eptr->anchorName != NULL)
    {
      XtFree((char *) eptr->anchorName);
      eptr->anchorName = NULL;
    }
    if (AnchorText != NULL)
    {
      eptr->anchorHRef = ParseMarkTag (AnchorText, MT_ANCHOR, AT_HREF);
      eptr->anchorName = ParseMarkTag (AnchorText, MT_ANCHOR, AT_NAME);
    }

    /*
     * Widget stuff
     */
    eptr->widget_data = MakeWidget (hw, edata,
				    (x + IMAGE_BORDER), (y + IMAGE_BORDER),
				    WidgetId, CurrentForm);

    /*
     * I have no idea what to do if we can't create the
     * widget.  It probably means we are so messed up we
     * will soon be crashing.
     */
    if (eptr->widget_data == NULL)
    {
      fprintf (stderr, "SetElement: Could not make widget.\n");
    }

    break;
  default:
    fprintf (stderr, "SetElement:  Unknown type %d\n", type);
    eptr->ele_id = ElementId;

    eptr->edata_len = 0;
    if (eptr->edata != NULL)
    {
      XtFree((char *) eptr->edata);
      eptr->edata = NULL;
    }
    if (eptr->anchorHRef != NULL)
    {
      XtFree((char *) eptr->anchorHRef);
      eptr->anchorHRef = NULL;
    }
    if (eptr->anchorName != NULL)
    {
      XtFree((char *) eptr->anchorName);
      eptr->anchorName = NULL;
    }
    break;
  }
}


/*
 * Change our drawing font 
 */
void
NewFont (fp)
XFontStruct *fp;
{
  /*
   * Deal with bad Lucidia descents.
   */
  if (fp->descent > fp->max_bounds.descent)
  {
    LineHeight = fp->max_bounds.ascent + fp->descent;
  }
  else
  {
    LineHeight = fp->max_bounds.ascent + fp->max_bounds.descent;
  }
}


/*
 * Place a linefeed at the end of a line.
 * Create and add the element record for it.
 */
void
LinefeedPlace (hw, x, y)
HTMLWidget hw;
int *x, *y;
{
  /*
   * At the end of every line check if we have a new MaxWidth
   */
  if ((*x + hw->html.margin_width) > MaxWidth)
  {
    MaxWidth = *x + hw->html.margin_width;
  }

  SetElement (hw, E_LINEFEED, currentFont, *x, *y, (char *) NULL);
}


/*
 * We have encountered a line break.  Incrment the line counter,
 * and move down some space.
 */
void
LineFeed (hw, x, y)
HTMLWidget hw;
int *x, *y;
{
  /*
   * Manipulate linefeed state for special pre-formatted linefeed
   * hack for broken HTMLs
   */
  if (Preformat)
  {
    switch (PF_LF_State)
    {
      /*
       * First soft linefeed
       */
    case 0:
      PF_LF_State = 1;
      break;
      /*
       * Collapse multiple soft linefeeds within a pre
       */
    case 1:
      return;
      break;
      /*
       * Ignore soft linefeeds after hard linefeeds
       * within a pre
       */
    case 2:
      return;
      break;
    default:
      PF_LF_State = 1;
      break;
    }
  }
  /*
   * No blank lines allowed at the start of a document.
   */
  else if (ElementId == 0)
  {
    return;
  }
  /*
   * For formatted documents there are 3 linefeed states.
   * 0 = in the middle of a line.
   * 1 = at left margin
   * 2 = at left margin with blank line above
   */
  else
  {
    PF_LF_State++;
    if (PF_LF_State > 2)
    {
      PF_LF_State = 2;
    }
  }

  /*
   * sanity check to set some line height if none was specified.
   */
  if (BaseLine <= 0)
  {
    BaseLine = LineHeight;
  }

  LinefeedPlace (hw, x, y);

  CharsInLine = 0;
  *x = TextIndent;
  *y = *y + BaseLine + LineBottom;

  LineBottom = 0;
  BaseLine = -100;

  NeedSpace = 0;
  LineNumber++;
}


/*
 * We want to make sure that future text starts at the left margin.
 * But if we are already there, don't put in a new line.
 */
void
ConditionalLineFeed (hw, x, y, state)
HTMLWidget hw;
int *x, *y;
int state;
{
  if (PF_LF_State < state)
  {
    /*
     * If this funtion is being used to insert a blank line,
     * we need to look at the percentVerticalSpace resource
     * to see how high to make the line.
     */
    if ((state == 2) && (hw->html.percent_vert_space > 0))
    {
      int l_height;

      l_height = LineHeight;
      LineHeight = LineHeight *
	hw->html.percent_vert_space / 100;
      LineFeed (hw, x, y);
      LineHeight = l_height;
    }
    else
    {
      LineFeed (hw, x, y);
    }
  }
}


/*
 * hack to make broken HTMLs within pre-formatted text have nice
 * looking linefeeds.
 */
void
HardLineFeed (hw, x, y)
HTMLWidget hw;
int *x, *y;
{
  /*
   * Manipulate linefeed state for special pre-formatted linefeed
   * hack for broken HTMLs
   */
  if (Preformat)
  {
    switch (PF_LF_State)
    {
      /*
       * First hard linefeed
       */
    case 0:
      PF_LF_State = 2;
      break;
      /*
       * Previous soft linefeed should have been ignored, so
       * ignore this hard linefeed, but set state like it
       * was not ignored.
       */
    case 1:
      PF_LF_State = 2;
      return;
      break;
      /*
       * Honor multiple hard linefeeds.
       */
    case 2:
      break;
    default:
      PF_LF_State = 2;
      break;
    }
  }

  /*
   * sanity check to set some line height if none was specified.
   */
  if (BaseLine <= 0)
  {
    BaseLine = LineHeight;
  }

  LinefeedPlace (hw, x, y);

  CharsInLine = 0;
  *x = TextIndent;
  *y = *y + BaseLine + LineBottom;

  LineBottom = 0;
  BaseLine = -100;

  NeedSpace = 0;
  LineNumber++;
}


static void
AdjustBaseLine ()
{
  int baseline;

  baseline = Current->font->max_bounds.ascent;

  if (BaseLine == -100)
  {
    BaseLine = baseline;
    Current->y_offset = 0;
    if (LineBottom == 0)
    {
      LineBottom = LineHeight - baseline;
    }
    else
    {
      /*
       * It is possible (with the first item
       * in a line being a top aligned image)
       * for LineBottom to have already been
       * set. It now needs to be
       * corrected as we set a real
       * BaseLine
       */
      if ((LineHeight - baseline) > (LineBottom - baseline))
      {
	LineBottom = LineHeight - baseline;
      }
      else
      {
	LineBottom = LineBottom - baseline;
      }
    }
  }
  else if (baseline <= BaseLine)
  {
    if (baseline < BaseLine)
    {
      Current->y_offset = BaseLine - baseline;
    }
    else
    {
      Current->y_offset = 0;
    }

    if ((LineHeight - baseline) > LineBottom)
    {
      LineBottom = LineHeight - baseline;
    }
  }
  else
  {
    struct ele_rec *eptr;
    int line, incy;

    incy = baseline - BaseLine;
    BaseLine = baseline;

    /*
     * Go back over this line
     * and move everything down
     * a little.
     */
    eptr = Current;
    line = eptr->line_number;
    while ((eptr->prev != NULL) &&
	   (eptr->prev->line_number == line))
    {
      eptr = eptr->prev;
      eptr->y_offset = eptr->y_offset + incy;
    }

    if ((LineHeight - baseline) > LineBottom)
    {
      LineBottom = LineHeight - baseline;
    }
  }
}


/*
 * Place the bullet at the beginning of an unnumbered
 * list item. Create and add the element record for it.
 */
void
BulletPlace (hw, x, y)
HTMLWidget hw;
int *x, *y;
{
  int width, l_height;

  /*
   * Save the font's line height, and set your own for this
   * element.  Restore the fonts height when done.
   * Deal with bad Lucidia descents.
   */
  l_height = LineHeight;
  if (hw->html.font->descent > hw->html.font->max_bounds.descent)
  {
    LineHeight = hw->html.font->max_bounds.ascent + hw->html.font->descent;
  }
  else
  {
    LineHeight = hw->html.font->max_bounds.ascent +
      hw->html.font->max_bounds.descent;
  }

  NeedSpace = 0;
  width = hw->html.font->max_bounds.width;
  SetElement (hw, E_BULLET, hw->html.font, *x, *y, (char *) NULL);
  LineHeight = l_height;
  /*
   * This should reall be here, but it is a hack for headers on list
   * elements to work if we leave it out
   PF_LF_State = 0;
   */
}


/*
 * Place a horizontal rule across the page.
 * Create and add the element record for it.
 */
void
HRulePlace (hw, x, y, width)
HTMLWidget hw;
int *x, *y;
unsigned int width;
{
  NeedSpace = 0;
  *x = hw->html.margin_width;
  SetElement (hw, E_HRULE, currentFont, *x, *y, (char *) NULL);
  *x = *x + width - (2 * hw->html.margin_width);
  NeedSpace = 1;
  PF_LF_State = 0;
}


/*
 * Place the number at the beginning of an numbered
 * list item. Create and add the element record for it.
 */
void
ListNumberPlace (hw, x, y, val)
HTMLWidget hw;
int *x, *y;
int val;
{
  int width, my_x;
  int dir, ascent, descent;
  XCharStruct all;
  char buf[20];

  sprintf (buf, "%d.", val);

  width = hw->html.font->max_bounds.lbearing +
      hw->html.font->max_bounds.rbearing;
  XTextExtents (currentFont, buf, strlen (buf), &dir, &ascent, &descent, &all);
  my_x = *x - (width / 2) - all.width;

  /*
   * Add a space after thenumber here so it will look right when
   * cut and pasted from a selection.
   */
  width = strlen (buf);
  buf[width] = ' ';
  buf[width + 1] = '\0';

  SetElement (hw, E_TEXT, currentFont, my_x, *y, buf);
  AdjustBaseLine ();
  CharsInLine = CharsInLine + strlen (buf);

  NeedSpace = 0;
  /*
   * This should reall be here, but it is a hack for headers on list
   * elements to work if we leave it out
   PF_LF_State = 0;
   */
}


/*
 * Place a piece of pre-formatted text. Add an element record for it.
 */
void
PreformatPlace (hw, mptr, x, y, width)
HTMLWidget hw;
struct mark_up *mptr;
int *x, *y;
unsigned int width;
{
  char *text;
  char *start;
  char *end;
  char *ptr;
  char tchar;
  int tab_count, char_cnt;
  int dir, ascent, descent;
  XCharStruct all;
  char *line;
  int line_x;

  text = mptr->text;

  line_x = *x;
  line = CompLine;
  if (line != NULL)
  {
    line[0] = '\0';
  }
  end = text;
  while (*end != '\0')
  {
    tab_count = 0;
    char_cnt = CharsInLine;
    /*
     * make start and end point to one word.  A word is either
     * a lone linefeed, or all whitespace before a word, plus
     * the text of the word itself.
     */
    start = end;
    /*
     * Throw out carriage returns and form-feeds
     */
    if ((*end == '\r') || (*end == '\f'))
    {
      start++;
      end++;
    }
    else if (*end == '\n')
    {
      end++;
      char_cnt++;
    }
    else
    {
      /*
       * Should be only spaces and tabs here, so if it
       * is not a tab, make it a space.
       * Break on linefeeds, they must be done separately
       */
      while (((int) ((unsigned char) *end) < 128) && (isspace (*end)))
      {
	if (*end == '\n')
	{
	  break;
	}
	else if (*end == '\t')
	{
	  tab_count++;
	  char_cnt = ((char_cnt / 8) + 1) * 8;
	}
	else
	{
	  *end = ' ';
	  char_cnt++;
	}
	end++;
      }
      while (((int) ((unsigned char) *end) > 127) ||
	     ((!isspace (*end)) && (*end != '\0')))
      {
	end++;
	char_cnt++;
      }
    }

    /*
     * Add the word to the end of this line, or insert
     * a linefeed if the word is a lone linefeed.
     * tabs expand to 8 spaces.
     */
    if (start != end)
    {
      int tlen;

      tchar = *end;
      *end = '\0';

      tlen = char_cnt + 1;
      if (tlen > CompWordLen)
      {
	CompWordLen += COMP_LINE_BUF_LEN;
	if (tlen > CompWordLen) CompWordLen = tlen;
	if (CompWord != NULL) XtFree(CompWord);
	CompWord = (char *)XtMalloc(CompWordLen);
      }
      ptr = CompWord;

      /*
       * If we have any tabs, expand them into spaces.
       */
      if (tab_count)
      {
	char *p1, *p2;
	int i, new;

	char_cnt = CharsInLine;
	p1 = ptr;
	p2 = start;
	while (*p2 != '\0')
	{
	  if (*p2 == '\t')
	  {
	    new = ((char_cnt / 8) + 1) * 8;
	    for (i = 0; i < (new - char_cnt); i++)
	    {
	      *p1++ = ' ';
	    }
	    p2++;
	    char_cnt = new;
	  }
	  else
	  {
	    *p1++ = *p2++;
	    char_cnt++;
	  }
	}
	*p1 = '\0';
      }
      else
      {
	strcpy (ptr, start);
      }

#ifdef ASSUME_FIXED_WIDTH_PRE
      all.width = currentFont->max_bounds.width * strlen (ptr);
#else
      XTextExtents (currentFont, ptr, strlen (ptr), &dir,
		    &ascent, &descent, &all);
#endif /* ASSUME_FIXED_WIDTH_PRE */

      if (*start == '\n')
      {
	if ((line != NULL) && (line[0] != '\0'))
	{
	  SetElement (hw, E_TEXT, currentFont, line_x, *y, line);
	  /*
	   * Save width here to avoid an 
	   * XTextExtents call later.
	   */
	  Current->width = *x - line_x + 1;

	  AdjustBaseLine ();
	  PF_LF_State = 0;

	  line[0] = '\0';
	}

	HardLineFeed (hw, x, y);
	line_x = *x;
	NeedSpace = 0;
      }
      else
      {
	char *tptr;
	int tlen;

	if (line == NULL)
	{
	  tlen = strlen (ptr) + 1;
	}
	else
	{
	  tlen = strlen (line) + strlen (ptr) + 1;
	}
	if (tlen > CompLineLen)
	{
	  CompLineLen += COMP_LINE_BUF_LEN;
	  if (tlen > CompLineLen)
	  {
	    CompLineLen = tlen;
	  }
	  tptr = (char *)XtMalloc(CompLineLen);
	  if (CompLine != NULL)
	  {
	    strcpy (tptr, CompLine);
	    XtFree(CompLine);
	  }
	  else
	  {
	    tptr[0] = '\0';
	  }
	  CompLine = tptr;
	}
	line = CompLine;

	strcat (line, ptr);

	*x = *x + all.width;
	CharsInLine = CharsInLine + strlen (ptr);
	NeedSpace = 1;
      }
      *end = tchar;
    }
  }
  if ((line != NULL) && (line[0] != '\0'))
  {
    SetElement (hw, E_TEXT, currentFont, line_x, *y, line);
    /*
     * Save width here to avoid an 
     * XTextExtents call later.
     */
    Current->width = *x - line_x + 1;

    AdjustBaseLine ();
    PF_LF_State = 0;
    line[0] = '\0';
  }
}


/*
 * Format and place a piece of text. Add an element record for it.
 */
void
FormatPlace (hw, mptr, x, y, width)
HTMLWidget hw;
struct mark_up *mptr;
int *x, *y;
unsigned int width;
{
  char *text;
  char *start;
  char *end;
  char *ptr;
  char tchar;
#ifdef DOUBLE_SPACE_AFTER_PUNCT
  char tchar2;
#endif /* DOUBLE_SPACE_AFTER_PUNCT */
  int stripped_space;
  int added_space;
  int double_space;
  int dir, ascent, descent;
  XCharStruct all;
  char *line;
  int line_x;

  text = mptr->text;

  line_x = *x;
  line = CompLine;
  if (line != NULL)
  {
    line[0] = '\0';
  }
  end = text;
  while (*end != '\0')
  {
    /*
     * make start and end point to one word.
     * set flag if we removed any leading white space.
     * set flag if we add any leading white space.
     */
    stripped_space = 0;
    added_space = 0;
    start = end;
    while (((int) ((unsigned char) *start) < 128) && (isspace (*start)))
    {
      stripped_space = 1;
      start++;
    }

    end = start;
    while (((int) ((unsigned char) *end) > 127) ||
	   ((!isspace (*end)) && (*end != '\0')))
    {
      end++;
    }

    /*
     * Add the word to the end of this line, or insert
     * a linefeed an put the word at the start of the next line.
     */
    if (start != end)
    {
      int nobreak;
      int tlen;

      /*
       * nobreak is a horrible hack that specifies special
       * conditions where line breaks are just not allowed
       */
      nobreak = 0;

      tchar = *end;
      *end = '\0';

      /*
       * Malloc temp space if needed, leave room for
       * 2 spaces and a end of string char
       */
      tlen = strlen (start) + 3;
      if (tlen > CompWordLen)
      {
	CompWordLen += COMP_LINE_BUF_LEN;
	if (tlen > CompWordLen) CompWordLen = tlen;
	if (CompWord != NULL) XtFree(CompWord);
	CompWord = (char *)XtMalloc(CompWordLen);
      }
      ptr = CompWord;

      if ((NeedSpace > 0) && (stripped_space))
      {
	if (NeedSpace == 2)
	{
	  strcpy (ptr, "  ");
	  added_space = 2;
	}
	else
	{
	  strcpy (ptr, " ");
	  added_space = 1;
	}
      }
      else
      {
	strcpy (ptr, "");
      }
      strcat (ptr, start);

#ifdef DOUBLE_SPACE_AFTER_PUNCT
      /*
       * If this text ends in '.', '!', or '?' we need
       * to set up the addition of two spaces after it.
       */
      tchar2 = ptr[strlen (ptr) - 1];
      if ((tchar2 == '.') || (tchar2 == '!') || (tchar2 == '?'))
      {
	double_space = 1;
      }
      else
      {
	double_space = 0;
      }
#else
      double_space = 0;
#endif /* DOUBLE_SPACE_AFTER_PUNCT */

      XTextExtents (currentFont, ptr, strlen (ptr), &dir,
		    &ascent, &descent, &all);

      /*
       * Horrible hack for punctuation following
       * font changes to not go on the next line.
       */
      if ((MY_ISPUNCT (*ptr)) && (added_space == 0))
      {
	char *tptr;

	/*
	 * Take into account whole streams of
	 * punctuation.
	 */
	nobreak = 1;
	tptr = ptr;
	while ((*tptr != '\0') && (MY_ISPUNCT (*tptr)))
	{
	  tptr++;
	}
	if (*tptr != '\0')
	{
	  nobreak = 0;
	}
      }

      /*
       * No linebreaks if this whole line is just too
       * long.
       */
      if (*x == TextIndent)
      {
	nobreak = 1;
      }

      if (((*x + all.width + MarginW) <= width) || (nobreak))
      {
	char *tptr;
	int tlen;

	if (line == NULL)
	{
	  tlen = strlen (ptr) + 1;
	}
	else
	{
	  tlen = strlen (line) + strlen (ptr) + 1;
	}
	if (tlen > CompLineLen)
	{
	  CompLineLen += COMP_LINE_BUF_LEN;
	  if (tlen > CompLineLen)
	  {
	    CompLineLen = tlen;
	  }
	  tptr = (char *)XtMalloc(CompLineLen);
	  if (CompLine != NULL)
	  {
	    strcpy (tptr, CompLine);
	    XtFree(CompLine);
	  }
	  else
	  {
	    tptr[0] = '\0';
	  }
	  CompLine = tptr;
	}
	line = CompLine;

	strcat (line, ptr);
      }
      else
      {
	char *tptr, *tptr2;
	int tlen;

	if ((line != NULL) && (line[0] != '\0'))
	{
	  SetElement (hw, E_TEXT, currentFont, line_x, *y, line);
	  /*
	   * Save width here to avoid an 
	   * XTextExtents call later.
	   */
	  Current->width = *x - line_x + 1;

	  AdjustBaseLine ();
	  PF_LF_State = 0;

	  line[0] = '\0';
	}

	LineFeed (hw, x, y);
	line_x = *x;

	/*
	 * If we added a space before, remove it now
	 * since we are at the beginning of a new line
	 */
	if (added_space)
	{
	  tptr2 = (char *) (ptr + added_space);
	}
	else
	{
	  tptr2 = ptr;
	}
	XTextExtents (currentFont, tptr2, strlen (tptr2), &dir,
		      &ascent, &descent, &all);

	if (line == NULL)
	{
	  tlen = strlen (tptr2) + 1;
	}
	else
	{
	  tlen = strlen (line) + strlen (tptr2) + 1;
	}
	if (tlen > CompLineLen)
	{
	  CompLineLen += COMP_LINE_BUF_LEN;
	  if (tlen > CompLineLen)
	  {
	    CompLineLen = tlen;
	  }
	  tptr = (char *)XtMalloc(CompLineLen);
	  if (CompLine != NULL)
	  {
	    strcpy (tptr, CompLine);
	    XtFree(CompLine);
	  }
	  else
	  {
	    tptr[0] = '\0';
	  }
	  CompLine = tptr;
	}
	line = CompLine;

	strcat (line, tptr2);
      }

      /*
       * Set NeedSpace for one or 2 spaces based on
       * whether we are after a '.', '!', or '?'
       * or not.
       */
      if (double_space)
      {
	NeedSpace = 2;
      }
      else
      {
	NeedSpace = 1;
      }

      *x = *x + all.width;
      *end = tchar;
    }
    /*
     * Else if there is trailing whitespace, add it now
     */
    else if ((NeedSpace > 0) && (stripped_space))
    {
      char *tptr;
      char *spc;
      int tlen;

      if (NeedSpace == 2)
      {
	spc = XtNewString ("  ");
      }
      else
      {
	spc = XtNewString (" ");
      }

      XTextExtents (currentFont, spc, strlen (spc), &dir,
		    &ascent, &descent, &all);

      /*
       * Sigh, adding this one little space might force a
       * line break.
       */
      if ((*x + all.width + MarginW) <= width)
      {
	if (line == NULL)
	{
	  tlen = strlen (spc) + 1;
	}
	else
	{
	  tlen = strlen (line) + strlen (spc) + 1;
	}
	if (tlen > CompLineLen)
	{
	  CompLineLen += COMP_LINE_BUF_LEN;
	  if (tlen > CompLineLen)
	  {
	    CompLineLen = tlen;
	  }
	  tptr = (char *)XtMalloc(CompLineLen);
	  if (CompLine != NULL)
	  {
	    strcpy (tptr, CompLine);
	    XtFree(CompLine);
	  }
	  else
	  {
	    tptr[0] = '\0';
	  }
	  CompLine = tptr;
	}
	line = CompLine;

	strcat (line, spc);
      }
      /*
       * Ok, the space forced a linefeed, but now we must
       * also drop the space since we don't want it if we
       * have a linefeed here.
       */
      else
      {
	if ((line != NULL) && (line[0] != '\0'))
	{
	  SetElement (hw, E_TEXT, currentFont, line_x, *y, line);
	  /*
	   * Save width here to avoid an 
	   * XTextExtents call later.
	   */
	  Current->width = *x - line_x + 1;

	  AdjustBaseLine ();
	  PF_LF_State = 0;

	  line[0] = '\0';
	}

	LineFeed (hw, x, y);
	line_x = *x;

	all.width = 0;
      }

      *x = *x + all.width;
      if (spc) XtFree(spc);
      NeedSpace = 0;
    }
  }
  if ((line != NULL) && (line[0] != '\0'))
  {
    SetElement (hw, E_TEXT, currentFont, line_x, *y, line);
    /*
     * Save width here to avoid an 
     * XTextExtents call later.
     */
    Current->width = *x - line_x + 1;

    AdjustBaseLine ();
    PF_LF_State = 0;
    line[0] = '\0';
  }
}


/*
 * Place an image. Add an element record for it.
 */
void
ImagePlace (hw, mptr, x, y, width)
HTMLWidget hw;
struct mark_up *mptr;
int *x, *y;
unsigned int width;
{
  char *tptr;

#ifdef SPACE_HACK
  /*
   * If we are starting an image in formatted
   * text, and it needs a preceeding space, add
   * that space now.
   */
  if ((!Preformat) && (NeedSpace > 0))
  {
    int dir, ascent, descent;
    XCharStruct all;

    if (NeedSpace == 2) tptr = XtNewString ("  ");
    else tptr = XtNewString (" ");

    XTextExtents (currentFont, tptr, strlen (tptr), &dir, &ascent,
		  &descent, &all);
    SetElement (hw, E_TEXT, currentFont, *x, *y, tptr);
    /*
     * Save width here to avoid an 
     * XTextExtents call later.
     */
    Current->width = all.width;

    AdjustBaseLine ();
    *x = *x + all.width;
    CharsInLine = CharsInLine + strlen (tptr);
    if (tptr) XtFree(tptr);
    PF_LF_State = 0;
    NeedSpace = 0;
  }
#endif /* SPACE_HACK */

  tptr = ParseMarkTag (mptr->start, MT_IMAGE, "SRC");
  SetElement (hw, E_IMAGE, currentFont, *x, *y, tptr);

  /*
   * Only after we have placed the image do we know its dimensions.
   * So now look and see if the image is too wide, and if so go
   * back and insert a linebreak.
   */
  if ((Current->pic_data != NULL) && (!Preformat))
  {
    int extra;

    if ((hw->html.border_images == True) ||
	((Current->anchorHRef != NULL) && (!Current->pic_data->internal)))
    {
      extra = 2 * IMAGE_BORDER;
    }
    else
    {
      extra = 0;
    }

    if (((*x + Current->pic_data->width + extra + MarginW) > width) &&
	(Current->prev != NULL) && (Current->prev->line_number == LineNumber))
    {
      Current = Current->prev;
      LineFeed (hw, x, y);
      SetElement (hw, E_IMAGE, currentFont, *x, *y, tptr);
    }
  }
  /*
   * Clean up parsed SRC string
   */
  if (tptr != NULL) XtFree(tptr);

  /*
   * Yank out the name field, and stick it in text.
   * We may use this for ALT to at some later date.
   */
  if (Current->pic_data != NULL)
  {
    tptr = ParseMarkTag (mptr->start, MT_IMAGE, "NAME");
    Current->pic_data->text = tptr;
  }

  /*
   * Check if this image has the ISMAP attribute, so we know the
   * x,y coordinates of the image click are important.
   * Due to a special case (see below), this code can acutally
   * change the size, or anchor status of the image, thus we MUST
   * doit before we muck with the Baseline and stuff.
   */
  if (Current->pic_data != NULL)
  {
    Current->pic_data->fptr = NULL;
    tptr = ParseMarkTag (mptr->start, MT_IMAGE, "ISMAP");
    if (tptr != NULL)
    {
      XtFree(tptr);
      Current->pic_data->ismap = 1;
      /*
       * SUPER SPECIAL CASE!  (Thanks Marc)
       * If you have an ISMAP image inside a form,
       * And that form doesn't already have an HREF
       * by being inside an anchor,
       * (Being a DelayedHRef is considered no href)
       * clicking in that image will submit the form,
       * adding the x,y coordinates of the click as part
       * of the list of name/value pairs.
       */
      if ((CurrentForm != NULL) &&
	  ((Current->anchorHRef == NULL) ||
	   (IsDelayedHRef (hw, Current->anchorHRef))))
      {
	Current->pic_data->fptr = CurrentForm;
	Current->anchorHRef = IsMapForm (hw);
	Current->fg = hw->html.anchor_fg;
      }
    }
    else
    {
      Current->pic_data->ismap = 0;
    }
  }

  /*
   * Check if this image will be top aligned
   */
  tptr = ParseMarkTag (mptr->start, MT_IMAGE, "ALIGN");
  if (tptr != NULL && strcasecmp (tptr, "TOP") == 0)
  {
    Current->alignment = ALIGN_TOP;
  }
  else if (tptr != NULL && strcasecmp (tptr, "MIDDLE") == 0)
  {
    Current->alignment = ALIGN_MIDDLE;
  }
  else
  {
    Current->alignment = ALIGN_BOTTOM;
  }
  /*
   * Clean up parsed ALIGN string
   */
  if (tptr != NULL)
  {
    XtFree(tptr);
    tptr = NULL;
  }

  /*
   * Advance x position, and check the max
   * line height.  We need to follow this
   * image with a space.
   */
  if (Current->pic_data != NULL)
  {
    int extra;

    if ((hw->html.border_images == True) ||
	((Current->anchorHRef != NULL) &&
	 (!Current->pic_data->internal)))
    {
      extra = 2 * IMAGE_BORDER;
    }
    else
    {
      extra = 0;
    }

    if (BaseLine == -100)
    {
      BaseLine = 0;
    }

    *x = *x + Current->pic_data->width + extra;

    if (Current->alignment == ALIGN_TOP)
    {
      Current->y_offset = 0;

      if ((Current->pic_data->height + extra - BaseLine) >
	  LineBottom)
      {
	LineBottom = Current->pic_data->height + extra -
	  BaseLine;
      }
    }
    else if (Current->alignment == ALIGN_MIDDLE)
    {
      int baseline;

      baseline = (Current->pic_data->height + extra) / 2;

      if (baseline <= BaseLine)
      {
	Current->y_offset = BaseLine - baseline;
      }
      else
      {
	struct ele_rec *eptr;
	int line, incy;

	Current->y_offset = 0;

	incy = baseline - BaseLine;
	BaseLine = baseline;

	/*
	 * Go back over this line
	 * and move everything down
	 * a little.
	 */
	eptr = Current;
	line = eptr->line_number;
	while ((eptr->prev != NULL) && (eptr->prev->line_number == line))
	{
	  eptr = eptr->prev;
	  eptr->y_offset = eptr->y_offset + incy;
	}
      }

      if ((Current->pic_data->height + extra - BaseLine) >
	  LineBottom)
      {
	LineBottom = Current->pic_data->height + extra - BaseLine;
      }
    }
    else if ((Current->pic_data->height + extra) <= BaseLine)
    {
      Current->y_offset = BaseLine - (Current->pic_data->height + extra);
    }
    else if ((Current->pic_data->height + extra) > BaseLine)
    {
      struct ele_rec *eptr;
      int line, incy;

      incy = Current->pic_data->height + extra - BaseLine;
      BaseLine = Current->pic_data->height + extra;

      /*
       * Go back over this line
       * and move everything down
       * a little.
       */
      eptr = Current;
      line = eptr->line_number;
      while ((eptr->prev != NULL) &&
	     (eptr->prev->line_number == line))
      {
	eptr = eptr->prev;
	eptr->y_offset = eptr->y_offset + incy;
      }
    }

    if (BaseLine == 0)
    {
      BaseLine = -100;
    }
  }
  PF_LF_State = 0;
  NeedSpace = 1;
}


/*
 * Place an Widget. Add an element record for it.
 */
void
WidgetPlace (hw, mptr, x, y, width)
HTMLWidget hw;
struct mark_up *mptr;
int *x, *y;
unsigned int width;
{
  SetElement (hw, E_WIDGET, currentFont, *x, *y, mptr->start);

  /*
   * Only after we have placed the widget do we know its dimensions.
   * So now look and see if the widget is too wide, and if so go
   * back and insert a linebreak.
   */
  if ((Current->widget_data != NULL) && (!Preformat))
  {
    int extra;

    extra = 2 * IMAGE_BORDER;

    if (((*x + Current->widget_data->width + extra + MarginW) >
	 width) &&
	(Current->prev != NULL) &&
	(Current->prev->line_number == LineNumber))
    {
      WidgetId--;
      Current = Current->prev;
      LineFeed (hw, x, y);
      SetElement (hw, E_WIDGET, currentFont, *x, *y, mptr->start);
    }
  }

  /*
   * Advance x position, and check BaseLine and LineBottom.
   * We need to follow this widget with a space.
   */
  if (Current->widget_data != NULL)
  {
    int extra;
    int baseline;
    XFontStruct *fp;

    extra = 2 * IMAGE_BORDER;

    /*
     * Find the font used in this widget.  Then find its baseline
     */
    fp = GetWidgetFont (hw, Current->widget_data);
    if (fp == NULL)
    {
      baseline = Current->widget_data->height + extra;
    }
    /*
     * If no font, the baseline is the bottum of the widget
     */
    else
    {
      int border;

      border = ((Current->widget_data->height + extra) -
		(fp->max_bounds.ascent + fp->max_bounds.descent));
      baseline = (border / 2) + fp->max_bounds.ascent;
    }

    /*
     * Baseline == -100 is the special unset baseline value.
     */
    if (BaseLine == -100)
    {
      BaseLine = baseline;
      Current->y_offset = 0;
      /*
       * If linebottom isn't set, set it to
       * whatever of the height is below the baseline.
       */
      if (LineBottom == 0)
      {
	LineBottom = Current->widget_data->height +
	  extra - baseline;
      }
      /*
       * Else, it is possible that a linebottom has been
       * set even when we have no baseline yet (like if
       * the first item in the line was a top aligned image)
       * It now needs to be corrected as we set a real
       * BaseLine.
       */
      else
      {
	if ((Current->widget_data->height +
	     extra - baseline) >
	    (LineBottom - baseline))
	{
	  LineBottom =
	    Current->widget_data->height +
	    extra - baseline;
	}
	else
	{
	  LineBottom = LineBottom - baseline;
	}
      }
    }
    /*
     * Else we already have a baseline, and it is greater that
     * the baseline for this widget.
     * Set y_offset, and check linebottom.
     */
    else if (baseline <= BaseLine)
    {
      if (baseline < BaseLine)
      {
	Current->y_offset = BaseLine - baseline;
      }
      else
      {
	Current->y_offset = 0;
      }

      /*
       * Our line bottom may be greater than the
       * old one.
       */
      if ((Current->widget_data->height + extra - baseline) >
	  LineBottom)
      {
	LineBottom = Current->widget_data->height +
	  extra - baseline;
      }
    }
    else
      /*
       * Else we have a new baseline greater than the old baseline.
       */
    {
      struct ele_rec *eptr;
      int line, incy;

      /*
       * Figure out how much to move all the old stuff
       */
      incy = baseline - BaseLine;
      BaseLine = baseline;

      /*
       * Go back over this line
       * and move everything down
       * a little.
       */
      eptr = Current;
      line = eptr->line_number;
      while ((eptr->prev != NULL) &&
	     (eptr->prev->line_number == line))
      {
	eptr = eptr->prev;
	eptr->y_offset = eptr->y_offset + incy;
      }

      /*
       * Our line bottom may be greater than the
       * old one.
       */
      if ((Current->widget_data->height + extra - baseline) >
	  LineBottom)
      {
	LineBottom = Current->widget_data->height +
	  extra - baseline;
      }
    }

    /*
     * Advance the X position.
     */
    *x = *x + Current->widget_data->width + extra;
  }
  PF_LF_State = 0;
  NeedSpace = 1;
}


static void
PushFont (font)
XFontStruct *font;
{
  FontRec *fptr;

  fptr = (FontRec *)XtMalloc(sizeof(FontRec));
  fptr->font = font;
  fptr->next = FontStack;
  FontStack = fptr;
}


static XFontStruct *
PopFont ()
{
  XFontStruct *font;
  FontRec *fptr;

  if (FontStack->next != NULL)
  {
    fptr = FontStack;
    FontStack = FontStack->next;
    font = fptr->font;
    XtFree((char *) fptr);
  }
  else
  {
#ifdef VERBOSE
    fprintf (stderr, "Warning, popping empty font stack!\n");
#endif
    font = FontStack->font;
  }

  return (font);
}


/*
 * We've just terminated the current OPTION.
 * Put it in the proper place in the SelectInfo structure.
 * Move option_buf into options, and maybe copy into
 * value if is_value is set.
 */
static void
ProcessOption (sptr)
SelectInfo *sptr;
{
  int i, cnt;
  char **tarray;

  clean_white_space (sptr->option_buf);
  tarray = sptr->options;
  cnt = sptr->option_cnt + 1;
  sptr->options = (char **)XtMalloc(sizeof(char *) * cnt);
  for (i = 0; i < (cnt - 1); i++)
  {
    sptr->options[i] = tarray[i];
  }
  if (tarray != NULL)
  {
    XtFree((char *) tarray);
    tarray = NULL;
  }
  sptr->options[cnt - 1] = sptr->option_buf;
  sptr->option_cnt = cnt;

  tarray = sptr->returns;
  cnt = sptr->option_cnt;
  sptr->returns = (char **)XtMalloc(sizeof(char *) * cnt);
  for (i = 0; i < (cnt - 1); i++)
  {
    sptr->returns[i] = tarray[i];
  }
  if (tarray != NULL)
  {
    XtFree((char *) tarray);
    tarray = NULL;
  }
  sptr->returns[cnt - 1] = sptr->retval_buf;

  if (sptr->is_value)
  {
    tarray = sptr->value;
    cnt = sptr->value_cnt + 1;
    sptr->value = (char **)XtMalloc(sizeof(char *) * cnt);
    for (i = 0; i < (cnt - 1); i++)
    {
      sptr->value[i] = tarray[i];
    }
    if (tarray != NULL)
    {
      XtFree((char *) tarray);
      tarray = NULL;
    }
    sptr->value[cnt - 1] = XtNewString(sptr->option_buf);
    sptr->value_cnt = cnt;
  }
}


/*
 * 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 (value, text)
char *value;
char *text;
{
  int extra;
  char *buf;
  char *bptr;
  char *tptr;

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

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

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

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

  XtFree(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 (hw, mptr, x, y)
HTMLWidget hw;
struct mark_up *mptr;
int *x, *y;
{
  struct mark_up *mark;
  XFontStruct *font;
  int type, width;

  mark = mptr;
  type = mark->type;
  font = NULL;

  /*
   * If Ignore is set, we ignore all further elements until we get to the
   * end of the Ignore
   * Let text through so we can grab the title text.
   * Let title through so we can hit the end title.
   * Now also used for SELECT parseing
   * 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 ((Ignore) && (type != M_TITLE) && (type != M_NONE) &&
      (type != M_SELECT) && (type != M_OPTION) && (type != M_TEXTAREA))
  {
    return;
  }

  switch (type)
  {
    /*
     * Place the text.  Different functions based on whether it
     * is pre-formatted or not.
     */
  case M_NONE:
    if ((Ignore) && (CurrentSelect == NULL) && (TextAreaBuf == NULL))
    {
      if (TitleText == NULL)
      {
	TitleText = XtNewString (mptr->text);
      }
      else
      {
	char *tptr;

	tptr = (char *)XtMalloc(strlen(TitleText) + strlen(mptr->text) + 1);
	strcpy (tptr, TitleText);
	strcat (tptr, mptr->text);
	XtFree (TitleText);
	TitleText = tptr;
      }
    }
    else if ((Ignore) && (CurrentSelect != NULL))
    {
      if (CurrentSelect->option_buf != NULL)
      {
	char *tptr;

	tptr = (char *)XtMalloc(strlen (CurrentSelect->option_buf) +
				strlen (mptr->text) + 1);
	strcpy (tptr, CurrentSelect->option_buf);
	strcat (tptr, mptr->text);
	XtFree (CurrentSelect->option_buf);
	CurrentSelect->option_buf = tptr;
      }
    }
    else if ((Ignore) && (TextAreaBuf != NULL))
    {
      TextAreaBuf = TextAreaAddValue (TextAreaBuf, mptr->text);
    }
    else if (Preformat)
    {
      PreformatPlace (hw, mptr, x, y, Width);
    }
    else
    {
      FormatPlace (hw, mptr, x, y, Width);
    }
    break;
    /*
     * Titles are just set into the widget for retrieval by
     * XtGetValues().
     */
  case M_TITLE:
    if (mark->is_end)
    {
      Ignore = 0;
      hw->html.title = TitleText;
      TitleText = NULL;
    }
    else
    {
      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)
    {
      font = PopFont ();
    }
    else
    {
      PushFont (currentFont);
      font = hw->html.fixed_font;
    }
    break;
  case M_STRONG:
  case M_BOLD:
    if (mark->is_end)
    {
      font = PopFont ();
    }
    else
    {
      PushFont (currentFont);
      if (currentFont == hw->html.fixed_font ||
	  currentFont == hw->html.fixeditalic_font)
	font = hw->html.fixedbold_font;
      else if (currentFont == hw->html.plain_font ||
	       currentFont == hw->html.plainitalic_font)
	font = hw->html.plainbold_font;
      else
	font = hw->html.bold_font;
    }
    break;
  case M_EMPHASIZED:
  case M_VARIABLE:
  case M_CITATION:
  case M_ITALIC:
    if (mark->is_end)
    {
      font = PopFont ();
    }
    else
    {
      PushFont (currentFont);
      if (currentFont == hw->html.fixed_font ||
	  currentFont == hw->html.fixedbold_font)
	font = hw->html.fixeditalic_font;
      else if (currentFont == hw->html.plain_font ||
	       currentFont == hw->html.plainbold_font)
	font = hw->html.plainitalic_font;
      else
	font = hw->html.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:
    if (mark->is_end)
    {
      Strikeout = False;
    }
    else
    {
      Strikeout = True;
    }
    break;
    /*
     * Headers are preceeded and followed by a linefeed,
     * and the change the font.
     */
  case M_HEADER_1:
    ConditionalLineFeed (hw, x, y, 1);
    if (mark->is_end)
    {
      font = PopFont ();
      NewFont (font);
      currentFont = font;
      ConditionalLineFeed (hw, x, y, 2);
    }
    else
    {
      ConditionalLineFeed (hw, x, y, 2);
      PushFont (currentFont);
      font = hw->html.header1_font;
    }
    break;
  case M_HEADER_2:
    ConditionalLineFeed (hw, x, y, 1);
    if (mark->is_end)
    {
      font = PopFont ();
      NewFont (font);
      currentFont = font;
      ConditionalLineFeed (hw, x, y, 2);
    }
    else
    {
      ConditionalLineFeed (hw, x, y, 2);
      PushFont (currentFont);
      font = hw->html.header2_font;
    }
    break;
  case M_HEADER_3:
    ConditionalLineFeed (hw, x, y, 1);
    if (mark->is_end)
    {
      font = PopFont ();
      NewFont (font);
      currentFont = font;
      ConditionalLineFeed (hw, x, y, 2);
    }
    else
    {
      ConditionalLineFeed (hw, x, y, 2);
      PushFont (currentFont);
      font = hw->html.header3_font;
    }
    break;
  case M_HEADER_4:
    ConditionalLineFeed (hw, x, y, 1);
    if (mark->is_end)
    {
      font = PopFont ();
      NewFont (font);
      currentFont = font;
      ConditionalLineFeed (hw, x, y, 2);
    }
    else
    {
      ConditionalLineFeed (hw, x, y, 2);
      PushFont (currentFont);
      font = hw->html.header4_font;
    }
    break;
  case M_HEADER_5:
    ConditionalLineFeed (hw, x, y, 1);
    if (mark->is_end)
    {
      font = PopFont ();
      NewFont (font);
      currentFont = font;
      ConditionalLineFeed (hw, x, y, 2);
    }
    else
    {
      ConditionalLineFeed (hw, x, y, 2);
      PushFont (currentFont);
      font = hw->html.header5_font;
    }
    break;
  case M_HEADER_6:
    ConditionalLineFeed (hw, x, y, 1);
    if (mark->is_end)
    {
      font = PopFont ();
      NewFont (font);
      currentFont = font;
      ConditionalLineFeed (hw, x, y, 2);
    }
    else
    {
      ConditionalLineFeed (hw, x, y, 2);
      PushFont (currentFont);
      font = hw->html.header6_font;
    }
    break;
    /*
     * Anchors change the text color, and may set
     * underlineing attributes.
     * No linefeeds, so they can be imbedded anywhere.
     */
  case M_ANCHOR:
    if (mark->is_end)
    {
      Fg = hw->html.foreground;
      Underlines = 0;
      DashedUnderlines = False;
      AnchorText = NULL;
    }
    else
    {
      char *tptr;

      /*
       * Only change the color of anchors with
       * HREF tags, because other anchors are
       * not active.
       */
      tptr = ParseMarkTag (mark->start,
			   MT_ANCHOR, AT_HREF);
      if (tptr != NULL)
      {
	/*
	 * If internal check our internal list
	 * to change color if visited before.
	 */
	if (Internal == True)
	{
	  struct ref_rec *hptr;

	  hptr = FindHRef (
			    hw->html.my_visited_hrefs,
			    tptr);
	  if (hptr != NULL)
	  {
	    Fg = hw->html.visitedAnchor_fg;
	    Underlines = hw->html.num_visitedAnchor_underlines;
	    DashedUnderlines = hw->html.dashed_visitedAnchor_lines;
	  }
	  else
	  {
	    Fg = hw->html.anchor_fg;
	    Underlines = hw->html.num_anchor_underlines;
	    DashedUnderlines = hw->html.dashed_anchor_lines;
	  }
	}
	/*
	 * Else we may want to send
	 * the href back somewhere else and
	 * find out if we've visited it before
	 */
	else if (hw->html.previously_visited_test !=
		 NULL)
	{
	  if ((*(visitTestProc)
	       (hw->html.previously_visited_test))
	      (hw, tptr))
	  {
	    Fg = hw->html.visitedAnchor_fg;
	    Underlines = hw->html.num_visitedAnchor_underlines;
	    DashedUnderlines = hw->html.dashed_visitedAnchor_lines;
	  }
	  else
	  {
	    Fg = hw->html.anchor_fg;
	    Underlines = hw->html.num_anchor_underlines;
	    DashedUnderlines = hw->html.dashed_anchor_lines;
	  }
	}
	else
	{
	  Fg = hw->html.anchor_fg;
	  Underlines = hw->html.num_anchor_underlines;
	  DashedUnderlines = hw->html.dashed_anchor_lines;
	}
	if (tptr)
        {
	  XtFree(tptr);
	  tptr = NULL;
	}
      }
      AnchorText = mark->start;
    }
    break;
    /*
     * Just insert a linefeed, or ignore if this is prefomatted
     * text because the <P> will be followed be a linefeed.
     */
  case M_PARAGRAPH:
    ConditionalLineFeed (hw, x, y, 1);
    ConditionalLineFeed (hw, x, y, 2);
    break;
    /*
     * Just insert the image for now
     */
  case M_IMAGE:
    ImagePlace (hw, mptr, x, y, Width);
    break;
    /*
     * Can only be inside a SELECT tag.
     */
  case M_OPTION:
    if (CurrentSelect != NULL)
    {
      char *tptr;

      if (CurrentSelect->option_buf != NULL)
      {
	ProcessOption (CurrentSelect);
      }
      CurrentSelect->option_buf = XtNewString("");

      /*
       * Check if this option starts selected
       */
      tptr = ParseMarkTag (mark->start, MT_OPTION, "SELECTED");
      if (tptr != NULL)
      {
	CurrentSelect->is_value = 1;
	XtFree(tptr);
	tptr = NULL;
      }
      else
      {
	CurrentSelect->is_value = 0;
      }

      /*
       * Check if this option has an different
       * return value field.
       */
      tptr = ParseMarkTag (mark->start, MT_OPTION, "VALUE");
      if (tptr != NULL)
      {
	if (*tptr != '\0')
	{
	  CurrentSelect->retval_buf = tptr;
	}
	else
	{
	  CurrentSelect->retval_buf = NULL;
	  XtFree(tptr);
	  tptr = NULL;
	}
      }
      else
      {
	CurrentSelect->retval_buf = NULL;
      }
    }
    break;
    /*
     * Special INPUT tag.  Allows an option menu or
     * a scrolled list.
     * Due to a restriction in SGML, this can't just be a 
     * subset of the INPUT markup.  However, I can treat it
     * that way to avoid duplicating code.
     * As a result I combine SELECT and OPTION into a faked
     * up INPUT mark.
     */
  case M_SELECT:
    if (CurrentForm != NULL)
    {
      if ((mark->is_end) && (CurrentSelect != NULL))
      {
	int len;
	char *buf;
	char *start;
	char *options, *returns, *value;

	if (CurrentSelect->option_buf != NULL)
	{
	  ProcessOption (CurrentSelect);
	}

	options = ComposeCommaList(CurrentSelect->options,
				   CurrentSelect->option_cnt);
	returns = ComposeCommaList(CurrentSelect->returns,
				   CurrentSelect->option_cnt);
	value = ComposeCommaList(CurrentSelect->value,
				 CurrentSelect->value_cnt);
	FreeCommaList(CurrentSelect->options, CurrentSelect->option_cnt);
	FreeCommaList(CurrentSelect->returns, CurrentSelect->option_cnt);
	FreeCommaList(CurrentSelect->value, CurrentSelect->value_cnt);

	/*
	 * Construct a fake INPUT tag.
	 */
	len = strlen (MT_INPUT) +
	  strlen (options) +
	  strlen (returns) +
	  strlen (value) + strlen (
		       " type=select options=\"\" returns=\"\" value=\"\"");
	buf = (char *)XtMalloc(len + strlen (CurrentSelect->mptr->start) + 1);
	strcpy (buf, MT_INPUT);
	strcat (buf, " type=select");
	strcat (buf, " options=\"");
	strcat (buf, options);
	strcat (buf, "\" returns=\"");
	strcat (buf, returns);
	strcat (buf, "\" value=\"");
	strcat (buf, value);
	strcat (buf, "\"");
	strcat (buf, (char *)(CurrentSelect->mptr->start + strlen(MT_SELECT)));
	/*
	 * stick the fake in, saving the
	 * real one.
	 */
	start = CurrentSelect->mptr->start;
	CurrentSelect->mptr->start = buf;
	WidgetPlace (hw, CurrentSelect->mptr, x, y, Width);
	/*
	 * free the fake, put the original back
	 */
	XtFree(buf); buf = NULL;
	XtFree(options); options = NULL;
	XtFree(returns); returns = NULL;
	XtFree(value); value = NULL;
	CurrentSelect->mptr->start = start;

	XtFree((char *) CurrentSelect);
	CurrentSelect = NULL;
	Ignore = 0;
      }
      else if ((!mark->is_end) && (CurrentSelect == NULL))
      {
	CurrentSelect = (SelectInfo *)XtMalloc(sizeof (SelectInfo));
	CurrentSelect->hw = (Widget) hw;
	CurrentSelect->mptr = mptr;
	CurrentSelect->option_cnt = 0;
	CurrentSelect->returns = NULL;
	CurrentSelect->retval_buf = NULL;
	CurrentSelect->options = NULL;
	CurrentSelect->option_buf = NULL;
	CurrentSelect->value_cnt = 0;
	CurrentSelect->value = NULL;
	CurrentSelect->is_value = -1;
	Ignore = 1;
      }
    }
    break;
    /*
     * TEXTAREA is a replacement for INPUT type=text size=rows,cols
     * because SGML will not allow an arbitrary length value
     * field.
     */
  case M_TEXTAREA:
    if (CurrentForm != NULL)
    {
      if ((mark->is_end) && (TextAreaBuf != NULL))
      {
	char *start;
	char *buf;

	/*
	 * Finish a fake INPUT tag.
	 */
	buf = (char *)XtMalloc(strlen(TextAreaBuf) + 2);
	strcpy(buf, TextAreaBuf);
	strcat(buf, "\"");

	/*
	 * stick the fake in, saving the
	 * real one.
	 */
	start = mark->start;
	mark->start = buf;
	mark->is_end = 0;
	WidgetPlace (hw, mark, x, y, Width);

	/*
	 * free the fake, put the original back
	 */
	XtFree(buf); buf = NULL;
	XtFree(TextAreaBuf); TextAreaBuf = NULL;
	mark->start = start;
	mark->is_end = 1;
	Ignore = 0;
      }
      else if ((!mark->is_end) && (TextAreaBuf == NULL))
      {
	char *buf;
	int len;

	/*
	 * Construct  the start of
	 * a fake INPUT tag.
	 */
	len = strlen(MT_INPUT) + strlen(" type=textarea value=\"\"");
	buf = (char *)XtMalloc(len + strlen(mark->start) + 1);
	strcpy (buf, MT_INPUT);
	strcat (buf, (char *)(mark->start + strlen (MT_TEXTAREA)));
	strcat (buf, " type=textarea value=\"");

	TextAreaBuf = buf;
	Ignore = 1;
      }
    }
    break;
    /*
     * Just insert the widget.
     * Can only inside a FORM tag.
     * Special case the type=image stuff to become a special
     * IMG tag.
     */
  case M_INPUT:
    if (CurrentForm != NULL)
    {
      char *tptr;
      char *tptr2;

      tptr = ParseMarkTag (mptr->start, MT_INPUT, "TYPE");
      if ((tptr != NULL) && (strcasecmp (tptr, "image") == 0))
      {
	XtFree(tptr);
	tptr = (char *)XtMalloc(strlen (mptr->start) +
				strlen (" ISMAP") +
				strlen (MT_IMAGE) -
				strlen (MT_INPUT) + 1);
	strcpy (tptr, MT_IMAGE);
	strcat (tptr, (char *)(mptr->start + strlen (MT_INPUT)));
	strcat (tptr, " ISMAP");
	tptr2 = mptr->start;
	mptr->start = tptr;
	ImagePlace (hw, mptr, x, y, Width);
	mptr->start = tptr2;
	XtFree(tptr);
	tptr = NULL;
      }
      /*
       * hidden inputs have no element associated
       * with them, just a widget record.
       */
      else if ((tptr != NULL) && (strcasecmp (tptr, "hidden") == 0))
      {
	XtFree(tptr);
	tptr = NULL;
	WidgetId++;
	(void) MakeWidget (hw, mptr->start, x, y, WidgetId, CurrentForm);
      }
      else
      {
	if (tptr != NULL)
        {
	  XtFree(tptr);
	  tptr = NULL;
	}
	WidgetPlace (hw, mptr, x, y, Width);
      }
    }
    break;
    /*
     * Fillout forms.  Cannot be nested.
     */
  case M_FORM:
    ConditionalLineFeed (hw, x, y, 1);
    if ((mark->is_end) && (CurrentForm != NULL))
    {
      CurrentForm->end = WidgetId;
      ConditionalLineFeed (hw, x, y, 2);
      AddNewForm (hw, CurrentForm);
      CurrentForm = NULL;
    }
    else if ((!mark->is_end) && (CurrentForm == NULL))
    {
      ConditionalLineFeed (hw, x, y, 2);
      CurrentForm = (FormInfo *)XtMalloc(sizeof (FormInfo));
      CurrentForm->next = NULL;
      CurrentForm->hw = (Widget)hw;
      CurrentForm->action = ParseMarkTag(mark->start, MT_FORM, "ACTION");
      CurrentForm->method = ParseMarkTag(mark->start, MT_FORM, "METHOD");
      CurrentForm->enctype = ParseMarkTag(mark->start, MT_FORM, "ENCTYPE");
      CurrentForm->enc_entity = ParseMarkTag(mark->start,
					     MT_FORM, "ENCENTITY");
      CurrentForm->start = WidgetId;
      CurrentForm->end = -1;
    }
    break;
    /*
     * Addresses are just like headers.  A linefeed before and
     * after, and change the font.
     */
  case M_ADDRESS:
    ConditionalLineFeed (hw, x, y, 1);
    if (mark->is_end)
    {
      font = PopFont();
    }
    else
    {
      PushFont (currentFont);
      font = hw->html.address_font;
    }
    break;
    /*
     * Blockquotes increase the margin width.
     * They cannot be nested.
     */
  case M_BLOCKQUOTE:
    ConditionalLineFeed (hw, x, y, 1);
    if (mark->is_end)
    {
      MarginW = hw->html.margin_width;
      /*
       * Only unindent if we think we indented
       * when we started the blockquote
       */
      if (TextIndent <= (2 * MarginW))
      {
	TextIndent = MarginW;
      }
      ConditionalLineFeed (hw, x, y, 2);
      /*
       * The linefeed should have set x to TextIndent
       * but since it is conditional, it might not
       * have, so check it here.
       */
      if (*x > TextIndent)
      {
	*x = TextIndent;
      }
    }
    else
    {
      MarginW = 2 * hw->html.margin_width;
      /*
       * Only indent if the current indent
       * is less than what we want.
       */
      if (TextIndent < MarginW)
      {
	TextIndent = MarginW;
      }
      ConditionalLineFeed (hw, x, y, 2);
      /*
       * The linefeed should have set x to TextIndent
       * but since it is conditional, it might not
       * have, so check it here.
       */
      if (*x < TextIndent)
      {
	*x = TextIndent;
      }
    }
    break;
    /*
     * Plain text.  A single pre-formatted chunk of text
     * in its own font.
     */
  case M_PLAIN_TEXT:
    if (mark->is_end)
    {
      Preformat = 0;
      /*
       * Properly convert the Linefeed state
       * variable from preformat to formatted
       * state.
       */
      if (PF_LF_State == 2)
      {
	PF_LF_State = 1;
      }
      else
      {
	PF_LF_State = 0;
      }
      ConditionalLineFeed (hw, x, y, 1);
      font = PopFont ();
      NewFont (font);
      currentFont = font;
      ConditionalLineFeed (hw, x, y, 2);
    }
    else
    {
      ConditionalLineFeed (hw, x, y, 1);
      ConditionalLineFeed (hw, x, y, 2);
      Preformat = 1;
      PF_LF_State = 0;
      PushFont (currentFont);
      font = hw->html.plain_font;
    }
    break;
    /*
     * Listing text.  A different pre-formatted chunk of text
     * in its own font.
     */
  case M_LISTING_TEXT:
    if (mark->is_end)
    {
      Preformat = 0;
      /*
       * Properly convert the Linefeed state
       * variable from preformat to formatted
       * state.
       */
      if (PF_LF_State == 2)
      {
	PF_LF_State = 1;
      }
      else
      {
	PF_LF_State = 0;
      }
      ConditionalLineFeed (hw, x, y, 1);
      font = PopFont ();
      NewFont (font);
      currentFont = font;
      ConditionalLineFeed (hw, x, y, 2);
    }
    else
    {
      ConditionalLineFeed (hw, x, y, 1);
      ConditionalLineFeed (hw, x, y, 2);
      Preformat = 1;
      PF_LF_State = 0;
      PushFont (currentFont);
      font = hw->html.listing_font;
    }
    break;
    /*
     * Plain text.  The rest of the text is pre-formatted.
     * There is not end for this mark.
     */
  case M_PLAIN_FILE:
    ConditionalLineFeed (hw, x, y, 1);
    ConditionalLineFeed (hw, x, y, 2);
    Preformat = 1;
    PF_LF_State = 0;
    PushFont (currentFont);
    font = hw->html.plain_font;
    break;
    /*
     * Numbered lists, Unnumbered lists, 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, x, y, 1);
    width = hw->html.font->max_bounds.width;
    /*
     * If this is the outermost level of indentation,
     * add another linefeed for more white space.
     */
    if ((TextIndent <= MarginW) || ((mark->is_end) &&
			    ((TextIndent - ((INDENT_SPACES + 1) * width)) <=
			     MarginW)))
    {
      ConditionalLineFeed (hw, x, y, 2);
    }
    if (mark->is_end)
    {
      TextIndent = TextIndent -
	((INDENT_SPACES + 1) * width);
      if (TextIndent < MarginW)
      {
	TextIndent = MarginW;
      }
      IndentLevel--;
      if (IndentLevel < 0)
      {
	IndentLevel = 0;
      }

      /*
       * restore the old state if there is one
       */
      if (ListData->next != NULL)
      {
	DescRec *dptr;

	dptr = ListData;
	ListData = ListData->next;
	XtFree((char *) dptr);
	dptr = NULL;
      }
    }
    else
    {
      DescRec *dptr;

      dptr = (DescRec *)XtMalloc(sizeof(DescRec));
      /*
       * Save the old state, and start a new
       */
      if (type == M_NUM_LIST)
      {
	dptr->type = D_OLIST;
	dptr->count = 1;
      }
      else
      {
	dptr->type = D_ULIST;
	dptr->count = 0;
      }
      dptr->next = ListData;
      ListData = dptr;

      TextIndent = TextIndent + ((INDENT_SPACES + 1) * width);
      IndentLevel++;
    }
    *x = TextIndent;
    break;
    /*
     * Place the bullet element at the beginning of this item.
     */
  case M_LIST_ITEM:
    if (!mark->is_end)
    {
      ConditionalLineFeed (hw, x, y, 1);
      /*
       * for ordered/numbered lists
       * put numbers in place of bullets.
       */
      if (ListData->type == D_OLIST)
      {
	ListNumberPlace (hw, x, y,
			 ListData->count);
	ListData->count++;
      }
      else
      {
	BulletPlace (hw, x, y);
      }
    }
    break;
    /*
     * Description lists
     */
  case M_DESC_LIST:
    ConditionalLineFeed (hw, x, y, 1);
    ConditionalLineFeed (hw, x, y, 2);
    width = hw->html.font->max_bounds.width;
    if (mark->is_end)
    {
      if (DescType->type == D_TEXT)
      {
	TextIndent = TextIndent -
	  ((INDENT_SPACES + 1) * width);
	if (TextIndent < MarginW)
	{
	  TextIndent = MarginW;
	}
      }
      /*
       * restore the old state if there is one
       */
      if (DescType->next != NULL)
      {
	DescRec *dptr;

	dptr = DescType;
	DescType = DescType->next;
	XtFree((char *) dptr);
	dptr = NULL;
	/*
	 * If the old state had forced an
	 * indent, outdent it now.
	 */
	if (DescType->type == D_TITLE)
	{
	  TextIndent = TextIndent -
	    ((INDENT_SPACES + 1) * width);
	  if (TextIndent < MarginW)
	  {
	    TextIndent = MarginW;
	  }
	}
      }
    }
    else
    {
      DescRec *dptr;
      char *tptr;

      dptr = (DescRec *)XtMalloc(sizeof (DescRec));
      /*
       * Check is this is a compact list
       */
      tptr = ParseMarkTag(mark->start, MT_DESC_LIST, "COMPACT");
      if (tptr != NULL)
      {
	XtFree(tptr);
	tptr = NULL;
	dptr->compact = 1;
      }
      else
      {
	dptr->compact = 0;
      }
      /*
       * Description list stared after a title needs
       * a forced indentation here
       */
      if (DescType->type == D_TITLE)
      {
	TextIndent = TextIndent + ((INDENT_SPACES + 1) * width);
      }
      /*
       * Save the old state, and start a new
       */
      dptr->type = D_TITLE;
      dptr->next = DescType;
      DescType = dptr;
    }
    *x = TextIndent;
    break;
  case M_DESC_TITLE:
    ConditionalLineFeed (hw, x, y, 1);
    width = hw->html.font->max_bounds.width;
    /*
     * Special hack.  Don't indent again for
     * multiple <dt>'s in a row.
     */
    if (DescType->type == D_TEXT)
    {
      TextIndent = TextIndent - ((INDENT_SPACES + 1) * width);
      if (TextIndent < MarginW)
      {
	TextIndent = MarginW;
      }
    }
    DescType->type = D_TITLE;
    *x = TextIndent;
    break;
  case M_DESC_TEXT:
    width = hw->html.font->max_bounds.width;

    /*
     * 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) && (DescType->type == D_TITLE) &&
	(*x < (TextIndent + (INDENT_SPACES * width))))
    {
      NeedSpace = 0;
    }
    else
    {
      ConditionalLineFeed (hw, x, y, 1);
    }

    /*
     * Special hack.  Don't indent again for
     * multiple <dd>'s in a row.
     */
    if (DescType->type == D_TITLE)
    {
      TextIndent = TextIndent +
	((INDENT_SPACES + 1) * width);
    }
    DescType->type = D_TEXT;
    *x = TextIndent;
    break;
  case M_PREFORMAT:
    if (mark->is_end)
    {
      Preformat = 0;
      /*
       * Properly convert the Linefeed state
       * variable from preformat to formatted
       * state.
       */
      if (PF_LF_State == 2)
      {
	PF_LF_State = 1;
      }
      else
      {
	PF_LF_State = 0;
      }
      ConditionalLineFeed (hw, x, y, 1);
      if (saveFont != NULL)
      {
	hw->html.font = saveFont;
	saveFont = NULL;
      }
      font = PopFont ();
      NewFont (font);
      currentFont = font;
      ConditionalLineFeed (hw, x, y, 2);
    }
    else
    {
      ConditionalLineFeed (hw, x, y, 1);
      ConditionalLineFeed (hw, x, y, 2);
      Preformat = 1;
      PF_LF_State = 2;
      if (saveFont == NULL)
      {
	saveFont = hw->html.font;
	hw->html.font = hw->html.plain_font;
      }
      PushFont (currentFont);
      font = hw->html.font;
    }
    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;
    /*
     * No index inside a form
     */
    if (CurrentForm == NULL)
    {
      struct mark_up mark_tmp;

      /*
       * Start the form
       */
      ConditionalLineFeed (hw, x, y, 1);
      ConditionalLineFeed (hw, x, y, 2);
      CurrentForm = (FormInfo *)XtMalloc(sizeof (FormInfo));
      CurrentForm->next = NULL;
      CurrentForm->hw = (Widget) hw;
      CurrentForm->action = NULL;
      CurrentForm->action = ParseMarkTag(mark->start,  MT_INDEX, "ACTION");
      CurrentForm->method = ParseMarkTag(mark->start, MT_INDEX, "METHOD");
      CurrentForm->enctype = ParseMarkTag(mark->start, MT_INDEX, "ENCTYPE");
      CurrentForm->enc_entity = ParseMarkTag(mark->start, MT_INDEX,
					     "ENCENTITY");
      CurrentForm->start = WidgetId;
      CurrentForm->end = -1;

      /*
       * Horizontal rule
       */
      ConditionalLineFeed (hw, x, y, 1);
      HRulePlace (hw, x, y, Width);
      ConditionalLineFeed (hw, x, y, 1);

      /*
       * Text: "This is a searchable index.
       *  Enter search keywords: "
       */
      mark_tmp.text = XtNewString("This is a searchable index.  Enter search keywords: ");
      FormatPlace (hw, &mark_tmp, x, y, Width);

      /*
       * Fake up the text INPUT tag.
       */
      mark_tmp.start = XtNewString("input SIZE=25 NAME=\"isindex\"");
      WidgetPlace (hw, &mark_tmp, x, y, Width);

#ifdef ISINDEX_SUBMIT
      /*
       * Text: ";<CR> press this button to submit
       * the query: "
       */
      mark_tmp.text = XtNewString(";\n press this button to submit the query: ");
      FormatPlace (hw, &mark_tmp, x, y, Width);

      /*
       * Fake up the submit INPUT tag.
       */
      mark_tmp.start = XtNewString("input TYPE=\"submit\"");
      WidgetPlace (hw, &mark_tmp, x, y, Width);

      /*
       * Text: ".<CR>"
       */
      mark_tmp.text = XtNewString(".\n");
      FormatPlace (hw, &mark_tmp, x, y, Width);
#endif /* ISINDEX_SUBMIT */

      /*
       * Horizontal rule
       */
      ConditionalLineFeed (hw, x, y, 1);
      HRulePlace (hw, x, y, Width);
      ConditionalLineFeed (hw, x, y, 1);

      /*
       * Close the form
       */
      ConditionalLineFeed (hw, x, y, 1);
      CurrentForm->end = WidgetId;
      ConditionalLineFeed (hw, x, y, 2);
      AddNewForm (hw, CurrentForm);
      CurrentForm = NULL;
    }
    break;
  case M_HRULE:
    ConditionalLineFeed (hw, x, y, 1);
    HRulePlace (hw, x, y, Width);
    ConditionalLineFeed (hw, x, y, 1);
    break;
  case M_LINEBREAK:
    LineFeed (hw, x, y);
    break;
  default:
    break;
  }
  if ((font != NULL) && (font != currentFont))
  {
    NewFont (font);
    currentFont = font;
  }
}


/*
 * 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.
 */
void
FormatChunk (hw, x, y)
HTMLWidget hw;
int *x, *y;
{
  struct mark_up *mptr;

  /*
   * Format all objects
   */
  mptr = hw->html.html_objects;
  Last = NULL;
  while (mptr != NULL)
  {
    TriggerMarkChanges (hw, mptr, x, y);
    /*
     * Save last non-text mark
     */
    if (mptr->type != M_NONE)
    {
      Last = mptr;
    }
    mptr = mptr->next;
  }
}


/*
 * 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 (hw, Fwidth)
HTMLWidget hw;
int *Fwidth;
{
  int x, y;
  int width;
  struct mark_up *msave;
#ifdef TIMING
  gettimeofday (&Tv, &Tz);
  fprintf (stderr, "FormatAll enter (%d.%d)\n", Tv.tv_sec, Tv.tv_usec);
#endif

  width = *Fwidth;
  MaxWidth = width;

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

  /*
   * Initialize local variables, some from the widget
   */
  MarginW = hw->html.margin_width;
  Fg = hw->html.foreground;
  Bg = hw->core.background_pixel;
  Underlines = 0;
  DashedUnderlines = False;
  Width = width;
  TextIndent = MarginW;
  ElementId = 0;
  WidgetId = 0;
  LineNumber = 1;
  LineBottom = 0;
  BaseLine = -100;
  CharsInLine = 0;
  IndentLevel = 0;
  Ignore = 0;
  Preformat = 0;
  PF_LF_State = 0;
  NeedSpace = 0;
  Internal = False;
  Strikeout = False;
  AnchorText = NULL;
  DescType = &BaseDesc;
  ListData = &BaseDesc;
  DescType->type = D_NONE;
  DescType->count = 0;
  DescType->compact = 0;
  DescType->next = NULL;
  CurrentForm = NULL;
  CurrentSelect = NULL;
  TextAreaBuf = NULL;

  /*
   * Free the old title, if there is one.
   */
  if (hw->html.title != NULL)
  {
    XtFree(hw->html.title);
    hw->html.title = NULL;
  }
  TitleText = NULL;

#ifdef THROW_AWAY_OLD_LIST
  /*
   * Free up previously formatted elements
   */
  FreeLineList (hw->html.formatted_elements);
  hw->html.formatted_elements = NULL;
#endif

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

  /*
   * Set up a starting font, and starting x, y, position
   */
  NewFont (hw->html.font);
  currentFont = hw->html.font;
  saveFont = NULL;
  FontStack = &FontBase;
  FontStack->font = hw->html.font;

  x = TextIndent;
  y = hw->html.margin_height;

  /*
   * Start a null element list, to be filled in as we go.
   */
  Current = NULL;

  /*
   * If we have parsed special header text, fill it in now.
   */
  if (hw->html.html_header_objects != NULL)
  {
    msave = hw->html.html_objects;
    hw->html.html_objects = hw->html.html_header_objects;
    FormatChunk (hw, &x, &y);

    if (saveFont != NULL)
    {
      hw->html.font = saveFont;
      saveFont = NULL;
    }
    NewFont (hw->html.font);
    currentFont = hw->html.font;

    ConditionalLineFeed (hw, &x, &y, 1);

    hw->html.html_objects = msave;
  }

  /*
   * Format all objects for width
   */
  FormatChunk (hw, &x, &y);

  /*
   * If we have parsed special footer text, fill it in now.
   */
  if (hw->html.html_footer_objects != NULL)
  {
    if (saveFont != NULL)
    {
      hw->html.font = saveFont;
      saveFont = NULL;
    }
    NewFont (hw->html.font);
    currentFont = hw->html.font;

    Preformat = 0;
    PF_LF_State = 0;
    NeedSpace = 0;

    ConditionalLineFeed (hw, &x, &y, 1);

    msave = hw->html.html_objects;
    hw->html.html_objects = hw->html.html_footer_objects;
    FormatChunk (hw, &x, &y);

    hw->html.html_objects = msave;
  }

  /*
   * Ensure a linefeed after the final element.
   */
  ConditionalLineFeed (hw, &x, &y, 1);

  /*
   * Restore the proper font from unterminated preformatted text
   * sequences.
   */
  if (saveFont != NULL)
  {
    hw->html.font = saveFont;
    saveFont = NULL;
  }

  /*
   * Free any extra of the pre-allocated list.
   * Terminate the element list.
   */
  if ((Current != NULL) && (Current->next != NULL))
  {
    FreeLineList (Current->next);
    Current->next = NULL;
  }
  else if ((Current == NULL) && (hw->html.formatted_elements != NULL))
  {
    FreeLineList (hw->html.formatted_elements);
    hw->html.formatted_elements = NULL;
  }

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

  /*
   * Make the line array indexed into the element list
   * and store it into the widget
   */
  hw->html.line_count = LineNumber;
  if (hw->html.line_array != NULL) XtFree((char *) hw->html.line_array);
  hw->html.line_array = MakeLineList (hw->html.formatted_elements,
				      LineNumber);

  /*
   * If the passed in MaxWidth was wrong, correct it.
   */
  if (MaxWidth != width)
  {
    *Fwidth = MaxWidth;
  }

#ifdef TIMING
  gettimeofday (&Tv, &Tz);
  fprintf (stderr, "FormatAll exit (%d.%d)\n", Tv.tv_sec, Tv.tv_usec);
#endif
  return (y);
}


/*
 * Redraw a linefeed.
 * Basically a filled rectangle at the end of a line.
 */
void
LinefeedRefresh (hw, eptr)
HTMLWidget hw;
struct ele_rec *eptr;
{
  int x1, y1;
  unsigned int width, height;

#ifdef NO_EXTRA_FILLS
  /*
   * The actualt height of the rectangle to fill is strange, based
   * an a differente between eptr->font->(ascent/descent) and
   * eptr->font->max_bounds.(ascent/descent) which I don't quite
   * understand. But it works.
   * Deal with bad Lucidia descents.
   */
  x1 = eptr->x;
  if (x1 > (int) hw->core.width)
  {
    width = 0;
  }
  else
  {
    width = hw->core.width - x1;
  }
#ifdef SHORT_LINEFEEDS
  y1 = eptr->y + eptr->y_offset + eptr->font->max_bounds.ascent -
    eptr->font->ascent;
  height = eptr->font->ascent + eptr->font->descent;
#else
  y1 = eptr->y + eptr->font->max_bounds.ascent - eptr->font->ascent;
  height = eptr->line_height;
#endif /* SHORT_LINEFEEDS */
#else
  x1 = eptr->x;
  if (x1 > (int) hw->core.width)
  {
    width = 0;
  }
  else
  {
    width = hw->core.width - x1;
  }
#ifdef SHORT_LINEFEEDS
  y1 = eptr->y + eptr->y_offset;
  if (eptr->font->descent > eptr->font->max_bounds.descent)
  {
    height = eptr->font->max_bounds.ascent +
      eptr->font->descent;
  }
  else
  {
    height = eptr->font->max_bounds.ascent +
      eptr->font->max_bounds.descent;
  }
#else
  y1 = eptr->y;
  height = eptr->line_height;
#endif /* SHORT_LINEFEEDS */
#endif /* NO_EXTRA_FILLS */

  x1 = x1 - hw->html.scroll_x;
  y1 = y1 - hw->html.scroll_y;

  if (eptr->selected == True)
  {
    XSetForeground (XtDisplay (hw), hw->html.drawGC, eptr->fg);
  }
  else
  {
    XSetForeground (XtDisplay (hw), hw->html.drawGC, eptr->bg);
  }
  XFillRectangle (XtDisplay (hw->html.view), XtWindow (hw->html.view),
		  hw->html.drawGC,
		  x1, y1, width, height);
}


/*
 * Redraw part of a formatted text element, in the passed fg and bg
 */
void
PartialRefresh (hw, eptr, start_pos, end_pos, fg, bg)
HTMLWidget hw;
struct ele_rec *eptr;
int start_pos, end_pos;
unsigned long fg, bg;
{
  int ascent;
  char *tdata;
  int tlen;
  int x, y, width;
  int partial;

  XSetFont (XtDisplay (hw), hw->html.drawGC, eptr->font->fid);
  ascent = eptr->font->max_bounds.ascent;
  width = -1;
  partial = 0;

  if (start_pos != 0)
  {
    int dir, nascent, descent;
    XCharStruct all;

#ifdef ASSUME_FIXED_WIDTH_PRE
    if (eptr->font == hw->html.plain_font)
    {
      all.width = eptr->font->max_bounds.width * start_pos;
    }
    else
    {
      XTextExtents (eptr->font, (char *) eptr->edata,
		    start_pos, &dir, &nascent, &descent, &all);
    }
#else
    XTextExtents (eptr->font, (char *) eptr->edata,
		  start_pos, &dir, &nascent, &descent, &all);
#endif /* ASSUME_FIXED_WIDTH_PRE */
    x = eptr->x + all.width;
    tdata = (char *) (eptr->edata + start_pos);
    partial = 1;
  }
  else
  {
    x = eptr->x;
    tdata = (char *) eptr->edata;
  }

  if (end_pos != (eptr->edata_len - 2))
  {
    tlen = end_pos - start_pos + 1;
    partial = 1;
  }
  else
  {
    tlen = eptr->edata_len - start_pos - 1;
  }

  y = eptr->y + eptr->y_offset;

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

#ifndef NO_EXTRA_FILLS
  {
    int dir, nascent, descent;
    XCharStruct all;
    int height;

    /*
     * May be safe to used the cached full width of this
     * string, and thus avoid a call to XTextExtents
     */
    if ((!partial) && (eptr->width != 0))
    {
      all.width = eptr->width;
    }
    else
    {
#ifdef ASSUME_FIXED_WIDTH_PRE
      if (eptr->font == hw->html.plain_font)
      {
	all.width = eptr->font->max_bounds.width * tlen;
      }
      else
      {
	XTextExtents (eptr->font, (char *) tdata,
		      tlen, &dir, &nascent, &descent, &all);
      }
#else
      XTextExtents (eptr->font, (char *) tdata,
		    tlen, &dir, &nascent, &descent, &all);
#endif /* ASSUME_FIXED_WIDTH_PRE */
    }

    XSetForeground (XtDisplay (hw), hw->html.drawGC, bg);

    height = (eptr->font->max_bounds.ascent - eptr->font->ascent);
    if (height > 0)
    {
      XFillRectangle (XtDisplay (hw->html.view),
		      XtWindow (hw->html.view),
		      hw->html.drawGC, x, y,
		      (unsigned int) all.width, (unsigned int) height);
    }
    height = (eptr->font->max_bounds.descent - eptr->font->descent);
    if (height > 0)
    {
      XFillRectangle (XtDisplay (hw->html.view),
		      XtWindow (hw->html.view),
		      hw->html.drawGC, x,
		      (int) (y + eptr->font->max_bounds.ascent +
			     eptr->font->descent),
		      (unsigned int) all.width, (unsigned int) height);
    }
    width = all.width;
  }
#endif /* NO_EXTRA_FILLS */

  XSetForeground (XtDisplay (hw), hw->html.drawGC, fg);
  XSetBackground (XtDisplay (hw), hw->html.drawGC, bg);

  XDrawImageString (XtDisplay (hw->html.view), XtWindow (hw->html.view),
		    hw->html.drawGC,
		    x, y + ascent,
		    (char *) tdata, tlen);

  if (eptr->underline_number)
  {
    int i, ly;

    if (eptr->dashed_underline)
    {
      XSetLineAttributes (XtDisplay (hw), hw->html.drawGC, 1,
			  LineDoubleDash, CapButt, JoinBevel);
    }
    else
    {
      XSetLineAttributes (XtDisplay (hw), hw->html.drawGC, 1,
			  LineSolid, CapButt, JoinBevel);
    }

    if (width == -1)
    {
      int dir, nascent, descent;
      XCharStruct all;

#ifdef ASSUME_FIXED_WIDTH_PRE
      if (eptr->font == hw->html.plain_font)
      {
	all.width = eptr->font->max_bounds.width * tlen;
      }
      else
      {
	XTextExtents (eptr->font, (char *) tdata,
		      tlen, &dir, &nascent, &descent, &all);
      }
#else
      XTextExtents (eptr->font, (char *) tdata,
		    tlen, &dir, &nascent, &descent, &all);
#endif /* ASSUME_FIXED_WIDTH_PRE */
      width = all.width;
    }

    ly = (int) (y + eptr->font->max_bounds.ascent +
		eptr->font->descent - 1);

    for (i = 0; i < eptr->underline_number; i++)
    {
      XDrawLine (XtDisplay (hw->html.view),
		 XtWindow (hw->html.view), hw->html.drawGC,
		 x, ly, (int) (x + width), ly);
      ly -= 2;
    }
  }

  if (eptr->strikeout == True)
  {
    int ly;

    XSetLineAttributes (XtDisplay (hw), hw->html.drawGC, 1,
			LineSolid, CapButt, JoinBevel);

    if (width == -1)
    {
      int dir, nascent, descent;
      XCharStruct all;

#ifdef ASSUME_FIXED_WIDTH_PRE
      if (eptr->font == hw->html.plain_font)
      {
	all.width = eptr->font->max_bounds.width * tlen;
      }
      else
      {
	XTextExtents (eptr->font, (char *) tdata,
		      tlen, &dir, &nascent, &descent, &all);
      }
#else
      XTextExtents (eptr->font, (char *) tdata,
		    tlen, &dir, &nascent, &descent, &all);
#endif /* ASSUME_FIXED_WIDTH_PRE */
      width = all.width;
    }

    ly = (int) (y + eptr->font->max_bounds.ascent +
		eptr->font->descent - 1);
    ly = ly - ((hw->html.font->max_bounds.ascent +
		hw->html.font->descent) / 2);

    XDrawLine (XtDisplay (hw->html.view), XtWindow (hw->html.view),
	       hw->html.drawGC,
	       x, ly, (int) (x + width), ly);
  }
}


/*
 * Redraw a formatted text element
 */
void
TextRefresh (hw, eptr, start_pos, end_pos)
HTMLWidget hw;
struct ele_rec *eptr;
int start_pos, end_pos;
{
  if (eptr->selected == False)
  {
    PartialRefresh (hw, eptr, start_pos, end_pos,
		    eptr->fg, eptr->bg);
  }
  else if ((start_pos >= eptr->start_pos) && (end_pos <= eptr->end_pos))
  {
    PartialRefresh (hw, eptr, start_pos, end_pos,
		    eptr->bg, eptr->fg);
  }
  else
  {
    if (start_pos < eptr->start_pos)
    {
      PartialRefresh (hw, eptr, start_pos, eptr->start_pos - 1,
		      eptr->fg, eptr->bg);
      start_pos = eptr->start_pos;
    }
    if (end_pos > eptr->end_pos)
    {
      PartialRefresh (hw, eptr, eptr->end_pos + 1, end_pos,
		      eptr->fg, eptr->bg);
      end_pos = eptr->end_pos;
    }
    PartialRefresh (hw, eptr, start_pos, end_pos,
		    eptr->bg, eptr->fg);
  }
}


/*
 * Redraw a formatted bullet element
 */
void
BulletRefresh (hw, eptr)
HTMLWidget hw;
struct ele_rec *eptr;
{
  int width, line_height;
  int x1, y1;

/*
   width = eptr->font->max_bounds.width;
 */
  width = eptr->font->max_bounds.lbearing +
    eptr->font->max_bounds.rbearing;
  /*
   * Deal with bad Lucidia descents.
   */
  if (eptr->font->descent > eptr->font->max_bounds.descent)
  {
    line_height = eptr->font->max_bounds.ascent +
      eptr->font->descent;
  }
  else
  {
    line_height = eptr->font->max_bounds.ascent +
      eptr->font->max_bounds.descent;
  }
  x1 = eptr->x;
  y1 = eptr->y + eptr->y_offset + (line_height / 2) - (width / 4);
  x1 = x1 - hw->html.scroll_x;
  y1 = y1 - hw->html.scroll_y;
  XSetFont (XtDisplay (hw), hw->html.drawGC, eptr->font->fid);
  XSetForeground (XtDisplay (hw), hw->html.drawGC, eptr->fg);
  XSetBackground (XtDisplay (hw), hw->html.drawGC, eptr->bg);
  if (eptr->indent_level < 2)
  {
    XFillArc (XtDisplay (hw->html.view), XtWindow (hw->html.view),
	      hw->html.drawGC,
	      (x1 - width), y1,
	      (width / 2), (width / 2), 0, 23040);
  }
  else if (eptr->indent_level == 2)
  {
    XSetLineAttributes (XtDisplay (hw), hw->html.drawGC, 1,
			LineSolid, CapButt, JoinBevel);
    XDrawRectangle (XtDisplay (hw->html.view),
		    XtWindow (hw->html.view), hw->html.drawGC,
		    (x1 - width), y1,
		    (width / 2), (width / 2));
  }
  else if (eptr->indent_level > 2)
  {
    XSetLineAttributes (XtDisplay (hw), hw->html.drawGC, 1,
			LineSolid, CapButt, JoinBevel);
    XDrawArc (XtDisplay (hw->html.view), XtWindow (hw->html.view),
	      hw->html.drawGC,
	      (x1 - width), y1,
	      (width / 2), (width / 2), 0, 23040);
  }
}


/*
 * Redraw a formatted horizontal rule element
 */
void
HRuleRefresh (hw, eptr)
HTMLWidget hw;
struct ele_rec *eptr;
{
  int width, height;
  int x1, y1;

  width = (int) hw->html.view_width - (int) (2 * hw->html.margin_width);
  if (width < 0)
  {
    width = 0;
  }

  x1 = eptr->x;
  y1 = eptr->y;
  x1 = x1 - hw->html.scroll_x;
  y1 = y1 - hw->html.scroll_y;
  height = eptr->line_height;

  /* blank out area */
  XSetForeground (XtDisplay (hw), hw->html.drawGC, eptr->bg);
  XFillRectangle (XtDisplay (hw->html.view), XtWindow (hw->html.view),
		  hw->html.drawGC, x1, y1, width, height);
  y1 = y1 + (height / 2) - 1;

  XSetLineAttributes (XtDisplay (hw), hw->html.drawGC, 1,
		      LineSolid, CapButt, JoinBevel);
  /* changing the GC back and forth is not the most efficient way.... */
  XSetForeground (XtDisplay (hw), hw->html.drawGC, eptr->fg);
  XDrawLine (XtDisplay (hw->html.view), XtWindow (hw->html.view),
	     hw->html.drawGC,
	     x1, y1, (int) (x1 + width), y1);
  XDrawLine (XtDisplay (hw->html.view), XtWindow (hw->html.view),
	     hw->html.drawGC,
	     x1, y1 + 1, (int) (x1 + width), y1 + 1);
}


/*
 * Redraw a formatted image element.
 * The color of the image border reflects whether it is an active anchor
 * or not.
 * Actual Pixmap creation was put off until now to make sure we
 * had a window.  If it hasn't been already created, make the Pixmap
 * now.
 */
void
ImageRefresh (hw, eptr)
HTMLWidget hw;
struct ele_rec *eptr;
{
  if (eptr->pic_data != NULL)
  {
    int x, y, extra;

    x = eptr->x;
    y = eptr->y + eptr->y_offset;

    if ((hw->html.border_images == True) ||
	((eptr->anchorHRef != NULL) &&
	 (!eptr->pic_data->internal)))
    {
      extra = IMAGE_BORDER;
    }
    else
    {
      extra = 0;
    }

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

    XSetForeground (XtDisplay (hw), hw->html.drawGC, eptr->fg);
    XSetBackground (XtDisplay (hw), hw->html.drawGC, eptr->bg);
    XFillRectangle (XtDisplay (hw->html.view),
		    XtWindow (hw->html.view), hw->html.drawGC,
		    x, y,
		    (eptr->pic_data->width + (2 * extra)),
		    extra);
    XFillRectangle (XtDisplay (hw->html.view),
		    XtWindow (hw->html.view), hw->html.drawGC,
		    x,
		    (y + eptr->pic_data->height + extra),
		    (eptr->pic_data->width + (2 * extra)),
		    extra);
    XFillRectangle (XtDisplay (hw->html.view),
		    XtWindow (hw->html.view), hw->html.drawGC,
		    x, y,
		    extra,
		    (eptr->pic_data->height + (2 * extra)));
    XFillRectangle (XtDisplay (hw->html.view),
		    XtWindow (hw->html.view), hw->html.drawGC,
		    (x + eptr->pic_data->width + extra),
		    y,
		    extra,
		    (eptr->pic_data->height + (2 * extra)));

    if (eptr->pic_data->image == (Pixmap) NULL)
    {
      if (eptr->pic_data->image_data != NULL)
      {
	eptr->pic_data->image = InfoToImage (hw,
					     eptr->pic_data);
      }
      else
      {
	if (eptr->pic_data->delayed)
	{
	  if ((eptr->anchorHRef != NULL) &&
	      (!IsDelayedHRef (hw, eptr->anchorHRef)) &&
	      (!IsIsMapForm (hw, eptr->anchorHRef)))
	  {
	    eptr->pic_data->image = DelayedImage (
						   hw, True);
	  }
	  else
	  {
	    eptr->pic_data->image = DelayedImage (
						   hw, False);
	  }
	}
	else
	{
	  /*
	   * Could be that the user opened another
	   * window, and the Pixmap was freed, and
	   * then they overflowed the cache,
	   * and the XImage data was freed.
	   * If this image was ever successfully
	   * fetched, try again before giving up.
	   */
	  if ((eptr->pic_data->fetched) &&
	      (hw->html.resolveDelayedImage != NULL))
	  {
	    ImageInfo *pdata;

	    pdata = eptr->pic_data;
	    eptr->pic_data = (*(resolveImageProc)
			      (hw->html.resolveDelayedImage)) (hw,
							       eptr->edata);
	    if (eptr->pic_data != NULL)
	    {
	      eptr->pic_data->delayed = 0;
	      /*
	       * Mark images we have sucessfully
	       * loaded at least once
	       */
	      if (eptr->pic_data->image_data != NULL)
	      {
		eptr->pic_data->fetched = 1;
	      }
	      /*
	       * Copy over state information from
	       * the last time we had this image
	       */
	      eptr->pic_data->ismap =
		pdata->ismap;
	      eptr->pic_data->fptr =
		pdata->fptr;
	      eptr->pic_data->internal =
		pdata->internal;
	      eptr->pic_data->text =
		pdata->text;
	    }
	    else
	    {
	      eptr->pic_data = NoImageData (hw);
	      eptr->pic_data->delayed = 0;
	      eptr->pic_data->internal = 0;
	    }
	  }
	  else
	  {
	    eptr->pic_data->image = NoImage (hw);
	  }
	}
      }
    }

    if (eptr->pic_data->image != (Pixmap) NULL)
    {
      XCopyArea (XtDisplay (hw->html.view),
		 eptr->pic_data->image,
		 XtWindow (hw->html.view), hw->html.drawGC, 0, 0,
		 eptr->pic_data->width, eptr->pic_data->height,
		 (x + extra),
		 (y + extra));

    }
    if ((eptr->pic_data->delayed) && (eptr->anchorHRef != NULL) &&
	(!IsDelayedHRef (hw, eptr->anchorHRef)) &&
	(!IsIsMapForm (hw, eptr->anchorHRef)))
    {
      XSetForeground (XtDisplay (hw), hw->html.drawGC,
		      eptr->fg);
      XFillRectangle (XtDisplay (hw->html.view),
		      XtWindow (hw->html.view), hw->html.drawGC,
		      x, (y + AnchoredHeight (hw)),
		      (eptr->pic_data->width + (2 * extra)),
		      extra);
    }
  }
}


void
RefreshTextRange (hw, start, end)
HTMLWidget hw;
struct ele_rec *start;
struct ele_rec *end;
{
  struct ele_rec *eptr;

  eptr = start;
  while ((eptr != NULL) && (eptr != end))
  {
    if (eptr->type == E_TEXT)
    {
      TextRefresh (hw, eptr,
		   0, (eptr->edata_len - 2));
    }
    eptr = eptr->next;
  }
  if (eptr != NULL)
  {
    if (eptr->type == E_TEXT)
    {
      TextRefresh (hw, eptr,
		   0, (eptr->edata_len - 2));
    }
  }
}


/*
 * Refresh all elements on a single line into the widget's window
 */
void
PlaceLine (hw, line)
HTMLWidget hw;
int line;
{
  struct ele_rec *eptr;

  /*
   * Item list for this line
   */
  eptr = hw->html.line_array[line];

  while ((eptr != NULL) && (eptr->line_number == (line + 1)))
  {
    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_LINEFEED:
      LinefeedRefresh (hw, eptr);
      break;
    case E_IMAGE:
      ImageRefresh (hw, eptr);
      break;
    case E_WIDGET:
      WidgetRefresh (hw, eptr);
      break;
    }
    eptr = eptr->next;
  }
}


/*
 * 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 (hw, x, y, pos)
HTMLWidget hw;
int x, y;
int *pos;
{
  struct ele_rec *eptr;
  struct ele_rec *rptr;
  int i, start, end, line, guess;
  int tx1, tx2, ty1, ty2;

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

  /*
   * Narrow the search down to a 2 line range
   * before beginning to search element by element
   */
  start = -1;
  end = -1;

  /*
   * Heuristic to speed up redraws by guessing at the starting line.
   */
  guess = y / (hw->html.font->max_bounds.ascent +
	       hw->html.font->max_bounds.descent);
  if (guess > (hw->html.line_count - 1))
  {
    guess = hw->html.line_count - 1;
  }
  while (guess > 0)
  {
    if ((hw->html.line_array[guess] != NULL) &&
	(hw->html.line_array[guess]->y <= y))
    {
      break;
    }
    guess--;
  }
  if (guess < 0)
  {
    guess = 0;
  }

  for (i = guess; i < hw->html.line_count; i++)
  {
    if (hw->html.line_array[i] == NULL)
    {
      continue;
    }
    else if (hw->html.line_array[i]->y <= y)
    {
      start = i;
      continue;
    }
    else
    {
      end = i;
      break;
    }
  }

  /*
   * Search may have already failed, or it may be a one line
   * range.
   */
  if ((start == -1) && (end == -1))
  {
    return (NULL);
  }
  else if (start == -1)
  {
    start = end;
  }
  else if (end == -1)
  {
    end = start;
  }

  /*
   * Search element by element, for now we only search
   * text elements, images, and linefeeds.
   */
  eptr = hw->html.line_array[start];
  ty1 = eptr->y;
  /*
   * Deal with bad Lucidia descents.
   */
  if (eptr->font->descent > eptr->font->max_bounds.descent)
  {
    ty2 = eptr->y + eptr->font->max_bounds.ascent +
      eptr->font->descent;
  }
  else
  {
    ty2 = eptr->y + eptr->font->max_bounds.ascent +
      eptr->font->max_bounds.descent;
  }
  line = eptr->line_number;
  /*
   * Searches on this line should extend to the top of the
   * next line, if possible.  Which might be far away if there
   * is an image on this line.
   */
  if (((line + 1) < hw->html.line_count) &&
      (hw->html.line_array[line + 1] != NULL))
  {
    ty2 = hw->html.line_array[line + 1]->y - 1;
  }
  /*
   * Else we are at the last line, and need to find its height.
   * The linefeed at the end should know the max height of the line.
   */
  else
  {
    struct ele_rec *teptr;

    teptr = eptr;
    while (teptr != NULL)
    {
      if (teptr->type == E_LINEFEED)
      {
	break;
      }
      teptr = teptr->next;
    }
    if (teptr != NULL)
    {
      ty2 = teptr->y + teptr->line_height - 1;
    }
  }

  rptr = NULL;
  while ((eptr != NULL) && (eptr->line_number <= (end + 1)))
  {
    if (eptr->line_number != line)
    {
      ty1 = ty2;
      /*
       * Deal with bad Lucidia descents.
       */
      if (eptr->font->descent > eptr->font->max_bounds.descent)
      {
	ty2 = eptr->y + eptr->font->max_bounds.ascent +
	  eptr->font->descent;
      }
      else
      {
	ty2 = eptr->y + eptr->font->max_bounds.ascent +
	  eptr->font->max_bounds.descent;
      }
      line = eptr->line_number;
      /*
       * Searches on this line should extend to the top of
       * the next line, if possible.  Which might be far
       * away if there is an image on this line.
       */
      if (((line + 1) < hw->html.line_count) &&
	  (hw->html.line_array[line + 1] != NULL))
      {
	ty2 = hw->html.line_array[line + 1]->y - 1;
      }
      /*
       * Else we are at the last line, and need to find its
       * height.  The linefeed at the end should know the
       * max height of the line.
       */
      else
      {
	struct ele_rec *teptr;

	teptr = eptr;
	while (teptr != NULL)
	{
	  if (teptr->type == E_LINEFEED)
	  {
	    break;
	  }
	  teptr = teptr->next;
	}
	if (teptr != NULL)
	{
	  ty2 = teptr->y + teptr->line_height - 1;
	}
      }
    }

    if (eptr->type == E_TEXT)
    {
      int dir, ascent, descent;
      XCharStruct all;

      tx1 = eptr->x;
      XTextExtents (eptr->font, (char *) eptr->edata,
		    eptr->edata_len - 1, &dir,
		    &ascent, &descent, &all);
      tx2 = eptr->x + all.width;
      if ((x >= tx1) && (x <= tx2) && (y >= ty1) && (y <= ty2))
      {
	rptr = eptr;
	break;
      }
    }
    else if ((eptr->type == E_IMAGE) && (eptr->pic_data != NULL))
    {
      tx1 = eptr->x;
      tx2 = eptr->x + eptr->pic_data->width;
      if ((x >= tx1) && (x <= tx2) && (y >= ty1) && (y <= ty2))
      {
	rptr = eptr;
	break;
      }
    }
    else if (eptr->type == E_LINEFEED)
    {
      tx1 = eptr->x;
      if ((x >= tx1) && (y >= ty1) && (y <= ty2))
      {
	rptr = eptr;
	break;
      }
      else if (eptr->next == NULL)
      {
	rptr = eptr;
	break;
      }
      else if (eptr->next != NULL)
      {
	int tmpy;

	tmpy = eptr->next->y + eptr->next->line_height;
	tx2 = eptr->next->x;
	if ((x < tx2) && (y >= ty2) && (y <= tmpy))
	{
	  rptr = eptr;
	  break;
	}
      }
    }
    eptr = eptr->next;
  }

  /*
   * If we found an element, locate the exact character position within
   * that element.
   */
  if (rptr != NULL)
  {
    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 concatonates the passed string to the current string, managing
 * both the current string length, and the total buffer length.
 */
void
strcpy_or_grow (str, slen, blen, add)
char **str;
int *slen;
int *blen;
char *add;
{
  int newlen;
  int addlen;
  char *buf;

  /*
   * If necessary, initialize this string buffer
   */
  if (*str == NULL)
  {
    *str = (char *)XtMalloc(1024 * sizeof (char));
    *blen = 1024;
    strcpy (*str, "");
    *slen = 0;
  }

  buf = *str;
  if ((buf == NULL) || (add == NULL))
  {
    return;
  }

  addlen = strlen (add);

  newlen = *slen + addlen;
  if (newlen >= *blen)
  {
    newlen = ((newlen / 1024) + 1) * 1024;
    buf = (char *)XtMalloc(newlen * sizeof (char));
    memcpy (buf, *str, *blen);
    XtFree((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 (elist, startp, endp, start_pos, end_pos, space_width, lmargin)
struct ele_rec *elist;
struct ele_rec *startp;
struct ele_rec *endp;
int start_pos, 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 == NULL)
  {
    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 != NULL) && (eptr != end))
  {
    /*
     * Skip the special internal text
     */
    if (eptr->internal == True)
    {
      eptr = eptr->next;
      continue;
    }

    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 != NULL) && (eptr->internal == False))
  {
    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 (hw, elist, startp, endp, start_pos, end_pos,
			 space_width, lmargin)
HTMLWidget hw;
struct ele_rec *elist;
struct ele_rec *startp;
struct ele_rec *endp;
int start_pos, end_pos;
int space_width;
int lmargin;
{
  int line;
  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 == NULL)
  {
    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 bullett, 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) ||
       (start->prev->line_number != start->line_number)))
  {
    eptr = start;
    while ((eptr != NULL) && (eptr != end) &&
	   (eptr->type != E_LINEFEED))
    {
      eptr = eptr->next;
    }
    if ((eptr != NULL) && (eptr->type == E_LINEFEED))
    {
      newline = 1;
      if ((start->prev != NULL) &&
	  (start->prev->type == E_BULLET))
      {
	start = start->prev;
      }
    }
    else
    {
      newline = 0;
    }
  }
  else
  {
    newline = 0;
  }

  lead_spaces = 0;
  last = start;
  eptr = start;
  line = eptr->line_number;
  while ((eptr != NULL) && (eptr != end))
  {
    /*
     * Skip the special internal text
     */
    if (eptr->internal == True)
    {
      eptr = eptr->next;
      continue;
    }

    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 != '\0')
      {
	char *ptr;
	int cnt;

	cnt = 0;
	ptr = line_buf;
	while ((ptr != NULL) && (*ptr != '\0'))
	{
	  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 != NULL)
      {
	XtFree(line_buf);
	line_buf = NULL;
      }
    }
    last = eptr;
    eptr = eptr->next;
  }
  if ((eptr != NULL) && (eptr->internal == False))
  {
    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 != '\0')
      {
	char *ptr;
	int cnt;

	cnt = 0;
	ptr = line_buf;
	while ((ptr != NULL) && (*ptr != '\0'))
	{
	  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 != NULL)
      {
	XtFree(line_buf);
	line_buf = NULL;
      }
    }
    last = eptr;
  }
  if (line_buf != NULL)
  {
    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 != '\0')
    {
      char *ptr;
      int cnt;

      cnt = 0;
      ptr = line_buf;
      while ((ptr != NULL) && (*ptr != '\0'))
      {
	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 != NULL)
  {
    XtFree(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 (hw, list)
HTMLWidget hw;
struct mark_up *list;
{
  struct mark_up *mptr;
  int plain_text;
  int listing_text;
  int pcnt, lcnt, pwidth, lwidth;
  int width;
  char *ptr;

  /*
   * Loop through object list looking at the plain, preformatted,
   * and listing text
   */
  width = 0;
  pwidth = 0;
  lwidth = 0;
  plain_text = 0;
  listing_text = 0;
  mptr = list;
  while (mptr != NULL)
  {
    /*
     * 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 != NULL) && (*ptr != '\0'))
      {
	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 != NULL) && (*ptr != '\0'))
      {
	ptr = MaxTextWidth (ptr, &lcnt);
	if (lcnt > lwidth)
	{
	  lwidth = lcnt;
	}
      }
    }
    mptr = mptr->next;
  }
  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);
}
