/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: textprsr.cpp,v 1.1.2.1 2004/07/09 01:50:20 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include "hxtypes.h"
#include "hxassert.h"
#include <stdlib.h>
#include <string.h>

#include "rt_types.h" //for _CHAR, RED_GREEN_OR_BLUE, COLORTYPE

#include "hxslist.h" //for base class CHXSimpleList.
#include "hxstack.h" //for base class CHXStack.

#include "fontdefs.h"
#include "txtattrb.h" //for class TextAttributes
#include "txtcntnr.h" //for class TextContainer & class TextContainerList
#include "textline.h" //for class TextLine & TextLineList
#ifdef _WINDOWS
#ifdef _WIN16
#include <windows.h>
#endif /* _WIN16 */
#endif /* _WINDOWS */
#include "txtwindw.h" //for class TextWindow.
#include "parsing.h"  //for parsing helper functions.
#include "atocolor.h" //for string-to-COLORTYPE conversion functions.
#include "rt_string.h" //for stringCompare().
#include "atotime.h"
#include "fontinfo.h" //for GetCharacterWidth().
#include "hxstrutl.h" //for isspace()
#include "textprsr.h"

#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE		
static char HX_THIS_FILE[] = __FILE__;
#endif


///////////////////////////////////////////////////////////////////////////// 
//  Returns 1 + (index of pData where the <WINDOW ..> tag's '>' char is).
//		returns dataLength if failure to find valid <WINDOW..> tag.
ULONG32 TextParser::ParseHeader(void* pData, ULONG32 dataLength,
	ULONG32 ulRTFileFormatMarkupParsingMajorVersion,
	ULONG32 ulRTFileFormatMarkupParsingMinorVersion)
{
    //Now parse pData:

#if !defined(_CHARsizeInBytesIs1)
#error this code needs to be updated...
#endif

    _CHAR* pData_CHAR = (_CHAR*)pData;

    if(!m_pTextWindow  ||  !m_pTextWindow->m_pTLList  ||
	    !m_pTextWindow->m_pFontUndoTagList)
    {
	//can't do anything if a lack of memory meant that memory for
	// one or both of these objects  couldn't be allocated (which is
	// the only reason they should be NULL here):
	return dataLength;
    }
    
    //	Find the first '<' and then find the first '>' or end-of-data, and
    //  send the contents found to m_pTextWindow->parseHeaderTag():
    _CHAR* pHeaderTagBuf;
    LONG32 headerTagBufLen;

    LONG32 indexOfLeftBracket = -1;
    LONG32 indexOfRightBracket = -1;
    LONG32 indx;
    LONG32 len = LONG32(dataLength);
    for(indx=0; indx<len; indx++)
    {
/*XXXEH- for now, we have to assume that the first text encountered is not
  DBCS text; it and all text inside tags must be us-ascii charset:
	//added the following to handle DBCS chars:
	if((UCHAR)pData_CHAR[indx] >= DBCS_MIN_LEAD_BYTE_VAL)
	{
	    indx++; //skip this and the following trail byte.
	    continue;
	}
*/	
	if(pData_CHAR[indx] == '<')
	{
	    indexOfLeftBracket = indx;
	    break;
	}
    }
    if(indexOfLeftBracket != -1)
    {
	for(indx++; indx<len; indx++)
	{
/*XXXEH- for now, we have to assume that the first text encountered is not
  DBCS text; it and all text inside tags must be us-ascii charset:
	    //added the following to handle DBCS chars:
	    if((UCHAR)pData_CHAR[indx] >= DBCS_MIN_LEAD_BYTE_VAL)
	    {
		indx++; //skip this and the following trail byte.
		continue;
	    }
*/
	    if(pData_CHAR[indx] == '>')
	    {
		indexOfRightBracket = indx;
		break;
	    }	
	}
    }
    if(-1 == indexOfLeftBracket  ||  -1 == indexOfRightBracket  ||
	    ((indexOfRightBracket-indexOfLeftBracket)-1) <
	    LONG32(int(strlen("WINDOW"))) )
    {
	//Added this to allow <HTML> to be the first tag:
	if( ((indexOfRightBracket-indexOfLeftBracket)-1) >=
		LONG32(int(strlen("HTML"))) )
	{
	    if(('H' == pData_CHAR[indexOfLeftBracket]  ||
		    'h' == pData_CHAR[indexOfLeftBracket])  &&
		    ('T' == pData_CHAR[indexOfLeftBracket+1]  ||
		    't' == pData_CHAR[indexOfLeftBracket+1])  &&
		    ('M' == pData_CHAR[indexOfLeftBracket+2]  ||
		    'm' == pData_CHAR[indexOfLeftBracket+2])  &&
		    ('L' == pData_CHAR[indexOfLeftBracket+3]  ||
		    'l' == pData_CHAR[indexOfLeftBracket+3])    )
	    {	//XXXEH- need to handle type=html in parseHeaderTag():
		m_pTextWindow->parseHeaderTag("WINDOW type=HTML", 16,
			ulRTFileFormatMarkupParsingMajorVersion,
			ulRTFileFormatMarkupParsingMinorVersion);
		if(-1 == indexOfRightBracket)
		{   //XXXEH- should dataLength be returned here?!:
		    return 0L; //signals that no header tag was found (and
				//that the text starts at byte zero of file).
		}
		else
		{
		    return (indexOfRightBracket+1);
		}
	    }
	}
	//Added this to allow files with no header tag:
	// No '<' was found (or no valid header tag was found),
	//Need to set default vals to type
	// generic's default vals here:
	//XXXEH- need to handle type=plaintext in parseHeaderTag():
        char szTmpBuff[255] = {"WINDOW type=plaintext"}; /* Flawfinder: ignore */
	m_pTextWindow->parseHeaderTag(szTmpBuff, 21,
		ulRTFileFormatMarkupParsingMajorVersion,
		ulRTFileFormatMarkupParsingMinorVersion);
	if(-1 == indexOfRightBracket)
	{	//XXXEH- should dataLength be returned here?!:
	    return 0L; //this signals that no header tag was found (and
			//that the text starts at byte zero of the file).
	}
	else
	{
	    return (indexOfRightBracket+1);
	}
    }

    //	Next, parse the header's tag after copying it into pHeaderTagBuf:
    headerTagBufLen = indexOfRightBracket-indexOfLeftBracket-1;
    pHeaderTagBuf = new _CHAR[headerTagBufLen+1];//Add 1 for terminating '\0'
    HX_ASSERT_VALID_PTR(pHeaderTagBuf);
    if(NULL == pHeaderTagBuf)
    {
	return dataLength;  //return end-of-pData index to signal error.		
    }

    for(indx=0; indx<headerTagBufLen; indx++)
    {
	pHeaderTagBuf[indx] = pData_CHAR[indx+indexOfLeftBracket+1];
    }
    pHeaderTagBuf[headerTagBufLen] = '\0';

    //  Now parse the header to get the WINDOW tag, and, if it is
    //  found, get the "NAME=value" pairs and assign the TextWindow's
    //  appropriate objects' data to these requested values:
    if(!m_pTextWindow->parseHeaderTag(pHeaderTagBuf, headerTagBufLen,
	    ulRTFileFormatMarkupParsingMajorVersion,
	    ulRTFileFormatMarkupParsingMinorVersion))
    {	//  Returned FALSE because of invalid header tag:
	delete [] pHeaderTagBuf;
	pHeaderTagBuf = NULL;
	return dataLength; //return end-of-pData index to signal error.							
    }

    delete [] pHeaderTagBuf;
    pHeaderTagBuf = NULL;

    //This kills a bug in a tickertape where
    // the packet starts with <TL> but the <POS .. Y0= >
    // before it causes the renderer to use the Y0=0 value
    // that the ff or encoder incorrectly calculated for TL
    // text because it thought the "visible" window height
    // was 0:
    m_pTextWindow->setVisibleWindowWidth(m_pTextWindow->getWidth());
    m_pTextWindow->setVisibleWindowHeight(m_pTextWindow->getHeight());

    return (indexOfRightBracket+1);
}



