/* parsehtml.c - display code for html

ParseHTML() parses the HTML elements and generates a stream of commands for
displaying the document in the Paint buffer. The paint commands specify the
appearence of the document as a sequence of text, lines, and images. Each
command includes the position as a pixel offset from the start of the
document. This makes it easy to scroll efficiently in either direction.
The paint buffer must be freed and recreated if the window is resized.

The model needs to switch to relative offsets to enable the transition
to an wysiwyg editor for html+. Relative values for pixel offsets and
pointers to the html+ source would make it much easier to edit documents
as it would limit revisions to the paint stream to the region changed.

*/

/*

Janne Saarela
janne.saarela@hut.fi
28.7.1995

PrintSeqText() and PutText() now use PushValue() to place
the emph attribute to the PaintStream. The function prototypes
now have emph added as unsigned int.

*/


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

#include "www.h"
#include "tools.h"
#include "style.h"

#define LBUFSIZE 1024

extern Display *display;
extern int screen;
extern Window win;
extern GC disp_gc, gc_fill;
extern Cursor hourglass;
extern int UsePaper;
extern int debug;  /* controls display of errors */
extern int document;  /* HTMLDOCUMENT or TEXTDOCUMENT */
extern int busy;
extern int OpenURL;
extern int IsIndex;
extern int FindStr;
extern char *FindNextStr;
extern int SaveFile;
extern int sbar_width;
extern int statusHeight;
extern int ToolBarHeight;
extern unsigned long windowColor;
extern unsigned int win_width, win_height, tileWidth, tileHeight;
extern BOOL NoStyle;
extern Context *context;
extern unsigned long textColor, labelColor, windowTopShadow,
                     strikeColor, windowBottomShadow, windowShadow, windowColor;

/*
    The current top line is displayed at the top of the window,the pixel
    offset is the number of pixels from the start of the document.
*/

extern char *buffer;            /* the start of the document buffer */
extern long PixelOffset;        /* the pixel offset to top of window */
extern int hdrlen;              /* MIME header length at start of buffer */
extern long buf_height;
extern long lineHeight;
extern long chDescent;
extern int buf_width;
extern int PixelIndent;
extern int chStrike;
extern int spWidth;             /* width of space char */
extern int chWidth;             /* width of average char */
/*extern Doc NewDoc, CurrentDoc;*/
extern XFontStruct *pFontInfo;
extern XFontStruct *Fonts[FONTS];
extern int LineSpacing[FONTS], BaseLine[FONTS], StrikeLine[FONTS];
extern int ListIndent1, ListIndent2;
extern Frame background;
extern Image *images, *note_image, *caution_image, *warning_image;
extern Form *forms;
extern Doc *CurrentDoc;

char *bufptr;  /* parse position in the HTML buffer */
char *lastbufptr;  /* keep track of last position to store delta's */

Byte *TopObject;  /* first visible object in window */
Byte *paint; /* holds the sequence of paint commands */
int paintbufsize;     /* size of buffer, not its contents */
int paintlen;         /* where to add next entry */

int paintStartLine;   /* where line starts in the paint stream */
int above;            /* above baseline */
int below;            /* below baseline */
int voffset;          /* if positive then above baseline */
int IdAttributeFlag;  /* signals attribute is ID value */

int error;            /* set by parser */
int prepass;          /* true during table prepass */
int html_width;       /* tracks maximum width */
int min_width, max_width; /* table cell width */
int list_indent;
int damn_table=0; /* to debug table formatter */
extern int preformatted;
extern int font;  /* index into Fonts[] array */

static int EndTag, TagLen;
static int TokenClass, TokenValue, Token;
/* janet: not used: static char *EntityValue; */

unsigned int ui_n;
unsigned long ul_n;
int baseline;       /* from top of line */
long TermTop, TermBottom;

long PixOffset;     /* current offset from start of document */
long PrevOffset;    /* keep track for saving delta's */
long LastLIoffset;  /* kludge for <LI><LI> line spacing */
long ViewOffset;    /* for toggling between HTML/TEXT views */

extern long IdOffset;      /* offset for targetId */
extern char *targetptr;    /* for toggling view between HTML/TEXT views */
extern char *targetId;     /* for locating named Id during ParseHTML() */

int Here, TextLineWidth;
int HTMLInit = 0;
Image *start_figure, *figure;
long figEnd;
Form *form;

int LeftFlowMargin, RightFlowMargin;
int LeftMarginIndent = 0, RightMarginIndent = 0;
long FigureEnd = 0;

int class_len = 0;
char *class = NULL;
/* BOOL initial_cap = FALSE;*/

char *LastBufPtr, *StartOfLine, *StartOfWord; /* in HTML document */
static int LineLen, LineWidth, WordStart, WordWidth;
static char LineBuf[LBUFSIZE]; /* line buffer */

static char *Ones[] = {"i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"};
static char *Tens[] = {"x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"};
static char *Hundreds[] = {"c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"};

char small_caps[256] = {   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 
			  16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
			  32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
			  48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
			  64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
			  80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
			  96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
			  80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,123,124,125,126,127,
			 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
			 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
			 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
			 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
			 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
			 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
			 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
			 208,209,210,211,212,213,214,247,216,217,218,219,220,221,222,255 }; 

/* push 16 bit value onto paint buffer */

#define PushValue(p, value) ui_n = (unsigned int)value; *p++ = ui_n & 0xFF; *p++ = (ui_n >> 8) & 0xFF
#define Push32(p, value) ul_n = (unsigned long)value; *p++ = ul_n & 0xFF; *p++ = (ul_n >> 8) & 0xFF; *p++ = (ul_n >> 16) & 0xFF; *p++ = (ul_n >> 24) & 0xFF

/* expand paint stream to fit len bytes */

Byte *MakeRoom(int len)
{
    Byte *p;

    if (paintlen > paintbufsize - len)
    {
        paintbufsize = paintbufsize << 1;
        paint = (Byte *)realloc(paint, paintbufsize);
    }

    p = paint + paintlen;
    paintlen += len;
    return p;
}


void PrintBeginFrame(Frame *frame)
{
    Byte *p;
    /* janet: not used:    unsigned int len; */
    long offset;

    if (!prepass)
    {
        p = MakeRoom(FRAMESTLEN);
        frame->info = (p - paint);
        offset = frame->offset;
        *p++ = BEGIN_FRAME;
        PushValue(p, offset & 0xFFFF);
        PushValue(p, (offset >> 16) & 0xFFFF);
        PushValue(p, frame->indent);
        PushValue(p, frame->width);
        PushValue(p, 0);        /* subsequently filled in with height(1) */
	PushValue(p, 0);       /* subsequently filled in with height(2) */
        *p++ = frame->style;    /* frame's background style */
        *p++ = frame->border;   /* frame's border style */
#ifdef STYLE_COLOR_BORDER
        *p++ = frame->cb_ix;    /* frame's foreground color index */
#endif

#ifdef STYLE_BACKGROUND
	PutPointer(&p,(void *)StyleGet(S_BACKGROUND));
#endif

        PushValue(p, 0);        /* subsequently filled in with length */
    }
}

/* the size field after end of frame contents */
void PrintFrameLength(Frame *frame)
{
    Byte *p;
    unsigned int len;

    if (!prepass)
    {
     /* write the length field in frame's header */
        p = paint + frame->info + FRAMESTLEN - 2 ; /* --Spif 19-Oct-95 grmph !!! position of lenght in the paint stream */
        PushValue(p, frame->length);

     /* write the size field after frame's contents */
	p = MakeRoom(2); 
        len = p - paint - frame->info; /* --Spif 19-Oct-95 must see this */
	PushValue(p, len); 
    }
}

/* marker for pixel offset to end of frame */
void PrintEndFrame(Frame *parent, Frame *frame)
{
    Byte *p;
    unsigned int len;

    if (!prepass)
    {
        p = MakeRoom(FRAMENDLEN);
        len = p - (paint + frame->info);
        *p++ = END_FRAME;
        PushValue(p, len);
        PushValue(p, FRAMENDLEN-2);

     /* increase width of parent frame if necessary */

        if (parent)
        {
            len = frame->indent + frame->width + 2;

            if (parent->width < len)
                parent->width = len;

        /* and restore parent frame margins if this frame
           was involved in a text flow indent and is not
           obstructed by a subsequent text flow frame */

            /* howcome 7/10/94: prolonged the "if parent" block */

            if (frame->flow == ALIGN_LEFT)
                {
                    if (frame->pushcount == parent->leftcount)
                        {
                            parent->leftcount -= 1;
                            parent->leftmargin = frame->oldmargin;
                        }
                }
            else if (frame->flow == ALIGN_RIGHT)
                {
                    if (frame->pushcount == parent->rightcount)
                        {
                            parent->rightcount -= 1;
                            parent->rightmargin = frame->oldmargin;
                        }
                }
            else if (frame->flow == ALIGN_NOTE)
                {
                    if (frame->pushcount == parent->leftcount)
                        parent->leftcount -= 1;
                }
        }

    }
}

/* Write end markers for all peer frames and children.
   Note that the lists are flushed in the same order
   that items were pushed onto the list */
void FlushAllFrames(Frame *parent, Frame *frame)
{
    if (frame)
    {
        FlushAllFrames(parent, frame->next);
        FlushAllFrames(frame, frame->child);
        PrintEndFrame(parent, frame);
        FreeFrames(frame);
    }
}

/*
 Write end markers for any frames in peer list which have a
 finishing offset <= PixOffset. For any such frames, all
 descendant frames are flushed first. The process frees
 frames and removes them from the list of peers.

 If frame is the current frame then this procedure
 should be invoked as:

    frame->child = FlushFrames(frame, frame->child);

 This procedure assumes that BeginFrame() pushes
 new frames onto the front of the list of children,
 and guarantees that frames with the same offset are
 flushed to the paint stream in the order they were
 created. This property is needed for display purposes.
*/

Frame *FlushFrames(Frame *parent, Frame *frame)
{
    Frame *next;

    if (frame)
    {
        next = FlushFrames(parent, frame->next);

        if (frame->offset <= PixOffset)
        {
         /* first flush frame's children */
            FlushAllFrames(frame, frame->child);

         /* and now the frame itself */
            PrintEndFrame(parent, frame);
            free(frame);
            return next;
        }

        frame->next = next;
    }

    return frame;
}

void FlushPending(Frame *frame)
{
    if (frame && frame->child)
        frame->child = FlushFrames(frame, frame->child);
}

/* 
 The frame is created here, the new frame is returned so that the
 parser can later call EndFrame() at the end of the frame. Any frames
 which end before PixOffset are first flushed.
*/

Frame *BeginFrame(Frame *parent, int style, int border, int left, int right, BG_Style *bg)
{
    Frame *frame;

    FlushPending(parent);

 /* create frame and write begin frame marker */
    frame = (Frame *)calloc(1, sizeof(Frame));
    memset(frame, 0, sizeof(Frame));
    frame->offset = PixOffset;
    frame->indent = (parent ?  parent->leftmargin + left : left);
    frame->width = right - left;
    frame->style = style;
    frame->border = border;
#ifdef STYLE_COLOR_BORDER
    frame->cb_ix = (int)bg;
#else
    frame->cb_ix = 0;
#endif
    frame->flow = ALIGN_CENTER; /* implies this is a figure frame */
    frame->next = frame->child = NULL;
    frame->box_list = NULL;
    PrintBeginFrame(frame);
    return frame;
}

/* This writes the frame's height in the frame's header.
   Here we need to push the frame onto the front of the
   list of the parent frame's children so that FlushFrames()
   can write the end frame marker to the paint queue */
void EndFrame(Frame *parent, Frame *frame)
{
    Byte *p;
 /* janet: not used: Frame *next,  *prev; */

 /* update background.height if needed */

    if (PixOffset > background.height)
        background.height = PixOffset;

 /* write height into paint struct for frame */

    frame->height = PixOffset - frame->offset;
    p = paint + frame->info + 9; /* --Spif 19-Oct-95 offset in frame to height(1) and (2) */
    PushValue(p, frame->height & 0xFFFF);
    PushValue(p, (frame->height >> 16) & 0xFFFF);

 /* change frame->offset to end of frame */
    frame->offset = PixOffset;

 /* and now push onto list of children */
    frame->next = parent->child;
    parent->child = frame;
}

int ListCells(Frame *cells)
{
    int n;

    for (n = 0; cells != NULL; ++n)
    {
#if defined PRINTF_HAS_PFORMAT
        printf("address = %p, indent = %d, width = %d, height = %ld\n",
            cells, cells->indent, cells->width, cells->height);
#else
        printf("address = %lx, indent = %d, width = %d, height = %ld\n",
            cells, cells->indent, cells->width, cells->height);
#endif /* POINTER_IS_64BIT */
        cells = cells ->next;
    }

    return n;
}

/*
 Insert cell at end of list of cells
*/
void /* wm 19.Jan.95 */
InsertCell(Frame **cells, Frame *cell)
{
    Frame *frame, *next;

    frame = *cells;
    cell->next = NULL;

    if (frame == NULL)
        *cells = cell;
    else
    {
        for (frame = *cells;;)
        {
            next = frame->next;

            if (next == NULL)
            {
                frame->next = cell;
                break;
            }

            frame = next;
        }
    }
}

/*
 This routine adjusts height of all table cells which end
 on this row and then calls EndFrame() to move them to
 the list of frames awaiting PrintEndFrame()
*/

void FlushCells(Frame *parent, int row, Frame **cells)
{
    Frame *prev, *frame, *next;

    prev = NULL;
    frame = *cells;

    while (frame)
    {
        if (frame->lastrow <= row)
        {
            next = frame->next;

            if (prev)
                prev->next = next;
            else
                *cells = next;

            frame->height = PixOffset - frame->offset;
            frame->next = NULL;
            EndFrame(parent, frame);
            frame = next;
            continue;
        }

        prev = frame;
        frame = frame->next;
    }
}

/* insert TEXTLINE container */

void TextLineFrame(Frame *frame)
{
    Byte *p;

    if (prepass)
    {
        paintStartLine = 0;
        TextLineWidth = 0;
    }
    else
    {
        FlushPending(frame);
        TextLineWidth = 0;
        p = MakeRoom(TXTLINLEN);
        paintStartLine = p - paint;
        *p++ = TEXTLINE;
    
        PushValue(p, PixOffset & 0xFFFF);
        PushValue(p, (PixOffset >> 16) & 0xFFFF);

 /* baseline & indent set at end of line by EndOfLine() */

        *p++ = 0; *p++ = 0;
        *p++ = 0; *p++ = 0;
	*p++ = 0; *p++ = 0;
    }
}

/*
    This procedure writes the end of text line element
    and the baseline, line height and indent as appropriate
    for the frame's current margin settings. This handles
    horizontal alignment in the face of changing margins.
*/
void EndOfLine(Frame *frame, int align)
{
    unsigned int n, height, w;
    int indent, delta = 0, len;
    Byte *p;

    if (!prepass)
	StyleSetFlag(S_LEADING_FLAG,FALSE);

    if (paintStartLine >= 0)
    {
        /* fill in baseline for current line */

        if (!prepass)
        {
	    if (frame == &background)
            {
/*                w = background.width - background.rightmargin;*/
                w = background.width - background.rightmargin - (int)StyleGet(S_MARGIN_RIGHT);  /* howcome 26/2/95 */

                if (w > WinWidth - 4)
                    w = WinWidth - 4;

/*                w -= background.leftmargin;*/
                w -= background.leftmargin;

                if (align == ALIGN_LEFT)
                    delta = 0;
                else if (align == ALIGN_CENTER)
                    delta = (w - TextLineWidth) / 2;
                else if (align == ALIGN_RIGHT)
                    delta = w - TextLineWidth;
		else if (align == ALIGN_JUSTIFY)
		    delta = 0;
            }
            else
            {
                if (align == ALIGN_LEFT)
                    delta = 0;
                else if (align == ALIGN_CENTER)
                    delta = (frame->width /* - frame->leftmargin
                               - frame->rightmargin*/ - TextLineWidth) / 2;
                else if (align == ALIGN_RIGHT)
                    delta = frame->width/* - frame->leftmargin
                               - frame->rightmargin*/ - TextLineWidth;
		else if (align == ALIGN_JUSTIFY)
		    delta = 0;
	    }

            indent = (delta > 0 ? frame->leftmargin + delta : frame->leftmargin);
            height = above + below;

            paint[paintStartLine + 5] = (above & 0xFF);
            paint[paintStartLine + 6] = (above >> 8) & 0xFF;
            paint[paintStartLine + 7] = (indent & 0xFF);
            paint[paintStartLine + 8] = (indent >> 8) & 0xFF;
            paint[paintStartLine + 9] = (height & 0xFF);
            paint[paintStartLine + 10] = (height >> 8) & 0xFF;

	    p = MakeRoom(3);
            *p++ = '\0';  /* push end of elements marker */

            /* and write text line frame length */

            n = p - paint - paintStartLine;
            PushValue(p, n);
        }
	
	if(frame->flow == ALIGN_JUSTIFY) 
 	    len = LineWidth + frame->leftmargin;
 	else
 	    len = TextLineWidth + frame->leftmargin; 
  
        if (len > frame->width)
                frame->width = len;

        PixOffset += above + below;
        paintStartLine = -1;
        above = 0;
        below = 0;
        TextLineWidth = 0;
#if 0
        EndFigure();
#endif
    }

#if 0
    if (start_figure)
        PrintFigure(BEGIN_FIG);
#endif
}

/* push horizontal rule onto paint stream */

void PrintRule(Frame *frame, int type, int left, int right, int dy)
{
    Byte *p;
    /* not used:     int x; */

    if (paintStartLine < 0)
        TextLineFrame(frame);

    if (type == HLINE)
    {
        if (!prepass)
        {
            p = MakeRoom(RULEFLEN);
            *p++ = (RULE | type);
            PushValue(p, left);
            PushValue(p, right);
            PushValue(p, dy);
#ifdef STYLE_COLOR
	    *p++ = (Byte) StyleGet(S_COLOR);
#endif
#ifdef STYLE_BACKGROUND
	    PutPointer(&p,(void *)StyleGet(S_BACKGROUND)); /* must be set to the structures defined by the style --Spif 18-Oct-95 */
#endif
            above = max(above, 2);
            below = max(below, 2);
        }
    }
    else
    {
        if (frame == &background)
        {
            if (background.width - right > WinWidth - right)
                right += background.width - WinWidth;
        }

        right += frame->rightmargin;

        if (frame->width - right > TextLineWidth)
            TextLineWidth = frame->width - right;

/*
        above = max(above, 3);
        below = max(below, 5);
*/
        above = max(above, (int)StyleGet(S_MARGIN_TOP));
        below = max(below, (int)StyleGet(S_MARGIN_BOTTOM));

        if (!prepass)
        {
	    p = MakeRoom(RULEFLEN);
            *p++ = (RULE | type);
            PushValue(p, left);
            PushValue(p, (frame->width - right));
            PushValue(p, dy);
#ifdef STYLE_COLOR
	    *p++ = (Byte) StyleGet(S_COLOR);
#endif
#ifdef STYLE_BACKGROUND
	    PutPointer(&p, (void *)StyleGet(S_BACKGROUND)); /* must be set to the structures defined by the style --Spif 18-Oct-95 */
#endif
        }
    }
}


void PrintLine(Frame *frame, int left, int right, int top, int bottom)  /* staalesc 13/12/95 */
{
    Byte *p;

    if (paintStartLine < 0)
        TextLineFrame(frame);

    if (!prepass)
    {
        p = MakeRoom(LINEFLEN);
        *p++ = (LINE);
        PushValue(p, left);
        PushValue(p, right);
        PushValue(p, top);
        PushValue(p, bottom);
#ifdef STYLE_COLOR
      *p++ = (Byte) StyleGet(S_COLOR);
#endif
#ifdef STYLE_BACKGROUND
      PutPointer(&p, (void *)StyleGet(S_BACKGROUND));
#endif

        above = max(above, 2); /* staalesc: Are these correct??? */
        below = max(below, 2);
    }
}

/* push bullet onto paint stream */

void PrintBullet(Frame *frame, int depth, int font, int size)
{
    Byte *p;

    if (paintStartLine < 0)
        TextLineFrame(frame);

    if (Here + B_SIZE> TextLineWidth)
        TextLineWidth = Here + B_SIZE;

    above = max(above, ASCENT(font));
    below = max(below, DESCENT(font));

    if (!prepass)
    {
        p = MakeRoom(BULLETFLEN);
        *p++ = BULLET;
        PushValue(p, Here);
        PushValue(p, depth);

	*p++ = font; /*StyleGet(S_FONT);*/ /* howcome 25/4/95: adding vertical position of bullet item */
	*p++ = size; /*StyleGet(S_FONT_SIZE);*/ /* spif 8/1/96: size... */
	*p++ = (Byte) StyleGet(S_COLOR);
#ifdef STYLE_BACKGROUND
 	PutPointer(&p, (void *)StyleGet(S_BACKGROUND)); /* must be set to the structures defined by the style --Spif 18-Oct-95 */
#endif
    }
}

void WrapImageIfNeeded(Frame *frame, int align,
                       int left, int right, int width, int up, int down)
{
    if (!preformatted)
    {
        if (Here > left && Here + width + 2 > frame->width - frame->rightmargin - right)
        {
            EndOfLine(frame, align);

            Here = left;
            above = up;
            below = down;
        }
        else
        {
            if (up > above)
                above = up;

            if (down > below)
                below = down;
        }
    }
    else
    {
        if (up > above)
            above = up;

        if (down > below)
            below = down;
    }
}

