LYHandleP(me,
LYHandlePlike(me,
PRIVATE void HTML_end_element ARGS3(	HTStructured *,		me,	int,			element_number,	char **,		include){    int i = 0;    char *temp = NULL, *cp = NULL;    BOOL BreakFlag = FALSE;#ifdef USE_PSRC    if (psrc_view && !sgml_in_psrc_was_initialized) {	if (!psrc_nested_call) {	    HTTag * tag = &HTML_dtd.tags[element_number];	    char buf[200];	    int tag_charset=0;	    psrc_nested_call=TRUE;	    PSRCSTART(abracket); PUTS("</"); PSRCSTOP(abracket);	    PSRCSTART(tag);	    if (tagname_transform!=0)		PUTS(tag->name);	    else {		strcpy(buf,tag->name);		LYLowerCase(buf);		PUTS(buf);	    }	    PSRCSTOP(tag);	    PSRCSTART(abracket); PUTC('>'); PSRCSTOP(abracket);	    psrc_nested_call=FALSE;	    return;	}	/*fall through*/    }#endif#ifdef CAREFUL			/* parser assumed to produce good nesting */    if (element_number != me->sp[0].tag_number &&	HTML_dtd.tags[element_number].contents != SGML_EMPTY) {	CTRACE(tfp,		"HTMLText: end of element %s when expecting end of %s\n",		HTML_dtd.tags[element_number].name,		HTML_dtd.tags[me->sp->tag_number].name);		/* panic */    }#endif /* CAREFUL */    /*     *	If we're seeking MAPs, skip everything that's     *	not a MAP or AREA tag. - FM     */    if (LYMapsOnly) {	if (!(element_number == HTML_MAP || element_number == HTML_AREA)) {	    return;	}    }    /*     *	Pop state off stack if we didn't declare the element     *	SGML_EMPTY in HTMLDTD.c. - FM & KW     */    if (HTML_dtd.tags[element_number].contents != SGML_EMPTY) {	if ((element_number != me->sp[0].tag_number) &&	    me->skip_stack <= 0 &&	    HTML_dtd.tags[HTML_LH].contents != SGML_EMPTY &&	    (me->sp[0].tag_number == HTML_UL ||	     me->sp[0].tag_number == HTML_OL ||	     me->sp[0].tag_number == HTML_MENU ||	     me->sp[0].tag_number == HTML_DIR) &&	    (element_number == HTML_H1 ||	     element_number == HTML_H2 ||	     element_number == HTML_H3 ||	     element_number == HTML_H4 ||	     element_number == HTML_H6 ||	     element_number == HTML_H6)) {	    /*	     *	Set the break flag if we're popping	     *	a dummy HTML_LH substituted for an	     *	HTML_H# encountered in a list.	     */	    BreakFlag = TRUE;	}	if (me->skip_stack > 0) {	     CTRACE(tfp, "HTML:end_element: Internal call (level %d), leaving on stack - %s\n",			me->skip_stack, me->sp->style->name);	    me->skip_stack--;	} else if (me->stack_overrun == TRUE &&	    element_number != me->sp[0].tag_number) {	    /*	     *	Ignore non-corresponding tags if we had	     *	a stack overrun.  This is not a completely	     *	fail-safe strategy for protection against	     *	any seriously adverse consequences of a	     *	stack overrun, and the rendering of the	     *	document will not be as intended, but we	     *	expect overruns to be rare, and this should	     *	offer reasonable protection against crashes	     *	if an overrun does occur. - FM	     */	    return;	} else if (element_number == HTML_SELECT &&	    me->sp[0].tag_number != HTML_SELECT) {	    /*	     *	Ignore non-corresponding SELECT tags, since we	     *	probably popped it and closed the SELECT block	     *	to deal with markup which amounts to a nested	     *	SELECT, or an out of order FORM end tag. - FM	     */	    return;	} else if ((element_number != me->sp[0].tag_number) &&	    HTML_dtd.tags[HTML_LH].contents == SGML_EMPTY &&	    (me->sp[0].tag_number == HTML_UL ||	     me->sp[0].tag_number == HTML_OL ||	     me->sp[0].tag_number == HTML_MENU ||	     me->sp[0].tag_number == HTML_DIR) &&	    (element_number == HTML_H1 ||	     element_number == HTML_H2 ||	     element_number == HTML_H3 ||	     element_number == HTML_H4 ||	     element_number == HTML_H6 ||	     element_number == HTML_H6)) {	    /*	     *	It's an H# for which we substituted	     *	an HTML_LH, which we've declared as	     *	SGML_EMPTY, so just return. - FM	     */	    return;	} else if (me->sp < (me->stack + MAX_NESTING - 1)) {	    (me->sp)++;	    CTRACE(tfp, "HTML:end_element[%d]: Popped style off stack - %s\n",			(int) STACKLEVEL(me),			me->sp->style->name);	} else {	    CTRACE(tfp,  "Stack underflow error!  Tried to pop off more styles than exist in stack\n");	}    }    if (BreakFlag == TRUE)	return;    /*     *	Check for unclosed TEXTAREA. - FM     */    if (me->inTEXTAREA && element_number != HTML_TEXTAREA) {	if (TRACE) {	    fprintf(tfp, "Bad HTML: Missing TEXTAREA end tag\n");	} else if (!me->inBadHTML) {	    HTUserMsg(BAD_HTML_USE_TRACE);	    me->inBadHTML = TRUE;	}    }    if (!me->text && !LYMapsOnly) {	UPDATE_STYLE;    }    /*     *	Handle the end tag. - FM     */    switch(element_number) {    case HTML_HTML:	if (me->inA || me->inSELECT || me->inTEXTAREA) {	    if (TRACE) {		fprintf(tfp,			"Bad HTML: %s%s%s%s%s not closed before HTML end tag *****\n",			me->inSELECT ? "SELECT" : "",			(me->inSELECT && me->inTEXTAREA) ? ", " : "",			me->inTEXTAREA ? "TEXTAREA" : "",			((me->inSELECT || me->inTEXTAREA) && me->inA) ? ", " : "",			me->inA ? "A" : "");	    } else if (!me->inBadHTML) {		HTUserMsg(BAD_HTML_USE_TRACE);		me->inBadHTML = TRUE;	    }	}	break;    case HTML_HEAD:	if (me->inBASE &&	    !strcmp(me->node_anchor->address, LYlist_temp_url())) {	    /*	If we are parsing the List Page, and have a BASE after	     *	we are done with the HEAD element, propagate it back	     *	to the node_anchor object.  The base should have been	     *	inserted by showlist() to record what document the List	     *	Page is about, and other functions may later look for it	     *	in the anchor. - kw	     */	    StrAllocCopy(me->node_anchor->content_base, me->base_href);	}	if (HText_hasToolbar(me->text))	    HText_appendParagraph(me->text);	break;    case HTML_TITLE:	HTChunkTerminate(&me->title);	HTAnchor_setTitle(me->node_anchor, me->title.data);	HTChunkClear(&me->title);	/*	 *  Check if it's a bookmark file, and if so, and multiple	 *  bookmark support is on, or it's off but this isn't the	 *  default bookmark file (e.g., because it was on before,	 *  and this is another bookmark file that has been retrieved	 *  as a previous document), insert the current description	 *  string and filepath for it.  We pass the strings back to	 *  the SGML parser so that any 8 bit or multibyte/CJK	 *  characters will be handled by the parser's state and	 *  charset routines. - FM	 */	if (me->node_anchor->bookmark && *me->node_anchor->bookmark) {	    if ((LYMultiBookmarks == TRUE) ||		((bookmark_page && *bookmark_page) &&		 strcmp(me->node_anchor->bookmark, bookmark_page))) {		for (i = 0; i <= MBM_V_MAXFILES; i++) {		    if (MBM_A_subbookmark[i] &&			!strcmp(MBM_A_subbookmark[i],				me->node_anchor->bookmark)) {			StrAllocCat(*include, "<H2><EM>");			StrAllocCat(*include, gettext("Description:"));			StrAllocCat(*include, "</EM> ");			StrAllocCopy(temp,				     ((MBM_A_subdescript[i] &&				       *MBM_A_subdescript[i]) ?					 MBM_A_subdescript[i] : gettext("(none)")));			LYEntify(&temp, TRUE);			StrAllocCat(*include, temp);			StrAllocCat(*include, "<BR><EM>&nbsp;&nbsp;&nbsp;");			StrAllocCat(*include, gettext("Filepath:"));			StrAllocCat(*include, "</EM> ");			StrAllocCopy(temp,				     ((MBM_A_subbookmark[i] &&				       *MBM_A_subbookmark[i]) ?					 MBM_A_subbookmark[i] : gettext("(unknown)")));			LYEntify(&temp, TRUE);			StrAllocCat(*include, temp);			FREE(temp);			StrAllocCat(*include, "</H2>");			break;		    }		}	    }	}	break;    case HTML_STYLE:	/*	 *  We're getting it as Literal text, which, for now,	 *  we'll just ignore. - FM	 */	HTChunkTerminate(&me->style_block);	CTRACE(tfp, "HTML: STYLE content =\n%s\n",			    me->style_block.data);	HTChunkClear(&me->style_block);	break;    case HTML_SCRIPT:	/*	 *  We're getting it as Literal text, which, for now,	 *  we'll just ignore. - FM	 */	HTChunkTerminate(&me->script);	CTRACE(tfp, "HTML: SCRIPT content =\n%s\n",			    me->script.data);	HTChunkClear(&me->script);	break;    case HTML_BODY:	if (me->inA || me->inSELECT || me->inTEXTAREA) {	    if (TRACE) {		fprintf(tfp,			"Bad HTML: %s%s%s%s%s not closed before BODY end tag *****\n",			me->inSELECT ? "SELECT" : "",			(me->inSELECT && me->inTEXTAREA) ? ", " : "",			me->inTEXTAREA ? "TEXTAREA" : "",			((me->inSELECT || me->inTEXTAREA) && me->inA) ? ", " : "",			me->inA ? "A" : "");	    } else if (!me->inBadHTML) {		HTUserMsg(BAD_HTML_USE_TRACE);		me->inBadHTML = TRUE;	    }	}	break;    case HTML_FRAMESET:	change_paragraph_style(me, me->sp->style);  /* Often won't really change */	break;    case HTML_NOFRAMES:    case HTML_IFRAME:	LYEnsureDoubleSpace(me);	LYResetParagraphAlignment(me);	change_paragraph_style(me, me->sp->style);  /* Often won't really change */	break;    case HTML_BANNER:    case HTML_MARQUEE:    case HTML_BLOCKQUOTE:    case HTML_BQ:    case HTML_ADDRESS:		/*		 *  Set flag to know that style has ended.		 *  Fall through.		i_prior_style = -1;		 */	change_paragraph_style(me, me->sp->style);	UPDATE_STYLE;	if (me->sp->tag_number == element_number)	    LYEnsureDoubleSpace(me);	if (me->List_Nesting_Level >= 0)	    HText_NegateLineOne(me->text);	break;    case HTML_CENTER:    case HTML_DIV:	if (me->Division_Level >= 0)	    me->Division_Level--;	if (me->Division_Level >= 0)	    me->sp->style->alignment =				me->DivisionAlignments[me->Division_Level];	change_paragraph_style(me, me->sp->style);	UPDATE_STYLE;	me->current_default_alignment = me->sp->style->alignment;	if (me->List_Nesting_Level >= 0)	    HText_NegateLineOne(me->text);	break;    case HTML_H1:			/* header styles */    case HTML_H2:    case HTML_H3:    case HTML_H4:    case HTML_H5:    case HTML_H6:	if (me->Division_Level >= 0) {	    me->sp->style->alignment =				me->DivisionAlignments[me->Division_Level];	} else if (!strcmp(me->sp->style->name, "HeadingCenter") ||		   !strcmp(me->sp->style->name, "Heading1")) {	    me->sp->style->alignment = HT_CENTER;	} else if (!strcmp(me->sp->style->name, "HeadingRight")) {	    me->sp->style->alignment = HT_RIGHT;	} else {	    me->sp->style->alignment = HT_LEFT;	}	change_paragraph_style(me, me->sp->style);	UPDATE_STYLE;	if (styles[element_number]->font & HT_BOLD) {	    if (me->inBoldA == FALSE && me->inBoldH == TRUE) {		HText_appendCharacter(me->text, LY_BOLD_END_CHAR);	    }	    me->inBoldH = FALSE;	}	if (me->List_Nesting_Level >= 0)	    HText_NegateLineOne(me->text);	if (me->Underline_Level > 0 && me->inUnderline == FALSE) {	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);	    me->inUnderline = TRUE;	}	break;    case HTML_P:	LYHandleP(me,		 (CONST BOOL*)0, (CONST char **)0,		 include,		 FALSE);	break;    case HTML_FONT:	me->inFONT = FALSE;	break;    case HTML_B:			/* Physical character highlighting */    case HTML_BLINK:    case HTML_I:    case HTML_U:    case HTML_CITE:			/* Logical character highlighting */    case HTML_EM:    case HTML_STRONG:	/*	 *  Ignore any emphasis end tags if the	 *  Underline_Level is not set. - FM	 */	if (me->Underline_Level <= 0)	    break;	/*	 *  Adjust the Underline level counter, and	 *  turn off underlining if appropriate. - FM	 */	me->Underline_Level--;	if (me->inUnderline && me->Underline_Level < 1) {	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);	    me->inUnderline = FALSE;	    CTRACE(tfp, "Ending underline\n");	} else {	    CTRACE(tfp, "Underline Level is %d\n", me->Underline_Level);	}	break;    case HTML_ABBREV:	/* Miscellaneous character containers */    case HTML_ACRONYM:    case HTML_AU:    case HTML_AUTHOR:    case HTML_BIG:    case HTML_CODE:    case HTML_DFN:    case HTML_KBD:    case HTML_SAMP:    case HTML_SMALL:    case HTML_SUB:    case HTML_SUP:    case HTML_TT:    case HTML_VAR:	break;    case HTML_DEL:    case HTML_S:    case HTML_STRIKE:	HTML_put_character(me, ' ');	if (me->inUnderline == FALSE)	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);	HTML_put_string(me, ":DEL]");	if (me->inUnderline == FALSE)	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);	HTML_put_character(me, ' ');	me->in_word = NO;	break;    case HTML_INS:	HTML_put_character(me, ' ');	if (me->inUnderline == FALSE)	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);	HTML_put_string(me, ":INS]");	if (me->inUnderline == FALSE)	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);	HTML_put_character(me, ' ');	me->in_word = NO;	break;    case HTML_Q:	if (me->Quote_Level > 0)	    me->Quote_Level--;	/*	 *  Should check LANG and/or DIR attributes, and the	 *  me->node_anchor->charset and/or yet to be added	 *  structure elements, to determine whether we should	 *  use chevrons, but for now we'll always use double-	 *  or single-quotes. - FM	 */	if (!(me->Quote_Level & 1))	    HTML_put_character(me, '"');	else	    HTML_put_character(me, '\'');	break;    case HTML_PRE:				/* Formatted text */	/*	 *  Set to know that we are no longer in a PRE block.	 */	me->inPRE = FALSE;    case HTML_LISTING:				/* Literal text */    case HTML_XMP:    case HTML_PLAINTEXT:	if (me->comment_start)	    HText_appendText(me->text, me->comment_start);	change_paragraph_style(me, me->sp->style);  /* Often won't really change */	if (me->List_Nesting_Level >= 0) {	    UPDATE_STYLE;	    HText_NegateLineOne(me->text);	}	break;    case HTML_NOTE:    case HTML_FN:	change_paragraph_style(me, me->sp->style);  /* Often won't really change */	UPDATE_STYLE;	if (me->sp->tag_number == element_number)	    LYEnsureDoubleSpace(me);	if (me->List_Nesting_Level >= 0)	    HText_NegateLineOne(me->text);	me->inLABEL = FALSE;	break;    case HTML_OL:	me->OL_Counter[me->List_Nesting_Level < 11 ?			    me->List_Nesting_Level : 11] = OL_VOID;    case HTML_DL:    case HTML_UL:    case HTML_MENU:    case HTML_DIR:	me->List_Nesting_Level--;	CTRACE(tfp, "HTML_end_element: Reducing List Nesting Level to %d\n",		    me->List_Nesting_Level);	change_paragraph_style(me, me->sp->style);  /* Often won't really change */	UPDATE_STYLE;	if (me->List_Nesting_Level >= 0)	    LYEnsureSingleSpace(me);	break;    case HTML_SPAN:	/*	 *  Should undo anything we did based on LANG and/or DIR	 *  attributes, and the me->node_anchor->charset and/or	 *  yet to be added structure elements. - FM	 */	break;    case HTML_BDO:	/*	 *  Should undo anything we did based on DIR (and/or LANG)	 *  attributes, and the me->node_anchor->charset and/or	 *  yet to be added structure elements. - FM	 */	break;    case HTML_A:	/*	 *  Ignore any spurious A end tags. - FM	 */	if (me->inA == FALSE)	    break;	/*	 *  Set to know that we are no longer in an anchor.	 */	me->inA = FALSE;	UPDATE_STYLE;	if (me->inBoldA == TRUE && me->inBoldH == FALSE)	    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);	HText_endAnchor(me->text, me->CurrentANum);	me->CurrentANum = 0;	me->inBoldA = FALSE;	if (me->Underline_Level > 0 && me->inUnderline == FALSE) {	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);	    me->inUnderline = TRUE;	}	break;    case HTML_MAP:	FREE(me->map_address);	break;    case HTML_BODYTEXT:	/*	 *  We may need to look at this someday to deal with	 *  OBJECTs optimally, but just ignore it for now. - FM	 */	change_paragraph_style(me, me->sp->style);  /* Often won't really change */	break;    case HTML_TEXTFLOW:	/*	 *  We may need to look at this someday to deal with	 *  APPLETs optimally, but just ignore it for now. - FM	 */	change_paragraph_style(me, me->sp->style);  /* Often won't really change */	break;    case HTML_FIG:	if (me->inFIGwithP) {	    LYEnsureDoubleSpace(me);	} else {	    HTML_put_character(me, ' ');  /* space char may be ignored */	}	LYResetParagraphAlignment(me);	me->inFIGwithP = FALSE;	me->inFIG = FALSE;	change_paragraph_style(me, me->sp->style);  /* Often won't really change */	if (me->List_Nesting_Level >= 0) {	    UPDATE_STYLE;	    HText_NegateLineOne(me->text);	}	break;    case HTML_OBJECT:	/*	 *  Finish the data off.	 */	{	    int s = 0, e = 0;	    char *start = NULL, *first_end = NULL;	    BOOL have_param = FALSE;	    char *data = NULL;	    HTChunkTerminate(&me->object);	    data = me->object.data;	    while ((cp = strchr(data, '<')) != NULL) {		/*		 *  Look for nested OBJECTs.  This procedure		 *  could get tripped up if invalid comments		 *  are present in the content, or if an OBJECT		 *  end tag is present in a quoted attribute. - FM		 */		if (!strncmp(cp, "<!--", 4)) {		    data = LYFindEndOfComment(cp);		    cp = data;		} else if (s == 0 && !strncasecomp(cp, "<PARAM", 6)) {		    have_param = TRUE;		} else if (!strncasecomp(cp, "<OBJECT", 7)) {		    if (s == 0)			start = cp;		    s++;		} else if (!strncasecomp(cp, "</OBJECT", 8)) {		    if (e == 0)			first_end = cp;		    e++;		}		data = ++cp;	    }	    if (s > e) {		/*		 *  We have nested OBJECT tags, and not yet all of the		 *  end tags, so restore an end tag to the content, and		 *  pass a dummy start tag to the SGML parser so that it		 *  will resume the accumulation of OBJECT content. - FM		 */		CTRACE(tfp, "HTML: Nested OBJECT tags.  Recycling.\n");		if (*include == NULL) {		    StrAllocCopy(*include, "<OBJECT>");		} else {		    if (0 && strstr(*include, me->object.data) == NULL) {			StrAllocCat(*include, "<OBJECT>");		    }		}		me->object.size--;		HTChunkPuts(&me->object, "</OBJECT>");		change_paragraph_style(me, me->sp->style);		break;	    }	    if (s < e) {		/*		 *  We had more end tags than start tags, so		 *  we have bad HTML or otherwise misparsed. - FM		 */		if (TRACE) {		    fprintf(tfp,  "Bad HTML: Unmatched OBJECT start and end tags.  Discarding content:\n%s\n",			    me->object.data);		} else if (!me->inBadHTML) {		    HTUserMsg(BAD_HTML_USE_TRACE);		    me->inBadHTML = TRUE;		}		goto End_Object;	    }	    /*	     *	OBJECT start and end tags are fully matched,	     *	assuming we weren't tripped up by comments	     *	or quoted attributes. - FM	     */	    CTRACE(tfp, "HTML:OBJECT content:\n%s\n", me->object.data);	    /*	     *	OBJECTs with DECLARE should be saved but	     *	not instantiated, and if nested, can have	     *	only other DECLAREd OBJECTs.  Until we have	     *	code to handle these, we'll just create an	     *	anchor for the ID, if present, and discard	     *	the content (sigh 8-). - FM	     */	    if (me->object_declare == TRUE) {		if (me->object_id && *me->object_id)		    LYHandleID(me, me->object_id);		CTRACE(tfp, "HTML: DECLAREd OBJECT.  Ignoring!\n");		goto End_Object;	    }	    /*	     *	OBJECTs with NAME are for FORM submissions.	     *	We'll just create an anchor for the ID, if	     *	present, and discard the content until we	     *	have code to handle these. (sigh 8-). - FM	     */	    if (me->object_name != NULL) {		if (me->object_id && *me->object_id)		    LYHandleID(me, me->object_id);		CTRACE(tfp, "HTML: NAMEd OBJECT.  Ignoring!\n");		goto End_Object;	    }	    /*	     *	Deal with any nested OBJECTs by descending	     *	to the inner-most OBJECT. - FM	     */	    if (s > 0) {		if (start != NULL &&		    first_end != NULL && first_end > start) {		    /*		     *	Minumum requirements for the ad hoc parsing		     *	to have succeeded are met.  We'll hope that		     *	it did succeed. - FM		     */		    *first_end = '\0';		    data = NULL;		    StrAllocCopy(data, start);		    if (e > 1) {			for (i = e; i > 1; i--) {			    StrAllocCat(data, "</OBJECT><OBJECT>");			}		    }		    StrAllocCat(data, "</OBJECT>");		    StrAllocCat(*include, data);		    CTRACE(tfp, "HTML: Recycling nested OBJECT%s.\n",					(e > 1) ? "s" : "");		    FREE(data);		    goto End_Object;		} else {		    if (TRACE) {			fprintf(tfp,     "Bad HTML: Unmatched OBJECT start and end tags.  Discarding content.\n");		    } else if (!me->inBadHTML) {			HTUserMsg(BAD_HTML_USE_TRACE);			me->inBadHTML = TRUE;		    }		    goto End_Object;		}	    }	    /*	     *	If it's content has SHAPES, convert it to FIG. - FM	     */	    if (me->object_shapes == TRUE) {		CTRACE(tfp, "HTML: OBJECT has SHAPES.  Converting to FIG.\n");		StrAllocCat(*include, "<FIG ISOBJECT IMAGEMAP");		if (me->object_ismap == TRUE)		    StrAllocCat(*include, " IMAGEMAP");		if (me->object_id != NULL) {		    StrAllocCat(*include, " ID=\"");		    StrAllocCat(*include, me->object_id);		    StrAllocCat(*include, "\"");		}		if (me->object_data != NULL &&		    me->object_classid == NULL) {		    StrAllocCat(*include, " SRC=\"");		    StrAllocCat(*include, me->object_data);		    StrAllocCat(*include, "\"");		}		StrAllocCat(*include, ">");		me->object.size--;		HTChunkPuts(&me->object, "</FIG>");		HTChunkTerminate(&me->object);		StrAllocCat(*include, me->object.data);		goto End_Object;	    }	    /*	     *	If it has a USEMAP attribute and didn't have SHAPES,	     *	convert it to IMG. - FM	     */	    if (me->object_usemap != NULL) {		CTRACE(tfp, "HTML: OBJECT has USEMAP.  Converting to IMG.\n");		StrAllocCat(*include, "<IMG ISOBJECT");		if (me->object_id != NULL) {		    /*		     *	Pass the ID. - FM		     */		    StrAllocCat(*include, " ID=\"");		    StrAllocCat(*include, me->object_id);		    StrAllocCat(*include, "\"");		}		if (me->object_data != NULL &&		    me->object_classid == NULL) {		    /*		     *	We have DATA with no CLASSID, so let's		     *	hope it' equivalent to an SRC. - FM		     */		    StrAllocCat(*include, " SRC=\"");		    StrAllocCat(*include, me->object_data);		    StrAllocCat(*include, "\"");		}		if (me->object_title != NULL) {		    /*		     *	Use the TITLE for both the MAP		     *	and the IMGs ALT. - FM		     */		    StrAllocCat(*include, " TITLE=\"");		    StrAllocCat(*include, me->object_title);		    StrAllocCat(*include, "\" ALT=\"");		    StrAllocCat(*include, me->object_title);		    StrAllocCat(*include, "\"");		}		/*		 *  Add the USEMAP, and an ISMAP if present. - FM		 */		if (me->object_usemap != NULL) {		    StrAllocCat(*include, " USEMAP=\"");		    StrAllocCat(*include, me->object_usemap);		    if (me->object_ismap == TRUE)			StrAllocCat(*include, "\" ISMAP>");		    else			StrAllocCat(*include, "\">");		} else {		    StrAllocCat(*include, ">");		}		goto End_Object;	    }	    /*	     *	Add an ID link if needed. - FM	     */	    if (me->object_id && *me->object_id)		LYHandleID(me, me->object_id);	    /*	     *	Add the OBJECTs content if not empty. - FM	     */	    if (me->object.size > 1)		StrAllocCat(*include, me->object.data);	    /*	     *	Create a link to the DATA, if desired, and	     *	we can rule out that it involves scripting	     *	code.  This a risky thing to do, but we can	     *	toggle clickable_images mode off if it really	     *	screws things up, and so we may as well give	     *	it a try. - FM	     */	    if (clickable_images) {		if (me->object_data != NULL &&		    !have_param &&		    me->object_classid == NULL &&		    me->object_codebase == NULL &&		    me->object_codetype == NULL) {		    /*		     *	We have a DATA value and no need for scripting		     *	code, so close the current Anchor, if one is		     *	open, and add an Anchor for this source.  If		     *	we also have a TYPE value, check whether it's		     *	an image or not, and set the link name		     *	accordingly. - FM		     */		    if (me->inA)			StrAllocCat(*include, "</A>");		    StrAllocCat(*include, " -<A HREF=\"");		    StrAllocCat(*include, me->object_data);		    StrAllocCat(*include, "\">");		    if ((me->object_type != NULL) &&			!strncasecomp(me->object_type, "image/", 6)) {			StrAllocCat(*include, "(IMAGE)");		    } else {			StrAllocCat(*include, "(OBJECT)");		    }		    StrAllocCat(*include, "</A> ");		}	    }	}	/*	 *  Re-intialize all of the OBJECT elements. - FM	 */End_Object:	HTChunkClear(&me->object);	me->object_started = FALSE;	me->object_declare = FALSE;	me->object_shapes = FALSE;	me->object_ismap = FALSE;	FREE(me->object_usemap);	FREE(me->object_id);	FREE(me->object_title);	FREE(me->object_data);	FREE(me->object_type);	FREE(me->object_classid);	FREE(me->object_codebase);	FREE(me->object_codetype);	FREE(me->object_name);	change_paragraph_style(me, me->sp->style);  /* Often won't really change */	break;    case HTML_APPLET:	if (me->inAPPLETwithP) {	    LYEnsureDoubleSpace(me);	} else {	    HTML_put_character(me, ' ');  /* space char may be ignored */	}	LYResetParagraphAlignment(me);	me->inAPPLETwithP = FALSE;	me->inAPPLET = FALSE;	change_paragraph_style(me, me->sp->style);  /* Often won't really change */	break;    case HTML_CAPTION:	LYEnsureDoubleSpace(me);	LYResetParagraphAlignment(me);	me->inCAPTION = FALSE;	change_paragraph_style(me, me->sp->style);  /* Often won't really change */	me->inLABEL = FALSE;	break;    case HTML_CREDIT:	LYEnsureDoubleSpace(me);	LYResetParagraphAlignment(me);	me->inCREDIT = FALSE;	change_paragraph_style(me, me->sp->style);  /* Often won't really change */	me->inLABEL = FALSE;	break;    case HTML_FORM:	/*	 *  Check if we had a FORM start tag, and issue a	 *  message if not, but fall through to check for	 *  an open SELECT and ensure that the FORM-related	 *  globals in GridText.c are initialized. - FM	 */	if (!me->inFORM) {	    if (TRACE) {		fprintf(tfp, "Bad HTML: Unmatched FORM end tag\n");	    } else if (!me->inBadHTML) {		HTUserMsg(BAD_HTML_USE_TRACE);		me->inBadHTML = TRUE;	    }	}	/*	 *  Check if we still have a SELECT element open.	 *  FORM may have been declared SGML_EMPTY in HTMLDTD.c,	 *  and in that case SGML_character() in SGML.c is	 *  not able to ensure correct nesting; or it may have	 *  failed to enforce valid nesting.  If a SELECT is open,	 *  issue a message, then	 *  call HTML_end_element() directly (with a	 *  check in that to bypass decrementing of the HTML	 *  parser's stack) to close the SELECT. - kw	 */	if (me->inSELECT) {	    if (TRACE) {		fprintf(tfp,		   "Bad HTML: Open SELECT at FORM end. Faking SELECT end tag. *****\n");	    } else if (!me->inBadHTML) {		HTUserMsg(BAD_HTML_USE_TRACE);		me->inBadHTML = TRUE;	    }	    if (me->sp->tag_number != HTML_SELECT) {		SET_SKIP_STACK(HTML_SELECT);	    }	    HTML_end_element(me, HTML_SELECT, include);	}	/*	 *  Set to know that we are no longer in an form.	 */	me->inFORM = FALSE;	HText_endForm(me->text);	/*	 *  If we are in a list and are on the first line	 *  with no text following a bullet or number,	 *  don't force a newline.  This could happen if	 *  we were called from HTML_start_element() due	 *  to a missing FORM end tag. - FM	 */	if (!(me->List_Nesting_Level >= 0 && !me->inP))	    LYEnsureSingleSpace(me);	break;    case HTML_FIELDSET:	LYEnsureDoubleSpace(me);	LYResetParagraphAlignment(me);	change_paragraph_style(me, me->sp->style);  /* Often won't really change */	break;    case HTML_LEGEND:	LYEnsureDoubleSpace(me);	LYResetParagraphAlignment(me);	change_paragraph_style(me, me->sp->style);  /* Often won't really change */	break;    case HTML_LABEL:	break;    case HTML_BUTTON:	break;    case HTML_TEXTAREA:	{	    InputFieldData I;	    int chars;	    char *data;	    /*	     *	Make sure we had a textarea start tag.	     */	    if (!me->inTEXTAREA) {		if (TRACE) {		    fprintf(tfp, "Bad HTML: Unmatched TEXTAREA end tag\n");		} else if (!me->inBadHTML) {		    HTUserMsg(BAD_HTML_USE_TRACE);		    me->inBadHTML = TRUE;		}		break;	    }	    /*	     *	Set to know that we are no longer in a textarea tag.	     */	    me->inTEXTAREA = FALSE;	    /*	     *	Initialize.	     */	    I.align=NULL; I.accept=NULL; I.checked=NO; I.class=NULL;	    I.disabled=NO; I.error=NULL; I.height= NULL; I.id=NULL;	    I.lang=NULL; I.max=NULL; I.maxlength=NULL; I.md=NULL;	    I.min=NULL; I.name=NULL; I.size=NULL; I.src=NULL;	    I.type=NULL; I.value=NULL; I.width=NULL;	    I.value_cs = current_char_set;	    UPDATE_STYLE;	    /*	     *	Before any input field add a space if necessary.	     */	    HTML_put_character(me, ' ');	    me->in_word = NO;	    /*	     *	Add a return.	     */	    HText_appendCharacter(me->text, '\r');	    /*	     *	Finish the data off.	     */	    HTChunkTerminate(&me->textarea);	    data = me->textarea.data;	    FREE(temp);	    I.type = "textarea";	    I.size = me->textarea_cols;	    I.name = me->textarea_name;	    I.name_cs = me->textarea_name_cs;	    I.accept_cs = me->textarea_accept_cs;	    me->textarea_accept_cs = NULL;	    I.disabled = me->textarea_disabled;	    I.id = me->textarea_id;	    /*	     *	SGML unescape any character references in TEXTAREA	     *	content, then parse it into individual lines	     *	to be handled as a series of INPUT fields (ugh!).	     *	Any raw 8-bit or multibyte characters already have been	     *	handled in relation to the display character set	     *	in SGML_character().	     */	    me->UsePlainSpace = TRUE;	    TRANSLATE_AND_UNESCAPE_ENTITIES5(&me->textarea.data,						    me->UCLYhndl,						    current_char_set,						    me->UsePlainSpace, me->HiddenValue);	    /*	     *	Trim any trailing newlines and	     *	skip any lead newlines. - FM	     */	    if (*data != '\0') {		cp = (data + strlen(data)) - 1;		while (cp >= data && *cp == '\n') {		    *cp-- = '\0';		}		while (*data == '\n') {		    data++;		}	    }	    /*	     *	Load the first text line, or set	     *	up for all blank rows. - FM	     */	    if ((cp = strchr(data, '\n')) != NULL) {		*cp = '\0';		StrAllocCopy(temp, data);		*cp = '\n';		data = (cp + 1);	    } else {		if (*data != '\0') {		    StrAllocCopy(temp, data);		} else {		    FREE(temp);		}		data = "";	    }	    /*	     *	Display at least the requested number	     *	of text lines and/or blank rows. - FM	     */	    for (i = 0; i < me->textarea_rows; i++) {		int j;		for (j = 0; temp && temp[j]; j++) {		    if (temp[j] == '\r')			temp[j] = (temp[j+1] ? ' ' : '\0');		}		I.value = temp;		chars = HText_beginInput(me->text, me->inUnderline, &I);		for (; chars > 0; chars--)		    HTML_put_character(me, '_');		HText_appendCharacter(me->text, '\r');		if (*data != '\0') {		    if (*data == '\n') {			FREE(temp);			data++;		    } else if ((cp = strchr(data, '\n')) != NULL) {			*cp = '\0';			StrAllocCopy(temp, data);			*cp = '\n';			data = (cp + 1);		    } else {			StrAllocCopy(temp, data);			data = "";		    }		} else {		    FREE(temp);		}	    }	    /*	     *	Check for more data lines than the rows attribute.	     *	We add them to the display, because we support only	     *	horizontal and not also vertical scrolling. - FM	     */	    while (*data != '\0' || temp != NULL) {		int j;		for (j = 0; temp && temp[j]; j++) {		    if (temp[j] == '\r')			temp[j] = (temp[j+1] ? ' ' : '\0');		}		I.value = temp;		chars = HText_beginInput(me->text, me->inUnderline, &I);		for (chars = atoi(me->textarea_cols); chars > 0; chars--)		    HTML_put_character(me, '_');		HText_appendCharacter(me->text, '\r');		if (*data == '\n') {		    FREE(temp);		    data++;		} else if ((cp = strchr(data, '\n')) != NULL) {		    *cp = '\0';		    StrAllocCopy(temp, data);		    *cp = '\n';		    data = (cp + 1);		} else if (*data != '\0') {		    StrAllocCopy(temp, data);		    data = "";		} else {		    FREE(temp);		}	    }	    FREE(temp);	    cp = NULL;	    me->UsePlainSpace = FALSE;	    HTChunkClear(&me->textarea);	    FREE(me->textarea_name);	    me->textarea_name_cs = -1;	    FREE(me->textarea_cols);	    FREE(me->textarea_id);	    break;	}    case HTML_SELECT:	{	    char *ptr;	    /*	     *	Make sure we had a select start tag.	     */	    if (!me->inSELECT) {		if (TRACE) {		    fprintf(tfp, "Bad HTML: Unmatched SELECT end tag *****\n");		} else if (!me->inBadHTML) {		    HTUserMsg(BAD_HTML_USE_TRACE);		    me->inBadHTML = TRUE;		}		break;	    }	    /*	     *	Set to know that we are no longer in a select tag.	     */	    me->inSELECT = FALSE;	    /*	     *	Clear the disable attribute.	     */	    me->select_disabled = FALSE;	    /*	     *	Make sure we're in a form.	     */	    if (!me->inFORM) {		if (TRACE) {		    fprintf(tfp,			    "Bad HTML: SELECT end tag not within FORM element *****\n");		} else if (!me->inBadHTML) {		    HTUserMsg(BAD_HTML_USE_TRACE);		    me->inBadHTML = TRUE;		}		/*		 *  Hopefully won't crash, so we'll ignore it. - kw		 */	    }	    /*	     *	Finish the data off.	     */	    HTChunkTerminate(&me->option);	    /*	     *	Finish the previous option.	     */	    ptr = HText_setLastOptionValue(me->text,					   me->option.data,					   me->LastOptionValue,					   LAST_ORDER,					   me->LastOptionChecked,					   me->UCLYhndl,					   ATTR_CS_IN);	    FREE(me->LastOptionValue);	    me->LastOptionChecked = FALSE;	    if (HTCurSelectGroupType == F_CHECKBOX_TYPE ||		LYSelectPopups == FALSE) {		    /*		     *	Start a newline after the last checkbox/button option.		     */		    LYEnsureSingleSpace(me);	    } else {		/*		 *  Output popup box with the default option to screen,		 *  but use non-breaking spaces for output.		 */		if (ptr &&		    (me->sp[0].tag_number == HTML_PRE || me->inPRE == TRUE ||		     !me->sp->style->freeFormat) &&		    strlen(ptr) > 6) {		    /*		     *	The code inadequately handles OPTION fields in PRE tags.		     *	We'll put up a minimum of 6 characters, and if any		     *	more would exceed the wrap column, we'll ignore them.		     */		    for (i = 0; i < 6; i++) {			if (*ptr == ' ')			    HText_appendCharacter(me->text,HT_NON_BREAK_SPACE);			else			    HText_appendCharacter(me->text,*ptr);			ptr++;		    }		    HText_setIgnoreExcess(me->text, TRUE);		}		for (; ptr && *ptr != '\0'; ptr++) {		    if (*ptr == ' ')			HText_appendCharacter(me->text,HT_NON_BREAK_SPACE);		    else			HText_appendCharacter(me->text,*ptr);		}		/*		 *  Add end option character.		 */		if (!me->first_option) {		    HText_appendCharacter(me->text, ']');		    HText_setLastChar(me->text, ']');		    me->in_word = YES;		}		HText_setIgnoreExcess(me->text, FALSE);	    }	    HTChunkClear(&me->option);	    if (me->Underline_Level > 0 && me->inUnderline == FALSE) {		HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);		me->inUnderline = TRUE;	    }	    if (me->needBoldH == TRUE && me->inBoldH == FALSE) {		HText_appendCharacter(me->text, LY_BOLD_START_CHAR);		me->inBoldH = TRUE;		me->needBoldH = FALSE;	    }	}	break;    case HTML_TABLE:	me->inTABLE = FALSE;	if (!strcmp(me->sp->style->name, "Preformatted")) {	    break;	}	if (me->Division_Level >= 0)	    me->Division_Level--;	if (me->Division_Level >= 0)	    me->sp->style->alignment =				me->DivisionAlignments[me->Division_Level];	change_paragraph_style(me, me->sp->style);	UPDATE_STYLE;	me->current_default_alignment = me->sp->style->alignment;	if (me->List_Nesting_Level >= 0)	    HText_NegateLineOne(me->text);	break;/* These TABLE related elements may now not be SGML_EMPTY. - kw */    case HTML_TR:	if (HText_LastLineSize(me->text, FALSE)) {	    HText_setLastChar(me->text, ' ');  /* absorb next white space */	    HText_appendCharacter(me->text, '\r');	}	me->in_word = NO;	break;    case HTML_THEAD:    case HTML_TFOOT:    case HTML_TBODY:	break;    case HTML_COLGROUP:	break;    case HTML_TH:	break;    case HTML_TD:	break;/* More stuff that may now not be SGML_EMPTY any more: */    case HTML_DT:    case HTML_DD:    case HTML_LH:    case HTML_LI:    case HTML_OVERLAY:	break;    case HTML_MATH:	/*	 *  We're getting it as Literal text, which, until we can process	 *  it, we'll display as is, within brackets to alert the user. - FM	 */	HTChunkPutc(&me->math, ' ');	HTChunkTerminate(&me->math);	if (me->math.size > 2) {	    LYEnsureSingleSpace(me);	    if (me->inUnderline == FALSE)		HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);	    HTML_put_string(me, "[MATH:");	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);	    HTML_put_character(me, ' ');	    HTML_put_string(me, me->math.data);	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);	    HTML_put_string(me, ":MATH]");	    if (me->inUnderline == FALSE)		HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);	    LYEnsureSingleSpace(me);	}	HTChunkClear(&me->math);	break;    default:	change_paragraph_style(me, me->sp->style);  /* Often won't really change */	break;    } /* switch */#ifdef USE_COLOR_STYLE#if !OPT_SCN    TrimColorClass(HTML_dtd.tags[element_number].name,		   Style_className, &hcode);#else# if !OMIT_SCN_KEEPING    FastTrimColorClass(HTML_dtd.tags[element_number].name,		       HTML_dtd.tags[element_number].name_len,		       Style_className,		       &Style_className_end, &hcode);#  endif#endif    if (!REALLY_EMPTY(element_number))    {	CTRACE(tfp, "STYLE:end_element: ending non-EMPTY style\n");#if !defined(USE_HASH)	HText_characterStyle(me->text, element_number+STARTAT, STACK_OFF);#else	HText_characterStyle(me->text, HCODE_TO_STACK_OFF(hcode), STACK_OFF);#endif /* USE_HASH */    }#endif /* USE_COLOR_STYLE */}