/////////////////////////////////////////////////////////////////////////////
// Method:
//  ULONG32 TextParser::ParseText(...)
// Purpose:
//  This function receives the latest raw data (in pData) and
//  inserts it into the m_pTextWindow::TextContainerList with the latest
//  render attribute in m_pTextWindow::TextAttributeStacks.
//
// bDataHasREQUIREDContents gets set by this function to TRUE if any of
//  the TextContainers created from pData have the REQUIRED attribute.
//
// ulEarliestTimeOfNewData gets set with the time which is the earliest
// time of the TextLines into which this data is parsed.
//
// Return value is HXR_OK if in-bound data was valid and no error occurred,
// otherwise it's HXR_NO_DATA if pData is NULL or empty,
// HXR_ELEMENT_NOT_FOUND if all that is in pData is spaces, tabs, and/or
// newline characters, and HXR_NOT_INITIALIZED if ulEarliestTimeOfNewData
// was not set.
//
HX_RESULT TextParser::ParseText(void* pData, ULONG32 dataLength,
	LONG32 lTimeOffset,
	ULONG32& ulEarliestTimeOfNewData,
	ULONG32& ulEndTimeOfPacket,
	BOOL& bRef_DataHasREQUIREDContents,
	BOOL bFileFormatIsCallingThis,
	ULONG32 ulByteOffsetIntoFile,
	TextLine** ppFirstTextLineInPkt
	)
{
    *ppFirstTextLineInPkt = NULL;
    ULONG32 ulStartByteOfFirstTL = 0L;
    BOOL bFirstTLwasFound = FALSE;

    //Look through the data for the first valid markup tag.  Any text found
    //	before that gets put in a new TextContainer object and is inserted
    //	into the TextContainerList part of the TextWindow object.  If that
    //	list is empty, a new TextContainer is added to it with the default
    //	characteristics for the text.  If a valid markup tag is found before
    //	the end of the pData is reached, the text that follows it, up to the
    //	next tag, goes into a new TextContainer object that is added to the
    //	list and has the text-rendering characteristic specified in that tag:
	
#if !defined(_CHARsizeInBytesIs1)
#error this code needs to be updated...
#endif
    _CHAR* pData_CHAR = (_CHAR*)pData;

    //Added this for when this is called by the file format
    ulEarliestTimeOfNewData = TIME_INVALID;
    BOOL bEarliestTimeOfNewDataIsValid = FALSE;

    //Added this for when this is called by the file format
    ulEndTimeOfPacket = TIME_INVALID;

    bRef_DataHasREQUIREDContents = FALSE;


    if(!pData_CHAR  ||  dataLength<1)
    {
	return HXR_NO_DATA; //there's nothing to do.
    }

    ULONG32 ulOriginalDataLength = dataLength;
    if('\0' == pData_CHAR[dataLength-1])
    {
	dataLength -= 1;
    }

    BOOL bIsLiveSource = m_pTextWindow->isLiveSource();

    TextContainer* pTC = NULL;

    //Added this for when this is called by the file format
    TextLine* pTL = NULL;

    //Find the first '<' or end-of-data, whichever comes first, and put
    //	any raw text found before it into the TextContainer list:

    LONG32 startIndex=0L;

    //Added the following which keep track of where
    // each TextLine starts/ends in the file (this is ignored if
    // bFileFormatIsCallingThis is FALSE):
    ULONG32 ulCurLineStartByteNumOfFile = ulByteOffsetIntoFile;
    ULONG32 ulCurLineEndByteNumOfFile = ulByteOffsetIntoFile;

    //Changed this to skip newlines only:
    //First, skip all newline characters at start of this string only
    // if this is the very first packet:
    IncrementCurrentPacketNum();
    if(GetCurrentPacketNum() <= 1L)
    {
	for(startIndex=0L; startIndex<(LONG32)dataLength; startIndex++)
	{
	    if(pData_CHAR[startIndex] != '\n'  &&
		    pData_CHAR[startIndex] != '\r')
	    {
	        break;
	    }
	}
    }

    if((LONG32)dataLength == startIndex)
    {	//All that were found were newline chars, so quit:
	return HXR_ELEMENT_NOT_FOUND;
    }

    //Reset the ptr and dataLength to where the first
    // non-newline is:
    dataLength = dataLength-startIndex;
    pData_CHAR = &(pData_CHAR[startIndex]);
    startIndex = 0L;

    LONG32 len = LONG32(dataLength);
    //Got rid of this and replaced with 
    // m_pTextWindow's functions to keep track of this because there
    // was a bug if a <BR> was the last thing in a packet, using just
    // the following local variable meant that this info got lost
    // when this function was done, so the next packet's data would
    // not end up with the proper number of line breaks before it:
    ///LONG32 numBreakTagsEncountered = 0L;
    
    BOOL bSomeCharsFoundSinceLastBreakTag = FALSE;

    //For bug #6906:
    BOOL bSomeCharsFoundSinceLastPosTag = FALSE;
    BOOL bUserPosTagFoundSinceLastTextContainer = FALSE;

    ULONG32 ulCurCharset;

    ULONG32 ulNumPREtagNewlineCharsFound = 0L;

    do //Now find the next '<', starting at pData_CHAR[startIndex]:
    {
	ulCurCharset = m_pTextWindow->peekAtCharsetStack();
	LONG32 indexOfLeftBracket = -1;
	LONG32 indexOfRightBracket = -1;
	BOOL bSlashFoundAtEndOfTag = FALSE;
	BOOL bIgnoringNewlineChars = FALSE;
	BOOL bDealingWithTabCharWithPre = FALSE;
	LONG32 indx;
	for(indx=startIndex; indx<len; indx++,
		bSomeCharsFoundSinceLastPosTag=TRUE,
		bSomeCharsFoundSinceLastBreakTag=TRUE)
	{
	    _CHAR ch = pData_CHAR[indx];

	    //added the following to handle DBCS chars:
	    if((ulCurCharset & HX_DBCS_CHARSET)  &&
		    (UCHAR)ch >= DBCS_MIN_LEAD_BYTE_VAL)
	    {
		indx++; //skip this and the following trail byte.
		continue;
	    }

	    if('<' == ch)
	    {
		indexOfLeftBracket = indx;
		bSomeCharsFoundSinceLastBreakTag=FALSE; //Fixes bug #6903.
		bSomeCharsFoundSinceLastPosTag=FALSE; //Helps fix bug #6906.
		break;
	    }
	    //Convert any tab chars outside a tag to spaces if we're not
	    // currently between a <PRE> and a </PRE>, else leave '\t' alone
	    // and let TextWindow::insertAtEndOfList() calculate where the
	    // next tab stop is:
	    BOOL bTabCharHandled = FALSE;
	    if('\t' == ch  ||  '\v' == ch
		    ||  '\0' == ch) //<--added this check for safety.
	    {
		pData_CHAR[indx] = ' ';
		//treat tab char as a tab, not a space, inside PRE text:
		if(('\t' == ch)  &&  (m_pTextWindow->peekAtIsPreStack()) )
		{
		    // pretend we've found the start and end of a tag so a
		    // new TC will be created with this char and the
		    // next will start just after this char.
		    {
			bTabCharHandled = TRUE;
			indexOfLeftBracket = indx;
			indexOfRightBracket = indexOfLeftBracket;
			bDealingWithTabCharWithPre = TRUE;
			bSomeCharsFoundSinceLastBreakTag = TRUE;
			bSomeCharsFoundSinceLastPosTag = TRUE;
			break; //indexOf[Left&Right]Bracket vars are set
				// to act as a fake tag just after this '\t'.
		    }
		}
	    }
	    //Reduce any string of newlines into either one or zero spaces,
	    // one space if !bSomeCharsFoundSinceLastBreakTag, 0 otherwise:
	    // UNLESS we're currently between a <PRE> and a </PRE>. in which
	    // case leave them alone.
	    // If we are ignoring extra spaces, then do this for spaces and
	    // tab chars, too:
	    else if(('\n' == ch  ||  '\r' == ch)  ||
		    (m_pTextWindow->IgnoreExtraSpaces()  &&  
		      ((!bTabCharHandled && ('\t' == ch  ||  '\v' == ch))  ||
		      ' ' == ch
		      )
		    )
		   )
	    {
		LONG32 firstNewlineIndex = indx;
		pData_CHAR[firstNewlineIndex] = ' ';
		indexOfLeftBracket = indx;
		if(indexOfLeftBracket+1==len)
		{
		    //If we're at the end of the packet and it ends with a
		    // space or tab char and we're ignoring extra spaces,
		    // then we want to make sure NOT to ignore this last
		    // space if it's solo:
		    if('\n' != ch  &&  '\r' != ch  &&
			    m_pTextWindow->IgnoreExtraSpaces())
		    {
			indexOfLeftBracket++;
		    }
		}
		LONG32 newLineCharCount=1L;
//XXXXXEH-19980918:
		BOOL bNonSpaceTabNewlineCharWasLastFound = FALSE;
		for(indx++; indx<len; indx++, newLineCharCount++)
		{
		    _CHAR ch2 = pData_CHAR[indx];
		    if('\n' == ch2  ||  '\r'== ch2)
		    {
			pData_CHAR[indx] = ' ';
			if(m_pTextWindow->peekAtIsPreStack())
			// pretend we've found the start and end of a tag so
			// a new TC will be created with this char and the
			// next will start just after this char:
			{
			    //See if this is a PC "\r\n" newline:
			    if('\r' == pData_CHAR[indx-1]  &&  '\n' == ch2)
			    {
				indx++;
			    }
			    ulNumPREtagNewlineCharsFound++;
			}
		    }
		    else if(m_pTextWindow->IgnoreExtraSpaces()  &&
			    !m_pTextWindow->peekAtIsPreStack()  &&
			    (!bTabCharHandled && ('\t' == ch2  ||
				    '\v' == ch2)) )
		    {
			;
		    }
		    else if(m_pTextWindow->IgnoreExtraSpaces()  &&
			    !m_pTextWindow->peekAtIsPreStack()  &&
			    ' ' == ch2)
		    {
			;
		    }
		    else
		    {
//XXXXXEH-19980918>>
			//C_RTRNDR_DIFF>>
			if(IsBeta1Player())
			{
			    break;
			}
			//END C_RTRNDR_DIFF.
			if('<'!=ch2)
			{
			    bNonSpaceTabNewlineCharWasLastFound = TRUE;
			}
//<<XXXXXEH-19980918.
			break;
		    }
		}
		indx--;	 //back up one for outer for loop.

		if(bSomeCharsFoundSinceLastBreakTag)
		{
//XXXXXEH-is fix for bug 4881 here?!?  If UNIX uses '\n' instead of "\r\n",
// and we don't handle that count==1 here, what in the Wide Wide World Of
// Sports happens??  (Needs checking in same code in rtrender/c_rtrndr.cpp)
		    //Doesn't follow a new line, so just reduce to 1 space:
		    if(newLineCharCount >= 2L)
		    {
			indexOfRightBracket =
				firstNewlineIndex + newLineCharCount - 1L;
			indexOfLeftBracket = firstNewlineIndex + 1L;
			bIgnoringNewlineChars = TRUE;
			bSomeCharsFoundSinceLastBreakTag = TRUE;
			break; //indexOf[Left&Right]Bracket vars are set
				// to act as a fake tag after the first one.
		    }
		}
		else 
		//we want to ignore all newline characters found
		// because they were preceded by a line break tag, so
		// pretend we've found the start and end of a tag so a new
		// TC will be created ending just before this char and the
		// next will start just after this char.
		{
		    indexOfLeftBracket = firstNewlineIndex;
		    indexOfRightBracket =
			    firstNewlineIndex + newLineCharCount - 1L;
		    bIgnoringNewlineChars = TRUE;
//XXXXXEH-19980918>>
		    //C_RTRNDR_DIFF>>
		    if(IsBeta1Player())
		    {
			bSomeCharsFoundSinceLastPosTag = TRUE;
			bSomeCharsFoundSinceLastBreakTag = TRUE;
		    }
		    else
		    //<<END C_RTRNDR_DIFF.
		    {
			bSomeCharsFoundSinceLastBreakTag =
				bSomeCharsFoundSinceLastPosTag = 
				bNonSpaceTabNewlineCharWasLastFound;
		    }
//<<XXXXXEH-19980918.
		    break; //indexOf[Left&Right]Bracket vars are set.
			    // to act as a fake tag where newlines are..
		}
	    }
	}

	if(-1L != indexOfLeftBracket  &&  -1L == indexOfRightBracket)
	{
	    //Check if we're inside an HTML-style ("<!-- ... -->") comment,
	    // and then ignore all '>'s until we see one preceeded by "--",
	    // i.e., only "-->" ends a comment:
	    if(len-indexOfLeftBracket >= 4)
	    {
		if(!stringCompare(&pData_CHAR[indexOfLeftBracket], 4,
			"<!--", 4))
		{
		    m_pTextWindow->incrementCommentTagNestCount();
		}
	    }

	     //find the closing '>':
	    for(indx++; indx<len; indx++)
	    {
		_CHAR ch = pData_CHAR[indx];

/*XXXEH- for now, we have to assume that the text encountered is not
  DBCS text; all text inside tags must be us-ascii charset:
		//added the following to handle DBCS chars:
		if((UCHAR)ch >= DBCS_MIN_LEAD_BYTE_VAL)
		{
		    indx++; //skip this and the following trail byte.
		    continue;
		}
*/
		if('>' == ch)
		{
		    //Check if we're inside an HTML-style ("<!-- ... -->")
		    // comment, which could contain a '>' (which should be
		    // ignored) before the closing "-->":
		    if(m_pTextWindow->getCommentTagNestCount())
		    {
			if(indx-startIndex >= 2)
			{
			    if(!stringCompare(&pData_CHAR[indx-2], 3,
				    "-->", 3))
			    {
				if(m_pTextWindow->
					decrementCommentTagNestCount() > 0L)
				{
				    continue; //we're still inside a nested
						// comment.
				}
			    }
			    else
			    {
				continue;
			    }
			}
		    }

		    indexOfRightBracket = indx;
		    //Added this to make this XML-compatible:
		    if(indexOfRightBracket>0)
		    {
			if(pData_CHAR[indexOfRightBracket-1] == '/')
			{
			    bSlashFoundAtEndOfTag = TRUE;
			    pData_CHAR[indexOfRightBracket-1] = ' ';
			}
		    }
		    break;
		}	
		else if('<' == ch  &&
			m_pTextWindow->getCommentTagNestCount())
		{
		    //Check if we're a comment nested inside another
		    // ("<!-- ... -->") comment:
		    if(len-indx >= 4)
		    {
			if(!stringCompare(&pData_CHAR[indx], 4,
				"<!--", 4))
			{
			    m_pTextWindow->incrementCommentTagNestCount();
			}
		    }
		}

		//Convert any newline or tab chars to spaces:
		if('\n' == ch  ||  '\r' == ch  ||
				'\t' == ch  ||  '\v' == ch
				||  '\0' == ch)
		{
		    pData_CHAR[indx] = ' ';	 
		    //Note: don't need to track #of conversions inside tag
		}
	    }
	    if(-1 == indexOfRightBracket)
	    {	//No valid end-of-tag found, so ignore all text from
		//  indexOfLeftBracket on; this is done by putting a '\0'
		//  at pData_CHAR[indexOfLeftBracket]:
		pData_CHAR[indexOfLeftBracket] = '\0';
		len = indexOfLeftBracket - startIndex;
	    }
	}
	
	LONG32 tempLen;
	BOOL bPreTabOrNewlineCharOnly = FALSE;
	if(indexOfLeftBracket != -1)
	{
	    tempLen = indexOfLeftBracket - startIndex;
	    pData_CHAR[indexOfLeftBracket] = '\0';
	    if(!tempLen)
	    {
		//Special case where a tab char was the only text found
		// between tags, and, since we're faking like a PRE tab
		// is a tag, tempLen ended up 0 and the while loop, below,
		// was not getting entered:
		if(bDealingWithTabCharWithPre)
		{
		    bPreTabOrNewlineCharOnly = TRUE;
		}
	    }
	}
	else
	{
	    tempLen = len - startIndex;
	}


	ulCurCharset = m_pTextWindow->peekAtCharsetStack();

	//Added the following "while" to allow for
	// word wrap; break the text up wherever there is a
	// space char.  (Also, Changed "if(tempLen.." to
	// "while(tempLen.."
	ULONG32 tmpStartIndex = startIndex;
	while(tempLen > 0  ||
		bPreTabOrNewlineCharOnly)
	{
	    ULONG32 theFollowingSpaceCharIndex = tempLen;
	    char savedChar = '\0';
	    _CHAR* pCurText = &pData_CHAR[tmpStartIndex];

	    //C_RTRNDRDIFF >>:
	    //this needs to be reset prior to the creation of each T.C.:
	    BOOL bCurLineHasREQUIREDContents = FALSE;
	    //<< END C_RTRNDRDIFF.

	    if(m_pTextWindow->usingWordwrap()  &&
		    !m_pTextWindow->peekAtIsPreStack())
	    {
		//Allow wordwrap to happen in DBCS/UNICODE between chars, not
		// just where spaces are, because each character represents
		// a "word" and they are rarely separated by spaces:
		//XXXEH- NOTE: I assume that UNICODE characters that have a
		// zero-valued first byte are really SBCS characters and
		// should not get wordwrapped (because they may be in the
		// middle of a SB word):
		if(ulCurCharset & HX_UNICODE_CHARSET  ||
			ulCurCharset & HX_DBCS_CHARSET)
		{
		    UINT16 sCharWidthInPixels = 0;
		    UINT16 sChar = '\0';
		    LONG32 lNumBytesOfChar = 1L;
		    if(ulCurCharset & HX_UNICODE_CHARSET  ||
			    (ulCurCharset & HX_DBCS_CHARSET  &&
			    (UCHAR)(*pCurText)>= DBCS_MIN_LEAD_BYTE_VAL) )
		    {
			//Is a 2-byte character, so deal with it,
			// making sure there's a trail byte
			lNumBytesOfChar++;
			HX_ASSERT(tempLen>=lNumBytesOfChar);
			UCHAR tmp = *pCurText;
			sChar = (((UINT16)tmp)<<8 | (UCHAR)pCurText[1]);
		    }
		    else
		    {
			sChar = (INT16)((UCHAR)(*pCurText));
		    }

/*  XXXEH-need while loop on each char until we find which character
    exceeds the window's right edge and only then end the text blob...:
		    sCharWidthInPixels = GetCharacterWidth(
			    sChar,
			    m_pTextWindow->peekAtFontFaceStack(),
			    m_pTextWindow->peekAtPointSizeStack(),
			    //XXXEH- isBold and isItalicized need peek..()
			    // functions written for them; assuming they are
			    // TRUE only means wordwrap will happen a few
			    // pixels early, if that:
			    TRUE, TRUE,
			    ulCurCharset);

		    BOOL windowWidthExceeded = 
			    m_pTextWindow->getCurrentTextLineEndX() +
			    sCharWidthInPixels >
			    m_pTextWindow->getWidth();

    XXXEH- ...but for now, just make each (2-byte)DBCS/UNICODE character be in its
    own TextContainer, although this can be less efficient:
*/
		    //If we are going from DB char to DB char or from DB char
		    // to SB char, or from SB char to DB char, or SB char is
		    // a space, newline, or tab, or is a UNICODE character
		    // with a non-zero first byte, or is a UNICODE space, tab,
		    // or newline, then we can do a wordwrap
		    // after this char, otherwise we should not:
		    BOOL bNextCharExists = tempLen>lNumBytesOfChar;
		    //Initialize to opposite of what cur character is:
		    BOOL bNextCharIsTwoByte = (lNumBytesOfChar==1);
		    if(bNextCharExists)
		    {
			bNextCharIsTwoByte =
				(ulCurCharset & HX_UNICODE_CHARSET  &&
				((UCHAR)(pCurText[lNumBytesOfChar])!=0))  ||
			    (ulCurCharset & HX_DBCS_CHARSET  &&
			    (UCHAR)(pCurText[lNumBytesOfChar])>=
			    DBCS_MIN_LEAD_BYTE_VAL);
		    }
		    BOOL bIsUNICODEcharThatCanBeWordwrapped = FALSE;
		    if(ulCurCharset & HX_UNICODE_CHARSET)
		    {
			if(*pCurText!=0)
			{
			    bIsUNICODEcharThatCanBeWordwrapped = TRUE;
			}
			else
			{
			    UCHAR ch = pCurText[1]; 
			    if(' '==ch  ||  '\n'==ch  ||
				    '\r'==ch  ||  '\t'==ch)
			    {
				bIsUNICODEcharThatCanBeWordwrapped = TRUE;
			    }
			}
		    }

		    if((!(1==lNumBytesOfChar)  ||  bNextCharIsTwoByte)  ||
			    (1==lNumBytesOfChar  &&  (' '==*pCurText  ||
			    '\n'==*pCurText  ||  '\r'==*pCurText  ||
			    '\t'==*pCurText))  ||
			    bIsUNICODEcharThatCanBeWordwrapped )
		    {
			//End the text blob here so that wordwrap can occur
			// if this character extends off the right edge
			// of the window:
			theFollowingSpaceCharIndex = lNumBytesOfChar-1;
			savedChar = pCurText[theFollowingSpaceCharIndex+1];
			pCurText[theFollowingSpaceCharIndex+1] = '\0';
		    }
		    //If we're in a bunch of single-byte DBCS chars, then we
		    // need to find the end of them (or the first space, tab,
		    // newline therein), and allow for wordwrap there:
		    else if(1==lNumBytesOfChar  &&  tempLen)
		    {
			_CHAR* pTmpText = pCurText;
			LONG32 lTmpTextLen = tempLen;
			BOOL bSpaceTabOrNewlineFound=FALSE;
			do
			{
			    lNumBytesOfChar = (UCHAR)(*pTmpText)>=
				    DBCS_MIN_LEAD_BYTE_VAL? 2:1;
			    if(1==lNumBytesOfChar)
			    {
				if(' '==*pTmpText  ||  '\n'==*pTmpText  ||
					'\r'==*pTmpText  ||  '\t'==*pTmpText)
				{
				    bSpaceTabOrNewlineFound = TRUE;
				    break;
				}
			    }
			    else
			    {
				lTmpTextLen++;
				pTmpText--;
				break;
			    }
			    pTmpText++;
			    lTmpTextLen--;
			}while(lTmpTextLen);
			if(lTmpTextLen)
			{
			    //then we encountered a double-byte char or a
			    // space, tab, or newline char, so ok to wordwrap
			    // here:
			    theFollowingSpaceCharIndex = tempLen-lTmpTextLen;
			    savedChar=pCurText[theFollowingSpaceCharIndex+1];
			    pCurText[theFollowingSpaceCharIndex+1] = '\0';
			}
		    }
		}
		else //This is SBCS:
		{
		    ULONG32 firstNonSpaceCharIndex =
			    skipSpacesTabsAndNewlineChars(
			    pCurText, tempLen, 0);
		    ULONG32 dummyVar;
		    if(firstNonSpaceCharIndex < (ULONG32)tempLen)
		    {
			theFollowingSpaceCharIndex =
				findNextSpaceTabOrNewLineChar(
				pCurText, tempLen,
				firstNonSpaceCharIndex, dummyVar, ulCurCharset);
			if(theFollowingSpaceCharIndex+1 < (ULONG32)tempLen)
			{
			    savedChar = pCurText[theFollowingSpaceCharIndex+1];
			    pCurText[theFollowingSpaceCharIndex+1] = '\0';
			}
		    }
		}
	    }


	    //C_RTRNDRDIFF >>:
	    //Added the following to keep track of where
	    // in the buffer the TextContainer's data starts:
	    ULONG32 ulTextContainerBufStartIndex = tmpStartIndex;
	    //<< END C_RTRNDRDIFF.

	    if(!bPreTabOrNewlineCharOnly)
	    {
		//Now, look for "&lt;" or "&gt;" and, if found, end the
		// TextContainer at the end of the "&lt;" or "&gt;" & replace
		// it with '<' or '>'; also, look for "&#n;"
		// (where n>8 && n<=255) and translate into the ASCII char
		// with that value, and look for "&nbsp;" for ' ' and "&amp;"
		// for '&':
		ULONG32 ulIndexOfAmpersand = findNextChar('&',
			pCurText,
			theFollowingSpaceCharIndex,
			0L,
			ulCurCharset);
		ULONG32 ulIndexOfLastCharInTCsBuf=theFollowingSpaceCharIndex;
		ULONG32 theOriginalFollowingSpaceCharIndex =
			theFollowingSpaceCharIndex;
		if(theFollowingSpaceCharIndex > ulIndexOfAmpersand  &&
			theFollowingSpaceCharIndex - ulIndexOfAmpersand >= 4)
		{
		    FindEscapeSequenceChar(pCurText,
			    ulIndexOfLastCharInTCsBuf,
			    theFollowingSpaceCharIndex, ulIndexOfAmpersand,
			    ulCurCharset);
		}

		//Create a new one and add it to the list:
		pTC = new TextContainer(pCurText,
			ulIndexOfLastCharInTCsBuf+1>(ULONG32)tempLen?
			(ULONG32)tempLen:ulIndexOfLastCharInTCsBuf+1);
		//First, restore pData_CHAR:
		if(theOriginalFollowingSpaceCharIndex+1 < (ULONG32)tempLen)
		{
		    pCurText[theOriginalFollowingSpaceCharIndex+1] =
			    savedChar;
		}
		//then, reset tempLen and tmpStartIndex:
		{
		    tempLen -= theFollowingSpaceCharIndex+1;
		    tmpStartIndex += theFollowingSpaceCharIndex+1;
		}
	    }
	    else //is a tab char inside PRE so send a space char:
	    {
		//Create a new one and add it to the list:
		pTC = new TextContainer(" ", 1);
		bPreTabOrNewlineCharOnly = FALSE; //so while loop will quit.
	    }

	    HX_ASSERT(pTC);
	    if(!pTC)
	    {	//C_RTRNDRDIFF:  (different due to return type)
		return HXR_OUTOFMEMORY;//mem alloc error, so quit
	    }
	    pTC->setNumNewlinesAtStart(
		    m_pTextWindow->getNumBreakTagsEncountered());
	    m_pTextWindow->clearNumBreakTagsEncountered();

	    //This has to be kept track of because insertAtEndOfList(),
	    // below, resets m_bClearWasJustSent to FALSE:
	    BOOL bClearWasJustSent = m_pTextWindow->m_bClearWasJustSent;

	    BOOL bStopRenderingTimeIsMoreRecent =
		    IsTimeAMoreRecentThanTimeB(
		    m_pTextWindow->GetLatestSentTimeToStopRendering(),
		    m_pTextWindow->GetLatestSentTimeToRender(),
		    bIsLiveSource);

	    //Reset the current endtime so all text
	    // that follows a <CLEAR> has an endtime equal to that of
	    // the end of the stream:
	    if(bClearWasJustSent
		    //This fixes a bug where the author wanted something
		    // to end at a certain time after the <CLEAR> but
		    // couldn't without a <time> tag after the <CLEAR>,
		    // e.g., "<time start=10 end=20><CLEAR>foo" should
		    // behave as follows: foo appears at 10 seconds and
		    // goes away at 20; however:
		    // "<time end=10>blah <time start=10><CLEAR>foo" should
		    // have blah appear up to 10 seconds and foo appear from
		    // 10 seconds until the end of the stream (or until the
		    // next CLEAR tag's start time).
		    &&  !bStopRenderingTimeIsMoreRecent)
	    {
		//If liveSource, start time of stream may not be at time 0
		// and ULONG_MAX may be *earlier* than cur time (since time
		// val is ULONG32 and may wrap); we want to set this to
		// "infinity" (which is 0xfffffffe):
		m_pTextWindow->SetLatestSentTimeToStopRendering(
			(ULONG32)ULONG_MAX);
		if(bIsLiveSource)
		{
		    m_pTextWindow->SetLatestSentTimeToStopRendering(
			    TIME_INFINITY);
		    if(TIME_INVALID == m_pTextWindow->
			    GetLatestSentTimeToStopRendering())
		    {
			m_pTextWindow->SetLatestSentTimeToStopRendering(1L);
		    }
		}
	    }

	    //Set begin and end render times of this new TextContainer
	    //  to the begin/end values of the most recent <TIME ..> tag:
	    pTC->setBeginTime(
		    m_pTextWindow->GetLatestSentTimeToRender()
		    - lTimeOffset); //cz
	    pTC->setEndTime(
		    m_pTextWindow->GetLatestSentTimeToStopRendering()
		    - lTimeOffset); //cz

	    //C_RTRNDRDIFF >>
	    //This is needed at the file format end because we need to keep
	    // track of when the last <TIME ... end=t> was seen so we can
	    // send it in the packet's opaque-data header:
	    pTC->setMostRecentTimeTagEndTime(pTC->getEndTime());
	    //<< END C_RTRNDRDIFF.

	    if(m_pTextWindow->hasValidURL())
	    {
		pTC->copyIntoHrefBuf(m_pTextWindow->getURL(),
			m_pTextWindow->getLenURLbuf(),
			m_pTextWindow->getTargetOfURL() );
		//C_RTRNDRDIFF >>
		ULONG32 ulNumOverrides = m_pTextWindow->
			getNumberOfFontColorPushesInsideLinkText();
		if(ulNumOverrides > 0)
		{
		    pTC->setNumLinkColorOverrides(ulNumOverrides);
		}
		//<< END C_RTRNDRDIFF.
	    }

	    m_pTextWindow->setTextAttributesToTopsOfStacksVals(*pTC);

	    if(bDealingWithTabCharWithPre  &&
		    !bSomeCharsFoundSinceLastBreakTag)
	    {
		//If all we've got is a tab char in the TC, don't
		// paint any bg color:
		pTC->setTextBackgroundColor(DEFAULT_TEXT_BGCOLOR);
	    }

	    bUserPosTagFoundSinceLastTextContainer = FALSE;

	    if(!m_pTextWindow->insertAtEndOfList(pTC,
		    //C_RTRNDRDIFF:
		    !bFileFormatIsCallingThis,
		    bDealingWithTabCharWithPre))
	    {
		delete pTC;
	    }
	    else
	    {
		//C_RTRNDRDIFF >>
		if(pTC->isRequired())
		{
		    bRef_DataHasREQUIREDContents =
			    bCurLineHasREQUIREDContents = TRUE;
		}
		//<< END C_RTRNDRDIFF.

		//Added this to adjust start & end times
		// based on visibility in window, e.g., the start time of
		// pTC might be 0 but it doesn't scroll into the window
		// until 4300msec, so this calculates and adjusts to that:
		BOOL bShouldNeverBeFalse =
			pTC->adjustStartAndEndTimes(m_pTextWindow);
		HX_ASSERT(bShouldNeverBeFalse);

		if(m_pTextWindow->isLooping())
		{
		    //We don't want to use the end time calculated in 
		    // adjustStartAndEndTimes() above because looping means
		    // the end time may be "infinite" since it could loop
		    // "forever"; however, if there is an active <TIME end=t>
		    // tag, use t instead of "infinity".
		    // NOTE: as soon as another CLEAR tag is seen, this
		    // T.C.'s endTime will be reset to time of the CLEAR:
		    pTC->setEndTime(m_pTextWindow->
			    GetLatestSentTimeToStopRendering());
		}

		//C_RTRNDRDIFF >>
		if(TYPE_TELEPROMPTER == m_pTextWindow->getType())
		{
		    //XXXEH-* need a better fix for this!:
		    //We want to give each line a maximum lifetime of 90sec
		    // so that, when a T.C. moves up and out of the window,
		    // it doesn't persist as time-valid (and consequently
		    // persist in the live packet-resend queue or in the
		    // file format's list of what to send after a seek).
		    // Setting its end time to the time this T.C. is "bumped"
		    // out of the window is tricky and this 90second-duration
		    // hack "solves" the problem for now but this needs to
		    // be rivisited:
		    if(pTC->getEndTime() - pTC->getStartTime() > 90000)
		    {
			pTC->setEndTime(pTC->getStartTime() + 90000);
		    }
		}
		//<< END C_RTRNDRDIFF.

		if(bIsLiveSource  &&  TIME_INVALID == pTC->getEndTime())
		{
		    //Make end time be "infinity":
		    pTC->setEndTime(TIME_INFINITY);
		}

		//Moved this from below "CLEAR" parsing
		// code so that the adjustment would be made after the pTC's
		// official start time is known:
		if(bClearWasJustSent)
		{
		    ULONG32 ulTmpStartTime = pTC->getStartTime();
		    ULONG32 ulTmpEndTime = pTC->getEndTime();
		    m_pTextWindow->SetLatestSentTimeToRender(ulTmpStartTime);

		    //	is just after a <CLEAR> tag, so clear everything in
		    //	TextContainerList whose m_endTime is greater
		    //	than the latestSentTimeToRender:  
		    m_pTextWindow->MarkAllForClear(  //(the T.C.List version)
			    bIsLiveSource);
		    //Now, since pTC is already inserted in the T.C.List, its
		    // endTime may have been adjusted in MarkAllForClear(),
		    // so let's restore it:
		    pTC->setStartTime(ulTmpStartTime);
		    pTC->setEndTime(ulTmpEndTime);

		    //C_RTRNDRDIFF >>:
		    //Now call the T.L.List version of MarkAllForClear() and
		    // make sure the TextLines all have the correct end times
		    // otherwise a player Seek() may result in time-dead data
		    // being sent over the wire and immediately discarded by
		    // the renderer:
//#error:XXXXXEH- put bp here & see why not for renderer:
		    m_pTextWindow->m_pTLList->MarkAllForClear(
			    m_pTextWindow->GetLatestSentTimeToRender(),
			    bIsLiveSource);
		    //<< END C_RTRNDRDIFF.

		    m_pTextWindow->setTimeOfLastClearTag();

		    //follows a CLEAR tag but has no "newlines" before it,
		    // so we can't set num newlines to non-zero, so this
		    // tells us we've started a newline with no prior spacing
		    pTC->isFakeNewLine(TRUE);
		}

		//C_RTRNDRDIFF >>:
		TextLine* pTL_prev = m_pTextWindow->m_pTLList->end();
		
		// Added the following code to create a new TextLine if pTC
		// is start of new line, or to update the latest TextLine in
		// the list if pTC is not the start of a new line:
		if(pTC->isStartOfNewLine()  ||
			pTC->isFakeNewLine()  ||
			//Added this because sometimes a TextLine gets split
			// into two based on where the read() into the file
			// ends and the next read() begins, so, if we haven't
			// found a TL yet, make this the first one in the
			// next packet (and don't append it to the existing
			// pTL_prev even though it may be part of the same
			// line):
			!bFirstTLwasFound  ||
			!pTL_prev)  //Bug #708 killer; the
				    // very first TextLine was not being
				    // created if it didn't start a newline.
		{
		    ulCurLineStartByteNumOfFile = ulByteOffsetIntoFile +
			    ulTextContainerBufStartIndex;

		    //if prev exsits, end it just before the new one starts:
		    if(pTL_prev)
		    {
			pTL_prev->setEndByteInFile(
				ulCurLineStartByteNumOfFile-1);
		    }

		    pTL = new TextLine((TextLine&)(*pTC));

		    HX_ASSERT_VALID_PTR(pTL);
		    if(NULL == pTL)
		    {
			break;  //Houston, We've had a problem.
		    }
		    
		    //Do this so that the memcopied 
		    // TextAttributes part of pTC did not copy the ptr to the
		    // href buffer into pTL's TextAttributes part:
		    pTL->clearWithoutDeletingHrefBuf();
		    if(pTC->getHrefBuf()  &&  pTC->getHrefBufLen())
		    {
			pTL->copyIntoHrefBuf(pTC->getHrefBuf(),
				pTC->getHrefBufLen(),
				pTC->getTargetOfURL());
		    }

		    pTL->setLineNumOfFile(
			    m_pTextWindow->m_pTLList->nodeCount()+1);
		    pTL->setStartByteInFile(ulCurLineStartByteNumOfFile);
		    pTL->setEndByteInFile(0L);
		    pTL->setTimeOfLastClear(
			    m_pTextWindow->getTimeOfLastClearTag());

		    if(!m_pTextWindow->m_pTLList->insertAtEndOfList(pTL))
		    {
			delete pTL; //insert failed so kill it.
		    }
		    else //posible core dump without this else!!!
		    if(pTL->getStartByteInFile() < ulStartByteOfFirstTL  ||
			    !bFirstTLwasFound)
		    {
			*ppFirstTextLineInPkt = pTL;
			ulStartByteOfFirstTL = pTL->getStartByteInFile();
			bFirstTLwasFound = TRUE;
		    }
		    //Make sure the current TextLine is considered
		    // <REQUIRED> to be sent if any of it's text is
		    // set as <REQUIRED>:
		    if(bCurLineHasREQUIREDContents)
		    {
		        pTL->isRequired(TRUE);
		    }
		}
		else
		{
		    //update the (current) TextLine's times because this
		    // new pTC is part of the current TextLine (pTL_prev):
		    if(pTL_prev)
		    {
			pTL_prev->updateStartAndEndTimes(pTC, bIsLiveSource);
			//Make sure the current TextLine is considered
			// <REQUIRED> to be sent if any of it's text is
			// set as <REQUIRED>:
			if(bCurLineHasREQUIREDContents)
			{
			    pTL_prev->isRequired(TRUE);
			}
		    }
		}
		BOOL bEarliestTimeOfNewDataIsMoreRecent =
			IsTimeAMoreRecentThanTimeB(
			ulEarliestTimeOfNewData,
			pTC->getStartTime(),
			bIsLiveSource);
		//So ff can know next packet's time:
		if(bEarliestTimeOfNewDataIsMoreRecent  ||
			!bEarliestTimeOfNewDataIsValid)
		{
		    ulEarliestTimeOfNewData = pTC->getStartTime();
		    bEarliestTimeOfNewDataIsValid = TRUE;
		}

		BOOL bGetEndTimeIsMoreRecent =
			IsTimeAMoreRecentThanTimeB(
			pTC->getEndTime(),
			ulEndTimeOfPacket,
			bIsLiveSource);
		if(TIME_INVALID == ulEndTimeOfPacket)
		{
		    //(in)sanity check: Is the first time it's been used:
		    HX_ASSERT(!bGetEndTimeIsMoreRecent);
		}

		//So ff can know next packet's end time
		// so it can decide whether it's too late to send the packet:
		if(bGetEndTimeIsMoreRecent
			||  (ULONG32)TIME_INVALID==ulEndTimeOfPacket)
		{
		    ulEndTimeOfPacket = pTC->getEndTime();
		}
	    }
	    //<< END C_RTRNDRDIFF.

	} //end "while(tempLen > 0)".

	//Now, for PRE tag newlines found after pTC's text, increment
	// the num newlines to perform on the next TC:
	if(ulNumPREtagNewlineCharsFound)
	{
	    for(ULONG32 ix=0L; ix<ulNumPREtagNewlineCharsFound; ix++)
	    {
		m_pTextWindow->incrementNumBreakTagsEncountered();
	    }
	    ulNumPREtagNewlineCharsFound = 0L;
	}

	if(-1 == indexOfLeftBracket  ||  -1 == indexOfRightBracket)
	{
	    break; //We're done with pData.
	}

	if(bIgnoringNewlineChars  ||  bDealingWithTabCharWithPre)
	{
	    bIgnoringNewlineChars = bDealingWithTabCharWithPre = FALSE;
	    startIndex = indexOfRightBracket+1;//where next tag-search starts
	    if(startIndex >= len)
	    {
		break;
	    }
	    continue;
	}

	pData_CHAR[indexOfLeftBracket] = '<'; //retore the char.


	//Now, find out what's in the markup tag, first temporarily
	// NULL-terminating it where the tag ends, then converting it
	// to uppercase:
	pData_CHAR[indexOfRightBracket] = '\0';
	_CHAR* pTagContents = &pData_CHAR[indexOfLeftBracket+1];
	ULONG32 tagContentsLen = indexOfRightBracket-indexOfLeftBracket-1;
	if(bSlashFoundAtEndOfTag) //XML-style end-of-tag "/" was found:
	{
	    tagContentsLen--;
	}
	convertToUpperCase(pTagContents, tagContentsLen);
	switch(pTagContents[0])
	{
	    case '/': //tag is the end of a binary tag, e.g. "</B>":
	    {
		if(tagContentsLen > 1)
		{
		    HandleEndTag(pTagContents, tagContentsLen,
			    bSomeCharsFoundSinceLastBreakTag,
			    bSomeCharsFoundSinceLastPosTag,
			    bUserPosTagFoundSinceLastTextContainer,
			    //C_RTRNDRDIFF >>
			    ulByteOffsetIntoFile,
			    indexOfLeftBracket,
			    indexOfRightBracket,
			    bFileFormatIsCallingThis);
			    //<< END C_RTRNDRDIFF.
		}//end "if(tagContentsLen > 1)".
	    }//end "case '/':"
	    break;

	    case 'A':
		if(tagContentsLen > 2)
		{
		    if(' '==pTagContents[1])
		    {   
			//we've found "A " so far...
			_CHAR* pLval;
			_CHAR* pRval;
			_CHAR* pRestOfData = &pTagContents[2];
			ULONG32 restOfDataLen = tagContentsLen-2;
			ULONG32 nextTokenStartIndex, nextTokenEndIndex;
			ULONG32 nextValStartIndex, nextValEndIndex;
			do
			{
			    if(GetNextTokenLvalueRvaluePair(pRestOfData, restOfDataLen,
				    nextTokenStartIndex, nextTokenEndIndex,
				    nextValStartIndex, nextValEndIndex) )
			    {
				pLval = &pRestOfData[nextTokenStartIndex];
				pRestOfData[nextTokenEndIndex]='\0';
				pRval = &pRestOfData[nextValStartIndex];
				pRestOfData[nextValEndIndex]='\0';
			    }
			    else
			    {
				break;
			    }

			    if(!stringCompare(pLval, 
				    nextTokenEndIndex-nextTokenStartIndex, 
				    "HREF", 4)  &&  
				    nextValEndIndex-nextValStartIndex > 0L)
			    {
				m_pTextWindow->setURL(pRval,
					nextValEndIndex-nextValStartIndex);
/* C_RTRNDRDIFF:
	    Don't do this at File Format side -- sending the <A > tag will
	    be enough for the renderer to know the <U> and <FONT color=> 
	    information it needs; (we don't want this duplicated in the
	    packet header now that packet headers include <A > tags):
			    //Added the following to set
			    // the text that follows to the hyperlink color:
			    m_pTextWindow->pushTextColorStack(
				    m_pTextWindow->getLinkColor(), TRUE);
			    m_pTextWindow->setNumberOfFontColorPushesInsideLinkText(0L);
			    if(m_pTextWindow->usingUnderlineHyperlinks())
			    {
				m_pTextWindow->pushIsUnderlinedStack(TRUE);
			    }
*/
			    }
			    else if(!stringCompare(pLval,
				    nextTokenEndIndex-nextTokenStartIndex,
				    "TARGET", 6))
			    {
				//Redo this in case it was in quotes:
				convertToUpperCase(pRval,
					nextValEndIndex-nextValStartIndex);
				if(!stringCompare(pRval,
				    nextValEndIndex-nextValStartIndex,
				    "_PLAYER", 7))
				{
				    m_pTextWindow->setTargetOfURL(
					    URL_TARGET_PLAYER);
				}
				else if(!stringCompare(pRval,
				    nextValEndIndex-nextValStartIndex,
				    "_BROWSER", 7))
				{
				    m_pTextWindow->setTargetOfURL(
					    URL_TARGET_BROWSER);
				}
				else
				{
				    m_pTextWindow->setTargetOfURL(
					    URL_TARGET_INVALID);
				}
			    }

			    if(nextValEndIndex < restOfDataLen)
			    {
				if(0L == nextValEndIndex)
				{
				    break; //leave the do loop.
				}
				pRestOfData = &pRestOfData[nextValEndIndex+1];
				restOfDataLen -= (nextValEndIndex+1);
			    }
			    else
			    {
				restOfDataLen = 0L;
			    }

			} while(restOfDataLen > 0L);
		    }
		}
		break;

	    case 'B':
		if(!stringCompare(pTagContents, tagContentsLen,
			"B", 1)) //start of bold tag
		{
		    m_pTextWindow->pushIsBoldStack(TRUE);
		}
		//or maybe a line break tag:
		else if(!stringCompare(pTagContents, tagContentsLen,
			"BR", 2))
		{
		    //If we got <pos ..>x<br/>, where "x" contains no
		    // plain text (other than spaces, tabs, newlines),
		    // then don't do a line break at all, but rather
		    // update the NewPktStartYAtTimeZero() value:
		    if(bUserPosTagFoundSinceLastTextContainer  &&
			    !bSomeCharsFoundSinceLastPosTag)
		    {
			LONG32 lCurY =  m_pTextWindow->
				GetNewPktStartYAtTimeZero();
			HX_ASSERT(INVALID_LONG32 != lCurY);
			m_pTextWindow->
				SetNewPktStartYAtTimeZero(
				lCurY + DEFAULT_LINE_BREAK_SIZE);
		    }
		    else
		    {
			m_pTextWindow->incrementNumBreakTagsEncountered();
		    }
		    bSomeCharsFoundSinceLastBreakTag=0L;
		}
		break;

	    case 'C':
		if (tagContentsLen>=5  &&  !stringCompare(pTagContents, 5,
			"CLEAR", 5))
		{   
		    // /ONLY do the following if it's live:
		    if (bIsLiveSource)
		    {
			// /These are needed for seeking in non-live  case,
			// but in live they just cause a memory build-up
			// without any benefit.
			ULONG32 numTextLinesDeleted =
				m_pTextWindow->m_pTLList->flush();
			// /These are needed up to the point where the clear
			// tag's begin time is effective, and, in non-live,
			// you can have a clear tag in the middle of a packet
			// and we don't want to remove the text that precedes
			// it if that text is valid now and if the clear has
			// a begin time that is currently in the future.
			ULONG32 numTextContainersDeleted =
				m_pTextWindow->TextContainerList::flush();
		    }
		    m_pTextWindow->m_bClearWasJustSent = TRUE;
		    //XXXEH- moved this out of txtwindw.cpp's
		    // insertAtEndOfList() so, if "<BR/><CLEAR>foo" is seen,
		    // the <BR/> will be ignored, but if <CLEAR><BR/>foo" is
		    // seen, the <BR/> will be honored:
		    m_pTextWindow->clearNumBreakTagsEncountered();
		    m_pTextWindow->setTimeOfLastClearTag();
//#error:XXXXXEH- if isLiveSource, return and start a new pkt here!!
		}
		else if(!stringCompare(pTagContents, tagContentsLen,
			"CENTER", 6))
		{   
		    m_pTextWindow->pushIsCenteredStack(TRUE);
		    //Do a line break, but only if 0 line breaks so far,
		    // and not if there is no raw text yet, i.e., not if
		    // this <CENTER> tag starts the data part of the file,
		    // nor if a <CLEAR> tag was just sent:
		    if(!m_pTextWindow->getNumBreakTagsEncountered()  &&
			    !m_pTextWindow->m_bClearWasJustSent  &&
			    m_pTextWindow->size()>0L)
		    {
			//If we got <pos ..>x<center>, where "x" contains no
			// plain text (other than spaces, tabs, newlines),
			// then don't do a line break at all:
			if(bUserPosTagFoundSinceLastTextContainer  &&
				!bSomeCharsFoundSinceLastPosTag)
			{
			    ;
			}
			else
			{
			    m_pTextWindow->
				    incrementNumBreakTagsEncountered();
			}
			bSomeCharsFoundSinceLastBreakTag=0L;
		    }
		}
		break;

	    case 'F':
	    {
		BOOL bIsFontTag = FALSE;
		if(tagContentsLen > 5)
		{
		    if(pTagContents[1]=='O'  &&  pTagContents[2]=='N'  &&
			    pTagContents[3]=='T'  &&  pTagContents[4]==' ')
		    {   //we've found "FONT " so far...
			bIsFontTag = TRUE;
			_CHAR* pLval;
			_CHAR* pRval;
			_CHAR* pRestOfData = &pTagContents[5];
			ULONG32 restOfDataLen = tagContentsLen-5;
			ULONG32 nextTokenStartIndex, nextTokenEndIndex;
			ULONG32 nextValStartIndex, nextValEndIndex;

			//Added the following variable to
			// keep track of all attributes that get pushed in
			// the following do loop so that the fontStack can
			// tell us what to do with a subsequent </FONT> tag;
			// E.g., "<FONT size=+2 color=red>" could get parsed
			// below and we need to keep track of the fact that
			// +2 and red were pushed on their respective stacks
			// at the same time so that a subsequent </FONT> can
			// force a pop of BOTH the size and the color stacks:
			ULONG32 ulCurrentStacksPushed = 0L;
			do
			{
			    if(GetNextTokenLvalueRvaluePair(pRestOfData,
				    restOfDataLen,
				    nextTokenStartIndex, nextTokenEndIndex,
				    nextValStartIndex, nextValEndIndex) )
			    {
				pLval = &pRestOfData[nextTokenStartIndex];
				pRestOfData[nextTokenEndIndex]='\0';
				pRval = &pRestOfData[nextValStartIndex];
				pRestOfData[nextValEndIndex]='\0';
			    }
			    else
			    {
				break;
			    }

			    _CHAR* tmpPtr = pRval;
			    ULONG32 tokenValLen =
				    nextValEndIndex-nextValStartIndex;

			    if(!stringCompare(pLval, 
				    nextTokenEndIndex-nextTokenStartIndex, 
				    "SIZE", 4)  &&
				    //Don't use if size already pushed:
				    !(ulCurrentStacksPushed & FONT_SIZE))
			    {
				LONG32 lTmp = 0L;
				if(1L == tokenValLen)
				{
				    _CHAR ch = tmpPtr[0];
				    if(ch >= '0'  &&  ch <= '9')
				    {
					lTmp = LONG32(ch - '0');
					if(!lTmp)
					{
					    lTmp = 1L;
					}
					else if(lTmp > 7L)
					{
					    lTmp = 7L;
					}
				    }
				}
				//"+0" exists so default val could be had
				// without using </FONT> to get back to it:
				if(lTmp==3L  ||  
					!stringCompare(tmpPtr, tokenValLen,
					"+0", 2))
				{
				    m_pTextWindow->pushFontPointSizeStack(
					    DEFAULT_FONT_PTSIZE, FALSE);
				    ulCurrentStacksPushed |= FONT_SIZE;
				}
				else if(lTmp==4L  ||  
					!stringCompare(tmpPtr, tokenValLen,
					"+1", 2))
				{
				    m_pTextWindow->pushFontPointSizeStack(
					    FONT_SIZE_PLUS1, FALSE);
				    ulCurrentStacksPushed |= FONT_SIZE;
				}
				else if(lTmp==5L  ||  
					!stringCompare(tmpPtr, tokenValLen, 
					"+2", 2))
				{
				    m_pTextWindow->pushFontPointSizeStack(
						    FONT_SIZE_PLUS2, FALSE);
				    ulCurrentStacksPushed |= FONT_SIZE;
				}
				else if(lTmp==6L  ||  
					!stringCompare(tmpPtr, tokenValLen,
					"+3", 2))
				{
				    m_pTextWindow->pushFontPointSizeStack(
						    FONT_SIZE_PLUS3, FALSE);
				    ulCurrentStacksPushed |= FONT_SIZE;
				}
				else if(lTmp==7L  ||  
					!stringCompare(tmpPtr, tokenValLen,
					"+4", 2))
				{
				    m_pTextWindow->pushFontPointSizeStack(
						    FONT_SIZE_PLUS4, FALSE);
				    ulCurrentStacksPushed |= FONT_SIZE;
				}
				else if(lTmp==2L  ||  
					!stringCompare(tmpPtr, tokenValLen,
					"-1", 2))
				{
				    m_pTextWindow->pushFontPointSizeStack(
						    FONT_SIZE_MINUS1, FALSE);
				    ulCurrentStacksPushed |= FONT_SIZE;
				}
				else if(lTmp==1L  ||  
					!stringCompare(tmpPtr, tokenValLen,
					"-2", 2)
					)
				{
				    m_pTextWindow->pushFontPointSizeStack(
						    FONT_SIZE_MINUS2, FALSE);
				    ulCurrentStacksPushed |= FONT_SIZE;
				}
				else if(tokenValLen>=2)
				{
				    INT32 i = atoi(tmpPtr);
				    if(i < -2)
				    {
					m_pTextWindow->
						pushFontPointSizeStack(
						FONT_SIZE_MINUS2, FALSE);
					ulCurrentStacksPushed |= FONT_SIZE;
				    }
				    else if(i > 4)
				    {
					m_pTextWindow->
						pushFontPointSizeStack(
						FONT_SIZE_PLUS4, FALSE);
					ulCurrentStacksPushed |= FONT_SIZE;
				    }
				    //this "else" should only get entered
				    // if "-0" was seen; we have to push
				    // something for each <FONT> tag so that
				    // the next </FONT> tag doesn't cause
				    // an erroneous pop of the prior <FONT>
				    // tag's push:
				    else
				    {
					m_pTextWindow->
						pushFontPointSizeStack(
						DEFAULT_FONT_PTSIZE, FALSE);
					ulCurrentStacksPushed |= FONT_SIZE;
				    }
				}
			    } //end of "else if( ..."SIZE" ...)".

			    else if(!stringCompare(pLval, 
				    nextTokenEndIndex-nextTokenStartIndex, 
				    "COLOR", 5)  &&
				    //Don't use if color already pushed:
				    !(ulCurrentStacksPushed & FONT_COLOR))
			    {
				COLORTYPE colortype_retVal;
				BOOL isAValidColor = FALSE;
				if(tokenValLen >= 1  &&  '#'==tmpPtr[0]  ||  
					(tokenValLen>=2  &&
					'\"'==tmpPtr[0]  &&  '#'==tmpPtr[1]) )
				{
				    //Color was presented as "#RRGGBB" in hex:
				    isAValidColor = 
					    convertColorValStringToCOLORTYPE(
					    tmpPtr, tokenValLen,
					    colortype_retVal);
				}
				//  Returns FALSE if colorName contains an
				//  unrecognized color:
				else
				{	//Color was named, e.g., "darkblue":
				    isAValidColor = convertColorNameToCOLORTYPE(
					    tmpPtr, tokenValLen, colortype_retVal);
				}
				if(isAValidColor)
				{
				    //Is A legal color name, so push it on stack:
				    m_pTextWindow->pushTextColorStack(
					    colortype_retVal, FALSE);
				    ulCurrentStacksPushed |= FONT_COLOR;
				    //Added the following to see
				    // if we're inside an <A> tag so that we can
				    // properly back out font colors after hyperlink
				    // text is done:
				    if(m_pTextWindow->hasValidURL())
				    {
					m_pTextWindow->
					        incrementNumberOfFontColorPushesInsideLinkText();
				    }
				}
				//else ignore the tag; is invalid color.
			    }  //end of "else if( ..."COLOR" ...)".

			    else if(!stringCompare(pLval, 
				    nextTokenEndIndex-nextTokenStartIndex, 
				    "BGCOLOR", 7)  &&
				    //Don't use if bgcolor already pushed:
				    !(ulCurrentStacksPushed & FONT_BGCOLOR))
			    {
				COLORTYPE colortype_retVal;
				BOOL isAValidColor = FALSE;
				if(tokenValLen >= 1  &&  '#'==tmpPtr[0]  ||  
					(tokenValLen>=2  &&
					'\"'==tmpPtr[0]  &&  '#'==tmpPtr[1]) )
				{
				    //Color was presented as "#RRGGBB" in hex:
				    isAValidColor = 
					    convertColorValStringToCOLORTYPE(
					    tmpPtr, tokenValLen,
					    colortype_retVal);
				}
				//  Returns FALSE if colorName contains an
				//  unrecognized color:
				else
				{	//Color was named, e.g., "darkblue":
				    isAValidColor = convertColorNameToCOLORTYPE(
					    tmpPtr, tokenValLen, colortype_retVal);
				}
				if(isAValidColor)
				{
				    //Is A legal color name, so push it on stack:
				    m_pTextWindow->pushTextBackgroundColorStack(
					    colortype_retVal, FALSE);
				    ulCurrentStacksPushed |= FONT_BGCOLOR;
				}
				//else ignore the tag; is invalid color.
			    } //end of "else if( ..."BGCOLOR" ...)".

			    else if(!stringCompare(pLval, 
				    nextTokenEndIndex-nextTokenStartIndex, 
				    "CHARSET", 7)  &&  tokenValLen>1  &&
				    //Don't use if charset already pushed:
				    !(ulCurrentStacksPushed & FONT_CHARSET))
			    {
				//  Returns FALSE if tmpPtr contains an 
				//  unrecognized charset:
				ULONG32 ulCharset_retVal = CHARSET__default;
				if('\"' == tmpPtr[0])
				{
				    tmpPtr=tmpPtr+1;
				    tokenValLen--;
				}
				if('\"' == tmpPtr[tokenValLen-1])
				{
				    tokenValLen--;
				}

				if (HXR_OK ==
					convertCharsetNameToCharsetULONG32(
					tmpPtr, tokenValLen,
					REALTEXT_MAX_CHARSET_LEVEL_SUPPORTED,
					ulCharset_retVal))
				{
				    //Is a legal charset name, so push it on stack:
				    m_pTextWindow->pushFontCharsetStack(
					    ulCharset_retVal, FALSE);
				    ulCurrentStacksPushed |= FONT_CHARSET;
				}
				//else ignore the tag; is invalid charset.
			    } //end of "else if( ..."CHARSET" ...)".

			    //Added this else-if to handle
			    // font "faces", e.g., "helvetica":
			    else if( (!stringCompare(pLval, 
				    nextTokenEndIndex-nextTokenStartIndex, 
				    "FACE", 4)  ||
					!stringCompare(pLval, 
					nextTokenEndIndex-nextTokenStartIndex, 
					"NAME", 4)
				    )  &&  tokenValLen>1  &&
				    //Don't use if face has already been pushed:
				    !(ulCurrentStacksPushed & FONT_FACE) )
			    {
				//XXXEH- Replace the
				// following hack with a function to do this:
				{//hack start:
				    if('\"' == tmpPtr[0])
				    {
					tmpPtr=tmpPtr+1;
					tokenValLen--;
				    }
				    if('\"' == tmpPtr[tokenValLen-1])
				    {
					tmpPtr[tokenValLen-1] = '\0'; 
					tokenValLen--;
				    }
				    tmpPtr[tokenValLen] = '\0';
				    ///convertToUpperCase(tmpPtr, tokenValLen);
				    {
					//Is an allowed font face name, so push it on
					// the stack:
					ULONG32 faceIndx =
						getFontFaceIndexFromString(
						tmpPtr, tokenValLen,
						m_pTextWindow->
						    getMajorContentVersion(),
						m_pTextWindow->
						    getMinorContentVersion()
						);
					m_pTextWindow->pushFontFaceStack(
						faceIndx);
					ulCurrentStacksPushed |= FONT_FACE;
				    }
				}//hack end.
			    } //end of "else if( ..."FACE" ...)".

			    
			    if(nextValEndIndex < restOfDataLen)
			    {
				if(0L == nextValEndIndex)
				{
				    break; //leave the do loop.
				}
				pRestOfData = &pRestOfData[nextValEndIndex+1];
				restOfDataLen -= (nextValEndIndex+1);
			    }
			    else
			    {
				restOfDataLen = 0L;
			    }

			} while(restOfDataLen > 0L);
			//The following code sets up the
			// font stack to keep track of ALL parameters
			// pushed in the above do-while loop so that a
			// subsequent </FONT> can pop all involoved stacks
			// simultaneously:
			if(0L != ulCurrentStacksPushed)
			{
			    m_pTextWindow->pushFontStack(
				    ulCurrentStacksPushed);
			    //C_RTRNDRDIFF >>:
			    //Added this to keep track of location in file of
			    // every FONT tag so, when forming a packet, it
			    // can be determined where the matching <FONT>
			    // for each </FONT> is and, for each </FONT>
			    // whose matching <FONT> is in a prior packet,
			    // we can prepend an appropriate <FONT> tag in
			    // the packet's header:
			    m_pTextWindow->pushFontTagStartByteInFileStack(
				    ulByteOffsetIntoFile+indexOfLeftBracket);
			    //<< END C_RTRNDRDIFF.
			}
		    } //end of "if(pTagContents[1]==...) <==i.e., if "FONT".
		}
		//C_RTRNDRDIFF >>:
		// /Don't document this until it gets QA'd:
		// In non-live scenarios, you don't want to do most of these:
		// /This allows live-text author to blow away all state
		// information without having to use closing tags for
		// every yet-unclosed tag so far.  This allows a memory-
		// build-up problem to be controlled by the author in
		// the case where they've sent a lot of open tags but
		// haven't kept track of what needs closing.  Syntax
		// is flush="all" or flush="tags".  "tags" clears out all
		// font, bold, ...etc. stacks.  "all" does that and also
		// clears out other structures like savedPacketDataList:
		if (!bIsFontTag  &&  tagContentsLen >= 5  &&
			!stringCompare(pTagContents, 5, "FLUSH", 5)  &&
			/* bIsLiveSource  && */  isspace(pTagContents[5]))
		{
		    UINT32 ui = tagContentsLen - 5;
		    _CHAR* pFlushTarget = strstr(&pTagContents[6], "TARGET");
		    if (pFlushTarget)
		    {
			_CHAR* pTmp = &pFlushTarget[6];
			if (pTmp)
			{
			    while (isspace(*pTmp))
			    {
				pTmp++; ui--;
			    }
			    if ('=' == *pTmp)
			    {
				*pTmp++; ui--;
				while (isspace(*pTmp))
				{
				    pTmp++; ui--;
				}
				if ('\"' == *pTmp)
				{
				    pTmp++; ui--;
				    while (isspace(*pTmp))
				    {
					pTmp++; ui--;
				    }
				    if (*pTmp)
				    {
					// /Flags are:
					// / 0x1 =font stacks
					// / 0x2 =ticker stacks
					// / 0x4 =bold, Italics,
					// /  underlined, strikethrough
					// / 0x8 =indent amount stacks
					// / 0x10=TextContainerList
					// / 0x20=TextLineList (m_pTLList)
					ULONG32 ulFlags = 0x0;
					ULONG32 ulTmpLen =
						(ULONG32)strlen(pTmp);
					if (ui>=3 && !stringCompare(pTmp,
						3, "all", 3))
					{
					    m_pTextWindow->reset();
					    ulFlags = 0xFFFFFFFF;
					}
					else if (ulTmpLen>=4  &&  *pTmp  &&
						!stringCompare(pTmp, 4,
						"tags", 4))
					{
					    ulFlags |= 0x00000007;
					}

					if ( (ulFlags & 0x00000001)  ||
						(ulTmpLen>=8  &&  *pTmp  &&
						!stringCompare(pTmp, 8,
						"fontTags", 8)) )
					{
					    m_pTextWindow->
						    TextAttributeStacks::
						    flushFontStacks();
					    m_pTextWindow->
						    m_pFontUndoTagList->
						    flush();
					    ulFlags |= 0x00000001;
					}
					if ( (ulFlags & 0x00000002)  ||
						(ulTmpLen>=10  &&  *pTmp  &&
						!stringCompare(pTmp, 10,
						"tickerTags", 10)) )
					{
					    m_pTextWindow->
						    TextAttributeStacks::
						    flushTickerStacks();
					    ulFlags |= 0x00000002;
					}
					if ( (ulFlags & 0x0000004)  ||
						(ulTmpLen>=4  &&  *pTmp  &&
						!stringCompare(pTmp, 4,
						"bius", 4)) )
					{
					    m_pTextWindow->
						    TextAttributeStacks::
						    flushBIUSandBlinkStacks();
					    ulFlags |= 0x00000004;
					}
					if ( (ulFlags & 0x00000008)  ||
						(ulTmpLen>=6  &&  *pTmp  &&
						!stringCompare(pTmp, 6,
						"indent", 6)) )
					{
					    m_pTextWindow->
						    TextAttributeStacks::
						    flushIndentAmtStack();
					    ulFlags |= 0x00000008;
					}
					if ( (ulFlags & 0x00000010)  ||
						(ulTmpLen>=8  &&  *pTmp  &&
						!stringCompare(pTmp, 8,
						"wordList", 8)) )
					{
					    m_pTextWindow->
						  TextContainerList::flush();
					    ulFlags |= 0x00000010;
					}
					if ( (ulFlags & 0x00000020)  ||
						(ulTmpLen>=8  &&  *pTmp  &&
						!stringCompare(pTmp, 8,
						"lineList", 8)) )
					{
					    m_pTextWindow->m_pTLList->flush();
					    ulFlags |= 0x00000020;
					}
				    }
				}
			    }
			}
		    }
		}
		//<< END C_RTRNDRDIFF.
		break;
	    } // /End case 'F'.

	    case 'H':
		//Added the following to allow better
		// handling of HTML text imported as RealText:
		//XXXEH- for now, treat as 2 line breaks, but in future,
		// also draw a line (horizontal rule):
		if(!stringCompare(pTagContents, tagContentsLen,
			"HR", 2))
		{
		    //Added the surrounding if() so that
		    // this code doesn't get called if horizontal motion:
		    if(m_pTextWindow->getCrawlRate() == 0  ||
			    m_pTextWindow->getScrollRate() != 0)
		    {
			//If we got <pos ..>x<hr/>, where "x" contains no
			// plain text (other than spaces, tabs, newlines),
			// then don't do a line break at all, but rather
			// update the NewPktStartYAtTimeZero() value:
			if(bUserPosTagFoundSinceLastTextContainer  &&
				!bSomeCharsFoundSinceLastPosTag)
			{
			    LONG32 lCurY =  m_pTextWindow->
				    GetNewPktStartYAtTimeZero();
			    HX_ASSERT(INVALID_LONG32 != lCurY);
			    m_pTextWindow->
				    SetNewPktStartYAtTimeZero(
				    lCurY + (2*DEFAULT_LINE_BREAK_SIZE));
			}
			else
			{
			   m_pTextWindow->incrementNumBreakTagsEncountered();
			   m_pTextWindow->incrementNumBreakTagsEncountered();
			}
			bSomeCharsFoundSinceLastBreakTag=0L;
		    }
		}


	    case 'I':
		    if(!stringCompare(pTagContents, tagContentsLen,
			    "I", 1)) //start of italics tag
		    {
			m_pTextWindow->pushIsItalicizedStack(TRUE);
		    }
		    break;

	    case 'L':
		    if(!stringCompare(pTagContents, tagContentsLen,
			    "LOOP", 4))
		    {
			m_pTextWindow->loop(TRUE);
		    }
		    //Added the following to allow better
		    // handling of HTML text imported as RealText:
		    //XXXEH- for now, treat as line break tag, but in future,
		    // add indent based on depth of nested ULs and OLs:
		    else if(!stringCompare(pTagContents, tagContentsLen,
			    "LI", 2)) 
		    {
			//Added the surrounding if() so that
			// this code doesn't get called if horizontal motion:
			if(m_pTextWindow->getCrawlRate() == 0  ||
				m_pTextWindow->getScrollRate() != 0)
			{
			    //If we got <pos ..>x<center>, where "x" contains 
			    // no plain text (other than spaces, tabs,
			    // newlines), then don't do a line break at all:
			    if(bUserPosTagFoundSinceLastTextContainer  &&
				    !bSomeCharsFoundSinceLastPosTag)
			    {
				;
			    }
			    else
			    {
				m_pTextWindow->
					incrementNumBreakTagsEncountered();
			    }
			    bSomeCharsFoundSinceLastBreakTag=0L;
			}
		    }
		    break;

	    case 'O':
		    //Added the following to allow better
		    // handling of HTML text imported as RealText:
		    //XXXEH- for now, treat as line break tag, but in future,
		    // add indent based on depth of nested ULs and OLs:
		    if(!stringCompare(pTagContents, tagContentsLen,
			    "OL", 2)) 
		    {
			UINT16 curIndentAmtInPixels =
				m_pTextWindow->
				peekAtLineIndentAmtInPixelsStack();
			m_pTextWindow->pushLineIndentAmtInPixelsStack(
				curIndentAmtInPixels +
				ORDERED_LIST_INDENT_AMT);
			//Added the surrounding if() so that
			// this code doesn't get called if horizontal motion:
			if(m_pTextWindow->getCrawlRate() == 0  ||
				m_pTextWindow->getScrollRate() != 0)
			{
			    BOOL bFollowsPosTag =
				    (bUserPosTagFoundSinceLastTextContainer
				    && !bSomeCharsFoundSinceLastPosTag);
			    //Do a line break, but only if 0 line breaks so
			    // far, and not if there is no raw text yet,
			    // i.e., not if this <OL> tag starts the data
			    // part of the file, nor if a <CLEAR> tag was
			    // just sent:
			    if(!m_pTextWindow->getNumBreakTagsEncountered()
				    &&
				    //C_RTRNDR_DIFF>>
				    (IsBeta1Player()  ||
				    //<<END C_RTRNDR_DIFF.
				    bFollowsPosTag  ||
				    (!m_pTextWindow->m_bClearWasJustSent  &&
				    m_pTextWindow->size()>0L)))
			    {
				//If we got <pos ..>x<ol>, where "x" contains 
				// no plain text (other than spaces, tabs,
				// newlines), then don't do a line break at
				// all, but DO add ORDERED_LIST_INDENT_AMT
				// amount to the NewPktStartXAtTimeZero()
				// value:
				if(bFollowsPosTag  &&  !IsBeta1Player())
				{
				    LONG32 lCurX = m_pTextWindow->
					    GetNewPktStartXAtTimeZero();
				    HX_ASSERT(INVALID_LONG32 != lCurX);
				    m_pTextWindow->
					    SetNewPktStartXAtTimeZero(
					    lCurX +
					    (ORDERED_LIST_INDENT_AMT));
				}
				else
				{
				    m_pTextWindow->
					  incrementNumBreakTagsEncountered();
				}
				bSomeCharsFoundSinceLastBreakTag=0L;
			    }
			}
		    }
		    break;

			    
	    case 'P':
		    if(!stringCompare(pTagContents, tagContentsLen,
			    "P", 1)) //start of paragraph tag
		    {
			//If we got <pos ..>x<p>, where "x" contains no
			// plain text (other than spaces, tabs, newlines),
			// then don't do a line break at all, but rather
			// update the NewPktStartYAtTimeZero() value:
			if(bUserPosTagFoundSinceLastTextContainer  &&
				!bSomeCharsFoundSinceLastPosTag)
			{
			    LONG32 lCurY =  m_pTextWindow->
				    GetNewPktStartYAtTimeZero();
			    HX_ASSERT(INVALID_LONG32 != lCurY);
			    m_pTextWindow->
				    SetNewPktStartYAtTimeZero(
				    lCurY + (2*DEFAULT_LINE_BREAK_SIZE));
			}
			else
			{
			    m_pTextWindow->
				    incrementNumBreakTagsEncountered();
			    m_pTextWindow->
				    incrementNumBreakTagsEncountered();
			}
			bSomeCharsFoundSinceLastBreakTag=0L;
		    }
		    else if(!stringCompare(pTagContents, tagContentsLen,
			    "PRE", 3)) //start of PRE tag
		    {
			m_pTextWindow->pushIsPreStack();

			//Acts like a <P> tag as far as newlines go:
			//Do a line break, but only if 0 line breaks so far,
			// and not if there is no raw text yet, i.e., not if
			// this <PRE> tag starts the data part of the file,
			// nor if a <CLEAR> tag was just sent:
			if(!m_pTextWindow->getNumBreakTagsEncountered()  &&
//XXXXXEH-19980918>>
				//C_RTRNDR_DIFF>>
				(IsBeta1Player()  ||
				//<<END C_RTRNDR_DIFF.
				(!m_pTextWindow->m_bClearWasJustSent  &&
				m_pTextWindow->size()>0L)) )
//<<XXXXXEH-19980918.
			{			
			    //If we got <pos ..>x<pre>, where "x" contains
			    // no plain text (other than spaces, tabs,
			    // newlines), then don't do a line break at all:
			    if(bUserPosTagFoundSinceLastTextContainer  &&
				    !bSomeCharsFoundSinceLastPosTag)
			    {
				;
			    }
			    else
			    {
				m_pTextWindow->
					incrementNumBreakTagsEncountered();
				m_pTextWindow->
					incrementNumBreakTagsEncountered();
			    }
			    bSomeCharsFoundSinceLastBreakTag=0L;
			}

			//Set cur font face to courier:
			m_pTextWindow->pushFontFaceStack(
				COURIERTT_FONT_FACE_INDX);
			m_pTextWindow->pushFontStack(FONT_FACE);
			//C_RTRNDRDIFF >>:
			//Added this to keep track of location in file of
			// every FONT tag so, when forming a packet, it
			// can be determined where the matching <FONT>
			// for each </FONT> is and, for each </FONT>
			// whose matching <FONT> is in a prior packet,
			// we can prepend an appropriate <FONT> tag in
			// the packet's header:
			m_pTextWindow->pushFontTagStartByteInFileStack(
				ulByteOffsetIntoFile+indexOfLeftBracket);
			//<< END C_RTRNDRDIFF.
		    }
		    else if(tagContentsLen >= 7) //7==min length (e.g.,
						// "POS X=8" is 7 chars long)
		    {
			if('O' == pTagContents[1]) 
			{   //is possibly a <TIME > tag; min length for TIME
			    //  is 10: e.g., <TIME end=1> (10 chars)
			    if(pTagContents[2]=='S'  &&
				    pTagContents[3]==' ')
			    {   //we've found "POS " so far...
				_CHAR* pLval;
				_CHAR* pRval;
				_CHAR* pRestOfData = &pTagContents[4];
				ULONG32 restOfDataLen = tagContentsLen-4;
				ULONG32 nextTokenStartIndex, 
					nextTokenEndIndex;
				ULONG32 nextValStartIndex, nextValEndIndex;
				do
				{
				    if(GetNextTokenLvalueRvaluePair(
					    pRestOfData,
					    restOfDataLen,
					    nextTokenStartIndex,
					    nextTokenEndIndex,
					    nextValStartIndex, 
					    nextValEndIndex) )
				    {
					pLval = &pRestOfData[
						nextTokenStartIndex];
					pRestOfData[nextTokenEndIndex]='\0';
					pRval = &pRestOfData[
						nextValStartIndex];
					pRestOfData[nextValEndIndex]='\0';
				    }
				    else
				    {
					break;
				    }

				    _CHAR* tmpPtr = pRval;
				    ULONG32 tokenValLen =
					    nextValEndIndex -
					    nextValStartIndex;

				    BOOL bErr = FALSE;

				    BOOL bIsX0orY0 = TRUE;

				    if((bIsX0orY0 = !stringCompare(pLval, 
					    nextTokenEndIndex -
					    nextTokenStartIndex,
					    "X0", 2))  ||
					    //Only allow user to set POS X=n
					    // if window has no motion to it:
					    (!m_pTextWindow->getScrollRate()
					    &&
					    !m_pTextWindow->getCrawlRate()
					    &&
					    !stringCompare(pLval,
					    nextTokenEndIndex -
					    nextTokenStartIndex,
					    "X", 1) ) )
				    {
					//In case
					//     <POS X0="15"/> <POS X="23">foo
					// arrives at the start of a packet,
					// make sure to clear the pkt start Y
					// so the X="23" will be used instead
					// of 15.  The problem was that the
					// space char separating the 2 didn't
					// become a TextContainer in the list
					// and thus, when "foo" was inserted,
					// the ...XAtTimeZero was still valid
					// and was used as a result and the
					// 23 was ignored:
					if(!bIsX0orY0)
					{
					    m_pTextWindow->
						    m_bUseXPOSVal = TRUE;
					}
					
					ULONG32 ulXAtTimeZero = 0L;
					//tmpPtr is always NULL-terminated
					// at this pt:
					ulXAtTimeZero =
					    m_pTextWindow->
					    string_to_ULONG32(tmpPtr, bErr);
					if(!bErr)
					{
					    m_pTextWindow->
						   SetNewPktStartXAtTimeZero(
						   (LONG32)ulXAtTimeZero);
					    //Fixes bug #6906; if
					    // "<BR/><POS .../>foo" is seen,
					    // the <BR/> will be ignored, but
					    // if <POS ../><BR/>foo" is seen,
					    // the <BR/> will be honored:
					    m_pTextWindow->
					      clearNumBreakTagsEncountered();
				    bUserPosTagFoundSinceLastTextContainer
						    = !bIsX0orY0;
					    bSomeCharsFoundSinceLastPosTag =
						    FALSE;
					}
					//else ignore the tag; is invalid X
				    } //end of "if( ..."X0"...)".

				    else if((bIsX0orY0 =!stringCompare(pLval,
					    nextTokenEndIndex -
					    nextTokenStartIndex,
					    "Y0", 2))  ||
					    //Only allow user to set POS X=n
					    // if window has no motion to it:
					    (!m_pTextWindow->getScrollRate()
					    &&
					    !m_pTextWindow->getCrawlRate()
					    &&
					    !stringCompare(pLval,
					    nextTokenEndIndex -
					    nextTokenStartIndex,
					    "Y", 1) ) )
				    {
					//In case
					//     <POS Y0="20"/> <POS Y="10">foo
					// arrives at the start of a packet,
					// make sure to clear the pkt start Y
					// so the Y="10" will be used instead
					// of 20.  The problem was that the
					// space char separating the 2 didn't
					// become a TextContainer in the list
					// and thus, when "foo" was inserted,
					// the ...YAtTimeZero was still valid
					// and was used as a result and the
					// 10 was ignored:
					if(!bIsX0orY0)
					{
					    m_pTextWindow->
						    m_bUseYPOSVal = TRUE;
					}
					
					ULONG32 ulYAtTimeZero = 0L;
					//tmpPtr is always NULL-terminated
					// at this pt:
					ulYAtTimeZero =
					    m_pTextWindow->
					    string_to_ULONG32(tmpPtr, bErr);
					if(!bErr)
					{
					    m_pTextWindow->
						   SetNewPktStartYAtTimeZero(
						   (LONG32)ulYAtTimeZero);
					    //Fixes bug #6906; if
					    // "<BR/><POS .../>foo" is seen,
					    // the <BR/> will be ignored, but
					    // if <POS ../><BR/>foo" is seen,
					    // the <BR/> will be honored:
					    m_pTextWindow->
					      clearNumBreakTagsEncountered();
				    bUserPosTagFoundSinceLastTextContainer
						    = !bIsX0orY0;
					    bSomeCharsFoundSinceLastPosTag =
						    FALSE;
					}
					//else ignore the tag; is invalid Y
				    } //end of "else if( ..."Y0" ...)".

				    if(nextValEndIndex < restOfDataLen)
				    {
					if(0L == nextValEndIndex)
					{
					    break; //leave the do loop.
					}
					pRestOfData = &pRestOfData[
						nextValEndIndex+1];
					restOfDataLen -= (nextValEndIndex+1);
				    }
				    else
				    {
					restOfDataLen = 0L;
				    }

				} while(restOfDataLen > 0L);
			    } //end "POS " tag parsing.
			}
		    }
		    break;

	    case 'R':
		    //added the following case so
		    // that a forced deletion of all prior-streamed
		    // data could be made (noting that <CLEAR> should
		    // do this but doesn't until the FF is changed
		    // to handle packet sending, and thus seeking,
		    // at the expected time)  This is to remain
		    // undocumented:
		    if(!stringCompare(pTagContents, tagContentsLen,
			    "RESET", 5))
		    {
			//go through list and delete everything:
			m_pTextWindow->deleteAllNoLongerVisible();
		    }
		    //C_RTRNDRDIFF >>:
		    else if(!stringCompare(pTagContents, tagContentsLen,
			    "REQUIRED", 8))
		    {
			m_pTextWindow->pushIsRequiredStack();			
		    }
		    // << END C_RTRNDRDIFF.
		    break;


	    case 'S':
		    if(!stringCompare(pTagContents, tagContentsLen,
			    "S", 1)) //start of strike-out tag
		    {
			m_pTextWindow->pushIsStruckOutStack(TRUE);
		    }
		    break;

	    case 'T':
		if(tagContentsLen >= 2)
		{
		    BOOL isTUtag, isTLtag;
		    isTUtag = isTLtag = FALSE;
		    if('U' == pTagContents[1])
		    {	
			//changed this so, for example, 
			//  <TUblah> won't work but <TU blah> will:
			if(tagContentsLen > 2)
			{
			    if(' ' ==  pTagContents[2]  ||
				    '\t'  == pTagContents[2]  ||
				    '\n'  == pTagContents[2]  ||
				    '\r'  == pTagContents[2]  )
			    {
				//is <TU>, or "TextUpper" tag for tickertape:
				isTUtag = TRUE;
				m_pTextWindow->isTickerUpperText(isTUtag);
			    }
			}
			else  //tagContentsLen == 2, so is <TU>:
			{
			    //is <TU>, or "TextUpper" tag for tickertape:
			    isTUtag = TRUE;
			    m_pTextWindow->isTickerUpperText(isTUtag);
			}
		    }	
		    else if('L' == pTagContents[1]) 
		    {	
			//changed this so, for example, 
			//  <TLblah> won't work but <TL blah> will:
			if(tagContentsLen > 2)
			{
			    if(' ' ==  pTagContents[2]  ||
				    '\t'  == pTagContents[2]  ||
				    '\n'  == pTagContents[2]  ||
				    '\r'  == pTagContents[2]  )
			    {
				//is <TL>, or "TextLower" tag for tickertape:
				isTLtag = TRUE;
				m_pTextWindow->isTickerUpperText(!isTLtag);
			    }
			}
			else  //tagContentsLen == 2, so is <TL>:
			{
			    //is <TL>, or "TextLower" tag for tickertape:
			    isTLtag = TRUE;
			    m_pTextWindow->isTickerUpperText(!isTLtag);
			}
		    }
		    else if('I' == pTagContents[1]  &&  tagContentsLen >= 10) 
		    {	//is possibly a <TIME ..> tag; min length for TIME
			//  is 10: e.g., <TIME end=1> (10 chars)
			if(pTagContents[2]=='M'  &&  pTagContents[3]=='E'  &&
				pTagContents[4]==' ')
			{   //we've found "TIME " so far...
			    _CHAR* pLval;
			    _CHAR* pRval;
			    _CHAR* pRestOfData = &pTagContents[5];
			    ULONG32 restOfDataLen = tagContentsLen-5;
			    ULONG32 nextTokenStartIndex, nextTokenEndIndex;
			    ULONG32 nextValStartIndex, nextValEndIndex;
			    do
			    {
				if(GetNextTokenLvalueRvaluePair(pRestOfData,
					restOfDataLen,
					nextTokenStartIndex,
					nextTokenEndIndex,
					nextValStartIndex, nextValEndIndex) )
				{
				    pLval =&pRestOfData[nextTokenStartIndex];
				    pRestOfData[nextTokenEndIndex]='\0';
				    pRval = &pRestOfData[nextValStartIndex];
				    pRestOfData[nextValEndIndex]='\0';
				}
				else
				{
				    break;
				}

				_CHAR* tmpPtr = pRval;
				ULONG32 tokenValLen =
					nextValEndIndex-nextValStartIndex;

				if(!stringCompare(pLval, 
					nextTokenEndIndex-nextTokenStartIndex, 
					"BEGIN", 5)
					//be consistent w/RA:
					|| !stringCompare(pLval, 
					nextTokenEndIndex-nextTokenStartIndex, 
					"START", 5)
					)
				{
				    ULONG32 timeValInMillisec = 0L;
				    if(convertTimeStringToULONG32(
					    tmpPtr, tokenValLen, 
					    timeValInMillisec))
				    {
					m_pTextWindow->SetLatestSentTimeToRender(
						timeValInMillisec
						- lTimeOffset); //cz);
				    }
				    //else ignore the tag; is invalid BEGIN time.
				} //end of "if( ..."BEGIN" ...||..."START"...)".

				if(!stringCompare(pLval, 
					nextTokenEndIndex-nextTokenStartIndex, 
					"END", 3))
				{
				    ULONG32 timeValInMillisec = 0L;
				    if(convertTimeStringToULONG32(
					    tmpPtr, tokenValLen, 
					    timeValInMillisec))
				    {
					if(bIsLiveSource  &&
						TIME_INVALID ==
						timeValInMillisec)
					{
					    //Make end time be "infinity":
					    timeValInMillisec =
						    TIME_INFINITY;
					}
					m_pTextWindow->
						SetLatestSentTimeToStopRendering(
						timeValInMillisec
						- lTimeOffset);//cz);
				    }
				    //else ignore the tag; is invalid END time.
				} //end of "else if( ..."END" ...)".

				if(nextValEndIndex < restOfDataLen)
				{
				    if(0L == nextValEndIndex)
				    {
					break; //leave the do loop.
				    }
				    pRestOfData = &pRestOfData[nextValEndIndex+1];
				    restOfDataLen -= (nextValEndIndex+1);
				}
				else
				{
				    restOfDataLen = 0L;
				}

			    } while(restOfDataLen > 0L);
			} //end "TIME " tag parsing.
		    }
		    //Added the following to allow better
		    // handling of HTML text imported as RealText:
		    //XXXEH- for now, just treat as line break tag, and
		    //just look at first 2-3 chars (ignoring optional <TR>-tag
		    //parameters like "align=") so "TR" or "TR " are valid:
		    else if(!stringCompare(pTagContents, tagContentsLen,
			    "TR", 2)  ||
			    (tagContentsLen>=3  &&  
			    !stringCompare(pTagContents, 3, "TR ", 3)) )
		    {
			//Added the surrounding if() so that
			// this code doesn't get called if horizontal motion:
			if(m_pTextWindow->getCrawlRate() == 0  ||
				m_pTextWindow->getScrollRate() != 0)
			{
			    //If we got <pos ..>x<tr>, where "x" contains
			    // no plain text (other than spaces, tabs,
			    // newlines), then don't do a line break at all:
			    if(bUserPosTagFoundSinceLastTextContainer  &&
				    !bSomeCharsFoundSinceLastPosTag)
			    {
				;
			    }
			    else
			    {
				m_pTextWindow->
					incrementNumBreakTagsEncountered();
				m_pTextWindow->
					incrementNumBreakTagsEncountered();
			    }
			    bSomeCharsFoundSinceLastBreakTag=0L;
			}
		    }

		    //Now, see if there are any additional attributes for
		    // ticker's upper/lower text:
		    if(tagContentsLen > 2  &&  (isTUtag  ||  isTLtag))
		    {
			ULONG32 nameTokenStartIndex, nameTokenEndIndex,
				indexOfEqualsSign;
			ULONG32 valueTokenStartIndex, valueTokenEndIndex,
				dummy;
			_CHAR tmpChar1, tmpChar2;
			ULONG32 nameTokenLen, valTokenLen;
			nameTokenStartIndex =
				skipSpacesTabsAndNewlineChars(
				pTagContents, tagContentsLen,
				2); //start at [2] and skip spaces.
			if(nameTokenStartIndex == tagContentsLen)
			{   //only spaces were found so rest of tag is empty:
			    break; 
			}
			nameTokenEndIndex = findNextSpaceTabOrNewLineChar(
				pTagContents, tagContentsLen, 
				nameTokenStartIndex, indexOfEqualsSign,
				//In-tag text is always us-ascii:
				CHARSET__us_ascii);
			if(indexOfEqualsSign < tagContentsLen-1) 
			{   //"(name)=(one or more chars)" was found:
			    if(nameTokenEndIndex == indexOfEqualsSign+1)
			    {	/*  "(name)=(1 or more space chars)(val)" was
				 *  found:  */
				valueTokenStartIndex = 
					skipSpacesTabsAndNewlineChars(
					pTagContents, tagContentsLen, 
					nameTokenEndIndex);
				if(valueTokenStartIndex == tagContentsLen)
				{
				    break;
				}
			    }
			    else //"(name)=(val)" was found:
			    {
				nameTokenEndIndex = indexOfEqualsSign;
				valueTokenStartIndex = indexOfEqualsSign+1;
			    }
			    valueTokenEndIndex = 
				    findNextSpaceTabOrNewLineChar(
				    pTagContents, tagContentsLen, 
				    valueTokenStartIndex, dummy,
				    //In-tag text is always us-ascii:
				    CHARSET__us_ascii);
			    //Put a '\0' at the character just past where
			    // the (name) part is:
			    tmpChar1 = pTagContents[nameTokenEndIndex];
			    pTagContents[nameTokenEndIndex] = '\0';
			    nameTokenLen = 
				    nameTokenEndIndex - nameTokenStartIndex;
			    if(!stringCompare(
				    &pTagContents[nameTokenStartIndex],
				    nameTokenLen,
				    "COLOR", 5))
			    {
				COLORTYPE colortype_retVal;

				tmpChar2 = pTagContents[valueTokenEndIndex];
				pTagContents[valueTokenEndIndex] = '\0';
				
				//Added this so "#RRGGBB"
				// works as well as "colorname" does:
				BOOL isAValidColor = FALSE;
				char* pTemp_Ptr =
					&pTagContents[valueTokenStartIndex];
				valTokenLen = valueTokenEndIndex - 
					valueTokenStartIndex;
				if(valTokenLen>=1  &&  '#'==pTemp_Ptr[0]  ||
					(valTokenLen>=2  &&
					'\"'==pTemp_Ptr[0]  && 
					'#'==pTemp_Ptr[1])  )
				{
				    //Color was presented as "#RRGGBB" in hex:
				    isAValidColor = 
					    convertColorValStringToCOLORTYPE(
					    pTemp_Ptr, valTokenLen, 
					    colortype_retVal);
				}
				//  Returns FALSE if colorName contains an
				//  unrecognized color:
				else
				{	//Color was named, e.g., "darkblue":
				    isAValidColor = convertColorNameToCOLORTYPE(
					    pTemp_Ptr, valTokenLen, 
					    colortype_retVal);
				}
				if(isAValidColor)
				{
				    //  Is a legal color name, so push it on
				    //  the appropriate stack:
				    if(isTUtag)
				    {
					m_pTextWindow->
						pushTickerUpperColorStack(
						colortype_retVal);
				    }
				    else
				    {
					m_pTextWindow->
						pushTickerLowerColorStack(
						colortype_retVal);
				    }
				}
				//else ignore the tag; is invalid color.

				//Restore the orig char:
				pTagContents[valueTokenEndIndex] = tmpChar2; 
			    }
			    //Restore it from '\0':
			    pTagContents[nameTokenEndIndex] = tmpChar1; 
			}
			else
			{
#if defined(SHOW_CODE_CHANGE_MESSAGES)
#pragma message("EH- in "__FILE__", \
\"(name)(SPACES)=(0 or more SPACES)(value)\"-parsing is not \
yet handled.")
#endif
			    ;
			}
		    }
		}
		break;

	    case 'U':
		if(!stringCompare(pTagContents, tagContentsLen,
			"U", 1)) //start of underlined tag
		{
		    m_pTextWindow->pushIsUnderlinedStack(TRUE);
		}
		//Added the following to allow better
		// handling of HTML text imported as RealText:
		//XXXEH- for now, treat as line break tag, but in future,
		// add indent based on depth of nested ULs and OLs:
		else if(!stringCompare(pTagContents, tagContentsLen,
			"UL", 2)) 
		{
		    //added the following:
		    UINT16 curIndentAmtInPixels =
			    m_pTextWindow->
			    peekAtLineIndentAmtInPixelsStack();
		    m_pTextWindow->pushLineIndentAmtInPixelsStack(
			    curIndentAmtInPixels +
			    UNORDERED_LIST_INDENT_AMT);
		    //Added the surrounding if() so that
		    // this code doesn't get called if horizontal motion:
		    if(m_pTextWindow->getCrawlRate() == 0  ||
			    m_pTextWindow->getScrollRate() != 0)
		    {
			BOOL bFollowsPosTag =
				(bUserPosTagFoundSinceLastTextContainer  &&
				!bSomeCharsFoundSinceLastPosTag);
			//Do a line break, but only if 0 line breaks so far,
			// and not if there is no raw text yet, i.e., not if
			// this <UL> tag starts the data part of the file,
			// nor if a <CLEAR> tag was just sent:
			if(!m_pTextWindow->getNumBreakTagsEncountered()  &&
//XXXXXEH-19980918>>
				//C_RTRNDR_DIFF>>
				(IsBeta1Player()  ||
				//<<END C_RTRNDR_DIFF.
				bFollowsPosTag  ||
				(!m_pTextWindow->m_bClearWasJustSent  &&
				m_pTextWindow->size()>0L)))
//<<XXXXXEH-19980918.
			{
			    //If we got <pos ..>x<ul>, where "x" contains 
			    // no plain text (other than spaces, tabs,
			    // newlines), then don't do a line break at
			    // all, but DO add ORDERED_LIST_INDENT_AMT
			    // amount to the NewPktStartXAtTimeZero()
			    // value:
			    if(bFollowsPosTag  &&  !IsBeta1Player())
			    {
				LONG32 lCurX = m_pTextWindow->
					GetNewPktStartXAtTimeZero();
				HX_ASSERT(INVALID_LONG32 != lCurX);
				m_pTextWindow->
					SetNewPktStartXAtTimeZero(
					lCurX +
					(UNORDERED_LIST_INDENT_AMT));
			    }
			    else
			    {
				m_pTextWindow->
				      incrementNumBreakTagsEncountered();
			    }
			    bSomeCharsFoundSinceLastBreakTag=0L;
			}
		    }
		}
		break;


	    default:
		break;
	}

	pData_CHAR[indexOfRightBracket] = '>'; //restore the char.
	
	startIndex = indexOfRightBracket+1; //where next tag-search starts.

    } while (startIndex < len);

    //C_RTRNDRDIFF >>:
    //Make sure we set the end byte of the last TextLine inserted (to the
    // last byte read in the file):
    TextLine* pTL_lastInserted = m_pTextWindow->m_pTLList->end();
    if(pTL_lastInserted)
    {
	pTL_lastInserted->
		setEndByteInFile(ulByteOffsetIntoFile+dataLength-1);
    }
    //<< END C_RTRNDRDIFF.
    
    
    return (bEarliestTimeOfNewDataIsValid ? HXR_OK:HXR_NOT_INITIALIZED);
} // end "TextParser::ParseText()".