void WrapFieldIfNeeded(Field *field, Frame *frame, int align, int left, int right)
{
    if (!preformatted)
    {
        if (Here > left && field->x + field->width > frame->width - frame->rightmargin - right)
        {
            EndOfLine(frame, align);
            Here = left;
            field->x = Here;
            above = field->above;
            below = field->height - above;
        }
        else
        {
            if (above < field->above)
                above = field->above;

            if (below < field->height - field->above)
                below = field->height - above;
        }
    }
    else
    {
        if (above < field->above)
            above = field->above;

        if (below < field->height - field->above)
            below = field->height - field->above;
    }
}

/* push text input field */

void PrintInputField(Frame *frame, Field *field)
{
    Byte *p;

    if (paintStartLine < 0)
        TextLineFrame(frame);

    if (field->x + field->width > TextLineWidth)
        TextLineWidth = field->x + field->width;

    if (!prepass)
    {
        p = MakeRoom(INPUTFLEN);
        field->object = p - paint;
        *p++ = INPUT;
	PutPointer(&p,(void *)field);
#ifdef STYLE_BACKGROUND
	PutPointer(&p, (void *)StyleGet(S_BACKGROUND)); /* must be set to the structures defined by the style --Spif 18-Oct-95 */
#endif
    }
}

/* push normal or preformatted string */
/* emph contains font and emphasis */

void RealPrintString(Frame *frame, unsigned int emph, int font,
                    int delta, char *ref, char *buf, int len, int width, BOOL tight_font, Byte text_color, BG_Style *text_background)
{
    Byte *p;

/*    int indent; */

/*    indent = Here + LeftMarginIndent;*/ /* figures on LHS */
/*    indent = Here + style->margin_left;*/ /* howcome 21/2/95 */

    if (paintStartLine < 0)
        TextLineFrame(frame);

    if (!prepass)
        p = MakeRoom(STRINGFLEN);

    if (Here + width > TextLineWidth)
        TextLineWidth = Here + width;


    /* In the case of a big initial, the descent should be calculated
       specifically for the character in question */

    if (tight_font) {
	above = max(above, Fonts[font]->per_char[*buf].ascent);
/*
	if (above == 0)
	    above = max(above, ASCENT(font));
*/
	below = max(below, Fonts[font]->per_char[*buf].descent);
/*
	if (below == 0)
	    below = max(below, DESCENT(font));
*/
    } else {
	if (!StyleGetFlag(S_LEADING_FLAG))
	    above = max(above, ASCENT(font) + (int)StyleGet(S_FONT_LEADING));
	else
	    above = max(above, ASCENT(font));
	below = max(below, DESCENT(font)); /* howcome 28/8/95: is this the right place to add leading? */
    }                                      /* --Spif 13-Nov-95 this was not the right place ;) */

    if (!prepass)
    {
        *p++ = (preformatted ? (STRING | PRE_TEXT) : STRING);
	PushValue (p, emph);
        *p++ = font;

	if ( StyleGetFlag(S_INDENT_FLAG))
	{
	    /*   Here += StyleGet(S_INDENT); */
	    Here += StyleGetFlag(S_INDENT_FLAG); /* flag carrying a value... hum... --Spif 14-Nov-95 */
	    StyleSetFlag(S_INDENT_FLAG,FALSE);
	};

	if (StyleGetFlag(S_MARGIN_TOP_FLAG)) /* this is the right place for this, but it doesn't work... yet ;) --Spif 16-Jan-96 */
	{
	    PixOffset += StyleGetFlag(S_MARGIN_TOP_FLAG);
	    StyleSetFlag(S_MARGIN_TOP_FLAG, FALSE);
	};

#ifdef STYLE_COLOR
	*p++ = text_color;
#endif
#ifdef STYLE_BACKGROUND
	PutPointer(&p,(void *)text_background); /* must be set to the structures defined by the style --Spif 18-Oct-95 */
#endif
        *p++ = delta + 128;  /* baseline offset 128 is zero, 255 is +127, 0 is -128 */

/*
	if (frame->leftcount) {
	    PushValue(p, Here - style->margin_left);
	} else {
*/
	    PushValue(p, Here);
/*	}*/

        PushValue(p, len);
        PushValue(p, width);
	PutPointer(&p, (void *)buf);
	PutPointer(&p, (void *)ref);
    }
}


void PrintString(Frame *frame, unsigned int emph, int font,
                    int delta, char *buf, int len, int width, int wrapped)
{
    int i, j, k, fix, index, icw = 0;
    int sc_width,dumpint; /* --Spif 5/10/95 small caps fix */
    int OldTextLineWidth, OldHere; 
    BOOL emph_set = FALSE;
    BOOL ok;
    char dump_char[2];

    fix = (int)StyleGet(S_FONT);

    if ( ((int)StyleGet(S_TEXT_EFFECT) == TEXT_EFFECT_INITIAL_CAP) && (StyleGetFlag(S_INITIAL_FLAG)) ) {
	int alt_fix = (int)StyleGet(S_ALT_FONT);
	
	RealPrintString(frame, (emph_set ? 0 : emph), alt_fix, delta, buf, buf, 1, width, 
			TRUE, (Byte) StyleGet(S_ALT_COLOR), (BG_Style *)StyleGet(S_ALT_BACKGROUND) );
	emph_set = TRUE;
	icw = XTextWidth(Fonts[alt_fix], buf, 1);
	Here += icw;
	StyleSetFlag(S_INITIAL_FLAG, False);
	buf++; len--;
    }
    
    StyleSetFlag(S_FONT_LEADING,FALSE);

    if ((int)StyleGet(S_FONT_STYLE) == FONT_STYLE_SMALL_CAPS) {
	int sc_fix = (int)StyleGet(S_SMALL_CAPS_FONT);

	for(i=k=0; k<len; i++, k++) {
	    sc_width=CompoundTextWidth(buf + i, 0, 1,&dumpint) + (int)StyleGet(S_TEXT_SPACING); /* a hack... rename this stuff */
	    OldTextLineWidth = TextLineWidth ;    /* --Spif 6-Oct-95 keep this to update TextLineWidth with the right width */     
	    OldHere = Here;
	    /* we must give the entire width if it is emphasized, to tell the paint stream to display a beeautiful box around
	       the text, but the real TextLineWidth must be calculated with the width of only ONE character at a time */

	    if (buf[i]=='&') {
		for(ok= FALSE,j=i; !ok && j<len; j++)
	       		    ok = ((buf[j]==';') || (buf[j]==' '));  /* ' ' is here for dummy html writers ;) */		
		index = *dump_char = (char)entity(buf+i+1, &j);
		if (index < 0)
		    index+=256; 
		dump_char[1]=0;
 		RealPrintString(frame, (emph_set ? 0 : emph), sc_fix, delta, buf, &small_caps[index], 1, width,  /* how can we capitalize entities? */
				FALSE, (Byte) StyleGet(S_COLOR), (BG_Style *)StyleGet(S_BACKGROUND));
		emph_set = TRUE;
		if(((index + small_caps[index]) % 256))
		    Here += XTextWidth(Fonts[sc_fix], dump_char,1) + (int)StyleGet(S_TEXT_SPACING);
		else
		    Here += XTextWidth(Fonts[fix], dump_char,1) + (int)StyleGet(S_TEXT_SPACING);
		i += j -1;
	    } else if (islower(buf[i])) {
		RealPrintString(frame, (emph_set ? 0 : emph), sc_fix, delta, buf, &small_caps[buf[i]], 1, width,
				FALSE, (Byte) StyleGet(S_COLOR), (BG_Style *)StyleGet(S_BACKGROUND));
		emph_set = TRUE;
 		Here += XTextWidth(Fonts[sc_fix], &small_caps[buf[i]], 1) + (int)StyleGet(S_TEXT_SPACING); 
	    } else if (isdigit(buf[i])) {
		RealPrintString(frame, (emph_set ? 0 : emph), sc_fix, delta, buf, &small_caps[buf[i]], 1, width, 
				FALSE, (Byte) StyleGet(S_COLOR), (BG_Style *)StyleGet(S_BACKGROUND));
		emph_set = TRUE;
		Here += XTextWidth(Fonts[sc_fix], &small_caps[buf[i]], 1) + (int)StyleGet(S_TEXT_SPACING); 
	    } else {
		RealPrintString(frame, (emph_set ? 0 : emph), fix, delta, buf, &small_caps[buf[i]], 1, width, 
				FALSE, (Byte) StyleGet(S_COLOR), (BG_Style *)StyleGet(S_BACKGROUND));
		emph_set = TRUE;
		Here += XTextWidth(Fonts[fix], &small_caps[buf[i]], 1) + (int)StyleGet(S_TEXT_SPACING); 
	    }
	    if( OldHere + sc_width > OldTextLineWidth)  /* -- Spif: 6-Oct-95 small caps fix */ 
	        TextLineWidth = OldHere + sc_width;
	 };
	TextLineWidth -= (int)StyleGet(S_TEXT_SPACING);
	Here -= (int)StyleGet(S_TEXT_SPACING);
    } else if(StyleGet(S_TEXT_SPACING))
    {
	for(i=k=0; k<len; i++, k++) {
	    sc_width=CompoundTextWidth(buf + i, 0, 1,&dumpint) + (int)StyleGet(S_TEXT_SPACING); /* a hack... rename this stuff */
	    OldTextLineWidth = TextLineWidth ;  
	    OldHere = Here;
	    /* we must give the entire width if it is emphasized, to tell the paint stream to display a beautiful box around
	       the text, but the real TextLineWidth must be calculated with the width of only ONE character at a time */
	    
	    if (buf[i]=='&') {
		for(ok= FALSE,j=i; !ok && j<len; j++)
		    ok = ((buf[j]==';') || (buf[j]==' '));  /* ' ' is here for dummy html writers ;) */		
		RealPrintString(frame, (emph_set ? 0 : emph), fix, delta, buf, buf + i, 1, width,  /* how can we capitalize entities? */
				FALSE, (Byte) StyleGet(S_COLOR), (BG_Style *)StyleGet(S_BACKGROUND));
		emph_set = TRUE;
		*dump_char = entity(buf+i+1, &j);
		dump_char[1]=0;
 		Here += XTextWidth(Fonts[fix], dump_char,1) + (int)StyleGet(S_TEXT_SPACING);
		i += j -1;
	    } 
	    else {
		RealPrintString(frame, (emph_set ? 0 : emph), fix, delta, buf, buf + i, 1, width, 
				FALSE, (Byte) StyleGet(S_COLOR), (BG_Style *)StyleGet(S_BACKGROUND));
		emph_set = TRUE;
		Here += XTextWidth(Fonts[fix], buf + i, 1) + (int)StyleGet(S_TEXT_SPACING); 
	    }
	    if( OldHere + sc_width > OldTextLineWidth)
	        TextLineWidth = OldHere + sc_width;
	};
	TextLineWidth -= (int)StyleGet(S_TEXT_SPACING);
	Here -= + (int)StyleGet(S_TEXT_SPACING);
    } else {
	if(frame->flow != ALIGN_JUSTIFY)
	    RealPrintString(frame, (emph_set ? 0 : emph), fix, delta, buf, buf, len, width, 
			    FALSE, (Byte) StyleGet(S_COLOR), (BG_Style *)StyleGet(S_BACKGROUND));
	else
	{
	    int nb_space = 0;
	    int remaining_space;
	    
	    
	    for(k=0; k<len; k++)
		nb_space += (buf[k]==' ')||(buf[k]=='\n');
	    if(!nb_space || !wrapped){
		RealPrintString(frame, (emph_set ? 0 : emph), fix, delta, buf, buf, len, width, 
				FALSE, (Byte) StyleGet(S_COLOR), (BG_Style *)StyleGet(S_BACKGROUND));
	    }
	    else
	    {
		remaining_space = frame->width - width - Here - StyleGetFlag(S_INDENT_FLAG);
#if 1
/*		printf("len %d :nbspace %d : remaining_space %d\n", len, nb_space, remaining_space);
 *		printf("width %d, frame->width %d, frame->indent %d, Here %d\n", width, frame->width, frame->indent, Here);
 */ 
		for(i=k=0; k<len; i++, k++) {
		    if(buf[i]=='\n')
			sc_width=XTextWidth(Fonts[fix], " ", 1) + (remaining_space/(nb_space ? nb_space :1));
		    else
			sc_width=CompoundTextWidth(buf + i, 0, 1,&dumpint) + (*(buf+i)==' ')*(remaining_space/(nb_space ? nb_space :1));
		    OldTextLineWidth = TextLineWidth ;  
		    OldHere = Here;
		    /* we must give the entire width if it is emphasized, to tell the paint stream to display a beeautiful box around
		       the text, but the real TextLineWidth must be calculated with the width of only ONE character at a time */
		    
		    if (buf[i]=='&') {
			for(ok= FALSE,j=i; !ok && j<len; j++)
			    ok = ((buf[j]==';') || (buf[j]==' '));  /* ' ' is here for dummy html writers ;) */		
			RealPrintString(frame, (emph_set ? 0 : emph), fix, delta, buf, buf + i, 1, width,  /* how can we capitalize entities? */
					FALSE, (Byte) StyleGet(S_COLOR), (BG_Style *)StyleGet(S_BACKGROUND));
			emph_set = TRUE;
			*dump_char = entity(buf+i+1, &j);
			dump_char[1]=0;
			Here += XTextWidth(Fonts[fix], dump_char,1);
			i += j -1;
		    } 
		    else {
			if(buf[i]=='\n')
			{
			    emph_set = TRUE;
			    Here += XTextWidth(Fonts[fix], " ", 1) + (remaining_space/(nb_space ? nb_space :1)); 
			    remaining_space -= (remaining_space/(nb_space ? nb_space :1));
			    nb_space--;
			}
			else
			{
			    RealPrintString(frame, (emph_set ? 0 : emph), fix, delta, buf, buf + i, 1, width, 
					    FALSE, (Byte) StyleGet(S_COLOR), (BG_Style *)StyleGet(S_BACKGROUND));
			    emph_set = TRUE;
			    Here += XTextWidth(Fonts[fix], buf + i, 1) + (*(buf+i)==' ')*(remaining_space/(nb_space ? nb_space :1)); 
			    if(*(buf+i)==' ')
			    {
				remaining_space -= (remaining_space/(nb_space ? nb_space :1));
				nb_space--;
			    }
			}
		    }
		    if( OldHere + sc_width > OldTextLineWidth)
			TextLineWidth = OldHere + sc_width;
		}
#else
		    RealPrintString(frame, (emph_set ? 0 : emph), fix, delta, buf, buf, len, width, 
				    FALSE, (Byte) StyleGet(S_COLOR), (BG_Style *)StyleGet(S_BACKGROUND));
#endif
	    }
	}
	emph_set = TRUE;
	Here += (width - icw);
    }
}


int CompoundTextWidth(char *s, int start, int len, int *space_p)
{
    int width = 0, i, k, j, ok;
    int fix = (int)StyleGet(S_FONT);
    char dump_char[2];

    if (((int)StyleGet(S_TEXT_EFFECT) == TEXT_EFFECT_INITIAL_CAP) && (StyleGetFlag(S_INITIAL_FLAG))) {
	int alt_fix = (int)StyleGet(S_ALT_FONT);

	width += XTextWidth(Fonts[alt_fix], s, 1);
	start++; len--;
    }

    if ((int)StyleGet(S_FONT_STYLE) == FONT_STYLE_SMALL_CAPS) {
	for(i=k=start; k < (len + start); i++,k++) {
	    width += (int)StyleGet(S_TEXT_SPACING);
	    if (islower(s[i]))
		width += XTextWidth(Fonts[(int)StyleGet(S_SMALL_CAPS_FONT)], &small_caps[s[i]], 1);
	    else if (s[i]=='&') {
		for(ok= FALSE,j=i; !ok && j<(len+start); j++)
		    ok = ((s[j]==';') || (s[j]==' ')); 
		*dump_char = entity(s+i+1, &j);
		dump_char[1]=0;
		i+=j-1;
		width += XTextWidth(Fonts[(int)StyleGet(S_SMALL_CAPS_FONT)], dump_char, 1);
	    } else if (isdigit(s[i])) {
		width += XTextWidth(Fonts[(int)StyleGet(S_SMALL_CAPS_FONT)], s + i, 1);
	    } else
		width += XTextWidth(Fonts[fix], s + i, 1);
	}
	*space_p = XTextWidth(Fonts[(int)StyleGet(S_SMALL_CAPS_FONT)], " ", 1) + (int)StyleGet(S_TEXT_SPACING);
	return width;
    } else if(StyleGet(S_TEXT_SPACING)) {
	for(i=k=start; k < (len + start); i++,k++) {
	    width += (int)StyleGet(S_TEXT_SPACING);
	    if (s[i]=='&') {
		for(ok= FALSE,j=i; !ok && j<(len+start); j++)
		    ok = ((s[j]==';') || (s[j]==' ')); 
		*dump_char = entity(s+i+1, &j);
		dump_char[1]=0;
		i+=j-1;
		width += XTextWidth(Fonts[fix], dump_char, 1);
	    } else
		width += XTextWidth(Fonts[fix], s + i, 1);
	}
	*space_p = XTextWidth(Fonts[fix], " ", 1) + (int)StyleGet(S_TEXT_SPACING); /* WORD_SPACING instead */
	return width;
    } else {
	*space_p = XTextWidth(Fonts[fix], " ", 1);
	return (width + XTextWidth(Fonts[fix], s + start, len));
    }
}


/* Push explicit text onto paint stream */

void PrintSeqText(Frame *frame, unsigned int emph, int font, char *s, int width)
{
    Byte *p;
    int len;

    if (paintStartLine < 0)
        TextLineFrame(frame);

    if (!prepass)
    {
        len = strlen(s);
        p = MakeRoom(SEQTEXTFLEN(len));

        if (Here + width > TextLineWidth)
            TextLineWidth = Here + width;

        above = max(above, ASCENT(font));
        below = max(below, DESCENT(font));

        *p++ = SEQTEXT;
	PushValue (p, emph);
        *p++ = font;
#ifdef STYLE_COLOR
	*p++ = (Byte) StyleGet(S_COLOR);
#endif
#ifdef STYLE_BACKGROUND
  	PutPointer(&p, (void *)StyleGet(S_BACKGROUND)); /* must be set to the structures defined by the style --Spif 18-Oct-95 */
#endif
        PushValue(p, Here);
        PushValue(p, 0);
        *p++ = len;
        memcpy(p, s, len);
    }
}

/* for use by html-math parser */
int TextWidth(int font, char *str, int len, int *up, int *down)
{
    *up = Fonts[font]->max_bounds.ascent;
    *down = Fonts[font]->max_bounds.descent;
    return XTextWidth(Fonts[font], str, len);
}

/* for use by html-math parser */

void FontSize(int font, int *ascent, int *descent)
{
    *ascent = Fonts[font]->max_bounds.ascent;
    *descent = Fonts[font]->max_bounds.descent;
}

box *CreateBox(int x, int y, int width, int height)
{
    box *new_box;
    
    new_box = (box *)malloc(sizeof(box));
    
    if(!new_box)
    {
	fprintf(stderr, "Ran Out of memory in CreateBox, Exiting\n");
	exit(1);
    }

    new_box->x      = x;
    new_box->y      = y;
    new_box->width  = width;
    new_box->height = height;
    
    return new_box;
}

void AddBox(Frame *frame, int x, int y, int width, int height)
{
    box_link *new_link;
    box      *new_box;

    if(!frame)
	return;
#if 0
    printf("Adding Box %d,%d -> %d,%d\n", x, y, width, height); 
#endif
    new_link = (box_link *)malloc(sizeof(box_link));
    if(!new_link)
    {
    	fprintf(stderr, "Ran Out of memory in AddBox, Exiting\n");
	exit(1);
    }
    new_link->box  = CreateBox(x, y, width, height);
    new_link->next = frame->box_list; /* we add the box in first position */
    frame->box_list = new_link;
}

box *IsInBox(Frame *frame, int x, int y)
{
    box_link *curr_box;
    box_link *is_in;

    if(!frame)
	return 0;

    for(is_in=NULL, curr_box = frame->box_list;!is_in && curr_box; curr_box = curr_box->next)
	if((x >= curr_box->box->x) && (x <= curr_box->box->x + curr_box->box->width)
	   && (y >= curr_box->box->y) && (y <= curr_box->box->y + curr_box->box->height))
	    is_in = curr_box;

    return (is_in) ? is_in->box : NULL;
}

void CopyBoxList(Frame *dest_frame, Frame *frame)
{
    box_link *curr_box;

    if(!(dest_frame && frame))
	return;
    
    for(curr_box = frame->box_list;curr_box; curr_box = curr_box->next)
	AddBox(dest_frame, curr_box->box->x, curr_box->box->y, curr_box->box->width, curr_box->box->height);
}

int RightMargin(Frame *frame, int right, int voffset)
{
    int rightMargin;
    box *thebox;

    if (frame == &background)
    {
        rightMargin = background.width - background.rightmargin - right;

        if (rightMargin > WinWidth - 4)
            rightMargin = WinWidth - 4;

        rightMargin -= frame->leftmargin;
    }
    else
        rightMargin = frame->width - right /*- frame->leftmargin - frame->rightmargin*/;

    if(frame)
    {
	thebox = IsInBox(frame, rightMargin, voffset);
	while(thebox)
	{
	    rightMargin = thebox->x - 1;
	    thebox = IsInBox(frame, rightMargin, voffset);
	}
    }	
    return rightMargin;
}

int LeftMargin(Frame *frame, int left, int voffset)
{
    int current_left;
    box *thebox;

    current_left = left;
    if(frame)
    {
	thebox = IsInBox(frame, current_left, voffset);
	while(thebox)
	{
	    current_left = thebox->x + thebox->width + 1;
	    thebox = IsInBox(frame, current_left, voffset);
	}
    }	
    return current_left;
}