// /If name isn't recognized, returns FALSE and charset_retVal is not valid:
HX_RESULT
TextParser::convertCharsetNameToCharsetULONG32(const char* pszCharsetName,
	    ULONG32 ulCharsetNameLen,
	    UINT16 uiMaxSupportedLevel,
	    ULONG32& ulCharset_retVal /*OUT*/)
{
    HX_RESULT hxrslt = HXR_OK;

    ulCharset_retVal = CHARSET__default;

    if (!pszCharsetName  ||  ulCharsetNameLen < 1)
    {
	hxrslt = HXR_INVALID_PARAMETER;
    }
    else if (!stringCompare(pszCharsetName, ulCharsetNameLen, 
	    "us-ascii", 8) )
    {
	ulCharset_retVal = CHARSET__us_ascii;
    }
    else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "iso-8859-1", 10) )
    {
	ulCharset_retVal = CHARSET__us_ascii;
	if (uiMaxSupportedLevel > 0)
	{
	    // /This is actually different from us-ascii in the upper-128
	    // chars; us-ascii's upper-128 are undefined, and we handle these
	    // 2 charsets better in level 1+ processing:
	    ulCharset_retVal = CHARSET__iso_8859_1;
	}
    }
    else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "mac-roman", 9) )
    {
	ulCharset_retVal = CHARSET__x_mac_roman;
    }
    else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "iso-2022-jp", 11) )
    {
	ulCharset_retVal = CHARSET__iso_2022_jp;
    }
    else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "x-sjis", 6) )
    {
	ulCharset_retVal = CHARSET__x_sjis;
    }
    else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "euc-kr", 6) )
    {
	ulCharset_retVal = CHARSET__euc_kr;
    }
    else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "x-euc-jap", 9) )
    {
	ulCharset_retVal = CHARSET__x_euc_jap;
    }
    else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "iso-2022-kr", 11) )
    {
	ulCharset_retVal = CHARSET__iso_2022_kr;
    }
    else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "big5", 4) )
    {
	ulCharset_retVal = CHARSET__big5;
    }
    else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "gb2312", 6) )
    {
	ulCharset_retVal = CHARSET__gb2312;
    }

    // /Extensions #1; only look for these if supported by the caller,
    // e.g., plain text in SMIL 2.0 supports these but RealText (1.6 and
    // earlier) does not.  Later versions of RT may or may not:
    else if (uiMaxSupportedLevel > 0)
    {
	if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "iso-8859-2", 10) )
	{
	    ulCharset_retVal = CHARSET__iso_8859_2;
	}
	else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "iso-8859-5", 10) )
	{
	    ulCharset_retVal = CHARSET__iso_8859_5;
	}
	else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "iso-8859-6", 10) )
	{
	    ulCharset_retVal = CHARSET__iso_8859_6;
	}
	else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "iso-8859-7", 10) )
	{
	    ulCharset_retVal = CHARSET__iso_8859_7;
	}
	else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "iso-8859-8", 10) )
	{
	    ulCharset_retVal = CHARSET__iso_8859_8;
	}
	else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "iso-8859-9", 10) )
	{
	    ulCharset_retVal = CHARSET__iso_8859_9;
	}
	else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "iso-8859-11", 11) )
	{
	    ulCharset_retVal = CHARSET__iso_8859_11;
	}
	else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "iso-8859-13", 11) )
	{
	    ulCharset_retVal = CHARSET__iso_8859_13;
	}
	else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "windows-1251", 12) )
	{
	    ulCharset_retVal = CHARSET__windows_1251;
	}
	else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "koi8-r", 6) )
	{
	    ulCharset_retVal = CHARSET__koi8_r;
	}
	else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "iso-ir-166", 10) )
	{
	    ulCharset_retVal = CHARSET__iso_ir_166;
	}
	else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "hangeul", 7) )
	{
	    ulCharset_retVal = CHARSET__hangeul;
	}
	else if (!stringCompare(pszCharsetName, ulCharsetNameLen,
	    "ksc5601", 7) )
	{
	    ulCharset_retVal = CHARSET__ksc5601;
	}
	// /Here's where you'd add level 2, if new charsets are ever supported:
	else
	{
	    hxrslt = HXR_FAILED;
	}
    }
    else
    {
	hxrslt = HXR_FAILED;
    }

    return hxrslt;
}

/////////////////////////////////////////////////////////////////////////////
// Method:
//  void TextParser::FindEscapeSequenceChar(...)
// Purpose:
//  This function searches for and handles HTML-style escape sequences that
//  start with '&' and end with ';', e.g., "&nbsp;" or "&#133;"
//
//  Looks for "&lt;" or "&gt;" and, if found, end the
//  TextContainer at the end of the "&lt;" or "&gt;" and replace
//  it with '<' or '>'; also, look for "&#n;"
//  (where n>8 && n<=255) and translate into the ASCII char
//  with that value, and look for "&nbsp;" for ' ' and "&amp;"
//  and replace with '&':
//
void TextParser::FindEscapeSequenceChar(_CHAR*& pCurText,
	ULONG32& ulIndexOfLastCharInTCsBuf,
	ULONG32& theFollowingSpaceCharIndex,
	ULONG32& ulIndexOfAmpersand,
	ULONG32& ulCurCharset)
{
    while(theFollowingSpaceCharIndex > ulIndexOfAmpersand  &&
	    theFollowingSpaceCharIndex - ulIndexOfAmpersand >= 4)
    {					 //4 is strlen("&lt;") ^.
	_CHAR* pTmp= &pCurText[ulIndexOfAmpersand+1];
	ULONG32 ulStrlenSpecialChar = 1L;
	BOOL is_lt = FALSE;
	BOOL is_gt = FALSE;
	BOOL is_num = FALSE;
	BOOL is_amp = FALSE;
	BOOL is_nbsp = FALSE;
	_CHAR translatedChar = '\0';
	//Note from "http://www.ncsa.uiuc.edu/General/Internet/WWW/
	//  HTMLPrimerP2.html#ES":
	//  - "Unlike the rest of HTML, the escape sequences are case
	//     sensitive. You cannot, for instance, use &LT; instead
	//     of &lt;".
	// Thus we only need to check lower case possibilities:
	if((is_lt=('l'==*pTmp))  ||  (is_gt=('g'==*pTmp))  ||
		(is_num=('#'==*pTmp))  ||  (is_amp=('a'==*pTmp))  ||
		(is_nbsp=('n'==*pTmp)) )
	{
	    pTmp++;
	    ulStrlenSpecialChar++;
	    INT32 num;
	    if((is_lt  ||  is_gt)  &&  't'==*pTmp)
	    {
		pTmp++;
		ulStrlenSpecialChar++;
		if(is_lt)
		{
		    translatedChar = '<';
		}
		else if(is_gt)
		{
		    translatedChar = '>';
		}
	    }
	    else if(is_amp  &&  'm'==*pTmp)
	    {
		pTmp++;
		ulStrlenSpecialChar++;
		if('p' == *pTmp)
		{
		    pTmp++;
		    ulStrlenSpecialChar++;
		    translatedChar = '&';
		}
	    }
	    else if(is_nbsp  &&  'b'==*pTmp)
	    {
		pTmp++;
		ulStrlenSpecialChar++;
		if('s' == *pTmp)
		{
		    pTmp++;
		    ulStrlenSpecialChar++;
		    if('p' == *pTmp)
		    {
			pTmp++;
			ulStrlenSpecialChar++;
			translatedChar = ' ';
		    }
		}
	    }
	    else if(is_num  &&  
		    (num = atoi(pTmp)) > 8) //#00 to #08 are unused.
	    {
		//11, 12, 14-31, 127-159, and 256+ are also unused:
		if(num!=11  &&  num!=12  &&  !(num>=14  &&  num<=31)
			&&  !(num>=127  && num<=159)  &&  num<=255)
		{
		    INT32 numDigitsOf_num = 0;
		    while(pTmp[numDigitsOf_num]>='0'  &&
			    pTmp[numDigitsOf_num]<='9')
		    {
			numDigitsOf_num++;
		    }
		    //Add the number of base-10 digits of num:
		    pTmp += numDigitsOf_num; //XXXEH- DBCS-friendly??
		    ulStrlenSpecialChar += (ULONG32)numDigitsOf_num;
		    translatedChar = _CHAR(num);
		}
	    }
	    if(translatedChar)
	    {
		if(';' == *pTmp)
		{
		    ulStrlenSpecialChar++;

		    pCurText[ulIndexOfAmpersand] = translatedChar;

		    BOOL bWeHitEndOfBuff = FALSE;
		    if(ulIndexOfLastCharInTCsBuf <=
			    ulStrlenSpecialChar + ulIndexOfAmpersand)
		    {
			bWeHitEndOfBuff = TRUE; //Fix for bug #6837.
		    }

		    ulIndexOfLastCharInTCsBuf = ulIndexOfAmpersand;

		    //If space char ends the string, make sure it
		    // still ends with space char:
		    if(theFollowingSpaceCharIndex ==
			    ulIndexOfAmpersand + ulStrlenSpecialChar
			    //Fix for bug #6837:
			    &&  !bWeHitEndOfBuff)
		    {
			pCurText[ulIndexOfAmpersand+1] = ' ';
			pCurText[ulIndexOfAmpersand+2] = '\0';
			ulIndexOfLastCharInTCsBuf++;
			ulStrlenSpecialChar++;
		    }
		    else
		    {
			pCurText[ulIndexOfAmpersand+1] = '\0';
		    }
		    theFollowingSpaceCharIndex =
			    ulIndexOfAmpersand+ulStrlenSpecialChar-1;
		    break;
		}
	    }
	}
	ulIndexOfAmpersand = findNextChar('&',
		pCurText,
		theFollowingSpaceCharIndex,
		//This is where in pCurText to
		// start searching for another '&':
		ulIndexOfAmpersand+ulStrlenSpecialChar,
		ulCurCharset);
    } //end "while(theFollowingSpaceCharIndex > ...)".
} // end "TextParser::FindEscapeSequenceChar()".