int Width(Frame *frame, int voffset)
{
    return frame->width;
}

void PutText(Frame *frame, unsigned int emph, int font, char *s, int len, int x, int y)
{
    Byte *p;

    if (paintStartLine < 0)
        TextLineFrame(frame);

    if (!prepass)
    {
        p = MakeRoom(SEQTEXTFLEN(len));
        *p++ = SEQTEXT;
	PushValue (p, emph);
        *p++ = font;
#ifdef STYLE_COLOR
	*p++ = (Byte) StyleGet(S_COLOR);
#endif
#ifdef STYLE_BACKGROUND
 	PutPointer(&p, (void *)StyleGet(S_BACKGROUND)); /* must be set to the structures defined by the style --Spif 18-Oct-95 */
#endif
	PushValue(p, x);
        PushValue(p, y);
        *p++ = len;
        memcpy(p, s, len);
    }
}

/* buf points to start of element in html source iff ismap is present */

void PrintImage(Frame *frame, int delta, unsigned int emph,
                char *buf, Image *image, unsigned int width, unsigned int height)
{
    Byte *p;
    Pixmap pixmap = 0;

    if (image)
	pixmap = image->pixmap;

    if (paintStartLine < 0)
        TextLineFrame(frame);

    if (Here + width > TextLineWidth)
        TextLineWidth = Here + width;

    if (prepass)  /* just update min/max widths */
    {
        if (width > min_width)
            min_width = width;
    }
    else
    {
        p = MakeRoom(IMAGEFLEN);

        *p++ = (IMAGE & 0xF) | (emph & (ISMAP | EMPH_ANCHOR | EMPH_INPUT ));
        PushValue(p, delta);
        PushValue(p, Here);
        PushValue(p, width);
        PushValue(p, height);
	PutPointer(&p,(void *)pixmap);
	PutPointer(&p, (void *)buf);
#ifdef STYLE_BACKGROUND
	PutPointer(&p, (void *)StyleGet(S_BACKGROUND)); /* must be set to the structures defined by the style --Spif 18-Oct-95 */
#endif
    }
}

#define NOBREAK 0
#define BREAK   1

/* check if current word forces word wrap and flush line as needed */
void WrapIfNeeded(Frame *frame, int align, unsigned int emph, int font, int left, int right)
{
    int WordLen, space, rightMargin;
    long line;

    if (paintStartLine < 0)
        TextLineFrame(frame);  /* alters the flow margins, if a figure just ended */

   
#ifdef OLD
    if (frame == &background)
    {
        rightMargin = background.width - background.rightmargin - right;

        if (rightMargin > WinWidth - 4)
            rightMargin = WinWidth - 4;

        rightMargin -= frame->leftmargin;
    }
    else
        rightMargin = frame->width - right /*- frame->leftmargin - frame->rightmargin*/;
#else
    rightMargin = RightMargin(frame, right, PixOffset);
#endif

    if(StyleGetFlag(S_INDENT_FLAG))
	rightMargin -= StyleGetFlag(S_INDENT_FLAG);

    LineBuf[LineLen] = '\0';  /* debug*/
    WordLen = LineLen - WordStart;
/*    WordWidth = XTextWidth(Fonts[font], LineBuf+WordStart, WordLen);*/
    WordWidth = CompoundTextWidth(LineBuf, WordStart, WordLen, &space);

/*    space = XTextWidth(Fonts[StyleGet(S_FONT)], " ", 1);*/    /* width of a space char */
/*    space = XTextWidth(Fonts[font], " ", 1);   */ /* width of a space char */
    line = LineSpacing[font];                   /* height of a line */

    if (WordWidth > min_width)      /* for tables */
        min_width = WordWidth;

    if (prepass)
    {
        TextLineWidth += WordWidth;
        LineWidth = LineLen = WordStart = 0;
        StartOfLine = bufptr;
    }
    else if (WordStart == 0 && Here + WordWidth > rightMargin)
    {
        /* word wider than window */
        if (left + WordWidth > rightMargin)
        {
            if (emph & EMPH_ANCHOR)
                WordWidth += 2;

            PrintString(frame, emph, font, voffset, StartOfLine, WordLen, WordWidth, FALSE);
            EndOfLine(frame, align);
	    LineWidth = LineLen = WordStart = 0;
            StartOfLine = bufptr;
        }
        else /* wrap to next line */
        {
            EndOfLine(frame, align);
	    LineWidth = WordWidth;
            LineLen = WordLen;
            WordStart = LineLen;
            StartOfLine = StartOfWord;
        }

       	Here = LeftMargin(frame, left, PixOffset);
    }
    else if (WordStart > 0 && Here + LineWidth + space + WordWidth > rightMargin)
    {
        if (emph & EMPH_ANCHOR)
            LineWidth += 2;

        PrintString(frame, emph, font, voffset, StartOfLine, WordStart-1, LineWidth, TRUE);

        EndOfLine(frame, align);
        Here = left;

     /* was memmove(LineBuf, LineBuf+WordStart, WordLen); 
        but memmove not available for SUNs and
        memcpy screws up for overlapping copies */

        {
            int n;
            char *p, *q;

            n = WordLen;
            p = LineBuf;
            q = LineBuf+WordStart;
            while (n-- > 0)
                *p++ = *q++;
        }

        LineWidth = WordWidth;
        LineLen = WordLen;
        WordStart = LineLen;
        StartOfLine = StartOfWord;
    }
    else /* word will fit on end of current line */
    {
        if (WordStart > 0)
            LineWidth += space;

        if (WordWidth > 0)
            LineWidth += WordWidth;

        WordStart = LineLen;
    }
}

void FlushLine(int linebreak, Frame *frame, int align,
                unsigned int emph, int font, int left, int right)
{
    int WordLen, space; /* janet: not used: delta, rightMargin */

/*    *StartOfLine = toupper(*StartOfLine);*//* howcome playing 5/5/95 */

    if (preformatted)
    {
        WordLen = LineLen - WordStart;
/*        LineWidth = XTextWidth(Fonts[font], LineBuf+WordStart, WordLen);*/
        LineWidth = CompoundTextWidth(LineBuf, WordStart, WordLen, &space);
    }
    else if (LineLen > 1 || (LineLen == 1 && LineBuf[0] != ' '))
        WrapIfNeeded(frame, align, emph, font, left, right);

    if (LineLen > 0)
    {
        if (emph & EMPH_ANCHOR)
            LineWidth += 2;

        /* watch out for single space as leading spaces
           are stripped by CopyLine */

        if (LineLen > 1 || LineBuf[0] != ' ')
            PrintString(frame, emph, font, voffset, StartOfLine, LineLen, LineWidth, FALSE);

        if (linebreak)
        {
            EndOfLine(frame, align);
            Here = left;
        }
/*
        else
            Here += LineWidth;
*/

        LineWidth = LineLen = WordStart = 0;
    }
    else if (linebreak)
    {
	/* watch out for empty preformatted lines */
        if (preformatted && TextLineWidth == 0)
            PixOffset += ASCENT(font) + DESCENT(font);

        if (paintStartLine >= 0)  /* was if (Here > left) */
        {
            EndOfLine(frame, align);
            Here = left;
        }
    }

    StartOfLine = StartOfWord = bufptr;
}




/* needs to cope with > in quoted text for ' and " */
void SwallowAttributes(void)
{
    int c;

    while ((c = *bufptr) && c != '>')
    {
        ++bufptr;
    }

    if (c == '>')
        ++bufptr;
}


/*
 Return a pointer to the ">" in an end comment (or to "\0" if there
 isn't one).  Example:
    "hiya <!-- <foo>there</foo> -- --whatever-- > there"
 pass this -^                      return this -^
 */

char *FindEndComment( char *ptr )
{
    char c, *s;

	for(;;)
	{
		ptr += 2; /* first character after opening "--" */

		if(!((s = strstr(ptr, "--")))) break;

		ptr = s + 1; /* second "-" -- ++ will put us on the next character */

		/* Skip past the optional white space (<= ' ' from GetToken) */

		while((c=*(++ptr)) && c <= ' ');

		if(c == '>')
		{
			return ptr; /* comment terminated correctly */
		}

		if(!strncmp(ptr, "--", 2))
		{
			/* comment is something like "<!--fine-- wrong>" */

			if(s=strchr(ptr,'>')) return s;

			break;
		}
	}

	/* Document ends in unfinished comment -- seek for '0' */

	if(*ptr) while(*(++ptr));

	return ptr;
}


/*
 char *tag points to start of tag string which is terminated
 by whitespace (including EOF) or a '>' character.

 return tag code or 0 if unknown.
*/