/////////////////////////////////////////////////////////////////////////////
// Method:
//  void TextParser::HandleEndTag(...)
// Purpose:
//  This function finds which, if any, valid end tag this is and handles the
//  "undoing" of the tag by popping the appropriate stack(s) and/or
//  resetting the appropriate state variables.
//
void TextParser::HandleEndTag(_CHAR* pTagContents, ULONG32 tagContentsLen,
	BOOL& bSomeCharsFoundSinceLastBreakTag,
	BOOL& bSomeCharsFoundSinceLastPosTag,
	BOOL& bUserPosTagFoundSinceLastTextContainer,
	ULONG32 ulByteOffsetIntoFile,
	LONG32 indexOfLeftBracket,
	LONG32 indexOfRightBracket,
	BOOL bFileFormatIsCallingThis)
{
    switch(pTagContents[1])
    {
	case 'A':   //is end of anchor tag:
	    if(!stringCompare(pTagContents, tagContentsLen,
		    "/A", 2)  &&
		    //Don't do all the following unless this
		    // was preceded by a valid <A HREF="foo">
		    m_pTextWindow->hasValidURL())
	    {
		m_pTextWindow->clearURL();
		if(bFileFormatIsCallingThis)
		{
		    break;
		}
		//Don't do this @ File Format side; sending the <A > tag will
		//be enough for the renderer to know the <U> & <FONT color=> 
		//information it needs; (we don't want this duplicated in the
		//packet header now that packet headers include <A > tags):

		//Added the following to go
		// back to the pre-hyperlink text's font color:
		////XXXEH- the following line was:
		//// popTextColorStack()
		////but "<A ..><FONT size=+1>blah</FONT></A>"
		////at the start of a packet would end up as:
		////"<FONT size=+1><A ..>blah</FONT></A>" 
		////(FONT and A get switched in the packet
		////header) and the </FONT> ends up popping
		////the color pushed by the <A > tag instead
		////of popping the FONT size, and then the
		////</A> pops the color again and all %#$!
		////breaks loose.  This should be revisited!
		////(See "BUGinHREFcolorDueTo...rtx"
		m_pTextWindow->popFontStack();
		if(m_pTextWindow->usingUnderlineHyperlinks())
		{
		    m_pTextWindow->popIsUnderlinedStack();
		}
		//Added the following to
		// handle backing out after hyperlink text is done:
		ULONG32 numColorPushes = 
			m_pTextWindow->
			getNumberOfFontColorPushesInsideLinkText();
		for(; numColorPushes >0; numColorPushes--)
		{
		    m_pTextWindow->popTextColorStack();
		}
		m_pTextWindow->
			setNumberOfFontColorPushesInsideLinkText(0L);
	    }
	    break;

	case 'B':
	    if(!stringCompare(pTagContents, tagContentsLen,
		    "/B", 2)) //end of bold tag
	    {
		m_pTextWindow->popIsBoldStack();
	    }
	    break;

	case 'C':
	    if(!stringCompare(pTagContents, tagContentsLen,
		    "/CENTER", 7))
	    {   
		BOOL bSomethingWasPopped =
			m_pTextWindow->popIsCenteredStack();
		//XXXEH- don't do this if() because we always want to do a
		// line break for a </CENTER> tag, even if it has no matching
		// <CENTER> tag, right?... (Fixes bug:
		// "BUG 2 at end-- unCENTER tag being ignored.rtx"):
		///if(bSomethingWasPopped)
		{
		    //Do a line break if this </CENTER> tag
		    // has a matching (preceding) "<CENTER>",
		    // but only if 0 line breaks so far:
		    if(!m_pTextWindow->
			    getNumBreakTagsEncountered()  &&
			    !m_pTextWindow->m_bClearWasJustSent)
		    {
			//If we got <pos ..>x</center>, where "x" contains no
			// plain text (other than spaces, tabs, newlines),
			// then don't do a line break at all:
			if(bUserPosTagFoundSinceLastTextContainer  &&
				!bSomeCharsFoundSinceLastPosTag)
			{
			    ;
			}
			else
			{
			    m_pTextWindow->
				    incrementNumBreakTagsEncountered();
			}
			bSomeCharsFoundSinceLastBreakTag=FALSE;
		    }
		}
	    }
	    break;

	case 'F':
	{
	    TextAttributes* pTA = NULL;
	    if(bFileFormatIsCallingThis)
	    {
		//pTA keeps track of what the
		// current font attributes are so that we can see
		// if this </FONT...> tag caused a change in any
		// of the font attribute values:
		pTA = new TextAttributes;
		HX_ASSERT_VALID_PTR(pTA);
		if(pTA)
		{
		    pTA->setTextColor(m_pTextWindow->
			    peekAtTextColorStack());
		    pTA->setTextBackgroundColor(m_pTextWindow->
			    peekAtTextBackgroundColorStack());
		    pTA->setFontPointSize(m_pTextWindow->
			    peekAtPointSizeStack()); 
		    pTA->setFontCharset(m_pTextWindow->
			    peekAtCharsetStack());
		    pTA->setFontFace(m_pTextWindow->
			    peekAtFontFaceStack());
		}
	    }

	    if(!stringCompare(pTagContents, tagContentsLen,
		    "/FONTSIZE", 9))
	    {
		m_pTextWindow->popFontPointSizeStack();
	    }
	    else if(!stringCompare(pTagContents,
		    tagContentsLen, "/FONTCOLOR", 10))
	    {
		m_pTextWindow->popTextColorStack();
		//Added the following to see
		// if we're inside an <A> tag so that we can
		// properly back out font colors after hyperlink
		// text is done:
		if(m_pTextWindow->hasValidURL())
		{
		    m_pTextWindow->
			    decrementNumberOfFontColorPushesInsideLinkText();
		}
	    }
	    else if(!stringCompare(pTagContents,
		    tagContentsLen, "/FONTBGCOLOR", 12))
	    {
		m_pTextWindow->popTextBackgroundColorStack();
	    }
	    else if(!stringCompare(pTagContents,
		    tagContentsLen, "/FONTCHARSET", 12))
	    {
		m_pTextWindow->popFontCharsetStack();
	    }
	    else if(!stringCompare(pTagContents,
		    tagContentsLen, "/FONTFACE", 9))
	    {
		m_pTextWindow->popFontFaceStack();
	    }
	    else if(!stringCompare(pTagContents,
		    tagContentsLen, "/FONT", 5))
	    {
		//pops the most recently-pushed value(s)
		// (size, color, ...etc):
		m_pTextWindow->popFontStack();
		//Added the following to
		// see if we're inside an <A> tag so that we
		// can properly back out font colors after
		// hyperlink text is done:
		if(m_pTextWindow->hasValidURL())
		{
		    m_pTextWindow->
	    decrementNumberOfFontColorPushesInsideLinkText();
		}
	    }

	    if(bFileFormatIsCallingThis)
	    {
		//Now, see if any of the FONT attributes are diff
		// than they were before this "</FONT...>" tag,
		// and, if so, insert the changed-to values into
		// a new TextLine and insert that into the
		// m_pTextWindow->m_pFontUndoTagList:
		if(pTA  &&  m_pTextWindow->m_pFontUndoTagList)
		{
		    //These are all "invalid" values that will
		    // only be changed to "valid" ones if the
		    // attribute changed as a result of the stack
		    // "pop":
		    ULONG32 ulFontPointSize = 0L;
		    ULONG32 ulTextColor = BAD_RGB_COLOR;
		    ULONG32 ulTextBGColor = BAD_RGB_COLOR;
		    ULONG32 ulFontCharset = INVALID_CHARSET;
		    ULONG32 ulFontFace = INVALID_FONT_INDEX;
		    BOOL bSomethingChanged = FALSE;

    //XXXEH- Kludge: force all of them to be done for every </FONT>
    ulFontPointSize = m_pTextWindow->peekAtPointSizeStack();
    ulTextColor = m_pTextWindow->peekAtTextColorStack();
    ulTextBGColor = m_pTextWindow->peekAtTextBackgroundColorStack();
    ulFontCharset = m_pTextWindow->peekAtCharsetStack();
    ulFontFace = m_pTextWindow->peekAtFontFaceStack();

		    if(pTA->getFontPointSize() != m_pTextWindow->
			    peekAtPointSizeStack()) 
		    {
			ulFontPointSize = m_pTextWindow->
				peekAtPointSizeStack();
			bSomethingChanged =TRUE;
		    }
		    if(pTA->getTextColor() != m_pTextWindow->
			    peekAtTextColorStack())
		    {
			ulTextColor = m_pTextWindow->
				peekAtTextColorStack();
			bSomethingChanged =TRUE;
		    }
		    if(pTA->getTextBackgroundColor() != 
			    m_pTextWindow->
			    peekAtTextBackgroundColorStack())
		    {
			ulTextBGColor = m_pTextWindow->
				peekAtTextBackgroundColorStack();
			bSomethingChanged =TRUE;
		    }
		    if(pTA->getFontCharset() != m_pTextWindow->
			    peekAtCharsetStack())
		    {
			ulFontCharset = m_pTextWindow->
				peekAtCharsetStack();
			bSomethingChanged =TRUE;
		    }
		    if(pTA->getFontFace() != m_pTextWindow->
			    peekAtFontFaceStack())
		    {
			ulFontFace = m_pTextWindow->
				peekAtFontFaceStack();
			bSomethingChanged =TRUE;
		    }				

		    ///if(bSomethingChanged)
		    {
			TextLine* pTL = new TextLine(
				ulByteOffsetIntoFile +
					indexOfLeftBracket,
				ulByteOffsetIntoFile +
					indexOfRightBracket,
				m_pTextWindow->
				popFontTagStartByteInFileStack(),
				ulFontPointSize,
				ulTextColor,
				ulTextBGColor,
				ulFontCharset,
				ulFontFace,
				m_pTextWindow->getMajorContentVersion(),
				m_pTextWindow->getMinorContentVersion());

			HX_ASSERT_VALID_PTR(pTL);
			if(NULL == pTL)
			{
			    break;//Houston, We've had a problem.
			}

			pTL->setSomethingChanged(bSomethingChanged);

			if(!m_pTextWindow->m_pFontUndoTagList->
				insertAtEndOfList(pTL))
			{
			    delete pTL;//insert failed: kill pTL.
			}
			else
			{
			    // /Don't let the font undo stack grow beyond a
			    // certain size for live streams:
			    if (m_pTextWindow->isLiveSource()  &&
				    m_pTextWindow->m_pFontUndoTagList->
				    GetCount() >
				    MAX_LIVE_FONT_UNDO_STACK_DEPTH)
			    {
				m_pTextWindow->m_pFontUndoTagList->RemoveHead();
			    }
			}
		    }
		}
		if(pTA)
		{
		    delete pTA;
		}
	    } //end "if(bFileFormatIsCallingThis)".
	    break;
	}


	case 'I':
	    if(!stringCompare(pTagContents, tagContentsLen,
		    "/I", 2)) //end of italics tag
	    {
		m_pTextWindow->popIsItalicizedStack();
	    }
	    break;

	case 'L':
	    if(!stringCompare(pTagContents, tagContentsLen,
		    "/LOOP", 5))
	    {
		m_pTextWindow->loop(FALSE);
	    }
	    break;

	case 'O':
	    //Added the following to allow better
	    // handling of HTML text imported as RealText:
	    //XXXEH- for now, treat as line break tag, but in future,
	    // add indent based on depth of nested ULs and OLs:
	    if(!stringCompare(pTagContents, tagContentsLen,
		    "/OL", 3)) 
	    {
		m_pTextWindow->popLineIndentAmtInPixelsStack();
		//Added the surrounding if() so that
		// this code doesn't get called if horizontal motion:
		if(m_pTextWindow->getCrawlRate() == 0  ||
			m_pTextWindow->getScrollRate() != 0)
		{
		    //only increment if 0 so far:
		    if(!m_pTextWindow->getNumBreakTagsEncountered())
		    {
			//If we got <pos ..>x</ol>, where "x" contains no
			// plain text (other than spaces, tabs, newlines),
			// then don't do a line break at all:
			if(bUserPosTagFoundSinceLastTextContainer  &&
				!bSomeCharsFoundSinceLastPosTag)
			{
			    ;
			}
			else
			{
			    m_pTextWindow->
				    incrementNumBreakTagsEncountered();
			}
			bSomeCharsFoundSinceLastBreakTag=FALSE;
		    }
		}
	    }
	    break;

	
	case 'P':
	    if(!stringCompare(pTagContents, tagContentsLen,
		"/P", 2)) //end of paragraph
	    {
		//behaves same as <P>, namely 2 line breaks:

		//If we got <pos ..>x</p>, where "x" contains no
		// plain text (other than spaces, tabs, newlines),
		// then don't do a line break at all, but rather
		// update the NewPktStartYAtTimeZero() value:
		if(bUserPosTagFoundSinceLastTextContainer  &&
			!bSomeCharsFoundSinceLastPosTag)
		{
		    LONG32 lCurY =  m_pTextWindow->
			    GetNewPktStartYAtTimeZero();
		    HX_ASSERT(INVALID_LONG32 != lCurY);
		    m_pTextWindow->
			    SetNewPktStartYAtTimeZero(
			    lCurY + (2*DEFAULT_LINE_BREAK_SIZE));
		}
		else
		{
		    m_pTextWindow->
			    incrementNumBreakTagsEncountered();
		    m_pTextWindow->
			    incrementNumBreakTagsEncountered();
		}
		bSomeCharsFoundSinceLastBreakTag=FALSE;
	    }
	    else if(!stringCompare(pTagContents, tagContentsLen,
		    "/PRE", 4)) //end of PRE-formatted text.
	    {
		//Acts like a </P> tag as far as newlines go:

		//If we got <pos ..>x</pre>, where "x" contains no
		// plain text (other than spaces, tabs, newlines),
		// then don't do a line break at all:
		if(bUserPosTagFoundSinceLastTextContainer  &&
			!bSomeCharsFoundSinceLastPosTag)
		{
		    ;
		}
		else
		{
		    m_pTextWindow->incrementNumBreakTagsEncountered();
		    m_pTextWindow->incrementNumBreakTagsEncountered();
		}
		bSomeCharsFoundSinceLastBreakTag=FALSE;

		if(!m_pTextWindow->popIsPreStack())
		{
		    break; //don't pop font, below, if has no matching <PRE>.
		}

		TextAttributes* pTA = NULL;
		if(bFileFormatIsCallingThis)
		{
		    //pTA keeps track of what the
		    // current font attributes are so that we can see
		    // if this </FONT...> tag caused a change in any
		    // of the font attribute values:
		    pTA = new TextAttributes;
		    HX_ASSERT_VALID_PTR(pTA);
		    if(pTA)
		    {
			pTA->setTextColor(m_pTextWindow->
				peekAtTextColorStack());
			pTA->setTextBackgroundColor(m_pTextWindow->
				peekAtTextBackgroundColorStack());
			pTA->setFontPointSize(m_pTextWindow->
				peekAtPointSizeStack()); 
			pTA->setFontCharset(m_pTextWindow->
				peekAtCharsetStack());
			pTA->setFontFace(m_pTextWindow->
				peekAtFontFaceStack());
		    }
		}

		//pops the "courier" font-face that was pushed
		// onto the font stack when the matching <PRE> tag was
		// parsed UNLESS the author erroneously put some other
		// <FONT> tag in the text after the <PRE> and without a
		// matching </FONT>, in which case the following
		// will pop the most recently-pushed FONT tag's
		// value(s) (size, color, ...etc):
		m_pTextWindow->popFontStack();

		if(bFileFormatIsCallingThis)
		{
		    //Now, see if any of the FONT attributes are diff
		    // than they were before this "</FONT...>" tag,
		    // and, if so, insert the changed-to values into
		    // a new TextLine and insert that into the
		    // m_pTextWindow->m_pFontUndoTagList:
		    if(pTA  &&  m_pTextWindow->m_pFontUndoTagList)
		    {
			//These are all "invalid" values that will
			// only be changed to "valid" ones if the
			// attribute changed as a result of the stack
			// "pop":
			ULONG32 ulFontPointSize = 0L;
			ULONG32 ulTextColor = BAD_RGB_COLOR;
			ULONG32 ulTextBGColor = BAD_RGB_COLOR;
			ULONG32 ulFontCharset = INVALID_CHARSET;
			ULONG32 ulFontFace = INVALID_FONT_INDEX;
			BOOL bSomethingChanged = FALSE;

	//XXXEH- Kludge: force all of them to be done for every </FONT>
	ulFontPointSize = m_pTextWindow->peekAtPointSizeStack();
	ulTextColor = m_pTextWindow->peekAtTextColorStack();
	ulTextBGColor = m_pTextWindow->peekAtTextBackgroundColorStack();
	ulFontCharset = m_pTextWindow->peekAtCharsetStack();
	ulFontFace = m_pTextWindow->peekAtFontFaceStack();

			if(pTA->getFontPointSize() != m_pTextWindow->
				peekAtPointSizeStack()) 
			{
			    ulFontPointSize = m_pTextWindow->
				    peekAtPointSizeStack();
			    bSomethingChanged =TRUE;
			}
			if(pTA->getTextColor() != m_pTextWindow->
				peekAtTextColorStack())
			{
			    ulTextColor = m_pTextWindow->
				    peekAtTextColorStack();
			    bSomethingChanged =TRUE;
			}
			if(pTA->getTextBackgroundColor() != 
				m_pTextWindow->
				peekAtTextBackgroundColorStack())
			{
			    ulTextBGColor = m_pTextWindow->
				    peekAtTextBackgroundColorStack();
			    bSomethingChanged =TRUE;
			}
			if(pTA->getFontCharset() != m_pTextWindow->
				peekAtCharsetStack())
			{
			    ulFontCharset = m_pTextWindow->
				    peekAtCharsetStack();
			    bSomethingChanged =TRUE;
			}
			if(pTA->getFontFace() != m_pTextWindow->
				peekAtFontFaceStack())
			{
			    ulFontFace = m_pTextWindow->
				    peekAtFontFaceStack();
			    bSomethingChanged =TRUE;
			}				

			///if(bSomethingChanged)
			{
			    TextLine* pTL = new TextLine(
				    ulByteOffsetIntoFile +
					    indexOfLeftBracket,
				    ulByteOffsetIntoFile +
					    indexOfRightBracket,
				    m_pTextWindow->
				    popFontTagStartByteInFileStack(),
				    ulFontPointSize,
				    ulTextColor,
				    ulTextBGColor,
				    ulFontCharset,
				    ulFontFace,
				    m_pTextWindow->getMajorContentVersion(),
				    m_pTextWindow->getMinorContentVersion());

			    HX_ASSERT_VALID_PTR(pTL);
			    if(NULL == pTL)
			    {
				break;//Houston, We've had a problem.
			    }

			    pTL->setSomethingChanged(bSomethingChanged);

			    if(!m_pTextWindow->m_pFontUndoTagList->
				    insertAtEndOfList(pTL))
			    {
				delete pTL;//insert failed: kill pTL.
			    }
			    else
			    {
				// /Don't let the font undo stack grow beyond a
				// certain size for live streams:
				if (m_pTextWindow->isLiveSource()  &&
					m_pTextWindow->m_pFontUndoTagList->
					GetCount() >
					MAX_LIVE_FONT_UNDO_STACK_DEPTH)
				{
				    m_pTextWindow->m_pFontUndoTagList->RemoveHead();
				}
			    }
			}
		    }
		    if(pTA)
		    {
			delete pTA;
		    }
		} //end "if(bFileFormatIsCallingThis)".
	    }
	    break;

	case 'R':
	    if(!stringCompare(pTagContents, tagContentsLen,
		    "/REQUIRED", 9))
	    {
		m_pTextWindow->popIsRequiredStack();			
	    }
	    break;
	    
	case 'S':
	    if(!stringCompare(pTagContents, tagContentsLen,
		    "/S", 2))//end of strike-out tag
	    {
		m_pTextWindow->popIsStruckOutStack();
	    }
	    break;

	case 'T':
	    //Added this to allow better
	    // handling of HTML text imported as RealText:
	    //XXXEH- for now, just treat as line break tag:
	    if(!stringCompare(pTagContents, tagContentsLen,
		    "/TABLE", 6)) 
	    {
		//Added the surrounding
		// if() so that this code doesn't get
		// called if horizontal motion:
		if(m_pTextWindow->getCrawlRate() == 0  ||
			m_pTextWindow->getScrollRate() != 0)
		{
		    //If we got <pos ..>x</table>, where "x" contains no
		    // plain text (other than spaces, tabs, newlines),
		    // then don't do a line break at all:
		    if(bUserPosTagFoundSinceLastTextContainer  &&
			    !bSomeCharsFoundSinceLastPosTag)
		    {
			;
		    }
		    else
		    {
			m_pTextWindow->
				incrementNumBreakTagsEncountered();
		    }
		    bSomeCharsFoundSinceLastBreakTag=FALSE;
		}
	    }
	    else if(!stringCompare(
		    pTagContents, tagContentsLen,
		    "/TU", 3) )
	    {
		m_pTextWindow->isTickerUpperText(FALSE);
	    }
	    else if(!stringCompare(
		    pTagContents, tagContentsLen,
		    "/TL", 3) )
	    {
		m_pTextWindow->isTickerUpperText(TRUE);
	    }
	    break;

	case 'U':
	    if(!stringCompare(pTagContents, tagContentsLen,
		    "/U", 2))//end of underlined tag
	    {
		m_pTextWindow->popIsUnderlinedStack();
	    }
	    //Added the following to allow
	    // better handling of HTML text imported as
	    // RealText:
	    //XXXEH- for now, treat as line break tag, but in
	    // future, add indent based on depth of nested
	    // ULs and OLs:
	    if(!stringCompare(pTagContents, tagContentsLen,
		    "/UL", 3)) 
	    {
		m_pTextWindow->popLineIndentAmtInPixelsStack();
		//Added the surrounding if() so that
		// this code doesn't get called if horizontal motion:
		if(m_pTextWindow->getCrawlRate() == 0  ||
			m_pTextWindow->getScrollRate() != 0)
		{
		    //only increment if 0 so far:
		    if(!m_pTextWindow->getNumBreakTagsEncountered())
		    {
			//If we got <pos ..>x</ul>, where "x" contains no
			// plain text (other than spaces, tabs, newlines),
			// then don't do a line break at all, but rather
			// update the NewPktStartYAtTimeZero() value:
			if(bUserPosTagFoundSinceLastTextContainer  &&
				!bSomeCharsFoundSinceLastPosTag)
			{
			    LONG32 lCurY =  m_pTextWindow->
				    GetNewPktStartYAtTimeZero();
			    HX_ASSERT(INVALID_LONG32 != lCurY);
			    m_pTextWindow->
				    SetNewPktStartYAtTimeZero(
				    lCurY + (DEFAULT_LINE_BREAK_SIZE));
			}
			else
			{
			    //If we got <pos ..>x</table>, where "x" contains
			    // no plain text (other than spaces, tabs,
			    // newlines), then don't do a line break at all:
			    if(bUserPosTagFoundSinceLastTextContainer  &&
				    !bSomeCharsFoundSinceLastPosTag)
			    {
				;
			    }
			    else
			    {
				m_pTextWindow->
					incrementNumBreakTagsEncountered();
			    }
			}
			bSomeCharsFoundSinceLastBreakTag=FALSE;
		    }
		}
	    }
	    break;

	default:
		break;
    } //end "switch(pTagContents[1])".
} //end TextParser::HandleEndTag().