int RecogniseTag(void)
{
    int c, len;
    char *s;

     s = bufptr + 1;
  
    /* handle comments correctly -- code by mlvanbie@valeyard.uwaterloo.ca */

    if (*s == '!' && (*(s+1) == '-') && (*(s+2) == '-'))
    {
        bufptr += 4; /* eat "<!--" */
        EndTag = 0;
    	TagLen = 0;  /* we will be manipulating bufptr directly */
        TokenClass = EN_UNKNOWN;

	bufptr = FindEndComment( bufptr + 2 );    /* +2: eat "<!" */

#if 0
        while ((s = strstr(bufptr, "--")))
        {
            bufptr = s + 1; /* stay on second "-" */

	        /* Skip past the optional white space (<= ' ' from GetToken) */

	        while((c=*(++bufptr)) && c <= ' ');

            if(c == '>')
            {
                return UNKNOWN; /* pretend we have an unknown tag */
            }

            bufptr = s + 1; /* handles "<!-- ... --->" (nonoptimal) */
        }

        /* Document ends in unfinished comment -- seek for '0' */

        if(*bufptr) while(*(++bufptr));
#endif

        return UNKNOWN; /* unknown tag */
    }

    if (*s == '/')
    {
        EndTag = 1;
        ++s;
    }
    else
        EndTag = 0;

    if ((c = *s) == '!' || c == '?')
        ++s;
    else if (c != '>' && !isalpha(c))
        return PCDATA;

  /* find end of tag to allow use of strncasecmp */

    while ((c = *s, isalpha(c)) || isdigit(c))
        ++s;

    TagLen = s - bufptr;        /* how far to next char after tag name */
    len = TagLen - EndTag - 1;  /* number of chars in tag name itself */
    s -= len;
    c = TOLOWER(*s);

    if (isalpha(c))
    {
        if (c == 'a')
        {
            if (len == 1 && strncasecmp(s, "a", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_ANCHOR;
            }

            if (len == 3 && strncasecmp(s, "alt", len) == 0)
            {
                TokenClass = EN_BLOCK;
                return TAG_ALT;
            }

            if (len == 5 && strncasecmp(s, "added", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_ADDED;
            }

            if (len == 7 && strncasecmp(s, "address", len) == 0)
            {
                TokenClass = EN_BLOCK;
                return TAG_ADDRESS;
            }

            if (len == 8 && strncasecmp(s, "abstract", len) == 0)
            {
                TokenClass = EN_BLOCK;
                return TAG_ABSTRACT;
            }
        }
        else if (c == 'b')
        {
            if (len == 1)
            {
                TokenClass = EN_TEXT;
                return TAG_BOLD;
            }

            if (len == 2 && strncasecmp(s, "br", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_BR;
            }

            if (len == 4 && strncasecmp(s, "body", len) == 0)
            {
                TokenClass = EN_MAIN;
                return TAG_BODY;
            }

            if (len == 10 && strncasecmp(s, "blockquote", len) == 0)
            {
                TokenClass = EN_BLOCK;
                return TAG_QUOTE;
            }

            if (len == 4 && strncasecmp(s, "base", len) == 0)
            {
                TokenClass = EN_SETUP;
                return TAG_BASE;
            }
        }
        else if (c == 'c')
        {
            if (len == 4)
            {
                if (strncasecmp(s, "code", len) == 0)
                {
                    TokenClass = EN_TEXT;
                    return TAG_CODE;
                }

                if (strncasecmp(s, "cite", len) == 0)
                {
                    TokenClass = EN_TEXT;
                    return TAG_CITE;
                }
            }
            else if (len == 7 && (strncasecmp(s, "caption", len) == 0))/* howcome 3/2/95: = -> == after hint from P.M.Hounslow@reading.ac.uk */
            {
                TokenClass = EN_BLOCK;
                return TAG_CAPTION;
            }
        }
        else if (c == 'd')
        {
            if (len == 3 && strncasecmp(s, "dfn", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_DFN;
            }

	    /* howcome 11/8/95: added upport for DIR */

            if (len == 3 && strncasecmp(s, "dir", len) == 0)
            {
                TokenClass = EN_LIST;
                return TAG_UL;
            }


            if (len != 2)
                return 0;

            if (strncasecmp(s, "dl", len) == 0)
            {
                TokenClass = EN_LIST;
                return TAG_DL;
            }

            if (strncasecmp(s, "dt", len) == 0)
            {
                TokenClass = EL_DEFLIST;
                return TAG_DT;
            }

            if (strncasecmp(s, "dd", len) == 0)
            {
                TokenClass = EL_DEFLIST;
                return TAG_DD;
            }
        }
        else if (c == 'e')
        {
            if (len == 2 && strncasecmp(s, "em", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_EM;
            }
        }
        else if (c == 'f')
        {
            if (len == 3 && strncasecmp(s, "fig", len) == 0)
            {
                TokenClass = EN_BLOCK;
                return TAG_FIG;
            }
	    
	    /* --Spif 10/10/95 Form tag must be checked ! */
	    if (len == 4 && strncasecmp(s, "form", len) == 0)
	    {
	        TokenClass = EN_TEXT /*EN_BLOCK*/;
		return TAG_FORM;
	    };
        }
        else if (c == 'h')
        {
            if (len == 4 && strncasecmp(s, "head", len) == 0)
            {
                TokenClass = EN_SETUP;
                return TAG_HEAD;
            }

            if (len != 2)
                return 0;

            TokenClass = EN_HEADER;
            c = TOLOWER(s[1]);

            switch (c)
            {
                case '1':
                    return TAG_H1;
                case '2':
                    return TAG_H2;
                case '3':
                    return TAG_H3;
                case '4':
                    return TAG_H4;
                case '5':
                    return TAG_H5;
                case '6':
                    return TAG_H6;
                case 'r':
                    TokenClass = EN_BLOCK;
                    return TAG_HR;
            }
        }
        else if (c == 'i')
        {
            if (len == 1)
            {
                TokenClass = EN_TEXT;
                return TAG_ITALIC;
            }

            if (len == 3 && strncasecmp(s, "img", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_IMG;
            }

            if (len == 5 && strncasecmp(s, "input", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_INPUT;
            }

            if (len == 7 && strncasecmp(s, "isindex", len) == 0)
            {
                TokenClass = EN_SETUP;
                return TAG_ISINDEX;
            }
        }
        else if (c == 'k')
        {
            if (len == 3 && strncasecmp(s, "kbd", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_KBD;
            }
        }
        else if (c == 'l')
        {
            if (len == 2 && strncasecmp(s, "li", len) == 0)
            {
                TokenClass = EN_LIST;
                return TAG_LI;
            }

            if (len == 4 && strncasecmp(s, "link", len) == 0)
            {
                TokenClass = EN_SETUP;
                return TAG_LINK;
            }

        }
        else if (c == 'm')
        {
            if (len == 4 && strncasecmp(s, "math", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_MATH;
            }

            if (len == 6 && strncasecmp(s, "margin", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_MARGIN;
            }

	    /* howcome 11/8/95: added MENU to be compatible with HTML2 */
            if (len == 4 && strncasecmp(s, "menu", len) == 0)
            {
                TokenClass = EN_LIST;
                return TAG_UL;
            }


        }
        else if (c == 'n')
        {
            if (len == 4 && strncasecmp(s, "note", len) == 0)
            {
                TokenClass = EN_BLOCK;
                return TAG_NOTE;
            }
        }
        else if (c == 'o')
        {
            if (len == 2 && strncasecmp(s, "ol", len) == 0)
            {
                TokenClass = EN_LIST;
                return TAG_OL;
            }

            if (len == 6 && strncasecmp(s, "option", len) == 0)
            {
                TokenClass = EN_TEXT;  /* kludge for error recovery */
                return TAG_OPTION;
            }
        }
        else if (c == 'p')
        {
            if (len == 1)
            {
                TokenClass = EN_BLOCK;
                return TAG_P;
            }

            if (len == 3 && strncasecmp(s, "pre", len) == 0)
            {
                TokenClass = EN_BLOCK;
                return TAG_PRE;
            }
        }
        else if (c == 'q')
        {
            if (len == 1)
            {
                TokenClass = EN_TEXT;
                return TAG_Q;
            }

            if (len == 5 && strncasecmp(s, "quote", len) == 0)
            {
                TokenClass = EN_BLOCK;
                return TAG_QUOTE;
            }
        }
        else if (c == 'r')
        {
            if (len == 7 && strncasecmp(s, "removed", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_REMOVED;
            }
        }
        else if (c == 's')
        {
            if (len == 1)
            {
                TokenClass = EN_TEXT;
                return TAG_STRIKE;
            }

            if (len == 3 && strncasecmp(s, "sup", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_SUP;
            }

            if (len == 3 && strncasecmp(s, "sub", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_SUB;
            }

            if (len == 4 && strncasecmp(s, "samp", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_SAMP;
            }

            if (len == 5 && strncasecmp(s, "small", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_SMALL;
            }

            if (len == 6 && strncasecmp(s, "strong", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_STRONG;
            }

            if (len == 6 && strncasecmp(s, "select", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_SELECT;
            }

            if (len == 6 && strncasecmp(s, "strike", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_STRIKE;
            }

	    /* howcome 26/2/95 */

            if (len == 5 && strncasecmp(s, "style", len) == 0)
            {
                TokenClass = EN_SETUP;
                return TAG_STYLE;
            }

        }
        else if (c == 't')
        {
            if (len == 5 && strncasecmp(s, "title", len) == 0)
            {
                TokenClass = EN_SETUP;
                return TAG_TITLE;
            }

            if (len == 2 && strncasecmp(s, "tt", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_TT;
            }

            if (len == 2 && strncasecmp(s, "tr", len) == 0)
            {
                TokenClass = EN_TABLE;
                return TAG_TR;
            }

            if (len == 2 && strncasecmp(s, "th", len) == 0)
            {
                TokenClass = EN_TABLE;
                return TAG_TH;
            }

            if (len == 2 && strncasecmp(s, "td", len) == 0)
            {
                TokenClass = EN_TABLE;
                return TAG_TD;
            }

            if (len == 5 && strncasecmp(s, "table", len) == 0)
            {
                TokenClass = EN_BLOCK;
                return TAG_TABLE;
            }

            if (len == 8 && strncasecmp(s, "textarea", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_TEXTAREA;
            }
        }
        else if (c == 'u')
        {
            if (len == 1)
            {
                TokenClass = EN_TEXT;
                return TAG_UNDERLINE;
            }

            if (len == 2 && strncasecmp(s, "ul", len) == 0)
            {
                TokenClass = EN_LIST;
                return TAG_UL;
            }
        }
        else if (c == 'v')
        {
            if (len == 3 && strncasecmp(s, "var", len) == 0)
            {
                TokenClass = EN_TEXT;
                return TAG_VAR;
            }
        }
        else if (c == 'x')
        {
            if (len == 3 && strncasecmp(s, "xmp", len) == 0)
            {
                TokenClass = EN_BLOCK;
                return TAG_PRE;
            }
        }
    }

    TokenClass = EN_UNKNOWN;
    return UNKNOWN; /* unknown tag */
}


void UnGetToken(void)
{
    bufptr = LastBufPtr;
}

/*
   The token type is returned in the global token.
   Characters are returned in TokenValue while TokenClass
   is used to return a class value e.g. EN_SETUP or EN_BLOCK.
   Entity definitions are pointed to by EntityValue.

   The bufptr is moved past the token, except at the end
   of the buffer - as a safety precaution.
*/

int GetToken(void)
{
    int c, k, n;

    LastBufPtr = bufptr;
    c = *(unsigned char *)bufptr;
    TokenValue = c;

    if (bufptr <= targetptr)
        ViewOffset = PixOffset;

    if (c == '<' && (Token = RecogniseTag()) != PCDATA)
    {
        bufptr += TagLen;   /* to first char after tag name */
        return Token;
    }

    TokenClass = EN_TEXT;
    EndTag = 0;

    if (c == '&' && (isalpha(bufptr[1]) || bufptr[1] == '#'))
    {
        n = entity(bufptr + 1, &k);

        if (n)
        {
            bufptr += k;
            TokenValue = n;
            Token = PCDATA;
            return Token;
        }
    }

    if (c <= ' ')
    {
        if (c == '\0')
        {
            Token = ENDDATA;
            TokenClass = EN_UNKNOWN;
            return Token;
        }
        
        ++bufptr;
        Token = WHITESPACE;
        return Token;
    }


    ++bufptr;
    Token = PCDATA;
    return Token;
}

/* void ParseAnchorAttrs(char **href, int *hreflen, char **name, int *namelen, char **class_p, int *class_len_p);*/

/* get token, skipping white space and unknown tokens */

int SkipSpace(void)
{
    for (;;)
    {
        while (GetToken() == WHITESPACE);

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        break;
    }

    return Token;
}



/* assumes bufptr points to start of attribute */
char *ParseAttribute(int *len)
{
    int c;
    char *attr;

    *len = 0;
    attr = bufptr;
    IdAttributeFlag = 0;

    for (;;)
    {
        c = *bufptr;

        if (c == '>' || c == '\0')
            return attr;

        if (c == '=' || IsWhite(c))
            break;

        ++(*len);
        ++bufptr;
    }

    if (*len == 2 && strncasecmp(attr, "id", 2) ==0)
        IdAttributeFlag = 1;

    return attr;
}

/* values start with "=" or " = " etc. */
char *ParseValue(int *len)
{
    int c, delim;
    char *value;

    *len = 0;

    while (c = *bufptr, IsWhite(c))
        ++bufptr;

    if (c != '=')
        return 0;

    ++bufptr;   /* past the = sign */

    while (c = *bufptr, IsWhite(c))
        ++bufptr;

    if (c == '"' || c == '\'')
    {
        delim = c;
        ++bufptr;
    }
    else
        delim = 0;

    value = bufptr;

    for (;;)
    {
        c = *bufptr;

        if (c == '\0')
	    if(delim)
		return 0;  /* a big and dirty hack --Spif 27-Oct-95 */
	    else
		break;

        if (delim)
        {
            if (c == delim)
            {
                ++bufptr;
                break;
            }
        }
        else if (c == '>' || IsWhite(c))
            break;

        ++(*len);
        ++bufptr;
    }

    if (IdAttributeFlag && value && targetId)
    {
        if (strlen(targetId) == *len &&
            strncasecmp(value, targetId, *len) == 0)
        {
            IdOffset = PixOffset;
        }
    }

    IdAttributeFlag = 0;
    return value;
}


/*
   HREF attribute defines original document URL and is added by
   the browser when making a local copy of a document so that
   relative references can be deferenced to their original links
*/

void ParseBase(Frame *frame)
{
    char *href, *name;
    int hreflen, namelen;

    ParseAnchorAttrs(&href, &hreflen, &name, &namelen, &class, &class_len);
    if(href)
	CurrentDoc->base = strndup(href, hreflen);
}

void ParseLink(Frame *frame)
{
    char *href=NULL, *rel=NULL;
    int hreflen = 0, rellen = 0;
    int c, n, m;
    char *attr, *value;

    for (;;)
    {
        c = *bufptr++;

        if (c == '\0')
            break;

        if (c == '>')
            break;

        if (IsWhite(c))
            continue;

        --bufptr;
        attr = ParseAttribute(&n);
        value = ParseValue(&m);

        if (n == 4 && strncasecmp(attr, "href", n) == 0)
        {
            href = value;
            hreflen = m;
            continue;
        }

        if (n == 3 && strncasecmp(attr, "rel", n) == 0)
        {
            rel = value;
            rellen = m;
            continue;
        }
    }

    if (href && rel) {
	if (!NoStyle && !CurrentDoc->link_style && (strncasecmp(rel, "style", 5) == 0))
	    CurrentDoc->link_style = (char *)StyleLoad(href, hreflen, CurrentDoc->pending_reload); 
	/* (char *) because StyleLoad not declared -> int implied , must add include files */
    }
}


void ParseTitle(int implied, Frame *frame)
{
    if (EndTag)
    {
        SwallowAttributes();
        return;
    }

    if (!implied)
        SwallowAttributes();

    /* skip leading white space - subsequently contigous
       white space is compressed to a single space */
    while (GetToken() == WHITESPACE);
    UnGetToken();

    LineLen = 0;

    for (;;)
    {
        GetToken();

        if (Token == TAG_TITLE && EndTag)
        {
            SwallowAttributes();
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (Token == WHITESPACE)
        {
            while (GetToken() == WHITESPACE);
            UnGetToken();

            if (LineLen < LBUFSIZE - 1)
                LineBuf[LineLen++] = ' ';
            continue;
        }

        if (Token != PCDATA)
        {
            UnGetToken();
            break;
        }

        if (LineLen < LBUFSIZE - 1)
            LineBuf[LineLen++] = TokenValue;
    }    

    LineBuf[LineLen] = '\0';
    SetBanner(LineBuf);
}

/* howcome 26/2/95 */

void ParseStyle(Frame *frame)
{
    if (EndTag)
    {
        SwallowAttributes();
        return;
    }

    SwallowAttributes();

    /* skip leading white space - subsequently contigous
       white space is compressed to a single space */
    while (GetToken() == WHITESPACE);
    UnGetToken();

    LineLen = 0;

    for (;;)
    {
        GetToken();

        if (Token == TAG_STYLE && EndTag)
        {
            SwallowAttributes();
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (LineLen < LBUFSIZE - 1)
            LineBuf[LineLen++] = TokenValue;
    }    

    LineBuf[LineLen] = '\0';
    if (!NoStyle && !CurrentDoc->head_style)
	CurrentDoc->head_style = strdup (LineBuf);
}



void ParseSetUp(int implied, Frame *frame)
{
    if (EndTag)
    {
        SwallowAttributes();
        return;
    }

    if (!implied)
        SwallowAttributes();

    for (;;)
    {
        while (GetToken() == WHITESPACE);
/*        UnGetToken(); */ /* howcome 10/7/95 part of comment handling */

        if (Token == TAG_HEAD && EndTag)
        {
            SwallowAttributes();
            break;
        }

        if (Token == TAG_TITLE)
        {
            UnGetToken();
	    ParseTitle(0, frame);
            continue;
        }

        if (Token == TAG_ISINDEX)
        {
            SwallowAttributes();
            IsIndex = 1;
            ClearStatus();
            continue;
        }

        if (Token == TAG_BASE)
        {
	    UnGetToken();
	    ParseBase(frame);
            continue;
        }

	/* howcome 26/2/95 */

	if (Token == TAG_STYLE)
	{
	    UnGetToken();
	    ParseStyle(frame);
	    continue;
	}

	/* howcome 25/4/95 */

	if (Token == TAG_LINK)
	{
	    UnGetToken();
	    ParseLink(frame);
	    continue;
	}

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (Token == PCDATA || Token == ENTITY)
        {
            UnGetToken();
            break;
        }

        if (Token == ENDDATA || TokenClass != EN_SETUP)
        {
            UnGetToken();
            break;
        }
    }
}

void ParseAnchorAttrs(char **href, int *hreflen, char **name, int *namelen, char **class_p, int *class_len_p)
{
    int c, n, m;
    char *attr, *value;

    *href = 0;
    *name = 0;

    for (;;)
    {
        c = *bufptr++;

        if (c == '\0')
            break;

        if (c == '>')
            break;

        if (IsWhite(c))
            continue;

        --bufptr;
        attr = ParseAttribute(&n);
        value = ParseValue(&m);

        if (n == 4 && strncasecmp(attr, "href", n) == 0)
        {
            *href = value;
            *hreflen = m;
            continue;
        }

        if (n == 4 && strncasecmp(attr, "name", n) == 0)
        {
            *name = value;
            *namelen = m;
            continue;
        }

        if (n == 5 && strncasecmp(attr, "class", n) == 0)
        {
	    if (STYLE_TRACE) {
		char *s = strndup(attr, n);
		fprintf(stderr,"style_class %s\n",s);
		Free(s);
	    }

	    *class_p = value;
	    *class_len_p = m;
        }

    }

    if (*href == NULL)
	if (TAG_TRACE)
	    fprintf(stderr,"ParseAnchorAttrs: *href == NULL, hreflen = %d\n", *hreflen);
}

/* howcome30/5/95: addded support for widht and height */

void ParseImageAttrs(char **href, int *hreflen, int *align, int *ismap, int *width, int *height)
{
    int c, n, m;
    char *attr, *value;

    *href = NULL;               /* howcome: NULL !! */
    *align = ALIGN_BOTTOM;
    *ismap = 0;
    *width = 0; *height = 0;

    for (;;)
    {
        c = *bufptr++;

        if (c == '\0')
            break;

        if (c == '>')
            break;

        if (IsWhite(c))
            continue;

        --bufptr;
        attr = ParseAttribute(&n);
        value = ParseValue(&m);

        if (n == 3 && strncasecmp(attr, "src", n) == 0)
        {
            *href = value;
            *hreflen = m;
            continue;
        }


        if (n == 5 && strncasecmp(attr, "width", n) == 0)
        {
	    *width = atoi(value);
            continue;
        }

        if (n == 6 && strncasecmp(attr, "height", n) == 0)
        {
            *height = atoi(value);
            continue;
        }

        if (n == 5 && strncasecmp(attr, "align", n) == 0)
        {
            if (m == 3 && strncasecmp(value, "top", m) == 0)
                *align = ALIGN_TOP;
            else if (m == 6 && strncasecmp(value, "middle", m) == 0)
                *align = ALIGN_MIDDLE;
            else if (m == 6 && strncasecmp(value, "bottom", m) == 0)
                *align = ALIGN_BOTTOM;

            continue;
        }

        if (n == 5 && strncasecmp(attr, "ismap", n) == 0)
            *ismap = 1;
    }
}

void ParseFigureAttrs(char **href, int *hreflen, int *align, int *width, int *height)
{
    int c, n, m;
    char *attr, *value;

    *href = 0;
    *align = ALIGN_CENTER;
    *width = 0; *height = 0;

    for (;;)
    {
        c = *bufptr++;

        if (c == '\0')
            break;

        if (c == '>')
            break;

        if (IsWhite(c))
            continue;

        --bufptr;
        attr = ParseAttribute(&n);
        value = ParseValue(&m);

        if (n == 3 && strncasecmp(attr, "src", n) == 0)
        {
            *href = value;
            *hreflen = m;
            continue;
        }

        if (n == 5 && strncasecmp(attr, "width", n) == 0)
        {
	    *width = atoi(value);
            continue;
        }

        if (n == 6 && strncasecmp(attr, "height", n) == 0)
        {
            *height = atoi(value);
            continue;
        }

        if (n == 5 && strncasecmp(attr, "align", n) == 0)
        {
            if (m == 4 && strncasecmp(value, "left", m) == 0)
                *align = ALIGN_LEFT;
            else if (m == 6 && strncasecmp(value, "center", m) == 0)
                *align = ALIGN_CENTER;
            else if (m == 5 && strncasecmp(value, "right", m) == 0)
                *align = ALIGN_RIGHT;
            else if (m == 9 && strncasecmp(value, "bleedleft", m) == 0)
                *align = ALIGN_BLEEDLEFT;
            else if (m == 10 && strncasecmp(value, "bleedright", m) == 0)
                *align = ALIGN_BLEEDRIGHT;

            continue;
        }
    }
}
void /* wm 19.Jan.95 */
ParseTextAreaAttrs(int *type, char **name, int *nlen,
                char **value, int *vlen, int *rows, int *cols, int *flags)
{
    int c, n, m; /* janet: not used: checked */
    char *attr, *attrval;

    *type = TEXTFIELD;
    *rows = 4;
    *cols = 20;
    *flags = 0;
    *name = *value = "";
    *nlen = *vlen = 0;

    for (;;)
    {
        c = *bufptr++;

        if (c == '\0')
            break;

        if (c == '>')
            break;

        if (IsWhite(c))
            continue;

        --bufptr;
        attr = ParseAttribute(&n);
        attrval = ParseValue(&m);

        if (n == 4 && strncasecmp(attr, "type", n) == 0)
        {
            if (m == 4 && strncasecmp(attrval, "text", m) == 0)
                *type = TEXTFIELD;
            else if (m == 8 && strncasecmp(attrval, "checkbox", m) == 0)
                *type = CHECKBOX;
            else if (m == 5 && strncasecmp(attrval, "radio", m) == 0)
                *type = RADIOBUTTON;

            continue;
        }

        if (n == 4 && strncasecmp(attr, "name", n) == 0)
        {
            *name = attrval;
            *nlen = m;
            continue;
        }

        if (n == 5 && strncasecmp(attr, "value", n) == 0)
        {
            *value = attrval;
            *vlen = m;
            continue;
        }

        if (n == 4 && strncasecmp(attr, "rows", n) == 0)
        {
            sscanf(attrval, "%d", rows);
            continue;
        }

        if (n == 4 && strncasecmp(attr, "cols", n) == 0)
        {
            sscanf(attrval, "%d", cols);
            continue;
        }

        if (n == 5 && strncasecmp(attr, "error", n) == 0)
        {
            *flags |= IN_ERROR;
            continue;
        }

        if (n == 8 && strncasecmp(attr, "disabled", n) == 0)
        {
            *flags |= DISABLED;
            continue;
        }

        if (n == 7 && strncasecmp(attr, "checked", n) == 0)
            *flags |= CHECKED;
    }
}

void /* --Spif 11-Oct-95 */
ParseFormAttrs(int *method, char **action, int *alen)
{
    int c, n, m;
    char *attr, *attrval;
    
    *action = NULL;
    *method = GET; /* implied must add encoded-type.. */
    for (;;)
    {
        c= *bufptr++;

        if (c == '\0')
            break;

        if (c == '>')
            break;

        if (IsWhite(c))
            continue;

	--bufptr;
        attr = ParseAttribute(&n);
        attrval = ParseValue(&m);
	if (n == 6 && strncasecmp(attr, "method", n) == 0)
        {
	    if ( m == 3 && strncasecmp(attrval, "get", m) ==0)
	        *method = GET;
	    else if ( m == 4 && strncasecmp(attrval, "post", m) ==0)
	        *method = POST;
	}
	if ( n == 6 && strncasecmp(attr, "action", n) == 0)
	{
	  *action = attrval;
	  *alen = m;
	  continue;
	};
    };
}

void /* wm 19.Jan.95 */
ParseInputAttrs(int *type, char **name, int *nlen,
                char **value, int *vlen, int *size, int *flags, Image **image)
{
    int c, n, m; /* janet: not used: checked */
    char *attr, *attrval, *href = NULL;
    int hreflen = 0; /* --SPif */

    *type = TEXTFIELD;
    *size = 20;
    *flags = 0;
    *name = *value = "";
    *nlen = *vlen = 0;
    hreflen = 0;
    *image=NULL;

    for (;;)
    {
        c = *bufptr++;

        if (c == '\0')
            break;

        if (c == '>')
            break;

        if (IsWhite(c))
            continue;

        --bufptr;
        attr = ParseAttribute(&n);
        attrval = ParseValue(&m);

        if (n == 4 && strncasecmp(attr, "type", n) == 0)
        {
            if (m == 4 && strncasecmp(attrval, "text", m) == 0)
                *type = TEXTFIELD;
            else if (m == 8 && strncasecmp(attrval, "checkbox", m) == 0)
                *type = CHECKBOX;
            else if (m == 5 && strncasecmp(attrval, "radio", m) == 0)
                *type = RADIOBUTTON;
            else if (m == 6 && strncasecmp(attrval, "submit", m) == 0)
                *type = SUBMITBUTTON;
            else if (m == 5 && strncasecmp(attrval, "reset", m) == 0)
                *type = RESETBUTTON;
	    else if (m == 5 && strncasecmp(attrval, "image", m) ==0)
	        *type = SUBMITBUTTON; /* --Spif image in html2 = Submit */
	    else if (m == 6 && strncasecmp(attrval, "hidden", m) ==0)
	        *type = HIDDEN;
	    else if (m == 6 && strncasecmp(attrval, "passwd", m) ==0)
	        *type = PASSWD;
            continue;
        }

        if (n == 4 && strncasecmp(attr, "name", n) == 0)
        {
            *name = attrval;
            *nlen = m;
            continue;
        }

        if (n == 5 && strncasecmp(attr, "value", n) == 0)
        {
            *value = attrval;
            *vlen = m;
            continue;
        }

        if (n == 4 && strncasecmp(attr, "size", n) == 0)
        {
            sscanf(attrval, "%d", size);
            continue;
        }

        if (n == 5 && strncasecmp(attr, "error", n) == 0)
        {
            *flags |= IN_ERROR;
            continue;
        }

        if (n == 8 && strncasecmp(attr, "disabled", n) == 0)
        {
            *flags |= DISABLED;
            continue;
        }

        if (n == 7 && strncasecmp(attr, "checked", n) == 0)
            *flags |= CHECKED;

        if (n == 8 && strncasecmp(attr, "multiple", n) == 0)
            *flags |= MULTIPLE;

	/* --Spif 9-Oct-1995 */ 
	if (n == 3 && strncasecmp(attr, "src", n) == 0)
        {
	    href = attrval;
	    hreflen = m;
	};

    }
    /* --Spif 9-Oct-1995 */
    if (hreflen > 0)
        *image = GetImage(href, hreflen, CurrentDoc->pending_reload);

}

void /* wm 19.Jan.95 */
ParseCellAttrs(int *rowspan, int *colspan, int *align, int *nowrap)
{
    int c, n, m;
    char *attr, *attrval;

    *rowspan = *colspan = 1;
    *align = ALIGN_CENTER;
    *nowrap = 0;

    for (;;)
    {
        c = *bufptr++;

        if (c == '\0')
            break;

        if (c == '>')
            break;

        if (IsWhite(c))
            continue;

        --bufptr;
        attr = ParseAttribute(&n);
        attrval = ParseValue(&m);

        if (n == 7 && strncasecmp(attr, "rowspan", n) == 0)
        {
            sscanf(attrval, "%d", rowspan);
            continue;
        }

        if (n == 7 && strncasecmp(attr, "colspan", n) == 0)
        {
            sscanf(attrval, "%d", colspan);
            continue;
        }

        if (n == 5 && strncasecmp(attr, "align", n) == 0)
        {
            if (m == 6 && strncasecmp(attrval, "center", m) == 0)
                *align = ALIGN_CENTER;
            else if (m == 5 && strncasecmp(attrval, "right", m) == 0)
                *align = ALIGN_RIGHT;
            else if (m == 4 && strncasecmp(attrval, "left", m) == 0)
                *align = ALIGN_LEFT;

            continue;
        }

        if (n == 6 && strncasecmp(attr, "nowrap", n) == 0)
            *nowrap = 1;
    }
}

void /* wm 19.Jan.95 */
ParseTableAttrs(int *border)
{
    int c, n, m;
    char *attr, *attrval;

    *border = 0;

    for (;;)
    {
        c = *bufptr++;

        if (c == '\0')
            break;

        if (c == '>')
            break;

        if (IsWhite(c))
            continue;

        --bufptr;
        attr = ParseAttribute(&n);
        attrval = ParseValue(&m);

        if (n == 6 && strncasecmp(attr, "border", n) == 0)
        {
	    int bd=1;   /* DJB 17-Jan-96 */
	    
	    if (attrval)
		sscanf(attrval, "%d", &bd);
	    
	    *border = bd ? 1: 0; /* Restrict to 1 and 0 ? */
            continue;
        }
    }
}

void 
ParseClassAttrs(char **class_p, int *class_len_p)
{
    int c, n, m;
    char *attr, *attrval;

    *class_p = NULL;
    *class_len_p = 0;

    for (;;)
    {
        c = *bufptr++;

        if (c == '\0')
            break;

        if (c == '>')
            break;

        if (IsWhite(c))
            continue;

        --bufptr;
        attr = ParseAttribute(&n);
        attrval = ParseValue(&m);

        if (n == 5 && strncasecmp(attr, "class", n) == 0)
        {
	    if (STYLE_TRACE) {
		char *s = strndup(attrval, m);
		fprintf(stderr,"style_class %s\n",s);
		Free(s);
	    }

	    *class_p = attrval;
	    *class_len_p = m;
        }
    }
}



void /* wm 19.Jan.95 */
ParseParaAttrs(int *align, char **class_p, int *class_len_p)
{
    int c, n, m;
    char *attr, *attrval;

    *class_p = NULL;
    *class_len_p = 0;

    for (;;)
    {
        c = *bufptr++;

        if (c == '\0')
            break;

        if (c == '>')
            break;

        if (IsWhite(c))
            continue;

        --bufptr;
        attr = ParseAttribute(&n);
        attrval = ParseValue(&m);

        if (n == 5 && strncasecmp(attr, "align", n) == 0)
        {
            if (m == 6 && strncasecmp(attrval, "center", m) == 0)
                *align = ALIGN_CENTER;
            else if (m == 5 && strncasecmp(attrval, "right", m) == 0)
                *align = ALIGN_RIGHT;
            else if (m == 4 && strncasecmp(attrval, "left", m) == 0)
                *align = ALIGN_LEFT;
	    else if (m == 7 && strncasecmp(attrval, "justify", m) == 0)
		*align = ALIGN_JUSTIFY;
        }

        if (n == 5 && strncasecmp(attr, "class", n) == 0)
        {
	    if (STYLE_TRACE) {
		char *s = strndup(attrval, m);
		fprintf(stderr,"style_class %s\n",s);
		Free(s);
	    }

	    *class_p = attrval;
	    *class_len_p = m;
        }
    }
}

#ifdef HTML3_HAS_EXPIRED
ParseBodyAttrs(char **bg_img, int *bg_len)
{
    int c, n, m;
    char *attr, *attrval;

    *bg_img = NULL;
    *bg_len = 0;

    for (;;)
    {
        c = *bufptr++;

        if (c == '\0')
            break;

        if (c == '>')
            break;

        if (IsWhite(c))
            continue;

        --bufptr;
        attr = ParseAttribute(&n);
        attrval = ParseValue(&m);

        if (n == 10 && strncasecmp(attr, "background", n) == 0)
        {          
	    *bg_img = attrval;
	    *bg_len = m;
	}
    }
}
#endif

Image *ParseNoteAttrs(void)
{
    int c, n, m, hreflen;
    char *attr, *attrval, *href = NULL;
    Image *image;

    image = NULL;
    hreflen = 0;

    for (;;)
    {
        c = *bufptr++;

        if (c == '\0')
            break;

        if (c == '>')
            break;

        if (IsWhite(c))
            continue;

        --bufptr;
        attr = ParseAttribute(&n);
        attrval = ParseValue(&m);

        if ((n == 4 && strncasecmp(attr, "role", n) == 0) || (n == 5 && strncasecmp(attr, "class", n) == 0))
        {
            if (m == 4 && strncasecmp(attrval, "note", m) == 0)
                image = note_image;
            else if (m == 3 && strncasecmp(attrval, "tip", m) == 0)
                image = note_image;
            else if (m == 7 && strncasecmp(attrval, "caution", m) == 0)
                image = caution_image;
            else if (m == 7 && strncasecmp(attrval, "warning", m) == 0)
                image = warning_image;
        }

        if (n == 3 && strncasecmp(attr, "src", n) == 0)
        {
            href = attrval;
            hreflen = m;
        }
    }

    if (hreflen > 0)
        image = GetImage(href, hreflen, CurrentDoc->pending_reload);

    return image;
}

void ParseOption(int implied, Frame *frame, Field *field, int font)
{
  /* janet: not used:     int width; */

    if (EndTag)
    {
        SwallowAttributes();
        return;
    }

    if (!implied)
        SwallowAttributes();

    LineLen = 0;

    for (;;)
    {
        GetToken();

        if (Token == TAG_OPTION && EndTag)
        {
            SwallowAttributes();
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        /* condense whitespace */

        if (Token == WHITESPACE)
        {
            while (GetToken() == WHITESPACE);
            UnGetToken();

            if (LineLen < LBUFSIZE - 1)
                LineBuf[LineLen++] = ' ';

            continue;
        }

        if (Token == PCDATA)
        {
            if (LineLen < LBUFSIZE - 1)
                LineBuf[LineLen++] = TokenValue;

            continue;
        }

     /* unexpected tag so terminate element */

        UnGetToken();
        break;
    }

    LineBuf[LineLen] = '\0';

    if (LineLen > 0)
        AddOption(field, font, LineBuf, LineLen);
}

void ParseSelect(int implied, Frame *frame, int font, int align, int left, int right)
{
    int type, nlen, vlen, size, flags;
    char *name, *value;
    Field *field;
    Image *image;

    if (EndTag)
    {
        SwallowAttributes();
        return;
    }

    if (!implied)
    {
        /* bodge parsing select's attributes as short term hack */
        ParseInputAttrs(&type, &name, &nlen, &value, &vlen, &size, &flags, &image);
    }
    else
    {
        name = value = bufptr; /* --Spif 16-Otc-95 click bug */
        nlen = vlen = flags = 0;

    }

    if (form == NULL)
        form = DefaultForm();
    if(nlen)
      field = GetField(form, OPTIONLIST, Here, name, nlen, value, vlen, 1, 6, flags|font);
    else
      field = GetField(form, OPTIONLIST, Here, bufptr, nlen, value, vlen, 1, 6, flags|font);
    WrapFieldIfNeeded(field, frame, align, left, right);
    PrintInputField(frame, field);

    for (;;)
    {
        while (GetToken() == WHITESPACE);

        if (Token == TAG_SELECT && EndTag)
        {
            SwallowAttributes();
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (Token == TAG_OPTION)
        {
            ParseOption(0, frame, field, font);
            continue;
        }

        if (Token == PCDATA)
        {
            ParseOption(1, frame, field, font);
            continue;
        }

     /* unexpected tag so terminate element */

        error |= ERR_SELECT;
        RecordError(bufptr, "ERR_SELECT"); /* howcome 11/10/94 */
        UnGetToken();
        break;
    }    

    Here += field->width;
}

void ParseEmph(Frame *frame, int align, unsigned int emph, int font, int left, int right)
{
    int ThisToken, WordLen, hreflen, namelen, nlen, vlen, size, valign, up, down,
        suboffset, type, rows, cols, delta, ismap, width, height, flags, indent = 0; /* janet: not used: h */
    char *href, *name, *value, *p;
    Image *image;
    Field *field;
    Bool element_started = FALSE;
    
/*    StylePush(style);*/
    
    if (EndTag)
    {
        SwallowAttributes();
        return;
    }
    
    suboffset = 0;
    ThisToken = Token;
    
    /* we only want to mark anchors that are hrefs, so we'll have to
       wait until we parse the anchor attributes to dterermine this */
/*
  if (ThisToken != TAG_ANCHOR)
  style = FormatElementStart(ThisToken, class, class_len)
  */
    switch (ThisToken)
    {
    case TAG_ANCHOR:
	ParseAnchorAttrs(&href, &hreflen, &name, &namelen, &class, &class_len);
	
	if (href) {
	    emph |= EMPH_ANCHOR;
	    FormatElementStart(Token, class, class_len);
	    element_started = TRUE;
	}
	
	if (name && targetId)
	{
	    if (strlen(targetId) == namelen &&
		strncasecmp(name, targetId, namelen) == 0)
	    {
		IdOffset = PixOffset;
	    }
	}
	
	if (preformatted)
	    break;
	
	/* otherwise skip leading white space - subsequently contigous
	   white space is compressed to a single space */
	
	while (GetToken() == WHITESPACE);
	UnGetToken();
	break;
	
    case TAG_MARGIN:
	SwallowAttributes();
	font = IDX_H2FONT;
	indent = XTextWidth(Fonts[IDX_NORMALFONT], "mmm", 3);
	left += indent;
	right -= indent;
	EndOfLine(frame, align);
	Here = left;
	PixOffset += LineSpacing[font];
	break;
	
    case TAG_TT:
    case TAG_CODE:
    case TAG_SAMP:
    case TAG_KBD:
	ParseClassAttrs(&class, &class_len);
	FormatElementStart(ThisToken, class, class_len);
	element_started = TRUE;
	font = StyleGet(S_FONT);
/*
  if (font == IDX_BNORMALFONT)
  font = IDX_BIFIXEDFONT;
  else
  font = IDX_FIXEDFONT;
  */
	break;
	
    case TAG_SMALL:
	ParseClassAttrs(&class, &class_len);
	FormatElementStart(ThisToken, class, class_len);
	element_started = TRUE;
	font = (int)StyleGet(S_FONT);
	break;
	
    case TAG_SUP:
	ParseClassAttrs(&class, &class_len);
	FormatElementStart(ThisToken, class, class_len);
	element_started = TRUE;
	font = (int)StyleGet(S_FONT);
	
	suboffset = ASCENT(font)/2;
	voffset += suboffset;
	above = max(above, voffset + ASCENT(font));
	break;
	
    case TAG_SUB:
	ParseClassAttrs(&class, &class_len);
	FormatElementStart(ThisToken, class, class_len);
	element_started = TRUE;
	font = (int)StyleGet(S_FONT);
	
	suboffset = -ASCENT(font)/2;
	voffset += suboffset;
	below = max(below, DESCENT(font) - voffset);
	break;
	
    case TAG_MATH:
	ParseClassAttrs(&class, &class_len);
	FormatElementStart(ThisToken, class, class_len);
	element_started = TRUE;
	font = (int)StyleGet(S_FONT);
/*	    left += style->indent;*/
	
	FlushLine(NOBREAK, frame, align, emph, font, left, right);
/*            font = IDX_IFIXEDFONT;*/
	
/*	    StylePush(style);*/
	ParseMath(frame, &up, &down);
/*	    style = StylePop();*/
	
	above = max(above, up);
	below = max(below, down);
	
	if (Here > TextLineWidth)
	    TextLineWidth = Here;
	
	if (element_started)
	    FormatElementEnd();
	return;
	
    case TAG_ITALIC:
    case TAG_EM:
    case TAG_DFN:
    case TAG_CITE:
    case TAG_VAR:
    case TAG_Q:
    case TAG_BOLD:
    case TAG_STRONG:
	ParseClassAttrs(&class, &class_len);
	FormatElementStart(ThisToken, class, class_len);
	element_started = TRUE;
	font = (int)StyleGet(S_FONT);
	break;
	
    case TAG_UNDERLINE:
	SwallowAttributes();
	emph |= EMPH_UNDERLINE;
	break;
	
    case TAG_STRIKE:
    case TAG_REMOVED:  /* <removed> doesn't work across <P> etc. */
	SwallowAttributes();
	emph |= EMPH_STRIKE;
	break;
	
    case TAG_ADDED:  /* doesn't work across <P> etc! */
	SwallowAttributes();
	emph |= EMPH_HIGHLIGHT;
	
	ParseClassAttrs(&class, &class_len);
	FormatElementStart(ThisToken, class, class_len);
	element_started = TRUE;
	font = (int)StyleGet(S_FONT);
/*
  if (preformatted)
  {
  if (font == IDX_BFIXEDFONT)
  font = IDX_BIFIXEDFONT;
  else
  font = IDX_IFIXEDFONT;
  }
  else
  {
  if (font == IDX_BNORMALFONT)
  font = IDX_BINORMALFONT;
  else
  font = IDX_INORMALFONT;
  }
  */
	break;
	
    case TAG_FORM: /* --Spif */
        name=NULL;
	ParseFormAttrs(&type, &name, &nlen);
	if ((form = (Form *)FindForm(forms, name)) == NULL)
	{
	    form = GetForm(type, name, nlen);
	};
	break;
	
    case TAG_TEXTAREA:  /* a short term bodge */
	ParseTextAreaAttrs(&type, &name, &nlen, &value, &vlen, &rows, &cols, &flags);
	FormatElementStart(ThisToken, NULL, 0);
	element_started = TRUE;
	
	if (form == NULL)
	    form = DefaultForm(); /* what a hack ! input outside a form is permitted ???? */
	
	field = GetField(form, type, Here+1, name, nlen, value, vlen, rows, cols, flags|font);
	WrapFieldIfNeeded(field, frame, align, left, right);
	PrintInputField(frame, field);
	Here += field->width + 2;
	StartOfLine = StartOfWord = bufptr;
	LineLen = LineWidth = WordStart = 0;
	
	if (element_started)
	    FormatElementEnd();
	return;
	
    case TAG_INPUT:   /* --Spif src=img ? */
	ParseInputAttrs(&type, &name, &nlen, &value, &vlen, &size, &flags, &image);
	
	FormatElementStart(ThisToken, NULL, 0);
	element_started = TRUE;
	
	if (form == NULL)
	    form = DefaultForm();
	
	if (type != HIDDEN) /* --Spif don't show hidden fields */ 
	{		
	    if (type == SUBMITBUTTON || type == RESETBUTTON)
		/* font = IDX_H3FONT; */
		font = (int)StyleGet(S_FONT);
	    
	    /* --Spif image only on submit and reset buttons... */    
	    if ((type == SUBMITBUTTON || type == RESETBUTTON) && (image !=NULL)) 
	    {
		int above1,below1;
		p=bufptr;
		while (*p != '<') p--;
		width = image->width + 8; /* +8 because of the emph_whatever... */
		height = image->height+ 8;
		if (! (width && height)){
		    width = IMG_WIDTH;
			height = IMG_HEIGHT;
		};
		/* --Spif hack */ emph |= EMPH_INPUT ; 
		delta = height;
		above1 = delta + 2;
		below1 = 0;
		field = GetField(form, type, Here+1, name, nlen, value, vlen, 1, size, flags|font);
		WrapImageIfNeeded(frame, align, left, right, width, above1, below1);
		PrintImage(frame, delta, emph, p, image, width, height);
		Here += width + 2; /* check that with emph... */ 
	    }
	    else
	    {
		if (type == SUBMITBUTTON || type == RESETBUTTON) /* test if no name and/or value tags --SPif 12-Oct-95 */
		{
		    if(nlen)
			field = GetField(form, type, Here+1, name, nlen, value, vlen, 1, size, flags|font);
		    else
			if(vlen)
			    field = GetField(form, type, Here+1, value, vlen, value, vlen, 1, size, flags|font); /* value as id */
			else  /* do this 'cause name is not necessary in submit & reset buttons */
			    if (type == SUBMITBUTTON)
				field = GetField(form, type, Here+1, bufptr , 14 , " Submit Query ", 14, 1, size, flags|font);
			    else                                     /* --Spif 11-Oct-95 it's ugly but it works */
				field = GetField(form, type, Here+1, bufptr , 7, " Reset ", 7, 1, size, flags|font); 
		}
		else
		    field = GetField(form, type, Here+1, name, nlen, value, vlen, 1, size, flags|font);
		
		WrapFieldIfNeeded(field, frame, align, left, right);
		PrintInputField(frame, field);
		Here += field->width+2;
	    };
	    
	    StartOfLine = StartOfWord = bufptr;
	    LineLen = LineWidth = WordStart = 0;
	    if (element_started)
		FormatElementEnd();
	}
	else
	{ /* add hidden input field */
	    field = GetField(form, type, Here+1, name, nlen, value, vlen, 1, size, flags|font);
	};
	return;
	
    case TAG_SELECT:
	ParseSelect(0, frame, font, align, left, right);
	StartOfLine = StartOfWord = bufptr;
	LineLen = LineWidth = WordStart = 0;
	if (element_started)
	    FormatElementEnd();
	return;
	
    case TAG_OPTION:
	ParseSelect(1, frame, font, align, left, right);
	StartOfLine = StartOfWord = bufptr;
	LineLen = LineWidth = WordStart = 0;
	if (element_started)
	    FormatElementEnd();
	return;
	
    case TAG_BR:
	ParseParaAttrs(&align, &class, &class_len);
	if (paintStartLine >= 0)
	    EndOfLine(frame, align);
	else
	    PixOffset += ASCENT((int)StyleGet(S_FONT)) + DESCENT((int)StyleGet(S_FONT));
	
	Here = left;
	StartOfLine = StartOfWord = bufptr;
	LineLen = LineWidth = WordStart = 0;
	if (element_started)
	    FormatElementEnd();
	return;
	
    case TAG_IMG:
	p = bufptr - 4;
	ParseImageAttrs(&href, &hreflen, &valign, &ismap, &width, &height);
	
	if ( (image = GetImage(href, hreflen, CurrentDoc->pending_reload)) ) {
	    width = image->width;
	    height = image->height;
	} 
	
	if (! (width && height)){
	    width = IMG_WIDTH;
	    height = IMG_HEIGHT;
	}
	
	if (ismap | (emph & EMPH_ANCHOR))
	{
	    if (ismap)
		emph |= ISMAP;
	    
	    width += 8;
	    height += 8;
	}
	else
	    p = NULL;
	
	{
	    int above1, below1;
	    
	    if (valign == ALIGN_BOTTOM)
	    {
		delta = height;
		above1 = delta + 2;
		below1 = 0;
		WrapImageIfNeeded(frame, align, left, right, width, above1, below1);
		PrintImage(frame, delta, emph, p, image, width, height);
	    }
	    else if (valign == ALIGN_MIDDLE)
	    {
		delta = height/2;
		above1 = delta + 2;
		below1 = (above1 < height ? height - above1 : 0);
		WrapImageIfNeeded(frame, align, left, right, width, above1, below1);
		PrintImage(frame, delta, emph, p, image, width, height);
	    }
	    else  /* ALIGN_TOP */
	    {
		delta = ASCENT(font) - 2;
		below1 = 2 + height - delta;
		
		if (below1 < 0)
		    below1 = 0;
		
		above1 = 2 + (height > below1 ? height - below1 : 0);
		
		WrapImageIfNeeded(frame, align, left, right, width, above1, below1);
		PrintImage(frame, delta, emph, p, image, width, height);
	    }
	    
	    Here += width + 2;
	}
	
	StartOfLine = StartOfWord = bufptr;
	LineLen = LineWidth = WordStart = 0;
	
	if (element_started)
	    FormatElementEnd();
	return;
	
    default:
	SwallowAttributes();
	break;
    }
    
    StartOfLine = StartOfWord = bufptr;
    LineLen = LineWidth = WordStart = 0;
    
    if (ThisToken == TAG_Q)
    {
	/* open quote char  */
/*        width = XTextWidth(Fonts[IDX_NORMALFONT], "``", 2);*/
/*        PrintSeqText(frame, EMPH_NORMAL, IDX_NORMALFONT, "``", width);*/
        width = XTextWidth(Fonts[(int)StyleGet(S_FONT)], "``", 2);
        PrintSeqText(frame, EMPH_NORMAL, (int)StyleGet(S_FONT), "``", width);
        Here += width;
    }

    for (;;)
    {
        GetToken();

        if (Token == ThisToken && EndTag)
        {
            SwallowAttributes();
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }


        if (TokenClass != EN_TEXT)
        {
	    if (Token != TAG_HR) {
	        error |= ERR_EMPH;
		RecordError(bufptr, "ERR_EMPH"); /* howcome 11/10/94 */
	    };
            UnGetToken();
            break;
        }

        if (Token == WHITESPACE)
        {
            if (preformatted)
            {
                if (TokenValue == '\n')
                    FlushLine(BREAK, frame, align, emph, font, left, right);
                else
                {
                    if (LineLen < LBUFSIZE - 1)
                        LineBuf[LineLen++] = ' ';
                }
                continue;
            }

            while (GetToken() == WHITESPACE);
            UnGetToken();

            if (Here == left && LineLen == 0)
            {
                StartOfLine = StartOfWord = bufptr;
                continue;
            }

            /* check that we have a word */

            if ((WordLen = LineLen - WordStart) > 0)
                WrapIfNeeded(frame, align, emph, font, left, right);

            if (LineLen < LBUFSIZE - 1)
                LineBuf[LineLen++] = ' ';

            WordStart = LineLen;
            StartOfWord = bufptr;
            continue;
        }

        if (IsTag(Token))
        {
            FlushLine(NOBREAK, frame, align, emph, font, left, right);
	    ParseEmph(frame, align, emph, font, left, right);
/*	    if (Token != TAG_BR && Token !=TAG_HR)
		FormatElementStart(s, class, class_len); */ /* howcome 30/2/95: revert style after emph section */
            continue;
        }

        /* must be PCDATA */

        if (LineLen < LBUFSIZE - 1)
            LineBuf[LineLen++] = TokenValue;
    }    

    LineBuf[LineLen] = '\0';
    FlushLine(NOBREAK, frame, align, emph, font, left, right);
    voffset -= suboffset;

    if (ThisToken == TAG_Q)
    {
      /* close quote char  */
/*        width = XTextWidth(Fonts[IDX_NORMALFONT], "''", 2);*/
/*        PrintSeqText(frame, IDX_NORMALFONT, "''", width);*/
        width = XTextWidth(Fonts[(int)StyleGet(S_FONT)], "''", 2);
        PrintSeqText(frame, EMPH_NORMAL, (int)StyleGet(S_FONT), "''", width);
        Here += width;
    }

    if (ThisToken == TAG_MARGIN)
    {
        EndOfLine(frame, align);
        Here = left -  indent;
        PixOffset += LineSpacing[font];
        StartOfLine = StartOfWord = bufptr;
        LineLen = LineWidth = WordStart = 0;
    }

/*    style = StylePop();*/

/*    style = FormatElementStart(revert_tag, NULL, 0); *//* reset to P style after emph */

    if (element_started)
	FormatElementEnd();
}

void ParseHeader(Frame *frame, int align, int left, int right, int outdent)
{
    int HeaderTag, WordLen, emph; /* janet: not used: , font; */

    align = -1;
    ParseParaAttrs(&align, &class, &class_len);

    FormatElementStart(Token, class, class_len);
    font = (int)StyleGet(S_FONT); 
    if (align < 0)
	align = StyleGet(S_ALIGN);

/*
    left += StyleGet(S_MARGIN_LEFT);
    right += StyleGet(S_MARGIN_RIGHT);
*/

    left = (int)StyleGet(S_MARGIN_LEFT);
    right = (int)StyleGet(S_MARGIN_RIGHT);

    if (EndTag) {
        return;
    }

    if (frame->leftmargin <= MININDENT) /* howcome: applied patch from dsr 18/11/94 */
        left -= outdent;

    HeaderTag  = Token;
    Here = left;

/*
    switch (HeaderTag)
    {
        case TAG_H1:
            font = IDX_H1FONT;
            break;

        case TAG_H2:
            font = IDX_H2FONT;
            break;

        case TAG_H3:
        case TAG_H4:
            font = IDX_H3FONT;
            break;

        default:
            font = IDX_H4FONT;
            break;
    }
*/

    PixOffset += (int)StyleGet(S_MARGIN_TOP);

    emph = EMPH_NORMAL;

    StartOfLine = StartOfWord = bufptr;
    LineLen = LineWidth = WordStart = 0;

    for (;;)
    {
        GetToken();

        if (Token == HeaderTag && EndTag)
        {
            SwallowAttributes();
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (TokenClass != EN_TEXT)
        {
            error |= ERR_HEADER;
            RecordError(bufptr, "ERR_HEADER"); /* howcome 11/10/94 */
            UnGetToken();
            break;
        }

        if (Token == WHITESPACE)
        {
            while (GetToken() == WHITESPACE);
            UnGetToken();

            if (Here == left && LineLen == 0)
            {
                StartOfLine = StartOfWord = bufptr;
                continue;
            }

            /* check that we have a word */

            if ((WordLen = LineLen - WordStart) > 0)
                WrapIfNeeded(frame, align, emph, (int)StyleGet(S_FONT), left, right);

            if (LineLen < LBUFSIZE - 1)
                LineBuf[LineLen++] = ' ';

            WordStart = LineLen;
            StartOfWord = bufptr;
            continue;
        }

        if (IsTag(Token))
        {
            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

            FlushLine(NOBREAK, frame, align, emph, (int)StyleGet(S_FONT), left, right);
            ParseEmph(frame, align, emph, font /* StyleGet(S_FONT) */, left, right);
/*	    if (Token != TAG_BR && Token !=TAG_HR)
		FormatElementStart(HeaderTag, class, class_len); */ /* howcome 30/2/95: revert style after emph section */
            continue;
        }

        /* must be PCDATA */

        if (LineLen < LBUFSIZE - 1)
            LineBuf[LineLen++] = TokenValue;
    }    

    LineBuf[LineLen] = '\0';
    FlushLine(BREAK, frame, align, emph, (int)StyleGet(S_FONT), left, right);
/*    PixOffset += LineSpacing[StyleGet(SF_ONT)]/2;*/
    PixOffset += (int)StyleGet(S_MARGIN_BOTTOM);

#if 0
    FormatElementStart(TAG_P, NULL, 0); /* reset to P style after headline */
    align = StyleGet(S_ALIGN); /* dummy call to force the initialization of last_value array */
#endif

    FormatElementEnd();

}

void ParsePara(int implied, Bool initial, Frame *frame, int palign, int tag, int left, int right)
{
    int WordLen, emph;
    int font;
    int align = -1;
    Frame *new_frame;
    int hack = 0;


    if (EndTag)
    {
        SwallowAttributes();
        return;
    }

    hack = paintStartLine>0 || damn_table;

    if (!implied) {
        ParseParaAttrs(&align, &class, &class_len);
    };/* else printf("implied P\n");*/

    FormatElementStart(tag, class, class_len);
    if (align < 0)
	align = (int)StyleGet(S_ALIGN);

    if (initial) {
	left = (int)StyleGet(S_MARGIN_LEFT);
    }

    font = (int)StyleGet(S_FONT);
    right = (int)StyleGet(S_MARGIN_RIGHT);

    if (Here < left)
        Here = left;

    if(!implied&&!hack)
    {
/*
 *	printf("----------------------------\nold values:\nindent %d, marginleft %d, right %d, width %d, left %d, right %d\n",
 *	       frame->indent, frame->leftmargin, frame->rightmargin, frame->width,left, right);
 *	printf("data margins left %d, right %d, padding %d\n", StyleGet(S_MARGIN_LEFT),StyleGet(S_MARGIN_RIGHT)
 *	       ,StyleGet(S_PADDING));
 */
	new_frame = (Frame *)calloc(1, sizeof(Frame));
	PixOffset += (int)StyleGet(S_PADDING)+ (int)StyleGet(S_MARGIN_TOP); /* must be S_PADDING_TOP */
	new_frame->offset = PixOffset;
	new_frame->leftmargin = StyleGet(S_PADDING);
	new_frame->rightmargin = StyleGet(S_PADDING);
#if 0
	    right + frame->rightmargin/* + StyleGet(S_MARGIN_RIGHT)*/;
#endif
	new_frame->indent = frame->indent + left; /* S_PADDING_LEFT */
	new_frame->width = frame->width - left - right/* -left -right */;
	new_frame->style = 0;
	new_frame->border = 0;
#ifdef STYLE_COLOR_BORDER
	new_frame->cb_ix = 0;
#else
	new_frame->cb_ix = 0;
#endif
	new_frame->flow = align;
	new_frame->next = new_frame->child = NULL;
	new_frame->box_list = NULL;
	CopyBoxList(new_frame, frame);
	new_frame->leftcount = frame->leftcount;
	new_frame->pushcount = frame->pushcount;
	new_frame->oldmargin = frame->oldmargin;
	PrintBeginFrame(new_frame); 
	left = (int)StyleGet(S_PADDING); /* S_PADDING_LEFT */
	right = (int)StyleGet(S_PADDING); /* S_PADDING_RIGHT */
	Here = left;
	Push(new_frame);
/*
 *	printf("new values:\nindent %d, marginleft %d, right %d, width %d, left %d, right %d\n",
 *	       new_frame->indent, new_frame->leftmargin, new_frame->rightmargin, new_frame->width, left, right);
 */ 
    }
    else
	new_frame = frame;
    emph = EMPH_NORMAL;

    /* skip leading white space - subsequently contigous
       white space is compressed to a single space */

    while (GetToken() == WHITESPACE);
    UnGetToken();

/*    above = below = 0;       */ /* commented out by howcome 26/7/94: in some cases (eg when there is a tall image in a DT), these still carry important information, e.g. in http://info.cern.ch/ */

    StartOfLine = StartOfWord = bufptr;
    LineLen = LineWidth = WordStart = 0;

    for (;;)
    {
        GetToken();

        if (Token == TAG_P && EndTag)
        {
            SwallowAttributes();
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (TokenClass != EN_TEXT)
        {
            UnGetToken();
            break;
        }

        if (Token == WHITESPACE)
        {
            while (GetToken() == WHITESPACE);
            UnGetToken();

            if (Here == left && LineLen == 0)
            {
                StartOfLine = StartOfWord = bufptr;
                continue;
            }

            /* check that we have a word */

            if ((WordLen = LineLen - WordStart) > 0)
                WrapIfNeeded(new_frame, align, emph, font, left, right);

            if (LineLen < LBUFSIZE - 1)
                LineBuf[LineLen++] = ' ';

            WordStart = LineLen;
            StartOfWord = bufptr;
            continue;
        }

        if (IsTag(Token))
        {
            if (EndTag)
            {
                SwallowAttributes();
                error |= ERR_EMPH;
		RecordError(bufptr, "ERR_EMPH"); /* howcome 24/11/94 */
                continue;
            }

            FlushLine(NOBREAK, frame, align, emph, font, left, right);
            ParseEmph(new_frame, align, emph, font, left, right);
	    StartOfLine = StartOfWord = bufptr; /* howcome 28/10/95: added to reset buffer after math -- does it break anything? */
/*	    if (Token != TAG_BR && Token !=TAG_HR)
		FormatElementStart(revert_tag, class, class_len);  *//* howcome 30/2/95: revert style after emph section */
            continue;
        }

        /* must be PCDATA */

        if (LineLen < LBUFSIZE - 1)
            LineBuf[LineLen++] = TokenValue;
    }    

    LineBuf[LineLen] = '\0';
    FlushLine(BREAK, new_frame, align, emph, font, left, right);
/*    PixOffset += LineSpacing[font]/3;*/

    if(!implied&&!hack)
    {
	char *p;

	Pop();
	new_frame->length = paintlen - new_frame->info - FRAMESTLEN;
	new_frame->height = PixOffset - new_frame->offset + 2*(int)StyleGet(S_PADDING);
	new_frame->offset -= (int)StyleGet(S_PADDING);
	PixOffset += (int)StyleGet(S_PADDING);
	new_frame->indent -= (int)StyleGet(S_PADDING); 
	p = paint + new_frame->info + 1;
        PushValue(p, new_frame->offset & 0xFFFF);
        PushValue(p, (new_frame->offset >> 16) & 0xFFFF);
        PushValue(p, new_frame->indent);
        PushValue(p, new_frame->width);
	PushValue(p, new_frame->height & 0xFFFF);
	PushValue(p, (new_frame->height >> 16) & 0xFFFF);
	PrintFrameLength(new_frame);
	PrintEndFrame(frame, new_frame); 
	FreeFrames(new_frame);
    };
    PixOffset += (int)StyleGet(S_MARGIN_BOTTOM);
    FormatElementEnd();
}

void ItemNumber(char *buf, int depth, int n)
{
    int w, ones, tens, hundreds, thousands;
    char *p, *q;

    w = depth % 3;

    if (w == 0)
        sprintf(buf, "%d.", n);
    else if (w == 1)
    {
        thousands = n/1000;
        n = n % 1000;
        hundreds = n/100;
        n = n % 100;
        tens = n/10;
        ones = n % 10;

        p = buf;

        while (thousands-- > 0)
               *p++ = 'm';

        if (hundreds)
        {
            q = Hundreds[hundreds-1];
            while ((*p++ = *q++));
            --p;
        }

        if (tens)
        {
            q = Tens[tens-1];
            while ((*p++ = *q++));
            --p;
        }

        if (ones)
        {
            q = Ones[ones-1];
            while ((*p++ = *q++));
            --p;
        }

        *p++ = ')';
        *p = '\0';
    }
    else
        sprintf(buf, "%c)", 'a' + (n-1)%26);
}


void ParsePRE(int implied, Bool initial, Frame *frame, int left, int right)
{
    int ch, n, emph, font, lastTokenValue; /* janet: not used: WordLen */

    if (EndTag)
    {
        SwallowAttributes();
        return;
    }

    FormatElementStart(Token, class, class_len);

    if (!implied) {
/*        SwallowAttributes();*/

	ParseClassAttrs(&class, &class_len);
/*
	StyleRegisterSequential(Token, class, class_len);
	StyleRegisterHierarchical(Token, class, class_len);
*/
/*	FormatElementStart(Token, class, class_len);*/
	font = (int)StyleGet(S_FONT);
    }


    if (initial) {
	left = (int)StyleGet(S_MARGIN_LEFT);
    }

    Here = left;

    preformatted = 1;
    emph = EMPH_NORMAL;
/*    font = IDX_FIXEDFONT;*/
    ch = CHWIDTH(font);

    /* skip leading newline */

    if (*bufptr == '\r')
        ++bufptr;

    if (*bufptr == '\n')
        ++bufptr;

    /* howcome 12/10/94: moved the two lines below from before the ++bufptr statements */

    StartOfLine = StartOfWord = bufptr;
    LineLen = LineWidth = WordStart = 0;

    for (;;)
    {
        lastTokenValue = TokenValue;
        GetToken();

        if (Token == TAG_PRE && EndTag)
        {
            SwallowAttributes();
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

     /* HR isn't really allowed in PRE, but ... */
        if (Token == TAG_HR)
        {
            FlushLine(BREAK, frame, (int)StyleGet(S_ALIGN), emph, font, left, right);
/*            SwallowAttributes();*/

            if (EndTag)
                continue;

	    ParseClassAttrs(&class, &class_len);
	    FormatElementStart(Token, class, class_len);


/*            error |= ERR_PRE;*/ /* howcome 30/5/95: remember not to flag.. */
/*            RecordError(bufptr, "ERR_PRE"); */ /* howcome 12/3/96: HR *is* allowed within PRE */

            PrintRule(frame, GROOVE, left, right, 0);
            EndOfLine(frame, (int)StyleGet(S_ALIGN));

	    FormatElementEnd();
            continue;
        }

     /* P isn't really allowed in PRE, but ... */
        if (Token == TAG_P)
        {
	    FlushLine(BREAK, frame, (int)StyleGet(S_ALIGN), emph, font, left, right);
	    /* there were two lines here... suspect a bug */
            SwallowAttributes();
            continue;
        }

     /* Ignore header tags in preformatted text */

        if (TokenClass == EN_HEADER)
        {
            error |= ERR_PRE;
            RecordError(bufptr, "ERR_PRE"); /* howcome 11/10/94 */
            SwallowAttributes();
            continue;
        }

        if (TokenClass != EN_TEXT)
        {
            error |= ERR_PRE;
            RecordError(bufptr, "ERR_PRE"); /* howcome 11/10/94 */
            UnGetToken();
            break;
        }

        if (Token == WHITESPACE && TokenValue == '\n')
        {
            FlushLine(BREAK, frame, (int)StyleGet(S_ALIGN), emph, font, left, right);
            continue;
        }

        if (IsTag(Token))
        {
	  /* janet: not used: 	    int s = style->tag; */

            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

            FlushLine(NOBREAK, frame, (int)StyleGet(S_ALIGN), emph, font, left, right);
            ParseEmph(frame, ALIGN_LEFT, emph, font, left, right);

	    /* howcome 15/3/95: try to align characters in pre mode */
            if (Token == TAG_ANCHOR)  /* emph & EMPH_ANCHOR)*/
                Here -= 2;
/*
	    if (Token != TAG_BR && Token !=TAG_HR)
		style = FormatElementStart(s, class, class_len);
*/
            continue;
        }

        /* must be PCDATA */

/* CopyLine can't work out how many spaces to use! */
        if (TokenValue == '\t')
        {
            n = LineLen;  /* + (Here - left)/ch; */
            n = 8 - n % 8;

            while (n-- > 0 && LineLen < LBUFSIZE - 1)
                LineBuf[LineLen++] = ' ';
            continue;
        }

        if (LineLen < LBUFSIZE - 1)
            LineBuf[LineLen++] = TokenValue;
    }    

    LineBuf[LineLen] = '\0';
    FlushLine((lastTokenValue == '\n' ? NOBREAK : BREAK), frame,
              (int)StyleGet(S_ALIGN), emph, font, left, right);

    PixOffset += LineSpacing[font]/3; /* fix whitespace bug dsr 16-Nov-94 */ /* howcome applied patch 23/1/94 */
    preformatted = 0;

/*    FormatElementStart(TAG_P, NULL, 0);*/ /* reset to P style after PRE */

    FormatElementEnd();
}



/* advance declaration due for nested lists */
void ParseUL(int implied, Bool initial, Frame *frame, int depth, int align, int left, int right);
void ParseOL(int implied, Bool initial, Frame *frame, int depth, int align, int left, int right);
void ParseDL(int implied, Bool initial, Frame *frame, int left, int align, int right);

void ParseLI(int implied, Frame *frame, int depth, int seq, int align, int left, int right)
{
    int indent, w;
    long y;
    char buf[16];

    if (EndTag)
    {
        SwallowAttributes();
        return;
   }

/*    indent = XTextWidth(Fonts[IDX_NORMALFONT], "mmm", 3);*/


    y = PixOffset;

    FormatElementStart(TAG_LI, class, class_len);

    if (!implied) {
	ParseClassAttrs(&class, &class_len);
/*	FormatElementStart(Token, class, class_len);*/
    }
    indent = (int)StyleGet(S_MARGIN_LEFT);


    w = left + indent/3;

    if (w < Here + 20)
        EndOfLine(frame, ALIGN_LEFT);

    Here = w;

    if (seq > 0)
    {
        ItemNumber(buf, depth++, seq);
        w = XTextWidth(Fonts[(int)StyleGet(S_FONT)], buf, strlen(buf));
        PrintSeqText(frame, EMPH_NORMAL, (int)StyleGet(S_FONT), buf, w);

        if (w + indent/3 > indent - 4)
            indent = 4 + w + indent/3;
    }
    else
        PrintBullet(frame, depth++, (int)StyleGet(S_FONT), (int)StyleGet(S_FONT_SIZE));

    if ((w = left + indent) > list_indent)    /* for tables */
        list_indent = w;

    for (;;)
    {
        while (GetToken() == WHITESPACE);
/*	style = FormatElementStart(Token, class, class_len); *//* howcome 15/3/94 */

        if (Token == TAG_LI && EndTag)
        {
            SwallowAttributes();
            break;
        }

        if (Token == TAG_UL)
        {
            UnGetToken();

            if (EndTag)
                break;

            ParseUL(0, False, frame, depth, align, left + indent, right);
            continue;
        }

        if (Token == TAG_OL)
        {
            UnGetToken();

            if (EndTag)
                break;

            ParseOL(0, False, frame, depth, align, left + indent, right);
            continue;
        }

        if (Token == TAG_DL)
        {
            UnGetToken();

            if (EndTag)
                break;

            ParseDL(0, False, frame, align, left + indent, right);
            continue;
        }

        if (Token == TAG_DT || Token == TAG_DD)
        {
            UnGetToken();
            ParseDL(1, False, frame, align, left + indent, right);
            continue;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (Token == TAG_P)
        {
            Here = left + indent;
/*	    StyleSetFlag(S_INDENT_FLAG,TRUE);
	    StyleSetFlag(S_LEADING_FLAG,TRUE);*/
/*	    PixOffset += style->margin_top;*//* howcome 15/3/95: not a good idea */
            ParsePara(0, False, frame, align, TAG_P, left + indent, right);
	    continue;
        }

        if (Token == TAG_HR)
        {
/*            SwallowAttributes();*/

	    ParseClassAttrs(&class, &class_len);
/*
	    StyleRegisterSequential(Token, class, class_len);
*/
	    FormatElementStart(Token, class, class_len);

            if (EndTag)
                continue;

            PrintRule(frame, GROOVE, left + indent, right, 0);
            EndOfLine(frame, (int)StyleGet(S_ALIGN));
            continue;
        }

	/* howcome 11/8/95: support for PRE added */

        if (Token == TAG_PRE)
        {
            ParsePRE(0, False, frame, left + indent, right);
            continue;
        }

        if (TokenClass == EN_TEXT)
        {
            UnGetToken();
            ParsePara(1, False, frame, align, TAG_P, left + indent, right);
            continue;
        }

     /* unexpected tag so terminate element */

        UnGetToken();
        break;
    }    

    /* kludge to cope with an <LI> element with no content */

    if (y == PixOffset)
        PixOffset += (4 * LineSpacing[IDX_NORMALFONT])/3;

    FormatElementEnd();
}

void ParseUL(int implied, Bool initial, Frame *frame, int depth, int align, int left, int right)
{
    if (EndTag)
    {
        SwallowAttributes();
        return;
    }

    FormatElementStart(Token, class, class_len);

    if (!implied) {
/*        SwallowAttributes();*/
	ParseClassAttrs(&class, &class_len);
/*
	StyleRegisterSequential(Token, class, class_len);
	StyleRegisterHierarchical(Token, class, class_len);
*/
/*	FormatElementStart(Token, class, class_len);*/
	left += (int)StyleGet(S_MARGIN_LEFT);
    }

    if (initial)	
	left += (int)StyleGet(S_MARGIN_LEFT);

    for (;;)
    {
        while (GetToken() == WHITESPACE);
/*	style = FormatElementStart(Token, class, class_len); */ /* howcome 15/3/95 */

        if (Token == TAG_UL && EndTag)
        {
            SwallowAttributes();
/*	    StyleDeregisterHierarchical();*/
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (Token == TAG_LI)
        {
            ParseLI(0, frame, depth, 0, align, left, right);
            continue;
        }

        if (Token == TAG_P || Token == TAG_HR || TokenClass == EN_TEXT)
        {
            if (EndTag)
            {
		SwallowAttributes();
                continue;
            }

            UnGetToken();
            ParseLI(1, frame, depth, 0, align, left, right);
            continue;
        }

        if (Token == TAG_UL || Token == TAG_OL ||
             Token == TAG_DL || Token == TAG_DT || Token == TAG_DD)
        {
            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

            UnGetToken();
            ParseLI(1, frame, depth, 0, align, left, right);
            continue;
        }

     /* unexpected tag so terminate element */

        error |= ERR_UL;
        RecordError(bufptr, "ERR_UL"); /* howcome 11/10/94 */
        UnGetToken();
        break;
    }    

    Here = left;

    FormatElementEnd();
}

void ParseOL(int implied, Bool initial, Frame *frame, int depth, int align, int left, int right)
{
    int seq;

    if (EndTag)
    {
        SwallowAttributes();
        return;
    }

    FormatElementStart(Token, class, class_len);

    if (!implied) {
	ParseClassAttrs(&class, &class_len);
/*
	StyleRegisterSequential(Token, class, class_len);
	StyleRegisterHierarchical(Token, class, class_len);
*/
/*	FormatElementStart(Token, class, class_len);*/
    }

    if (initial)
	left += (int)StyleGet(S_MARGIN_LEFT);

    seq = 0;

    for (;;)
    {
        ++seq;

        while (GetToken() == WHITESPACE);
/*	style = FormatElementStart(Token, class, class_len); *//* howcome 15/3/95 */

        if (Token == TAG_OL && EndTag)
        {
            SwallowAttributes();
/*	    StyleDeregisterHierarchical();*/
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (Token == TAG_LI)
        {
            ParseLI(0, frame, depth, seq, align, left, right);
            continue;
        }

        if (Token == TAG_P || Token == TAG_HR || TokenClass == EN_TEXT)
        {
            if (EndTag)
            {
		SwallowAttributes();
                continue;
            }

            UnGetToken();
            ParseLI(1, frame, depth, seq, align, left, right);
            continue;
        }

        if (Token == TAG_UL || Token == TAG_OL ||
             Token == TAG_DL || Token == TAG_DT || Token == TAG_DD)
        {
            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

            UnGetToken();
            ParseLI(1, frame, depth, seq, align, left, right);
            continue;
        }

     /* unexpected tag so terminate element */

        error |= ERR_OL;
        RecordError(bufptr, "ERR_OL"); /* howcome 11/10/94 */
        UnGetToken();
        break;
    }    

    Here = left;

    FormatElementEnd();
}

void ParseDT(int implied, Frame *frame, int align, int left, int right)
{
    int WordLen, emph, font;

    if (EndTag)
    {
        SwallowAttributes();
        return;
    }

    FormatElementStart(Token, class, class_len);

    if (!implied) {
/*        SwallowAttributes();*/
	ParseClassAttrs(&class, &class_len);
    }
/*
    StyleRegisterSequential(Token, class, class_len);
*/

    Here = left;

    emph = EMPH_NORMAL;

    font = (int)StyleGet(S_FONT);
/*    font = IDX_BNORMALFONT;*/

    /* skip leading white space - subsequently contigous
       white space is compressed to a single space */
    while (GetToken() == WHITESPACE);
    UnGetToken();

    StartOfLine = StartOfWord = bufptr;
    LineLen = LineWidth = WordStart = 0;

    for (;;)
    {
        GetToken();
/*	style = FormatElementStart(Token, class, class_len); *//* howcome 15/3/95 */

        if (Token == TAG_DT && EndTag)
        {
            SwallowAttributes();
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (TokenClass != EN_TEXT)
        {
            UnGetToken();
            break;
        }

        if (Token == WHITESPACE)
        {
            while (GetToken() == WHITESPACE);
            UnGetToken();

            /* check that we have a word */

            if ((WordLen = LineLen - WordStart) > 0)
                WrapIfNeeded(frame, align, emph, font, left, right);

            if (LineLen < LBUFSIZE - 1)
                LineBuf[LineLen++] = ' ';

            WordStart = LineLen;
            StartOfWord = bufptr;
            continue;
        }

        if (IsTag(Token))
        {
            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

            FlushLine(NOBREAK, frame, align, emph, font, left, right);
            ParseEmph(frame, align, emph, font, left, right);
/*	    if (Token != TAG_BR && Token !=TAG_HR)
		FormatElementStart(TAG_P, class, class_len); *//* howcome 6/3/95: should there be a revert_tag here ? */
            continue;
        }

        /* must be PCDATA */

        if (LineLen < LBUFSIZE - 1)
            LineBuf[LineLen++] = TokenValue;
    }    

    LineBuf[LineLen] = '\0';
    FlushLine(NOBREAK, frame, align, emph, font, left, right);
    Here += 5; /* +5 pb here? */

    FormatElementEnd();
}

void ParseDD(int implied, Frame *frame, int align, int left, int right)
{
    long y;

    if (EndTag)
    {
        SwallowAttributes();
        return;
    }

    FormatElementStart(Token, class, class_len);

    if (!implied) {
	ParseClassAttrs(&class, &class_len);
    }

    y = PixOffset;

    for (;;)
    {
        while (GetToken() == WHITESPACE);
/*	style = FormatElementStart(Token, class, class_len); *//* howcome 15/3/95 */

        if (Token == TAG_DD && EndTag)
        {
            SwallowAttributes();
            break;
        }

        if (Token == TAG_DL && EndTag)
        {
            UnGetToken();
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (Token == TAG_UL)
        {
            ParseUL(0, False, frame, 0, align, left, right);
            continue;
        }

        if (Token == TAG_OL)
        {
            ParseOL(0, False, frame, 0, align, left, right);
            continue;
        }

        if (Token == TAG_LI)
        {
            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

            UnGetToken();
            ParseUL(1, False, frame, 0, align, left, right);
            continue;
        }

        if (Token == TAG_DL)
        {
            if (PixOffset == y) /* force a line break */
            {
                EndOfLine(frame, align);
                Here = left;
            }

            ParseDL(0, False, frame, align, left, right);
            Here = left;
            continue;
        }

        if (Token == TAG_HR)
        {
	    ParseClassAttrs(&class, &class_len);
	    FormatElementStart(Token, class, class_len);

            if (EndTag)
                continue;

            PrintRule(frame, GROOVE, left, right, 0);
            EndOfLine(frame, (int)StyleGet(S_ALIGN));
            continue;
        }

	/* howcome 25/9/95: support for PRE added */

        if (Token == TAG_PRE)
        {
            ParsePRE(0, False, frame, left + (int)StyleGet(S_MARGIN_LEFT), right);
            continue;
        }

        if (Token == TAG_P)
        {
	    ParsePara(0, False, frame, align, TAG_P, left, right);
            continue;
        }

        if (TokenClass == EN_TEXT || Token == ENTITY)
        {
            UnGetToken();
            ParsePara(1, False, frame, align, TAG_P, left, right);
            continue;
        }

     /* unexpected tag so terminate element */

        if (Token != TAG_DT) {
            error |= ERR_DL;
            RecordError(bufptr, "ERR_DL"); /* howcome 11/10/94 */
        }
        UnGetToken();
        break;
    }

    /* kludge to cope with an <DD> element with no content */

    if (y == PixOffset)
    {
        PixOffset += LineSpacing[(int)StyleGet(S_FONT)]/3;
        EndOfLine(frame, (int)StyleGet(S_ALIGN));
    }

    FormatElementEnd();
}

void ParseDL(int implied, Bool initial, Frame *frame, int align, int left, int right)
{
    int indent, LastToken; /* janet: not used: delta */

    if (EndTag)
    {
        SwallowAttributes();
        return;
    }

    FormatElementStart(Token, class, class_len);

    if (!implied) {
/*        SwallowAttributes();*/
        ParseClassAttrs(&class, &class_len);
/*
	StyleRegisterSequential(Token, class, class_len);
	StyleRegisterHierarchical(Token, class, class_len);
*/
/*	FormatElementStart(Token, class, class_len);*/
	font = (int)StyleGet(S_FONT);
	left += (int)StyleGet(S_MARGIN_LEFT);
    }

    if (initial)
	left += (int)StyleGet(S_MARGIN_LEFT);

    LastToken = TAG_DL;
/*    indent = XTextWidth(Fonts[IDX_NORMALFONT], "mm", 2);*/
    indent = (int)StyleGet(S_MARGIN_LEFT);



    for (;;)
    {
        while (GetToken() == WHITESPACE);
/*	style = FormatElementStart(Token, class, class_len); *//* howcome 15/3/95 */

        if (Token == TAG_DL && EndTag)
        {
            SwallowAttributes();
/*	    StyleDeregisterHierarchical();*/

            if (LastToken == TAG_DT)
                EndOfLine(frame, align);
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (Token == TAG_DT )
        {
            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

            if (LastToken == TAG_DT)
                EndOfLine(frame, align);

            ParseDT(0, frame, align, left, right);
            LastToken = TAG_DT;
            continue;
        }

        if (Token == TAG_DD )
        {
            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

            if (LastToken != TAG_DT) {
                error |= ERR_DL;
                RecordError(bufptr, "ERR_DL"); /* howcome 11/10/94 */
            }

            ParseDD(0, frame, align, left + indent, right);
            LastToken = TAG_DD;
            continue;
        }

        if (Token == TAG_P || Token == TAG_HR || TokenClass == EN_TEXT ||
            Token == TAG_UL || Token == TAG_LI || Token == TAG_OL || Token == TAG_DL)
        {
            if (EndTag)
            {
		SwallowAttributes();
                continue;
            }

            error |= ERR_DL;
            RecordError(bufptr, "ERR_DL"); /* howcome 11/10/94 */

            UnGetToken();
            ParseDD(1, frame, align, left + indent, right);
            LastToken = TAG_DD;
            continue;
        }

     /* unexpected tag so terminate element */

        error |= ERR_DL;
        RecordError(bufptr, "ERR_DL"); /* howcome 11/10/94 */

        UnGetToken();
        break;
    }

    Here = left;    

    FormatElementEnd();
}

void ParseCaption(int implied, Frame *frame, int figure, int left, int right)
{
    int align;

    if (EndTag)
    {
        SwallowAttributes();
        return;
    }

    if (!implied)
        SwallowAttributes();

    align = (figure ? ALIGN_LEFT : ALIGN_CENTER);

    for (;;)
    {
        while (GetToken() == WHITESPACE);

        if (Token == TAG_CAPTION && EndTag)
        {
            SwallowAttributes();
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (Token == TAG_P)
        {
            Here = left;
	    PixOffset += (int)StyleGet(S_MARGIN_TOP);
            ParsePara(0, False, frame, align, TAG_P, left, right);
            continue;
        }

        if (TokenClass == EN_TEXT)
        {
            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

            UnGetToken();
            Here = left;
	    PixOffset += (int)StyleGet(S_MARGIN_TOP);
            ParsePara(1, False, frame, align, TAG_P, left, right);
            continue;
        }

     /* unexpected tag so terminate element */

        UnGetToken();
        break;
    }    
}

/* skip alt text for non-graphical displays */
void ParseAlt(int implied)
{
    if (!implied)
        SwallowAttributes();

    for (;;)
    {
        GetToken();

        if (Token == TAG_ALT && EndTag)
        {
            SwallowAttributes();
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (Token == TAG_P)
        {
            SwallowAttributes();
            continue;
        }

        if (TokenClass == EN_TEXT)
        {
            if (IsTag(Token))
                SwallowAttributes();

            continue;
        }

     /* unexpected tag so terminate element */

        UnGetToken();
        break;
    }    
}

void ParseFigure(int implied, Frame *frame, int left, int right)
{
    int align, font, hreflen, indent;
    char *href;
    Frame *figframe;
    Image *image;
    long FigureStart;
    int width, height;

/*
    frame = Pop();
    if(frame)
	Push(frame);
    else
	frame = eframe;
	*/
    if (EndTag)
    {
        SwallowAttributes();
        return;
    }

    align = ALIGN_CENTER;
    image = NULL;
    font = IDX_NORMALFONT;
    Here = left;
    indent = 0;

    if (!implied)
    {
        ParseFigureAttrs(&href, &hreflen, &align, &width, &height);
        if ((image = GetImage(href, hreflen, CurrentDoc->pending_reload))) {
	    width = image->width;
	    height = image->height;
	}

	if (! (width && height)) {
	    width = IMG_WIDTH;
	    height = IMG_HEIGHT;
	}
    }

/*    if (image)*/
    {
        if (align == ALIGN_RIGHT)
        {
            indent = frame->width - right - frame->rightmargin -
                        width - frame->leftmargin;
        }
        else if (align == ALIGN_CENTER)
        {
            indent = (frame->width - right - frame->rightmargin -
                        width - frame->leftmargin)/2;
        }
        else if (align == ALIGN_BLEEDLEFT)
            indent = 0;
        else if (align == ALIGN_BLEEDRIGHT)
        {
            indent = frame->width - frame->rightmargin -
                        width - frame->leftmargin;
        }
        else
/*            indent = left; */
	    indent = (int)StyleGet(S_MARGIN_LEFT);

        if (indent > 0 && indent < left)
            indent = left;

        FigureStart = PixOffset;
        figframe = BeginFrame(frame, 0, 0, indent, indent+width,NULL);
	Push(figframe);
	
        if (align == ALIGN_LEFT || align == ALIGN_BLEEDLEFT)
        {
            frame->leftcount += 1;
            figframe->pushcount = frame->leftcount;
            figframe->oldmargin = frame->leftmargin;
            figframe->flow = ALIGN_LEFT;
        }
        else if (align == ALIGN_RIGHT || align == ALIGN_BLEEDRIGHT)
        {
            frame->rightcount += 1;
            figframe->pushcount = frame->rightcount;
            figframe->oldmargin = frame->rightmargin;
            figframe->flow = ALIGN_RIGHT;
        }

        Here = 0; /* position image flush left in figframe */
        PrintImage(figframe, 0, 0, 0, image, width, height);
	above = 0;
        below = height;
        EndOfLine(figframe, ALIGN_LEFT);
#if 0
	if (align == ALIGN_LEFT || align == ALIGN_BLEEDLEFT)
	    AddBox(frame, frame->leftmargin, FigureStart, figframe->width, figframe->height);
	else
	    AddBox(frame, frame->width - frame->rightmargin - figframe->width, PixOffset, figframe->width, figframe->height);
#endif
	above = 0;
	Pop();
    }

    Here = 0;

    for (;;)
    {
        while (GetToken() == WHITESPACE);

        if (Token == TAG_FIG && EndTag)
        {
            SwallowAttributes();
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (Token == TAG_CAPTION)
        {
            ParseCaption(0, figframe, 1, 0, 0);
            continue;
        }

        if (Token == TAG_ALT)
        {
            ParseAlt(0);
            continue;
        }

        if (Token == TAG_P)
        {
            UnGetToken();
            ParseAlt(1);
            continue;
        }

        if (TokenClass == EN_TEXT)
        {
            UnGetToken();
            ParseAlt(1);
            continue;
        }

     /* unexpected tag so terminate element */

        error |= ERR_FIG;
        RecordError(bufptr, "ERR_FIG"); /* howcome 11/10/94 */
        UnGetToken();
        break;
    }    

/*    if (image)*/
    {
        figframe->height = PixOffset - FigureStart;
        figframe->length = paintlen - figframe->info - FRAMESTLEN;
        PrintFrameLength(figframe);
        EndFrame(frame, figframe);
	
        /* FigureEnd = PixOffset -= LineSpacing[font]/3; */

        if (align == ALIGN_LEFT || align == ALIGN_BLEEDLEFT)
        {
	    frame->leftmargin += width + 5;
            PixOffset = FigureStart;
        }
        else if (align == ALIGN_RIGHT || align == ALIGN_BLEEDRIGHT)
        {
	    frame->rightmargin += /*width*/ + 5;
            PixOffset = FigureStart;
        }
	if (align == ALIGN_LEFT || align == ALIGN_BLEEDLEFT)
	    AddBox(frame, frame->leftmargin-3, FigureStart-3, figframe->width+6, figframe->height+6);
	else
	    AddBox(frame, frame->width - frame->rightmargin - figframe->width-3, PixOffset-3, figframe->width+6, figframe->height+6);
    }
}

void ParseBlock(int implied, Frame *frame, int tag, int align, int font, int left, int right)
{
    int indent;
    long offset;
    Frame *blockframe;
    Image *image = NULL;

    if (EndTag)
    {
        SwallowAttributes();
        return;
    }


    FormatElementStart(Token, class, class_len);

    align = -1;

    if (!implied)
    {
        if (tag == TAG_NOTE)
            image = ParseNoteAttrs();
        else {
/*            SwallowAttributes();*/
	    ParseParaAttrs(&align, &class, &class_len);
	}

    }

    left += (int)StyleGet(S_MARGIN_LEFT);
    right += (int)StyleGet(S_MARGIN_RIGHT);
    PixOffset += (int)StyleGet(S_MARGIN_TOP);
    font = (int)StyleGet(S_FONT);
    if (align < 0)
	align = (int)StyleGet(S_ALIGN);
 

    Here = left;
    indent = 0;

    if (tag == TAG_NOTE)
    {
#if 0
        PrintRule(frame, GROOVE, left, right, 0);
        EndOfLine(frame, StyleGet(S_ALIGN));
#endif
        offset = PixOffset;

        if (image)
        {
            indent = 20 + image->width;
            blockframe = BeginFrame(frame, 0, 0, left, left+indent, NULL);
            Here = 0;
            above = 0;
            below = image->height;    
            PrintImage(blockframe, 0, 0, 0, image, image->width, image->height);
            EndOfLine(blockframe, (int)StyleGet(S_ALIGN));
            blockframe->height = image->height;
            blockframe->length = paintlen - blockframe->info - FRAMESTLEN;
            PrintFrameLength(blockframe);
            EndFrame(frame, blockframe);
/*            frame->leftcount += 1;
            blockframe->pushcount = frame->leftcount;
	    */     
	    blockframe->flow = ALIGN_NOTE;
	    AddBox(frame, Here, PixOffset, image->width, image->height);
	    /*  left += indent;*/
            Here = LeftMargin(frame, left, PixOffset);
            PixOffset = offset;
        }
    }

    for (;;)
    {
        while (GetToken() == WHITESPACE);

        if (Token == tag && EndTag)
        {
            SwallowAttributes();
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (Token == TAG_P)
        {
            Here = left;
/*	    PixOffset += StyleGet(S_MARGIN_TOP);*/
            ParsePara(0, False, frame, align, TAG_P, left, right);
            continue;
        }

        if (TokenClass == EN_TEXT)
        {
            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

            UnGetToken();
/*	    PixOffset += StyleGet(S_MARGIN_TOP); */
            ParsePara(1, False, frame, align, tag, left, right);
            continue;
        }

     /* unexpected tag so terminate element */

        error |= ERR_BLOCK;
        RecordError(bufptr, "ERR_BLOCK"); /* howcome 11/10/94 */

        UnGetToken();
        break;
    }    

    if (tag == TAG_NOTE)
    {
     /* ensure image ends before closing rule */
        if (image && PixOffset < offset + image->height)
            PixOffset = offset + image->height;
#if 0
        PrintRule(frame, GROOVE, left-indent, right, 0);
        EndOfLine(frame, StyleGet(S_ALIGN));
#endif
    }

    PixOffset += (int)StyleGet(S_MARGIN_BOTTOM);
    FormatElementEnd();
}


/* tag is TAG_TH or TAG_TD, col is column number starting from 1 upwards, returns cell height */
long ParseTableCell(int implied, Frame *frame, int row, Frame **cells, int border,
                      ColumnWidth **pwidths, int *pcol, int tag, int left, int right, BG_Style *bg)
{
    int align, nowrap, col, rowspan, colspan, m, font;
    long cellTop, cellHeight;
    Frame *cellframe = NULL, *newframes, preframe;
    ColumnWidth *widths;
    
    if (EndTag)
    {
        SwallowAttributes();
        return 0;
    }

    newframes = NULL;
    col = *pcol;
    widths = *pwidths;
    
    align = ALIGN_CENTER;
    nowrap = 0;

    if (prepass)
    {
        preframe.next = preframe.child = NULL;
	preframe.box_list = NULL;
        preframe.indent = 0;
        preframe.height = 0;
        preframe.width = 0;
        preframe.leftmargin = 0;
        preframe.rightmargin = 0;
        cellframe = &preframe;
    }

    list_indent = min_width = max_width = 0;
    rowspan = colspan = 1;

    if (!implied)
        ParseCellAttrs(&rowspan, &colspan, &align, &nowrap);

    *pcol += colspan;

    if (!prepass)
    {
        font = (tag == TAG_TH ? IDX_H3FONT : IDX_NORMALFONT);
        cellTop = PixOffset;
        left = widths[col].left;
        right = widths[col + colspan - 1].right;
        cellframe = BeginFrame(frame, 0, border, left-3, right+4, bg);
        cellframe->lastrow = row + rowspan - 1;

     /* left and right now adjusted to indents from frame margins */
        left = 3;  /* style */
        right = 4;

        /* try to make TH and TD baselines match for first line */
/*
        if (tag == TAG_TH)
            PixOffset += ASCENT(IDX_NORMALFONT) - ASCENT(IDX_H3FONT);

        PixOffset += LineSpacing[IDX_NORMALFONT]/3
*/

        PixOffset += LineSpacing[(int)StyleGet(S_FONT)]/3;

    }

    for (;;)
    {
        while (GetToken() == WHITESPACE);
/*	style = FormatElementStart(Token, class, class_len);*/

/*
	if (Token == TAG_TABLE && !EndTag)
	{
	       Push(bufptr); 
	       if(prepass) 
		   ParseTable(0, cellframe, left, right);
	       else
	       {
		   UnGetToken();
		   ParseTable(1, cellframe, left, right);
	       }
	       bufptr = (char *)Pop();
	       printf(", after : %X\n", bufptr);
	       while(strncasecmp(bufptr,"/TABLE",5))
	       {
		   bufptr++;
	       }
	       bufptr += 6;
	       printf("and then... %X\n", bufptr); 
	       break;
	}
*/	
        if (Token == TAG_DD && EndTag)
        {
            SwallowAttributes();
            break;
        }

        if (Token == TAG_DL && EndTag)
        {
            UnGetToken();
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (Token == TAG_UL)
        {
            ParseUL(0, False, cellframe, 0, align, left, right);
            continue;
        }

        if (Token == TAG_OL)
        {
            ParseOL(0, False, cellframe, 0, align, left, right);
            continue;
        }

        if (Token == TAG_LI)
        {
            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

            UnGetToken();
            ParseUL(1, False, cellframe, 0, align, left, right);
            continue;
        }

        if (Token == TAG_DL)
        {
            ParseDL(0, False, cellframe, align, left, right);
            Here = left;
            continue;
        }

        if (Token == TAG_HR)
        {
/*            SwallowAttributes();*/

	    ParseClassAttrs(&class, &class_len);
/*
	    StyleRegisterSequential(Token, class, class_len);
*/
	    FormatElementStart(Token, class, class_len);

            if (EndTag)
                continue;

            PrintRule(cellframe, GROOVE, left, right, 0);
            EndOfLine(cellframe, (int)StyleGet(S_ALIGN));
	    FormatElementEnd();
            continue;
        }

        if (Token == TAG_P)
        {
            Here = left;
	    PixOffset += (int)StyleGet(S_MARGIN_TOP);
/*            ParsePara(0, False, cellframe, align,
                      (tag == TAG_TH ? IDX_H3FONT : IDX_NORMALFONT), left, right); */
            ParsePara(0, False, cellframe, align, tag, left, right);
            continue;
        }

        if (TokenClass == EN_HEADER)
        {
            UnGetToken();
            ParseHeader(cellframe, align, left, right, 0);
            continue;
        }

        if (TokenClass == EN_TEXT || Token == ENTITY)
        {
            UnGetToken();
            Here = left;
	    PixOffset += (int)StyleGet(S_MARGIN_TOP);
            ParsePara(1, False, cellframe, align, tag, left, right);
            continue;
        }

        if (Token == TAG_PRE)
        {
            UnGetToken();
            ParsePRE(0, False, cellframe, left, right);
            continue;
        }

	if(TokenClass == EN_BLOCK)
	{
	    ParseBlock(0, cellframe, Token, ALIGN_LEFT, IDX_NORMALFONT, left, right);
	    Here = left;
            continue;
        }
     /* unexpected tag so terminate element */

        UnGetToken();
        break;
    }

    if (col > COLS(widths))   /* update table column count */
        COLS(widths) = col;

    m = MAXCOLS(widths);

    if (col + colspan > m)   /* double array size as needed */
    {
        while (col + colspan> m)
           m = m << 1;

	if (TABLE_TRACE)
	    fprintf(stderr,"ParseTableCell: realloc, size = %d\n",(m + 1) * sizeof(ColumnWidth));
        widths = *pwidths = (ColumnWidth *)realloc(widths, (m + 1) * sizeof(ColumnWidth));
	MAXCOLS(widths) = m;

        while (m >= col)   /* zero widths for newly allocated elements */
        {
            widths[m].min = 0;
            widths[m].max = 0;
            widths[m].rows = 0;
            --m;
        }
    }

    max_width = cellframe->width + left + right;
    min_width += list_indent;

    if (nowrap && max_width > min_width)
        min_width = max_width;

    if (colspan > 1)  /* apportion widths evenly */
    {
        min_width = min_width/colspan;
        max_width = max_width/colspan;

        for (m = 0; m < colspan; ++m)
        {
            if (min_width > widths[col + m].min)
                widths[col + m].min = min_width;

            if (max_width > widths[col + m].max)
                widths[col + m].max = max_width;
        }
    }
    else
    {
        if (min_width > widths[col].min)
            widths[col].min = min_width;

        if (max_width > widths[col].max)
            widths[col].max = max_width;
    }

    widths[col].rows = rowspan - 1;

    if (!prepass)
    {
        cellHeight = (PixOffset - cellTop)/rowspan;
        cellframe->height = cellHeight;  /* for debug only */
        widths[col].min = cellHeight;
        FlushPending(frame);
        cellframe->length = paintlen - cellframe->info - FRAMESTLEN ; 
        PrintFrameLength(cellframe);
        InsertCell(cells, cellframe);
        return cellHeight;
    }

    return 0;
}

void DummyCell(Frame *frame, int row, Frame **cells,
               int border, ColumnWidth *widths, int col)
{
    int left, right;
    Frame *cellframe;

    left = widths[col].left;
    right = widths[COLS(widths)].right;
    cellframe = BeginFrame(frame, 0, border, left-3, right+4, NULL);
    cellframe->lastrow = row;
    cellframe->length = paintlen - cellframe->info - FRAMESTLEN;
    PrintFrameLength(cellframe);
    InsertCell(cells, cellframe);
}

void ParseTableRow(int implied, Frame *frame, int row, Frame **cells,
                    int border, ColumnWidth **pwidths, int left, int right, BG_Style *bg)
{
    int cols = 1;
    long rowTop, rowHeight, cellHeight;
    /* janet: not used:    char *row_bufptr; */
    ColumnWidth *widths;

    if (EndTag)
    {
        SwallowAttributes();
        return;
    }

    if (!implied)
        SwallowAttributes();

    Here = left;
    rowTop = PixOffset;
    rowHeight = 0;

    for (;;)
    {
	PixOffset = rowTop;
	widths=*pwidths;
	
        /* if this cell spans more than one row */
        if (widths[cols].rows > 0)
        {
            widths[cols].rows -= 1;  /* decrement span count */

            if (!prepass) /* does spanned cell effect rowBottom? */
            {
                if (widths[cols].min > rowHeight)
                    rowHeight = widths[cols].min;
            }

            ++cols;
            continue;
        }

        while (GetToken() == WHITESPACE);

        if (Token == TAG_TR && EndTag)
        {
            if (EndTag)
            {
                SwallowAttributes();
                break;
            }

            UnGetToken();
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (Token == TAG_TH)
        {
            cellHeight = ParseTableCell(0, frame, row, cells, border,
                                        pwidths, &cols, TAG_TH, left, right, bg);

            if (cellHeight > rowHeight)
                rowHeight = cellHeight;
            continue;
        }

        if (Token == TAG_TD)
        {
            cellHeight = ParseTableCell(0, frame, row, cells, border,
                                        pwidths, &cols, TAG_TD, left, right, bg);

            if (cellHeight > rowHeight)
                rowHeight = cellHeight;
            continue;
        }

        if (Token == TAG_TABLE)
        {
            UnGetToken();
            break;
        }
	
        if (TokenClass == EN_LIST || TokenClass == EN_TEXT ||
            TokenClass == EN_HEADER || TokenClass == EN_BLOCK)
        {
            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

            UnGetToken();
            cellHeight = ParseTableCell(1, frame, row, cells, border,
                                        pwidths, &cols, TAG_TD, left, right, bg);

            if (cellHeight > rowHeight)
                rowHeight = cellHeight;
            continue;
        }

     /* unexpected tag so terminate element */

        UnGetToken();
        break;
    }    
    
    widths = *pwidths; /* Just in case it changes again */
    if (!prepass && cols <= COLS(widths))
        DummyCell(frame, row, cells, border, widths, cols);

    PixOffset = rowTop + rowHeight;

    if (!prepass)
        FlushCells(frame, row, cells);  /* calls EndFrame() for cells ending this row */
}

void ParseTable(int implied, Frame *frame, int left, int right)
{
    int row, border, i, w, W, x, min, max, spare, max_width; /* janet: not used: cols, field_count, new */
    long table_offset;
    char *table_bufptr;
    Frame *cells;
    Frame *new_frame;

    ColumnWidth *widths;

    if (EndTag)
    {
        SwallowAttributes();
        return;
    }

    if (!implied)
        ParseTableAttrs(&border);

    FormatElementStart(Token, NULL, 0);

    new_frame = Pop();
    Push(new_frame);

    damn_table = 1; /* debug table format */

    Here = left;
    ProcessTableForm(1);   /* what the HECK is THIS ??!! */

    widths = (ColumnWidth *)malloc((NCOLS + 1) * sizeof(ColumnWidth));

    COLS(widths) = 0;               /* current number of columns */
    MAXCOLS(widths) = NCOLS;        /* space currently allocated */
    widths[0].rows = 0;

    for (i = NCOLS; i > 0; --i)     /* zero widths for allocated elements */
    {
        widths[i].min = 0;
        widths[i].max = 0;
        widths[i].rows = 0;
    }

    prepass = 1;
    table_bufptr = bufptr;          /* note parse position for second pass */
    table_offset = PixOffset;

 draw_table:

    row = 0;
    cells = NULL;

    for (; row < 1000 ;) 
    {
        while (GetToken() == WHITESPACE);

	if (Token == TAG_TABLE && !EndTag)
        {
	    while(strncasecmp(bufptr,"/TABLE",5))
		bufptr++;
	    bufptr+=6;
	    break;
	}
	
	if (Token == TAG_TABLE && EndTag)
        {
            if (EndTag)
            {
                SwallowAttributes();
                break;
            }

            UnGetToken();
            break;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (Token == TAG_CAPTION)
        {
            ParseCaption(0, frame, 0, 0, 0);
            continue;
        }

        if (Token == TAG_TR)
        {
            ++row;
            ParseTableRow(0, new_frame, row, &cells, border, &widths, left, right,(BG_Style *)StyleGet(S_BACKGROUND));
            continue;

        }

        if (TokenClass == EN_LIST || TokenClass == EN_TEXT || TokenClass == EN_TABLE ||
            TokenClass == EN_HEADER || TokenClass == EN_BLOCK)
        {
            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

            UnGetToken();
            ++row;
            ParseTableRow(1, new_frame, row, &cells, border, &widths, left, right, (BG_Style *)StyleGet(S_BACKGROUND));
            continue;
        }

     /* unexpected tag so terminate element */

        UnGetToken();
        break;
    }    

    if (prepass)   /* assign actual column widths */
    {
        for (i = 1, min = 3, max = 3, W = 0; i <= COLS(widths); ++i)
        {
            min += 7 + widths[i].min;
            max += 7 + widths[i].max;
            W += widths[i].max - widths[i].min;
        }

        /* if max fits in window then use max widths */

        max_width = new_frame->width - right - left;

        if (max <= max_width)
        {
            x = left + (max_width - max)/2;

            for (i = 1; i <= COLS(widths); ++i)
            {
                widths[i].left = x + 3;
                x += 7 + widths[i].max;
                widths[i].right = x - 4;
            }
        }
        else if (min < max_width)
        {
            x = left;
            spare = max_width - min;

            for (i = 1; i <= COLS(widths); ++i)
            {
                w = widths[i].max - widths[i].min;
                widths[i].left = x + 3;
                x += 7 + widths[i].min + (spare * w)/W;
                widths[i].right = x - 4;
            }
        }
        else /* assign minimum column widths */
        {
            x = left;

            for (i = 1; i <= COLS(widths); ++i)
            {
                widths[i].left = x + 3;
                x += 7 + widths[i].min;
                widths[i].right = x - 4;
            }
        }

        /* and do second pass to draw table */

        bufptr = table_bufptr;
        PixOffset = table_offset;
        prepass = 0;
        ProcessTableForm(2);
        goto draw_table;
    }
   
    Free(widths);   /* free column widths */
    Here = left;
    PixOffset += (int)StyleGet(S_MARGIN_BOTTOM);
    FormatElementEnd();
    damn_table = 0;
}

/* left and right are the indents from the current LHS, RHS respectively */
void ParseBody(int implied, Frame *frame, int left, int right, int margin)
{
    int indent;

    if (EndTag)
    {
        SwallowAttributes();
        return;
    }

    indent = margin;

    if (!implied)
        SwallowAttributes();

    FormatElementStart(TAG_HTML, NULL, 0); /* howcome 3/4/95: first call to FormatElementStart */

    for (;;)
    {
        while (GetToken() == WHITESPACE);

        if (Token == TAG_BODY && EndTag)
        {
            SwallowAttributes();
            break;
        }

        if (IsTag(Token) && TokenClass == EN_HEADER)
        {
            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

	    ParseHeader(frame, ALIGN_LEFT, left, right, 0);
            continue;
        }

        if (Token == UNKNOWN)
        {
            SwallowAttributes();
            continue;
        }

        if (Token == TAG_UL)
        {
	    ParseUL(0, True, frame, 0, ALIGN_LEFT, left, right);
            continue;
        }

        if (Token == TAG_OL)
        {
            ParseOL(0, True, frame, 0, ALIGN_LEFT, left, right);
            continue;
        }

        if (Token == TAG_LI)
        {
            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

            error |= ERR_LI;
            RecordError(bufptr, "ERR_LI"); /* howcome 11/10/94 */
            UnGetToken();
            ParseUL(1, True, frame, 0, ALIGN_LEFT, left, right); /* howcoem 11/8/95: ALIGN_CENTER -> ALIGN_LEFT */
            continue;
        }

        if (Token == TAG_DL)
        {
/*            ParseDL(0, frame, StyleGet(S_ALIGN), StyleGet(S_MARGIN_LEFT), right+margin);*/
            ParseDL(0, True, frame, ALIGN_LEFT, left, right + margin);
            continue;
        }

        if (TokenClass == EN_TABLE)
        {
            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

            error |= ERR_TABLE;
            RecordError(bufptr, "ERR_TABLE"); /* howcome 11/10/94 */
            UnGetToken();
            ParseTable(1, frame, left, right);
            continue;
        }

        if (Token == TAG_TABLE)
        {
            ParseTable(0, frame, left, right+margin);
            continue;
        }

        if (Token == TAG_DT || Token == TAG_DD)
        {
            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

            error |= ERR_DL;
            RecordError(bufptr, "ERR_DL"); /* howcome 11/10/94 */
            UnGetToken();
            ParseDL(1, True, frame, ALIGN_LEFT, left, right);
            continue;
        }

        if (Token == TAG_HR)
        {
            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

/*            SwallowAttributes();*/


	    ParseClassAttrs(&class, &class_len);
/*
	    StyleRegisterSequential(Token, class, class_len);
*/
	    FormatElementStart(Token, class, class_len);
            PrintRule(frame, GROOVE, LeftMargin(frame, left, PixOffset) , RightMargin(frame, right, PixOffset), 0);
            EndOfLine(frame, (int)StyleGet(S_ALIGN));
	    FormatElementEnd();
            continue;
        }

        if (Token == TAG_PRE)
        {
            ParsePRE(0, True, frame, left, right);
            continue;
        }

        if (Token == TAG_P)
        {
            Here = left+margin;
/*	    PixOffset += top;*/
            ParsePara(0, True, frame, ALIGN_LEFT, TAG_P, left, right);
            continue;
        }

        if (TokenClass == EN_TEXT || Token == ENTITY)
        {
            if (EndTag)
            {
                SwallowAttributes();
                continue;
            }

            UnGetToken();
            Here = left + margin;
/*	    style = FormatElementStart(Token, class, class_len); *//* howcome 6/3/94: need to reset */

	    if (REGISTER_TRACE)
		fprintf(stderr,"implied: ");
#if 0
	    StyleRegisterSequential(TAG_P, NULL, 0); /* reason to live?? */
	    FormatElementStart(TAG_P, NULL, 0);
#endif
            ParsePara(1, True, frame, ALIGN_LEFT, TAG_P, left, right);
            continue;
        }

        if (Token == TAG_FIG)
        {
            ParseFigure(0, frame, left, right);
            continue;
        }

        if (Token == TAG_ADDRESS)
        {
/*	    style = FormatElementStart(Token, class, class_len);*/
            ParseBlock(0, frame, Token, ALIGN_LEFT, IDX_NORMALFONT, left, right);
            Here = left;
            continue;
        }

        if (Token == TAG_ABSTRACT)
        {
            ParseBlock(0, frame, Token, ALIGN_LEFT, IDX_NORMALFONT, left, right);
            Here = left;
            continue;
        }

        if (Token == TAG_QUOTE)
        {
            ParseBlock(0, frame, Token, ALIGN_CENTER, IDX_NORMALFONT, left, right);
            Here = left;
            continue;
        }

        if (Token == TAG_NOTE)
        {
            ParseBlock(0, frame, Token, ALIGN_LEFT, IDX_NORMALFONT, left, right);
            Here = left;
            continue;
        }

        if (Token == ENDDATA)
        {
            UnGetToken();
            break;
        }

        if (Token == TAG_BODY || TokenClass == EN_SETUP)
        {
            error |= ERR_SETUP;
            RecordError(bufptr, "ERR_SETUP"); /* howcome 11/10/94 */
            SwallowAttributes();
            continue;
        }
	/* must add form */

        /* unexpected but known tag name */
        error |= ERR_BODY;
        RecordError(bufptr, "ERR_BODY");
        SwallowAttributes();
    }
    FormatElementEnd();
}

void InitBackgroundFrame()
{
 /* initialise background frame */

    background.next = NULL;
    background.child = NULL;
    background.box_list = NULL;
    background.top = paint + FRAMESTLEN;

    if (TAG_TRACE)
#if defined PRINTF_HAS_PFORMAT
	fprintf(stderr,"parsehtml.c: ParseHTML: paint = %p background.top = %p\n", paint, background.top);
#else
	fprintf(stderr,"parsehtml.c: ParseHTML: paint = %lx background.top = %lx\n", paint, background.top);
#endif /* PRINTF_HAS_PFORMAT */

    background.offset = 0;
    background.indent = 0;
    background.height = 1;  /* zero might cause problems?? */
    background.width = WinWidth;
    background.info = 0;
    background.length = 3;
    background.style = 0;
    background.border = 0;
    background.leftmargin = MININDENT;
    background.rightmargin = MININDENT;
}

long ParseHTML(int *width, BOOL style_parse)
{
    int margin; /* janet: not used: WordLen, c */
    Byte *p;
    char *bg_name;
    int  bg_len;
    BG_Style *bg;
    Image *image;
    char *quoted_name;
    int i;

    PixOffset = 5;
    LastBufPtr = bufptr = buffer+hdrlen;
    error = prepass = preformatted = 0;
    paintlen = 0;
    IsIndex = 0;
    start_figure = figure = 0;
    font = paintStartLine = -1;
    FigureEnd = LeftMarginIndent = RightMarginIndent = 0;
    form = NULL;
    margin = 0; /* howcome 21/2/95 */

/*    margin = XTextWidth(Fonts[IDX_NORMALFONT], "mmm", 2);*/

    if (paintbufsize == 0)
    {
        paintbufsize = 8192;
        paint = (Byte *)malloc(paintbufsize);
    }

    InitBackgroundFrame();

    Push(&background); 

 /* Reserve space for background's begin frame object
    which is needed to simplify display and scrolling routines */

    MakeRoom(FRAMESTLEN);

    SkipSpace();

    if (Token == TAG_BASE)
    {
        ParseBase(&background);
        SkipSpace();
    }

    if (Token == TAG_HEAD)
        ParseSetUp(0, &background);
    else
    {
        UnGetToken();
        ParseSetUp(1, &background);
    }

    if (style_parse)
    {
	StyleParse();
	FormatElementStart(TAG_HTML, NULL, 0);
	background.leftmargin = (int)StyleGet(S_MARGIN_LEFT);
	background.rightmargin = (int)StyleGet(S_MARGIN_RIGHT);
	PixOffset = (int)StyleGet(S_MARGIN_TOP);
	background.offset = PixOffset;
    }
    else
	FormatElementStart(TAG_HTML, NULL, 0);

 /* kludge to cope with multiple BODY elements */
    while (SkipSpace() != ENDDATA)
    {
        if (Token == TAG_BODY)
	{
#ifdef HTML3_HAS_EXPIRED
	    ParseBodyAttrs(&bg_name, &bg_len);
	    if(bg_name)
	    {
		bg = StyleGet(S_BACKGROUND);
		if(!bg)
		{

		    quoted_name = (char *)calloc(bg_len+3, sizeof(char));
		    *quoted_name = '\"';
		    for(i=0; i<bg_len; i++)
			*(quoted_name+i+1) = *(bg_name+i);
		    *(quoted_name+bg_len+1) = '\"';
		    if(!CurrentDoc->style)
			CurrentDoc->style = StyleCopy(context->style);
		    StyleAddSimpleRule(CurrentDoc->style, TAG_HTML, S_BACKGROUND, StyleGetBackground(NULL, quoted_name), S_FALLBACK - S_ANY);
		    free(quoted_name);
		} else {
		    if(image = GetImage(bg_name, bg_len, CurrentDoc->pending_reload))
		    {
			bg->image = image;
			bg->flag |= S_BACKGROUND_IMAGE;
		    }
		}
#endif
		ParseBody(0, &background, MININDENT, MININDENT, margin);
	    }
	    else
	    {
		UnGetToken();
		ParseBody(1, &background, MININDENT, MININDENT, margin);
	    }
#ifdef HTML3_HAS_EXPIRED
	}
#endif
    }


    FlushAllFrames(NULL, background.child); /* flush remaining end of frames */
    background.child = NULL;
    PrintEndFrame(NULL, &background);
    *width = background.width;

 /* update background frame length */

    if (PixOffset > background.height)
        background.height = PixOffset;

    background.length = paintlen - FRAMESTLEN;

 /* and fill in begin frame object at start of paint buffer */

    if (TAG_TRACE)
#if defined PRINTF_HAS_PFORMAT
	fprintf(stderr,"parsehtml.c: ParseHTML: starting buffer at %p\n",paint);
#else
	fprintf(stderr,"parsehtml.c: ParseHTML: starting buffer at %lx\n",paint);
#endif /* PRINTF_HAS_PFORMAT */

 /* major bug fix: howcome found it in desparation 3/12/94 */

    background.top = paint + FRAMESTLEN;

    p = paint;
    *p++ = BEGIN_FRAME;
    PushValue(p, background.offset & 0xFFFF);
    PushValue(p, (background.offset >> 16) & 0xFFFF);
    PushValue(p, background.indent);
    PushValue(p, background.width);
    PushValue(p, background.height);
    PushValue(p, 0); 
    *p++ = background.style;
    *p++ = background.border;
#ifdef STYLE_COLOR_BORDER		/* changed from COLOR_STYLE 15/5/95 */
    *p++ = background.cb_ix;
#endif
#ifdef STYLE_BACKGROUND
    PutPointer(&p, (void *)StyleGet(S_BACKGROUND)); /* must be set to the structures defined by the style --Spif 18-Oct-95 */
#endif
    FormatElementEnd();
    PushValue(p, background.length);
    
    /* printf("Pushing now at 0x%xh BEGIN FRAME -> offest %d, indent %d,width %d,height %d\n",paint,
	  background.offset,background.indent,background.width,background.height);  */

    TopObject = paint;   /* obsolete */

    html_width = background.width;
    Pop();
    return background.height;
}


/* ParseImage doesn't really parse the image. It does, however, for
   the equivalent for an image that ParseHTML does for HTML: create a
   paintstream. In the case of a single image, the paint stream is
   very simple. Alternativley, one could have wrapped the image in
   dummy html and formatted the document as normal. This would have
   given greater flexibiliy (e.g. to put it into a table), but would
   have been more expensive. howcome 13/2/95 */

long ParseImage(Doc *doc, int *width)
{
    int margin;
    Byte *p;

    FormatElementStart(TAG_HTML, NULL, 0);
    FormatElementStart(TAG_IMG, NULL, 0);

    LastBufPtr = bufptr = buffer+hdrlen;
    error = prepass = preformatted = 0;
    paintlen = 0;
    IsIndex = 0;
    start_figure = figure = 0;
    font = paintStartLine = -1;
    FigureEnd = LeftMarginIndent = RightMarginIndent = 0;
    form = NULL;
    margin = XTextWidth(Fonts[IDX_NORMALFONT], "mmm", 2);

    if (paintbufsize == 0)
    {
        paintbufsize = 8192;
        paint = (Byte *)malloc(paintbufsize);
    }

    InitBackgroundFrame();
    Here = 0; /* background.leftmargin;*/
    PixOffset = 5;

    MakeRoom(FRAMESTLEN);

    PrintImage(&background, 0, 0, NULL, doc->image, doc->image->width, doc->image->height);
    EndOfLine(&background, ALIGN_LEFT);
    PrintEndFrame(NULL, &background);

    background.width = background.leftmargin + doc->image->width + background.rightmargin;
    background.height = 5 + doc->image->height + 5;
    background.length = paintlen - FRAMESTLEN;
    background.top = paint + FRAMESTLEN;

    p = paint;
    *p++ = BEGIN_FRAME;
    PushValue(p, background.offset & 0xFFFF);
    PushValue(p, (background.offset >> 16) & 0xFFFF);
    PushValue(p, background.indent);
    PushValue(p, background.width);
    PushValue(p, background.height);
    PushValue(p,0); /* --Spif 23-Oct-95 is this the source of all pbs ? */
    *p++ = background.style;
    *p++ = background.border;
#ifdef STYLE_COLOR_BORDER  		/* changed from COLOR_STYLE 15/5/95 */
    *p++ = background.cb_ix;
#endif
#ifdef STYLE_BACKGROUND
    PutPointer(&p, (void *)StyleGet(S_BACKGROUND)); /* must be set to the structures defined by the style --Spif 18-Oct-95 */
#endif
    PushValue(p, background.length);

/*    TopObject = paint;   *//* obsolete */

    html_width = background.width;
    *width = background.width;

    FormatElementEnd();
    FormatElementEnd();

    return background.height;
}
