Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3010 lines
84 KiB

/*
Enhanced NCSA Mosaic from Spyglass
"Guitar"
Copyright 1994 Spyglass, Inc.
All Rights Reserved
Author(s):
Eric W. Sink [email protected]
Jim Seidman [email protected]
*/
/* Note that several of the functions prototyped in HText.h are now
in platform-specific forms code. */
#include "all.h"
#include "history.h"
#include "marquee.h"
#include "mci.h"
#include "blob.h"
#ifdef FEATURE_VRML
#include "vrml.h"
#endif
/*
If an image has no alt text, this string is used as the alt text.
The alt text is displayed any time the image is not.
*/
#ifdef MAC
/* This mapping is not perfect (actually it's pretty bad),
but I tried to convert as many things as possible into
reasonably close approximations, or at least printing
characters that could convey the meaning. */
char IsoToMac[257] =
#ifndef USE_NCSA_MAPPING
" "
" "
" !\"#$%&'()*+,-./"
"0123456789:;<=>?"
"@ABCDEFGHIJKLMNO"
"PQRSTUVWXYZ[\\]^_"
"`abcdefghijklmno"
"pqrstuvwxyz{|}~¥"
" ÕÄÓÉ à^ SÇÎ "
" ÔÕÒÓ¥ÐÑ~ªsÈÏ Y"
"ÊÁ¢£Û´|¤¬©»ÇÂШ " /* First char is non-breaking space ('\0xca') */
"¡±23«µ¦á,1¼È///À"
"ËçåA€�®‚éƒæèíêëì"
"D„ñîïÍ…X¯ôòó†YÞ§"
"ˆ‡‰‹ŠŒ¾��Ž�‘“’”•"
"o–˜—™›šÖ¿�œžŸyߨ";
#else
/* NCSA mapping used in Mac 1.0.3 version */
" "
" "
" !\"#$%&'()*+,-./"
"0123456789:;<=>?"
"@ABCDEFGHIJKLMNO"
"PQRSTUVWXYZ[\\]^_"
"`abcdefghijklmno"
"pqrstuvwxyz{|}~¥"
" "
" "
"ÊÁ¢£Û´|¤¬©»ÇÂШ "
"¡± «µ¦áü ¼È À"
"Ëç廀�®‚éƒæèíêëì"
"Ü„ñîïÍ…X¯ôòó† Þ§"
"ˆ‡‰‹ŠŒ¾��Ž�‘“’”•"
"Ý–˜—™›šÖ¿�œžŸàߨ";
#endif
#endif
//#ifdef FEATURE_INTL // isspace doesn't work with non-ascii characters
// REVIEW: I think it's better just redefine this way than checking codepage.
#undef isspace
#define isspace(c) ((c==' ')||(c=='\t')||(c=='\n')||(c=='\r')||(c=='\v')|| \
(c=='\f'))
//#endif
static void HText_appendHorizEscape(HText * text, int cbElement);
HText *HText_new2(struct Mwin *tw, HTRequest *req, HTStream * output_stream, struct CharStream *pcsSource)
{
HText *text;
struct _www *w3doc;
char szAltText[32];
XX_DMsg(DBG_HTEXT, ("HText_new2 called\n"));
text = (HText *) GTR_MALLOC(sizeof(HText));
if (!text)
{
return NULL;
}
memset(text, 0, sizeof(HText));
/*
Setup working variables
*/
text->tw = tw;
text->pHtmlStream = NULL;
text->bOpen = FALSE;
text->bNewPara = TRUE;
text->bOnNewPara = TRUE;
text->bHorizEscape = FALSE;
text->bAnchor = FALSE;
text->bAnchorNoCache = FALSE;
text->bNeedListCRLF = FALSE;
text->hrefOffset = 0;
text->hrefLen = 0;
text->iStyle = 0;
text->fontBits = 0;
text->bSelect = FALSE;
text->bOption = FALSE;
text->bOptionValuePresent = FALSE;
text->bListSelect = FALSE;
text->bMultiListSelect = FALSE;
text->bNextOptionIsSelected = FALSE;
text->bDD = FALSE;
text->bTextArea = FALSE;
text->bStartingListItem = FALSE;
text->iCurrentSelect = -1;
text->iCurrentTextArea = -1;
text->bRecord = req->iFlags & HTREQ_RECORD;
text->szLocal = req->destination->szActualLocal;
text->next_list = 0;
text->baseFontSize = DEFAULT_HTML_FONT_SIZE;
text->fontSize = DEFAULT_FONT_SIZE;
text->fontFace = DEFAULT_FONT_FACE;
text->fontColor = (COLORREF)-1;
text->iElement = -1;
text->freeFormat = TRUE;
text->numEndPending = 0;
text->bDifferentFrame = FALSE;
text->bPendingTR = FALSE;
text->frameStateStackOffset = 0;
text->tableState = TS_NOT_IN_TABLE;
W3Doc_DisconnectFromWindow(text->tw->w3doc, text->tw);
w3doc = W3Doc_CreateAndInit(text->tw, req, pcsSource);
if (w3doc == NULL)
{
GTR_FREE(text);
return NULL;
}
text->w3doc = w3doc;
text->standardAltText_textOffset = text->w3doc->poolSize;
GTR_formatmsg(RES_STRING_ALTTEXT,szAltText,sizeof(szAltText));
text->standardAltText_textLen = strlen(szAltText);
HText_add_to_pool(text->w3doc, szAltText, text->standardAltText_textLen);
W3Doc_ConnectToWindow(w3doc, text->tw);
return text;
}
void HText_add_to_pool(struct _www *w3doc, const char *s, int len)
{
int i;
if (len < 0)
{
len = strlen(s);
}
if ((w3doc->poolSize + len) >= w3doc->poolSpace)
{
int newSpace;
char *newPool;
newSpace = w3doc->poolSpace + w3doc->poolSpace / 4;
if (newSpace < w3doc->poolSize + len)
{
newSpace = w3doc->poolSize + len;
}
newPool = (char *) GTR_REALLOC(w3doc->pool, newSpace);
if (!newPool)
{
/* Running low on memory - see if we can get at least enough to
add this string */
newSpace = w3doc->poolSize + len;
newPool = (char *) GTR_REALLOC(w3doc->pool, newSpace);
if (!newPool)
{
/* Not much we can do here without error propagation */
XX_Assert((0), ("Unable to grow pool - realloc failed"));
return;
}
}
memset(newPool + w3doc->poolSize, 0, newSpace - w3doc->poolSize);
w3doc->pool = newPool;
w3doc->poolSpace = newSpace;
}
for (i = 0; i < len; i++)
{
#ifdef MAC
w3doc->pool[w3doc->poolSize++] = IsoToMac[(unsigned char) s[i]];
#else
w3doc->pool[w3doc->poolSize++] = s[i];
#endif
}
}
/* Add to the pool without translating into the local character set */
void HText_add_to_pool_iso(struct _www *w3doc, const char *s, int len)
{
if (len < 0)
{
len = strlen(s);
}
if ((w3doc->poolSize + len) >= w3doc->poolSpace)
{
int newSpace;
char *newPool;
newSpace = w3doc->poolSpace * 2;
if (newSpace < w3doc->poolSize + len)
{
newSpace = w3doc->poolSize + len;
}
newPool = (char *) GTR_REALLOC(w3doc->pool, newSpace);
if (!newPool)
{
/* Running low on memory - see if we can get at least enough to
add this string */
newSpace = w3doc->poolSize + len;
newPool = (char *) GTR_REALLOC(w3doc->pool, newSpace);
if (!newPool)
{
/* Not much we can do here without error propagation */
XX_Assert((0), ("Unable to grow pool - realloc failed"));
return;
}
}
memset(newPool + w3doc->poolSize, 0, newSpace - w3doc->poolSize);
w3doc->pool = newPool;
w3doc->poolSpace = newSpace;
}
memcpy(w3doc->pool + w3doc->poolSize, s, len);
w3doc->poolSize += len;
}
void HText_add_element(HText * text, int type)
{
int len;
int i;
// Check to see if we have an implied <TD>. If we are in a table, but
// not in a cell (a.k.a. TS_IN_LIMBO) and the element that is being added isn't
// a frame element, then we must have encountered some text or HTML on the loose
// between cells.
if ( text->tableState == TS_IN_LIMBO && type != ELE_FRAME ) {
HText_beginFrame(text, ELE_FRAME_IS_CELL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
}
#if 0
if (text->bSelect)
{
XX_DMsg(DBG_HTEXT, ("HText_add_element called with type=%d in middle of SELECT!!\n", type));
return;
}
#endif
/* check for need to grow element array */
if (text->w3doc->elementCount >= text->w3doc->elementSpace)
{
int newSpace;
struct _element *newArray;
newSpace = text->w3doc->elementSpace + text->w3doc->elementSpace / 4;
newArray = (struct _element *) GTR_REALLOC(text->w3doc->aElements, newSpace * sizeof(struct _element));
if (!newArray)
{
/* See if we can at least get a bit more */
newSpace = text->w3doc->elementSpace + 20;
newArray = (struct _element *) GTR_REALLOC(text->w3doc->aElements, newSpace * sizeof(struct _element));
if (!newArray)
{
/* Not much we can do here without error propagation */
XX_Assert((0), ("Unable to grow element list - realloc failed"));
return;
}
}
XX_DMsg(DBG_HTEXT, ("HText_add_element: growing element array from %d to %d elements\n", text->w3doc->elementSpace, newSpace));
memset(newArray + text->w3doc->elementCount, 0, (newSpace - text->w3doc->elementCount) * sizeof(struct _element));
text->w3doc->aElements = newArray;
text->w3doc->elementSpace = newSpace;
}
text->bOpen = FALSE;
text->iPrevElement = text->iElement;
text->iElement = text->w3doc->elementCount++;
memset(&(text->w3doc->aElements[text->iElement]), 0, sizeof(struct _element));
if (text->w3doc->frame.elementTail != -1)
{
text->w3doc->aElements[text->w3doc->frame.elementTail].next = text->iElement;
// We only link frameNext up if a frame transition hasn't occurred
if ( !text->bDifferentFrame ) {
// If the previous element is a frame element, it's frameNext will get
// set properly only upon the end of frame
if ( text->w3doc->aElements[text->w3doc->frame.elementTail].type != ELE_FRAME )
text->w3doc->aElements[text->w3doc->frame.elementTail].frameNext = text->iElement;
}
}
// Clear frame transition flag
text->bDifferentFrame = FALSE;
// There is a stack that maintains the list of frames that ended, but needed to wait
// until the next element was added before linking the frameNext pointer. When
// multiple frames have endings pending that are resolved at once, the first (most
// recent) frame points to the newly created element. The rest point at -1, since
// the enclosing frame ended at the same time.
i = 0;
while ( text->numEndPending ) {
text->w3doc->aElements[text->pendingEnd[--text->numEndPending]].frameNext =
( i++ == 0 ) ? text->iElement : -1;
}
text->w3doc->aElements[text->iElement].next = -1;
text->w3doc->aElements[text->iElement].frameNext = -1;
text->w3doc->aElements[text->iElement].frameIndex = -1;
// If there is an enclosing frame that isn't top level, the element will
// have the index of the enclosing parent frame.
if ( text->frameStateStackOffset > 0 )
text->w3doc->aElements[text->iElement].frameIndex =
text->frameStateStack[text->frameStateStackOffset-1].elementIndex;
text->w3doc->frame.elementTail = text->iElement;
text->w3doc->aElements[text->iElement].type = type;
text->w3doc->aElements[text->iElement].pMarquee = NULL;
text->w3doc->aElements[text->iElement].pmo = NULL;
text->w3doc->aElements[text->iElement].pblob = NULL;
text->w3doc->aElements[text->iElement].pFrame = NULL;
XX_DMsg(DBG_HTEXT, ("HText_add_element created element %d with type=%d\n", text->iElement, type));
if (text->bAnchor)
{
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_ANCHOR;
if ( text->bAnchorNoCache )
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_ANCHORNOCACHE;
text->w3doc->aElements[text->iElement].hrefOffset = text->hrefOffset;
text->w3doc->aElements[text->iElement].hrefLen = (unsigned short) text->hrefLen;
text->w3doc->aElements[text->iElement].hrefContentLen = text->hrefContentLen;
if (TW_WasVisited(text->w3doc, &(text->w3doc->aElements[text->iElement])))
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_VISITED;
}
if (text->name[0])
{
switch (type)
{
case ELE_TEXT:
case ELE_IMAGE:
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_NAME;
len = strlen(text->name);
text->w3doc->aElements[text->iElement].nameOffset = text->w3doc->poolSize;
HText_add_to_pool_iso(text->w3doc, text->name, len);
text->w3doc->aElements[text->iElement].nameLen = len;
text->name[0] = '\0'; /* We only need one element with the name */
break;
default:
break;
}
}
if (type == ELE_TEXT)
{
text->w3doc->aElements[text->iElement].textOffset = text->w3doc->poolSize;
text->w3doc->aElements[text->iElement].textLen = 0;
#ifdef FEATURE_INTL
if (IsFECodePage(GETMIMECP(text->w3doc)) && (text->tableState == TS_IN_CELL))
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_CELLTEXT;
#endif
}
text->w3doc->aElements[text->iElement].iStyle = text->iStyle;
text->w3doc->aElements[text->iElement].fontBits = text->fontBits;
text->w3doc->aElements[text->iElement].baseline = -1;
if ( text->bCenter )
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_CENTER;
if ( text->bNoBreak || !text->freeFormat )
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_NOBREAK;
text->w3doc->aElements[text->iElement].fontSize = text->fontSize;
text->w3doc->aElements[text->iElement].fontFace = text->fontFace;
text->w3doc->aElements[text->iElement].fontColor = text->fontColor;
}
void HText_appendCRLF(HText * text)
{
XX_DMsg(DBG_HTEXT, ("appendCRLF\n"));
HText_add_element(text, ELE_NEWLINE);
text->bOnNewPara = text->bNewPara = TRUE;
}
void HText_appendCharacter(HText * text, char ch)
{
static char prev;
ELEMENT *pel;
#ifdef FEATURE_INTL
static BOOL fDBCS;
#endif
#define BIGGUS_LINEUS 256
XX_DMsg(DBG_HTEXT, ("HText_appendCharacter: %c(%d)\n", ch, ch));
if (text->bOption)
{
if (text->lenOption < MAX_NAME_STRING)
{
switch (ch)
{
case '\r':
case '\n':
break;
case ' ':
if (text->lenOption)
{
text->szOption[text->lenOption++] = ch;
text->szOption[text->lenOption] = 0;
}
break;
default:
text->szOption[text->lenOption++] = ch;
text->szOption[text->lenOption] = 0;
break;
}
}
return;
}
if (text->bTextArea)
{
switch (ch)
{
case '\r':
case '\n':
CS_AddChar(text->cs, '\r');
#ifndef MAC
CS_AddChar(text->cs, '\n');
#endif
break;
default:
CS_AddChar(text->cs, ch);
break;
}
return;
}
if (!text->freeFormat || !text->w3doc->pStyles->sty[text->iStyle]->freeFormat)
{ /* freeFormat means eat extra whitespace */
/* preformatted */
HText_addListCRLF(text);
text->bOnNewPara = text->bNewPara = FALSE;
switch (ch)
{
case '\t':
HText_add_element(text, ELE_TAB);
break;
case 12: /* CTRL-L */
case '\r':
case '\n':
if (!(prev == '\r' && ch == '\n'))
{
HText_appendCRLF(text);
}
prev = ch;
break;
default:
if (!text->bOpen)
{
HText_add_element(text, ELE_TEXT);
text->bOpen = TRUE;
}
HText_add_to_pool(text->w3doc, &ch, 1);
text->w3doc->aElements[text->iElement].textLen++;
// break up really long text elements so that we get progressive
// draw on download
#ifdef FEATURE_INTL
// If last character is DBCS 1st byte, We should wait 2nd byte.
if (!IsFECodePage(GETMIMECP(text->w3doc))
|| (fDBCS || !IsDBCSLeadByteEx(GETMIMECP(text->w3doc),ch)))
if (text->w3doc->aElements[text->iElement].textLen > BIGGUS_LINEUS)
#else
if (text->w3doc->aElements[text->iElement].textLen > BIGGUS_LINEUS)
#endif
text->bOpen = FALSE;
prev = ch;
#ifdef FEATURE_INTL
if (IsFECodePage(GETMIMECP(text->w3doc)))
fDBCS = (fDBCS) ? FALSE : IsDBCSLeadByteEx(GETMIMECP(text->w3doc),ch);
#endif
break;
}
}
else
{
/* isspace doesn't work with non-ascii characters */
if (ch > 0 && isspace(ch))
{
ch = ' ';
}
switch (ch)
{
case ' ':
if (text->bNewPara || text->bHorizEscape || text->bNeedListCRLF)
{
break;
}
if (prev == ' ')
{
if (!text->bOpen)
{
if (text->iElement >= 0)
{
pel = &text->w3doc->aElements[text->iElement];
if (pel->type == ELE_TEXT &&
pel->textLen > 0 &&
text->w3doc->pool[pel->textOffset + pel->textLen - 1] == ' ')
break;
}
HText_add_element(text, ELE_TEXT);
text->bOpen = TRUE;
HText_add_to_pool(text->w3doc, &ch, 1);
text->w3doc->aElements[text->iElement].textLen++;
}
}
else
{
if (!text->bOpen)
{
if (text->iElement >= 0)
{
pel = &text->w3doc->aElements[text->iElement];
if (pel->type == ELE_TEXT && (pel->lFlags & ELEFLAG_WBR))
{
HText_add_to_pool(text->w3doc, &ch, 1);
text->w3doc->aElements[text->iElement].textLen++;
break;
}
}
HText_add_element(text, ELE_TEXT);
text->bOpen = TRUE;
}
HText_add_to_pool(text->w3doc, &ch, 1);
text->w3doc->aElements[text->iElement].textLen++;
if (text->w3doc->aElements[text->iElement].textLen > BIGGUS_LINEUS)
text->bOpen = FALSE;
}
break;
default:
HText_addListCRLF(text);
if (!text->bOpen)
{
HText_add_element(text, ELE_TEXT);
text->bOpen = TRUE;
}
text->bOnNewPara = text->bNewPara = FALSE;
text->bHorizEscape = FALSE;
text->bStartingListItem = FALSE;
HText_add_to_pool(text->w3doc, &ch, 1);
text->w3doc->aElements[text->iElement].textLen++;
#ifdef FEATURE_INTL // If last character is DBCS 1st byte, We should wait 2nd byte.
if (!IsFECodePage(GETMIMECP(text->w3doc)) || (fDBCS || !IsDBCSLeadByteEx(GETMIMECP(text->w3doc),ch)))
if (text->w3doc->aElements[text->iElement].textLen > BIGGUS_LINEUS)
#else
if (text->w3doc->aElements[text->iElement].textLen > BIGGUS_LINEUS)
#endif
text->bOpen = FALSE;
break;
}
prev = ch;
#ifdef FEATURE_INTL
if (IsFECodePage(GETMIMECP(text->w3doc)))
fDBCS = (fDBCS) ? FALSE : IsDBCSLeadByteEx(GETMIMECP(text->w3doc),ch);
#endif
}
}
//
// Convert an ASCII hex digit character to binary
//
// On entry:
// ch: ASCII character '0'..'9','A'..'F', or 'a'..'f'
//
// Returns:
// binary equivalent of character interpretted at hex digit
//
// Note: returns 0 if given character isn't a hex digit
//
static BYTE GetHexDigit( char ch )
{
if ( ch >= '0' && ch <= '9' ) {
return (BYTE) ch - '0';
} else {
ch = toupper(ch);
if ( ch >= 'A' && ch <= 'F' )
return (BYTE) ch - 'A' + 10;
}
return 0;
}
//
// Convert an RGB hex string into a COLORREF
//
// On entry:
// hexRGB: string containing hex digits
// pRGB: pointer to COLORREF for result
//
// On exit:
// *pRGB: contains COLORREF equivalent of given hex string
//
// Note: The leniency of this routine is designed to emulate a popular Web browser
// Fundamentally, the idea is to divide the given hex string into thirds, with
// each third being one of the colors (R,G,B). If the string length isn't a multiple
// three, it is (logically) extended with 0's. Any character that isn't a hex digit
// is treated as if it were a 0. When each individual color spec is greater than
// 8 bits, the largest supplied color is used to determine how the given color
// values should be interpretted (either as is, or scaled down to 8 bits).
//
static BOOL GetHexRGBValue( const char *hexRGB, COLORREF *pRGB )
{
unsigned int rgb_vals[3];
int vlen = (lstrlen(hexRGB) + 2) / 3;
int i, j;
const char *p = hexRGB;
unsigned int max_seen = 0;
for ( i = 0; i < 3; i++ ) {
rgb_vals[i] = 0;
for ( j = 0; j < vlen; j++ ) {
rgb_vals[i] = rgb_vals[i] * 16 + GetHexDigit(*p);
if ( rgb_vals[i] > max_seen )
max_seen = rgb_vals[i];
if ( *p )
p++;
}
}
// If any individual color component uses more than 8 bits, scale all color values
// down.
while ( max_seen > 255 ) {
max_seen /= 16;
for ( i = 0; i < 3; i++ )
rgb_vals[i] /= 16;
}
*pRGB = RGB(rgb_vals[0], rgb_vals[1], rgb_vals[2]);
return TRUE;
}
#ifdef WANTED_DECENT_RGB_PARSING
//
// Convert a pair of hex digits to binary
//
// On entry:
// pValue: pointer to byte where result will be placed
// pHex: pointer to string that starts with two hex characters
//
// On exit:
// pValue: if success, has binary value equivalent two digit hex
//
// Returns:
// TRUE -> first two bytes of given string were hex digits
// FALSE -> non-hex digit encountered
//
static BOOL GetHexByte( BYTE *pValue, const char *pHex )
{
if ( isxdigit(*pHex) && isxdigit(*(pHex+1)) ) {
*pValue = GetHexDigit( *pHex ) * 16 + GetHexDigit( *(pHex+1) );
return TRUE;
}
return FALSE;
}
//
// Convert a six digit hex string to a COLORREF
//
// On entry:
// hexRGB: pointer to string containing six digit hex color value
// pRGB: pointer to location to place resulting COLORREF
//
// On exit:
// *pRGB: set to color equivalent for given hex string
//
// Returns:
// TRUE -> input string consisted of six hex digits
// FALSE -> non-hex digit encounted in input string
//
// Note: If input string begins with a '#' character, it will be ignored.
// Hence, legal input strings: "#hhhhhh" or "hhhhhh", where 'h' is a hex digit
//
static BOOL GetHexRGBValue( const char *hexRGB, COLORREF *pRGB )
{
BYTE red, green, blue;
if ( !hexRGB || strlen( hexRGB ) < 6 )
return FALSE;
if ( *hexRGB == '#' ) // move past optional #
hexRGB++;
if ( !GetHexByte( &red, hexRGB ) ||
!GetHexByte( &green, hexRGB+2 ) ||
!GetHexByte( &blue, hexRGB+4 )
)
return FALSE;
*pRGB = RGB(red, green, blue);
return TRUE;
}
#endif // WANTED_DECENT_RGB_PARSING
typedef struct color_name_value_rec {
const char *name;
COLORREF rgb;
} COLOR_NAME_VALUE_REC;
const char szCNV_red[] = "red";
const char szCNV_green[] = "lime";
const char szCNV_blue[] = "blue";
const char szCNV_yellow[] = "yellow";
const char szCNV_purple[] = "fuchsia";
const char szCNV_cyan[] = "aqua";
const char szCNV_darkred[] = "maroon";
const char szCNV_darkgreen[] = "green";
const char szCNV_darkblue[] = "navy";
const char szCNV_darkyellow[] = "olive";
const char szCNV_darkpurple[] = "purple";
const char szCNV_darkcyan[] = "teal";
const char szCNV_black[] = "black";
const char szCNV_darkgray[] = "gray";
const char szCNV_gray[] = "silver";
const char szCNV_white[] = "white";
static COLOR_NAME_VALUE_REC colorNameTable[] =
{
{ szCNV_red, RGB(255, 0, 0) },
{ szCNV_green, RGB( 0,255, 0) },
{ szCNV_blue, RGB( 0, 0,255) },
{ szCNV_yellow, RGB(255,255, 0) },
{ szCNV_purple, RGB(255, 0,255) },
{ szCNV_cyan, RGB( 0,255,255) },
{ szCNV_darkred, RGB(128, 0, 0) },
{ szCNV_darkgreen, RGB( 0,128, 0) },
{ szCNV_darkblue, RGB( 0, 0,128) },
{ szCNV_darkyellow, RGB(128,128, 0) },
{ szCNV_darkpurple, RGB(128, 0,128) },
{ szCNV_darkcyan, RGB( 0,128,128) },
{ szCNV_black, RGB( 0, 0, 0) },
{ szCNV_darkgray, RGB(128,128,128) },
{ szCNV_gray, RGB(192,192,192) },
{ szCNV_white, RGB(255,255,255) },
};
//
// Convert a text color spec string to a COLORREF
//
// On entry:
// textRGB: pointer to string containing a color name or six digit hex color value
// pRGB: pointer to location to place resulting COLORREF
//
// On exit:
// *pRGB: set to color equivalent for given hex string
//
// Returns:
// TRUE -> input string contained a valid color spec
// FALSE -> input string didn't contain a valid color spec
//
static BOOL GetRGBValue( const char *textRGB, COLORREF *pRGB )
{
int i;
if ( *textRGB == '#' ) // move past optional #
textRGB++;
//
// First see if it's a color name
//
for ( i = 0; i < ARRAY_ELEMENTS(colorNameTable); i++ ) {
if ( _stricmp( textRGB, colorNameTable[i].name ) == 0 ) {
*pRGB = colorNameTable[i].rgb;
return TRUE;
}
}
//
// Now see if it's a hex value
//
if ( GetHexRGBValue( textRGB, pRGB ) )
return TRUE;
return FALSE;
}
//
// Generate elements for a <BODY> tag
//
// On entry:
// text: (me)
// alink: ALINK attribute string (hex RGB color spec: "#hhhhhh" or "hhhhhh")
// background: BACKGROUND attribute string (<IMG SRC=x> style image reference)
// bgcolor: BGCOLOR attribute string (hex RGB color spec: "#hhhhhh" or "hhhhhh")
// link: LINK attribute string (hex RGB color spec: "#hhhhhh" or "hhhhhh")
// text_tag: TEXT attribute string (hex RGB color spec: "#hhhhhh" or "hhhhhh")
// vlink: VLINK attribute string (hex RGB color spec: "#hhhhhh" or "hhhhhh")
//
void HText_beginBody(HText * text, const char *alink, const char *background,
const char *bgcolor,
const char *bgproperties,
const char *leftMargin,
const char *link,
const char *text_tag,
const char *topMargin,
const char *vlink )
{
DOC_COLOR_INFO *pDCI = &text->w3doc->docColorInfo;
if ( leftMargin ) {
text->w3doc->flags |= W3DOC_FLAG_OVERRIDE_LEFT_MARGIN;
text->w3doc->left_margin = atoi(leftMargin);
if ( text->w3doc->left_margin < 0 || text->w3doc->left_margin > 255 )
text->w3doc->left_margin = 0;
}
if ( topMargin ) {
text->w3doc->flags |= W3DOC_FLAG_OVERRIDE_TOP_MARGIN;
text->w3doc->top_margin = atoi(topMargin);
if ( text->w3doc->top_margin < 0 || text->w3doc->top_margin > 255 )
text->w3doc->top_margin = 0;
}
if ( bgproperties && !GTR_strcmpi( bgproperties, "FIXED" ) ) {
if ( background ) // No point in fixed background if there's no background image
text->w3doc->bFixedBackground = TRUE;
}
if ( background && (text->w3doc->nBackgroundImageElement == -1) ) {
HText_appendInlineImage( text, background, NULL, NULL, NULL, FALSE,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, FALSE, NULL
#ifdef FEATURE_VRML
,NULL
#endif
);
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_BACKGROUND_IMAGE;
text->w3doc->nBackgroundImageElement = text->iElement;
}
if ( alink && GetRGBValue( alink, &pDCI->rgbActiveLinkColor ) )
pDCI->flags |= COLOR_INFO_FLAG_ALINK;
if ( vlink && GetRGBValue( vlink, &pDCI->rgbVisitedLinkColor ) )
pDCI->flags |= COLOR_INFO_FLAG_VLINK;
if ( bgcolor && GetRGBValue( bgcolor, &pDCI->rgbBackgroundColor ) )
pDCI->flags |= COLOR_INFO_FLAG_BACKGROUND;
if ( text_tag && GetRGBValue( text_tag, &pDCI->rgbTextColor ) )
pDCI->flags |= COLOR_INFO_FLAG_TEXT;
if ( link && GetRGBValue( link, &pDCI->rgbLinkColor ) )
pDCI->flags |= COLOR_INFO_FLAG_LINK;
}
void HText_beginAnchor(HText * text, char *href, char *name, char *size, BOOL nocache)
{
/*
TODO this can overflow the buffer (FAQ list on commercenet)
*/
if (href)
{
text->hrefLen = strlen(href);
text->hrefOffset = text->w3doc->poolSize;
HText_add_to_pool_iso(text->w3doc, href, text->hrefLen);
text->bAnchor = TRUE;
text->bAnchorNoCache = nocache;
text->bOpen = FALSE;
}
if (name)
{
strncpy(text->name, name, MAX_NAME_STRING);
text->name[MAX_NAME_STRING] = '\0';
text->bOpen = FALSE;
}
if (size)
text->hrefContentLen = atoi(size) * 1024;
else
text->hrefContentLen = 0;
}
void HText_endAnchor(HText * text)
{
if (text->bAnchor)
{
XX_DMsg(DBG_HTEXT, ("endAnchor\n"));
text->bAnchor = FALSE;
text->bAnchorNoCache = FALSE;
text->hrefOffset = 0;
text->hrefLen = 0;
text->bOpen = FALSE;
}
}
#ifdef FEATURE_OCX
void HText_beginEmbed(const char * text)
{
}
#endif
void HText_appendText(HText * text, CONST char *str)
{
char *p;
text->bOpen = FALSE;
p = (char *) str;
while (p && *p)
{
HText_appendCharacter(text, *p);
p++;
}
}
/* Common Parsing routines */
static STI rgAlignment[] = {
{"top", ALIGN_TOP},
{"left", ALIGN_LEFT},
{"right", ALIGN_RIGHT},
{"middle", ALIGN_MIDDLE},
{"center", ALIGN_MIDDLE},
{"absmiddle", ALIGN_MIDDLE},
{"absbottom", ALIGN_BOTTOM},
{"baseline", ALIGN_BASELINE},
{"texttop", ALIGN_TOP},
{"bottom", ALIGN_BOTTOM},
{"float-left",ALIGN_LEFT},
{"float-right",ALIGN_RIGHT}
};
static STI rgMciControls[] = {
{"on", MCI_OBJECT_FLAGS_SHOWCONTROLS},
{"show", MCI_OBJECT_FLAGS_SHOWCONTROLS},
{"yes", MCI_OBJECT_FLAGS_SHOWCONTROLS}
};
#define nMciControls (sizeof(rgMciControls)/sizeof(STI))
//
// Determine the index number of an string table value
//
// On entry:
// szText: attribute value to be looked up
// rgSti: array of valid values
// nElements: number of elements in the rgSti array
// nDefault: default index to return in given value (szText) is NULL or not valid
//
// On exit:
// Returns: index in rgSti array of the given value, or nDefault.
//
int StringTableToIndex(const char *szText, STI rgSti[], int nElements, int nDefault)
{
int z;
if ( szText ) {
for ( z = 0; z < nElements; ++z ) {
if ( !GTR_strcmpi(szText, rgSti[z].szText) )
return rgSti[z].nText;
}
}
return nDefault;
}
//
// Find the string given the index number of an string table value
//
// On entry:
// nText: index value to be looked up
// rgSti: array of valid values
// nElements: number of elements in the rgSti array
// nDefault: default string to return in given index isn't in table
//
// On exit:
// Returns: index in rgSti array of the given value, or nDefault.
//
char* IndexToString(int nText, STI rgSti[], int nElements, char *szDefault)
{
int z;
for ( z = 0; z < nElements; ++z ) {
if ( nText == rgSti[z].nText )
return rgSti[z].szText;
}
return szDefault;
}
#define LOOP_FOREVER ((DWORD)-1)
static STI rgLoopNames[] = {
{"forever", LOOP_FOREVER},
{"infinite", LOOP_FOREVER}
};
#define nLoopNames (sizeof(rgLoopNames)/sizeof(STI))
static DWORD GetLoopCount(const char *szText){
DWORD dwVal;
if (szText){
dwVal = StringTableToIndex(szText, rgLoopNames, nLoopNames, 0);
if (!dwVal) dwVal = atoi(szText);
}
else dwVal = 1;
return dwVal;
}
#ifdef FEATURE_CLIENT_IMAGEMAP
void HText_appendInlineImage(HText * text, const char *src, const char *width,
const char *height, const char *align, BOOL isMap, const char *useMap, const char *alt,
const char *border, const char *hspace, const char *vspace,
const char *mci, const char *loop, const char *start, const char *controls
#ifdef FEATURE_VRML
,const char *vrml
#endif
)
#else
void HText_appendInlineImage(HText * text, const char *src, const char *width,
const char *height, const char *align, BOOL isMap, const char *alt,
const char *border, const char *hspace, const char *vspace.
const char *mci, const char *loop, const char *start, const char *controls
#ifdef FEATURE_VRML
,const char *vrml
#endif
)
#endif
{
int len;
int nWidth, nHeight;
int the_border = 0;
int in_border;
char szSrc[32];
int iSpace;
#ifdef FEATURE_VRML
if (src||mci||vrml) {
#else
if (src||mci){
#endif
HText_add_element(text, ELE_IMAGE);
if (alt && alt[0])
{
text->w3doc->aElements[text->iElement].textOffset = text->w3doc->poolSize;
len = strlen(alt);
HText_add_to_pool(text->w3doc, alt, len);
text->w3doc->aElements[text->iElement].textLen = len;
}
else
{
text->w3doc->aElements[text->iElement].textOffset = text->standardAltText_textOffset;
text->w3doc->aElements[text->iElement].textLen = text->standardAltText_textLen;
}
if (isMap)
{
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_IMAGEMAP;
}
XX_DMsg(DBG_HTEXT, ("appendInlineImage: src=%s\n", src));
text->w3doc->aElements[text->iElement].alignment =
StringTableToIndex( align, rgAlignment, ARRAY_ELEMENTS(rgAlignment), ALIGN_BASELINE);
if ( text->w3doc->aElements[text->iElement].alignment != ALIGN_LEFT &&
text->w3doc->aElements[text->iElement].alignment != ALIGN_RIGHT )
text->bOnNewPara = FALSE;
#ifdef FEATURE_CLIENT_IMAGEMAP
if (useMap && *useMap)
{
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_USEMAP;
text->w3doc->aElements[text->iElement].myMap = Map_CreatePlaceholder(useMap);
text->w3doc->aElements[text->iElement].myMap->refCount++;
}
#endif
if (width && height)
{
nWidth = atoi(width);
nHeight = atoi(height);
// check if height and width are given in percent
if ( strchr( width, '%' ) )
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_PERCENT_WIDTH;
if ( strchr( height, '%' ) )
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_PERCENT_HEIGHT;
text->w3doc->aElements[text->iElement].displayWidth = nWidth;
text->w3doc->aElements[text->iElement].displayHeight = nHeight;
}
else
{
nWidth = 0;
nHeight = 0;
}
iSpace = hspace ? atoi(hspace) : text->w3doc->pStyles->space_after_image;
if ( iSpace < 0 || iSpace > 1024 ) // no particular reason for 1024, just seems big enough
iSpace = 0;
text->w3doc->aElements[text->iElement].hspace = iSpace;
iSpace = vspace ? atoi(vspace) : 0;
if ( iSpace < 0 || iSpace > 1024 ) // no particular reason for 1024, just seems big enough
iSpace = 0;
text->w3doc->aElements[text->iElement].vspace = iSpace;
in_border = border ? atoi(border) : 0;
if (text->w3doc->aElements[text->iElement].lFlags & ELEFLAG_ANCHOR) {
the_border = text->w3doc->pStyles->image_anchor_frame;
}
text->w3doc->aElements[text->iElement].border =
border ? in_border : the_border;
/*Create placeholder*/
text->w3doc->aElements[text->iElement].myImage = Image_CreatePlaceholder((char *) src, nWidth, nHeight);
// If the width and height are given, and aren't given as a percentage of
// client size, set the IMG_WHKNOWN flag in the image stucture
if ( !(text->w3doc->aElements[text->iElement].lFlags &
(ELEFLAG_PERCENT_WIDTH | ELEFLAG_PERCENT_HEIGHT)) )
{
if ( text->w3doc->aElements[text->iElement].myImage &&
text->w3doc->aElements[text->iElement].displayWidth &&
text->w3doc->aElements[text->iElement].displayHeight )
{
text->w3doc->aElements[text->iElement].myImage->flags |= IMG_WHKNOWN;
}
}
if (src){
#ifdef FEATURE_IMG_THREADS
Image_AddRef(text->w3doc->aElements[text->iElement].myImage);
#else
text->w3doc->aElements[text->iElement].myImage->refCount++;
#endif
}
if (mci){
ELEMENT *pElement;
DWORD dwFlags;
BOOL fDeleteBlob;
/*make our flags*/
dwFlags = 0;
if (controls && GTR_strcmpi( controls, "OFF" ) )
dwFlags |= MCI_OBJECT_FLAGS_SHOWCONTROLS;
if ( start ) {
char *szTemp;
szTemp = GTR_strdup(start);
if ( !szTemp )
return;
CharUpper(szTemp);
if ( strstr(szTemp, "MOUSEOVER" ) )
dwFlags |= MCI_OBJECT_FLAGS_PLAY_ON_MOUSE;
GTR_FREE(szTemp);
}
/*now we're an MciObject. look at that magic*/
pElement = &text->w3doc->aElements[text->iElement];
pElement->pblob = BlobConstruct();
if (pElement->pblob){
fDeleteBlob = FALSE;
if (BlobStoreUrl(pElement->pblob, (char*) mci)){
pElement->pmo = MciConstruct();
if (pElement->pmo){
/*construct the window and go on*/
if (!MciInit(pElement->pmo, text->tw, dwFlags, GetLoopCount(loop), text->iElement)) {
MciDestruct(pElement->pmo);
pElement->pmo = NULL;
fDeleteBlob = TRUE;
}
}
}
if (fDeleteBlob){
BlobDestruct(pElement->pblob);
pElement->pblob = NULL;
}
}
}
#ifdef FEATURE_VRML
// Construct our VRML viewer window.
// NOTE: We don't allow both MCI and VRML attributes to be active. Since
// MCI was there first, we defer to it and don't create a VRML window if
// it's present.
//
else if (vrml) {
ELEMENT *pElement;
BOOL fDeleteBlob;
fDeleteBlob = FALSE;
pElement = &text->w3doc->aElements[text->iElement];
pElement->pblob = BlobConstruct();
if (pElement->pblob){
if (BlobStoreUrl(pElement->pblob, (char*) vrml)){
VRMLConstruct(text->iElement, pElement,text->tw,(char *) vrml);
// Create hidden elements that we can use later to manage the downloading
// of VRMLInline files. These are extra geometry, textures and other stuff
// that are included by reference in a VRML .wrl file.
//
{
ELEMENT *pelHidden;
HText_add_element(text, ELE_IMAGE);
pelHidden = &text->w3doc->aElements[text->iElement];
pelHidden->myImage = Image_CreatePlaceholder((char *) src, nWidth, nHeight);
pelHidden->lFlags |= ELEFLAG_HIDDEN;
VRMLConstruct(text->iElement, pelHidden,text->tw,"");
pelHidden->pVrml->dwFlags = VRMLF_INLINE;
pElement->pVrml->nHiddenIndex = text->iElement;
}
}
if (fDeleteBlob){
BlobDestruct(pElement->pblob);
pElement->pblob = NULL;
}
}
}
#endif
}
else
{
GTR_formatmsg(RES_STRING_BADIMAGE,szSrc,sizeof(szSrc));
HText_appendText(text, szSrc);
}
}
//
// HText_beginInlineMarquee - Grabs parse information and attempts
// to fill in the information into Marquee structure, using defaults
// or mins where appropriate
//
// On entry:
// text: pointer an Htext
// ( other params .. )
//
// On exit:
// ( a marquee struc that is filled in )
//
void HText_beginInlineMarquee(HText * text, const char *width,
const char *height, const char *align, const char *pixs,
const char *timedelay, const char *dir,
const char *border, const char *hspace, const char *vspace,
const char *background, const char *bgcolor,
const char *behavior, const char *loop
)
{
int nWidth, nHeight;
int the_border = 0;
int in_border;
HText_add_element(text, ELE_MARQUEE);
text->bOnNewPara = FALSE;
text->w3doc->aElements[text->iElement].textOffset = text->standardAltText_textOffset;
text->w3doc->aElements[text->iElement].textLen = text->standardAltText_textLen;
text->w3doc->aElements[text->iElement].pMarquee = MARQUEE_Alloc();
if ( ! bgcolor || ! GetRGBValue( bgcolor,
&text->w3doc->aElements[text->iElement].pMarquee->dwBColor) )
{
/* grab our color now while we have the info around .. */
text->w3doc->aElements[text->iElement].pMarquee->dwBColor =
text->w3doc->docColorInfo.rgbBackgroundColor;
}
XX_DMsg(DBG_HTEXT, ("appendInlineMarquee: src=?\n"));
text->w3doc->aElements[text->iElement].alignment =
StringTableToIndex( align, rgAlignment, ARRAY_ELEMENTS(rgAlignment), ALIGN_BASELINE);
// default case(s)....
text->w3doc->aElements[text->iElement].pMarquee->wTime = DEF_TIMEDELAY;
text->w3doc->aElements[text->iElement].pMarquee->wPixs = DEF_PIXELS_P_SEC;
text->w3doc->aElements[text->iElement].pMarquee->wDirection = DIR_FROMRIGHT;
text->w3doc->aElements[text->iElement].pMarquee->wLoop = -1;
if ( timedelay )
{
text->w3doc->aElements[text->iElement].pMarquee->wTime = atoi(timedelay);
// we make it sure its not too small a delay, because some
// machines cannot keep up with small delays, they end up going
// the same speed as the slower ones
if ( text->w3doc->aElements[text->iElement].pMarquee->wTime < MIN_TIMEDELAY )
text->w3doc->aElements[text->iElement].pMarquee->wTime = MIN_TIMEDELAY;
}
if ( pixs )
{
text->w3doc->aElements[text->iElement].pMarquee->wPixs = atoi(pixs);
// Mark our Pixs as using percent
if ( strchr( pixs, '%' ) )
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_MARQUEE_PERCENT;
}
// check the direction of marquee
if ( dir )
{
if ( !GTR_strcmpi( dir, "LEFT" ) )
text->w3doc->aElements[text->iElement].pMarquee->wDirection = DIR_FROMRIGHT;
else if ( !GTR_strcmpi( dir, "RIGHT" ) )
text->w3doc->aElements[text->iElement].pMarquee->wDirection = DIR_FROMLEFT;
//else if ( !GTR_strcmpi( dir, "BOUNCE" ) )
// text->w3doc->aElements[text->iElement].pMarquee->wDirection = DIR_BOUNCE;
}
if ( behavior )
{
// we assumed scroll by default, if its not a scroll
// then lets figure out what it is..
if ( GTR_strcmpi( behavior, "SCROLL" ) )
{
// if its an alternate just let the code bounce it back and forth
if ( !GTR_strcmpi( behavior, "ALTERNATE" ) )
text->w3doc->aElements[text->iElement].pMarquee->wDirection = DIR_BOUNCE;
// if its a slide we need to figure out how to slide it, left or right
else if ( !GTR_strcmpi( behavior, "SLIDE" ) )
{
if ( text->w3doc->aElements[text->iElement].pMarquee->wDirection == DIR_FROMLEFT )
text->w3doc->aElements[text->iElement].pMarquee->wDirection = DIR_SLIDE_FROMLEFT;
else
text->w3doc->aElements[text->iElement].pMarquee->wDirection = DIR_SLIDE_FROMRIGHT;
// default to 1 loop for this operation
text->w3doc->aElements[text->iElement].pMarquee->wLoop = 1;
}
}
}
if ( loop )
{
// if its not infinite lets try and convert it to an integer
if ( GTR_strcmpi( loop, "INFINITE" ) )
{
text->w3doc->aElements[text->iElement].pMarquee->wLoop = atoi(loop);
// for now we peg it at infinite, if its 0 or less
if ( text->w3doc->aElements[text->iElement].pMarquee->wLoop <= 0 )
text->w3doc->aElements[text->iElement].pMarquee->wLoop = -1;
}
}
// If we don't know our width and height, we'll calc it
// later when we do know it ie in the reformater
nWidth = 0;
nHeight = 0;
// do we have a specified width and height?
// is the height or width given in percentages?
if (width)
{
nWidth = atoi(width);
if ( strchr( width, '%' ) )
{
if ( nWidth > 100 )
nWidth = 100;
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_PERCENT_WIDTH;
}
}
if (height)
{
nHeight = atoi(height);
if ( strchr( height, '%' ) )
{
if ( nHeight > 100 )
nHeight = 100;
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_PERCENT_HEIGHT;
}
}
text->w3doc->aElements[text->iElement].displayWidth = nWidth;
text->w3doc->aElements[text->iElement].displayHeight = nHeight;
text->w3doc->aElements[text->iElement].myImage = NULL;
// is there specified horizontal or vertical space around element?
text->w3doc->aElements[text->iElement].hspace =
hspace ? atoi(hspace) : text->w3doc->pStyles->space_after_image;
text->w3doc->aElements[text->iElement].vspace =
vspace ? atoi(vspace) : 0;
in_border = border ? atoi(border) : 0;
// if there's an A HREF then set the border to image anchor frame?
if (text->w3doc->aElements[text->iElement].lFlags & ELEFLAG_ANCHOR) {
the_border = text->w3doc->pStyles->image_anchor_frame;
}
// if there is a border given in HTML then use that,
// otherwise default to the normal border
// note: normal border = 0 if there is not A HREF around us
text->w3doc->aElements[text->iElement].border =
border ? in_border : the_border;
}
// ParseMeta - parses a <META> tag, this is designed,
// so it can parse HTTP text as well tag format for client pull
//
// **ppNewMeta - pointer to a pointer, store the location of where the new
// Meta tag will be stored, it may be stored in a HTTP-request struct
// or strait into a W3doc
// http_equiv - our HTTP-EQUIV=" " data .. NULL if we're being called without
// content - our CONTENT=" ... blah.. " string NULL if there ain't any
// bIsHTTPMetaTag - TRUE if we've called unusually while parsing a header
// otherwise its assumed we're being called while parsing SGML
void ParseMeta( METAINFO **ppNewMeta,
const char *http_equiv, const char *content, BOOL bIsHTTPMetaTag,
const char *base_url )
{
int i;
if (http_equiv)
{
// make sure this is refresh, otherwise
// bail since we don't support anything else
if ( GTR_strcmpi(http_equiv, "Refresh") != 0 )
return;
} else if ( ! bIsHTTPMetaTag )
{
// I belive we need this since we assume HTTP-EQUIV=Refresh
// and there may be other tags that we do not handle
// so lets return, and ignore it
return;
}
*ppNewMeta = GTR_MALLOC(sizeof(METAINFO));
if ( *ppNewMeta == NULL )
return;
(*ppNewMeta)->iDelay = 0; // default
(*ppNewMeta)->szURL = NULL; // default
(*ppNewMeta)->uiTimer = 0; // don't start the timer until after the page is Finish
(*ppNewMeta)->bInherit = FALSE; // default
if ( content )
{
BOOL bFoundEqual = FALSE;
BOOL bFoundURL = FALSE;
BOOL bFoundDigit = FALSE;
// parse the content string
i = 0;
while ( content[i] != '\0' )
{
if ( !bFoundEqual && !bFoundURL && !bFoundDigit && isdigit(content[i]))
{
// now we found an integer lets convert it
(*ppNewMeta)->iDelay = atoi(&content[i]);
bFoundDigit = TRUE;
}
// we found a equal that means the next alpha character
// is a part of the URL, so suck it down, and return
if ( bFoundEqual && !isspace(content[i]))
{
(*ppNewMeta)->szURL = GTR_MALLOC(lstrlen(&content[i])+1);
if ( (*ppNewMeta)->szURL == NULL )
return ;
strcpy( (*ppNewMeta)->szURL, &content[i] );
break;
}
// we found the three chars "URL" now can we find '='?
if ( bFoundURL && content[i] == '=' )
{
bFoundEqual = TRUE;
}
// if we found a Digit there should be a URL
// around here..
if ( bFoundDigit && _strnicmp( "URL", &content[i], 3 ) == 0 )
{
i += 2;
// we're now HERE in the string UR^L=http://
bFoundURL = TRUE;
}
i++;
} // while
} // if
// Get code from Image Tags to handle relative links...
if ( base_url && (*ppNewMeta)->szURL)
{
char *result = NULL;
result = HTParse((*ppNewMeta)->szURL,
base_url,
PARSE_PUNCTUATION | PARSE_ACCESS | PARSE_HOST | PARSE_PATH | PARSE_ANCHOR);
if ( result )
{
GTR_FREE((*ppNewMeta)->szURL);
(*ppNewMeta)->szURL = result;
}
}
}
void HText_appendHorizontalRule(HText * text, const char *align, const char *size,
const char *width, BOOL noshade )
{
XX_DMsg(DBG_HTEXT, ("appendHorizontalRule\n"));
HText_add_element(text, ELE_HR);
if ( size )
{
text->w3doc->aElements[text->iElement].vspace =
text->w3doc->aElements[text->iElement].border = atoi(size);
text->w3doc->aElements[text->iElement].vspace += text->w3doc->pStyles->hr_height - 1;
} else {
text->w3doc->aElements[text->iElement].vspace = text->w3doc->pStyles->hr_height;
text->w3doc->aElements[text->iElement].border = 2;
}
// hspace holds width info. 0 means none specified, negative values are percentages
text->w3doc->aElements[text->iElement].hspace = width ? atoi(width) : 0;
if ( width && strchr( width, '%' ) )
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_HR_PERCENT;
text->w3doc->aElements[text->iElement].alignment = ALIGN_MIDDLE;
if (align && !GTR_strcmpi((char *) align, "right"))
{
text->w3doc->aElements[text->iElement].alignment = ALIGN_RIGHT;
}
else if (align && !GTR_strcmpi((char *) align, "left"))
{
text->w3doc->aElements[text->iElement].alignment = ALIGN_LEFT;
}
if ( noshade )
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_HR_NOSHADE;
text->bOnNewPara = text->bNewPara = TRUE;
}
void HText_SetBRClearType( HText * text, const char *clear_tag_value )
{
text->w3doc->aElements[text->iElement].alignment = ALIGN_BASELINE; // assume no clear value
if ( clear_tag_value )
{
if ( !GTR_strcmpi((char *) clear_tag_value, "left"))
{
text->w3doc->aElements[text->iElement].alignment = ALIGN_LEFT;
} else if ( !GTR_strcmpi((char *) clear_tag_value, "right"))
{
text->w3doc->aElements[text->iElement].alignment = ALIGN_RIGHT;
} else if ( !GTR_strcmpi((char *) clear_tag_value, "both") ||
!GTR_strcmpi((char *) clear_tag_value, "all")
)
{
text->w3doc->aElements[text->iElement].alignment = ALIGN_ALL;
}
}
}
void HText_beginAppend(HText * text)
{
XX_DMsg(DBG_HTEXT, ("beginAppend\n"));
}
static void HText_Destroy( HText * text )
{
if ( text->pHtmlStream ) {
while ( PopStyleState( text->pHtmlStream ) )
;
}
GTR_FREE(text);
}
void HText_endAppend(HText * text)
{
int nEl;
XX_DMsg(DBG_HTEXT, ("endAppend\n"));
HText_add_element(text, ELE_ENDDOC);
text->w3doc->bIsComplete = TRUE;
if (text->bRecord && text->w3doc->title)
{
/* Update the global history in case we didn't have the title before */
GHist_Add(text->w3doc->szActualURL, text->w3doc->title, time(NULL),/*fCreateShortcut=*/TRUE);
/* so that the title shows up in the history menu... */
if (text->tw)
UpdateHistoryMenus(text->tw);
}
if (text->szLocal)
{
/* See if the local anchor we're jumping to has appeared yet */
nEl = TW_FindLocalAnchor(text->w3doc, text->szLocal);
if (nEl >= 0 && nEl != text->w3doc->frame.elementTail)
{
/* We found it! */
if (WAIT_GetWaitType(text->tw) >= waitNoInteract)
{
/* Let the user interact with the new document */
WAIT_UpdateWaitStack(text->tw, waitPartialInteract, INT_MAX);
}
/* Format any last little bit now that bIsComplete */
TW_Reformat(text->tw, NULL);
TW_ScrollToElement(text->tw, nEl);
text->szLocal = NULL; /* Don't free it - it's part of the struct DestInfo */
}
}
else
{
if (WAIT_GetWaitType(text->tw) >= waitNoInteract)
{
/* Let the user interact with the new document */
WAIT_UpdateWaitStack(text->tw, waitPartialInteract, INT_MAX);
}
/* Format any last little bit now that bIsComplete */
TW_Reformat(text->tw, NULL);
}
HText_Destroy( text );
}
void HText_abort(HText * text)
{
HText_add_element(text, ELE_ENDDOC);
TW_Reformat(text->tw, NULL);
HText_Destroy( text );
}
static void HText_appendHorizEscape(HText * text, int cbElement)
{
HText_add_element(text, cbElement);
text->bHorizEscape = TRUE;
}
void HText_appendVerticalTab(HText * text, int lines)
{
if (lines)
{
HText_add_element(text, ELE_VERTICALTAB);
text->w3doc->aElements[text->iElement].iBlankLines = lines;
text->bOnNewPara = text->bNewPara = TRUE;
}
}
void HText_appendParagraph(HText * text)
{
XX_DMsg(DBG_HTEXT, ("appendParagraph\n"));
/* Because we can have a <P> inside an <LI>, make sure it looks right. */
if (text->bStartingListItem)
{
text->bStartingListItem = FALSE;
}
else
{
HText_appendVerticalTab(text, 1);
}
text->bOnNewPara = text->bNewPara = TRUE;
}
void HText_setStyle(HText * text, int style_ndx)
{
struct GTRStyle *style;
int i;
style = NULL;
if (style_ndx >= 0 && style_ndx < COUNT_HTML_STYLES)
{
style = text->w3doc->pStyles->sty[style_ndx];
}
if (text->bStartingListItem)
{
text->bStartingListItem = FALSE;
}
else
{
/* Because of all the bad HTML out there, special case the situation
where an <LI> is immediately followed by a tag like an <H3> */
// HText_appendCRLF(text);
HText_appendVerticalTab(text, text->w3doc->pStyles->sty[text->iStyle]->spaceAfter);
for (i=0; i<text->w3doc->pStyles->sty[text->iStyle]->nLeftIndents; i++)
{
HText_appendHorizEscape(text, ELE_OUTDENT);
}
if (style)
{
for (i=0; i<style->nLeftIndents; i++)
{
HText_appendHorizEscape(text, ELE_INDENT);
}
HText_appendVerticalTab(text, style->spaceBefore);
}
}
text->iStyle = style_ndx;
text->fontBits = 0;
text->bOpen = FALSE;
}
void HText_beginStrikeOut(HText * text)
{
XX_DMsg(DBG_HTEXT, ("beginStrikeOut\n"));
text->fontBits |= FONTBIT_STRIKEOUT;
text->bOpen = FALSE;
}
void HText_endStrikeOut(HText * text)
{
XX_DMsg(DBG_HTEXT, ("endStrikeOut\n"));
text->fontBits &= (~FONTBIT_STRIKEOUT);
text->bOpen = FALSE;
}
void HText_beginFetch(HText * text, const char * desc, const char * guid, const char * required,
const char * src, const char * ts)
{
XX_DMsg(DBG_HTEXT, ("beginFetch\n"));
if (src && guid && ts) {
int len,totallen=0;
const char * pdesc = ""; // will point to description, or null string if no
// description
if (desc) pdesc = desc;
HText_add_element(text, ELE_FETCH);
// store src (URL to fetch) at hrefOffset
text->w3doc->aElements[text->iElement].hrefOffset = text->w3doc->poolSize;
len = strlen(src)+1;
HText_add_to_pool(text->w3doc, src, len);
text->w3doc->aElements[text->iElement].hrefLen = len;
// pack other parameters (guid, ts, desc) parameters together in that
// order, separated by '\0', at textOffset
text->w3doc->aElements[text->iElement].textOffset = text->w3doc->poolSize;
len = strlen(guid) + 1; // need the +1's to strlen because we want to get
// the terminating NULLs
totallen += len;
HText_add_to_pool(text->w3doc, guid, len);
len = strlen(ts) + 1;
totallen += len;
HText_add_to_pool(text->w3doc, ts, len);
len = strlen(pdesc) + 1;
totallen += len;
HText_add_to_pool(text->w3doc, pdesc, len);
text->w3doc->aElements[text->iElement].textLen = totallen;
// set horizontal and vertical size to zero so this doesn't
// affect layout
text->w3doc->aElements[text->iElement].vspace = 0;
text->w3doc->aElements[text->iElement].hspace = 0;
// if this is the first fetch tag for this page, set the fetch index
// to point at this element
if (text->tw->iIndexForNextFetch == -1) {
text->tw->iIndexForNextFetch = text->iElement;
}
}
}
void HText_beginBGSound(HText * text, const char * loop, const char * src, const char *start)
{
XX_DMsg(DBG_HTEXT, ("beginBGSound\n"));
if (src) {
ELEMENT * pElement;
// add an element for this
HText_add_element(text, ELE_BGSOUND);
// add the src URL to the document pool
text->w3doc->aElements[text->iElement].hrefOffset = text->w3doc->poolSize;
HText_add_to_pool(text->w3doc,src,strlen(src)+1);
pElement = &text->w3doc->aElements[text->iElement];
// set horizontal and vertical size to zero so this doesn't
// affect layout
pElement->vspace = 0;
pElement->hspace = 0;
/*for downloading, we are a blob*/
pElement->pblob = BlobConstruct();
if (pElement->pblob){
if (BlobStoreUrl(pElement->pblob, (char*) src)){
/*since we only need 1 DWORD of user data, store here*/
pElement->pblob->vp = (void*) GetLoopCount(loop);
}
else{
BlobDestruct(pElement->pblob);
pElement->pblob = NULL;
}
}
}
}
#ifdef FEATURE_INTL
void HText_beginSetFont(HText * text, BOOL setBaseFont, const char *color, const char *size, const char *face, int CharSet )
#else
void HText_beginSetFont(HText * text, BOOL setBaseFont, const char *color, const char *size, const char *face )
#endif
{
if ( size ) {
int new_size;
BOOL rel_plus = FALSE;
BOOL rel_minus = FALSE;
if ( *size == '+' ) {
rel_plus = TRUE;
size++;
} else if ( *size == '-' ) {
rel_minus = TRUE;
size++;
}
new_size = size ? atoi(size) : DEFAULT_HTML_FONT_SIZE;
if ( rel_plus )
new_size += text->baseFontSize;
else if (rel_minus )
new_size = text->baseFontSize - new_size;
if ( new_size < 1 )
new_size = 1;
if ( new_size >= NUM_FONT_SIZES )
new_size = NUM_FONT_SIZES - 1;
if ( setBaseFont )
text->baseFontSize = new_size;
else
text->fontSize = new_size;
}
if ( face )
#ifdef FEATURE_INTL
STY_AddFontFace( &text->fontFace, face, CharSet );
#else
STY_AddFontFace( &text->fontFace, face );
#endif
if ( color )
GetRGBValue( color, &text->fontColor );
text->bOpen = FALSE;
}
void HText_endSetFont(HText * text, BOOL setBaseFont )
{
XX_DMsg(DBG_HTEXT, ("endSetFont\n"));
text->bOpen = FALSE;
}
void HText_WordBreak(HText * text)
{
ELEMENT *pel;
XX_DMsg(DBG_HTEXT, ("WordBreak\n"));
if (text->iElement >= 0)
{
pel = &text->w3doc->aElements[text->iElement];
if (pel->type == ELE_TEXT && (pel->lFlags & ELEFLAG_NOBREAK))
pel->lFlags |= ELEFLAG_WBR;
}
text->bOpen = FALSE;
}
void HText_beginNoBreak(HText * text)
{
XX_DMsg(DBG_HTEXT, ("beginNoBreak\n"));
text->bNoBreak = TRUE;
text->bOpen = FALSE;
}
void HText_endNoBreak(HText * text)
{
XX_DMsg(DBG_HTEXT, ("endNoBreak\n"));
text->bNoBreak = FALSE;
text->bOpen = FALSE;
}
void HText_beginCenter(HText * text)
{
XX_DMsg(DBG_HTEXT, ("beginCenter\n"));
text->bCenter = TRUE;
text->bOpen = FALSE;
}
void HText_endCenter(HText * text)
{
XX_DMsg(DBG_HTEXT, ("endCenter\n"));
text->bCenter = FALSE;
text->bOpen = FALSE;
}
void HText_beginBold(HText * text)
{
XX_DMsg(DBG_HTEXT, ("beginBold\n"));
text->fontBits |= FONTBIT_BOLD;
text->bOpen = FALSE;
}
void HText_endBold(HText * text)
{
XX_DMsg(DBG_HTEXT, ("endBold\n"));
text->fontBits &= (~FONTBIT_BOLD);
text->bOpen = FALSE;
}
void HText_beginItalic(HText * text)
{
XX_DMsg(DBG_HTEXT, ("beginItalic\n"));
text->fontBits |= FONTBIT_ITALIC;
text->bOpen = FALSE;
}
void HText_endItalic(HText * text)
{
XX_DMsg(DBG_HTEXT, ("endItalic\n"));
text->fontBits &= (~FONTBIT_ITALIC);
text->bOpen = FALSE;
}
void HText_beginTT(HText * text)
{
XX_DMsg(DBG_HTEXT, ("beginTT\n"));
text->fontBits |= FONTBIT_MONOSPACE;
text->bOpen = FALSE;
}
void HText_endTT(HText * text)
{
XX_DMsg(DBG_HTEXT, ("endTT\n"));
text->fontBits &= (~FONTBIT_MONOSPACE);
text->bOpen = FALSE;
}
void HText_beginUnderline(HText * text)
{
XX_DMsg(DBG_HTEXT, ("beginUnderline\n"));
text->fontBits |= FONTBIT_UNDERLINE;
text->bOpen = FALSE;
}
void HText_endUnderline(HText * text)
{
XX_DMsg(DBG_HTEXT, ("endUnderline\n"));
text->fontBits &= (~FONTBIT_UNDERLINE);
text->bOpen = FALSE;
}
static int GetTypeTag( int type, const char *typetag )
{
if ( type == HTML_OL ) {
if ( typetag ) {
switch ( *typetag ) {
case 'A': return LIST_TYPETAG_CAP_LETTERS;
case 'a': return LIST_TYPETAG_LETTERS;
case 'I': return LIST_TYPETAG_CAP_ROMAN;
case 'i': return LIST_TYPETAG_ROMAN;
}
}
return LIST_TYPETAG_NUMBERS;
} else {
if ( typetag ) {
if ( !GTR_strcmpi((char *) typetag, "circle"))
return LIST_TYPETAG_CIRCLE;
else if ( !GTR_strcmpi((char *) typetag, "square"))
return LIST_TYPETAG_SQUARE;
}
}
return LIST_TYPETAG_DISK;
}
//
// Add a cover element to the link list of covered items
//
// On entry:
// pHead: address of the head of the cover link list
// row: starting row for the cover
// col: starting column for the cover
// rowspan: number of rows covered by this cover
// colspan: number of columns covered by this cover
//
// Note:
// Cells in tables can span multiple rows and columns. When a new cell is encountered
// we must determine its column number. If a previous row contained a cell that spans
// into the current row, the column number of the new cell isn't obvious, it must be
// derived based on the first "available" column index. The "cover" link list is a data
// structure that contains the information about cells that span rows or columns. This
// data is used when trying to figure out the column number of a cell.
//
static BOOL addCover( COVERED_CELL_INFO **pHead, int row, int col, int rowspan, int colspan )
{
// Allocate a new node
COVERED_CELL_INFO *p = GTR_MALLOC( sizeof(*p) );
if ( p ) {
// Stuff it with the given info
p->r.left = col;
p->r.top = row;
p->r.right = col + colspan;
p->r.bottom = row + rowspan;
// Point new node at existing list
p->next = *pHead;
// Update head to point at new node
*pHead = p;
return TRUE;
}
return FALSE;
}
//
// Given a row and column, see if that row and column is already in use by a covering cell
//
// On entry:
// pHead: address of the head of the cover link list
// row: row that may be covered
// col: column that may be covered
// rowSpan: number of rows that must be not covered
// colSpan: number of columns that must be not covered
// pNextPossibleColAvailable: pointer to int
//
// On exit:
// returns: TRUE -> row/col is already in use
// FALSE-> row/col is available (not used by a covering cell)
// *pNextPossibleColAvailable: If return is TRUE, contains next col index that is may
// be free (may not be free, also)
//
// Note:
// To keep the cover link list from getting too big, it cleans itself up as calls
// are made to isCovered. The assumption is that when asked, "is (r,c) available?",
// any covering cells that lie above and to the left will never be needed again. In
// other words, the request for cover information always proceeds monotonically
// increasing on row, and within a given row, monotonically increasing on column.
//
static BOOL isCovered( COVERED_CELL_INFO **pHead, int row, int col, int rowSpan, int colSpan,
int *pNextPossibleColAvailable )
{
COVERED_CELL_INFO *p = *pHead;
COVERED_CELL_INFO *old_p = NULL;
RECT newRect, sectRect;
// Build rectangle that describes the desired uncovered area
newRect.left = col;
newRect.right = col + colSpan;
newRect.top = row;
newRect.bottom = row + rowSpan;
while ( p ) {
if ( IntersectRect( &sectRect, &newRect, &p->r ) )
{
// Pass back the column immediately following this cover. Note that
// this may not be free, but it's the first cell that could conceivably
// be free
*pNextPossibleColAvailable = p->r.right;
return TRUE;
}
// Check to see if this cover node is obsolete
if ( row > p->r.bottom - 1 || (row == p->r.bottom - 1 && col > p->r.right - 1) ) {
COVERED_CELL_INFO *t = p->next;
// Remove this cover node
if ( old_p )
old_p->next = t;
else
*pHead = t;
GTR_FREE( p );
p = t;
} else {
old_p = p;
p = p->next;
}
}
return FALSE;
}
//
// Free up the nodes in a cover list
//
// On entry:
// head: head of cover linked list
//
// On exit:
// All the nodes in the cover link list have been freed
//
static void freeCoverList( COVERED_CELL_INFO *head )
{
COVERED_CELL_INFO *p;
while ( head ) {
p = head;
head = head->next;
GTR_FREE( p );
}
}
//
// Pop a frame state off the frame state stack
//
// On entry:
// text->frameStateStackOffset: next available element in frame stack array
// text->frameStateStack: array of FRAMESTATE_STACK elements
//
// On exit:
// returns: TRUE -> there was a frame state stack element to be popped off the stack
// *pFrameState: If returning TRUE, the frame state that was popped off the stack
//
// Note:
// A frame state is the bookkeeping structure needed while the parser is feeding
// us elements and we're in the middle of a frame.
//
// When popped off the stack, the cover list for the frame state is freed up, as it
// is only needed while the frame is a work in progress.
//
BOOL popFrameState( HText *text, FRAMESTATE_STACK *pFrameState )
{
if ( text->frameStateStackOffset > 0 ) {
*pFrameState = text->frameStateStack[--(text->frameStateStackOffset)];
freeCoverList( pFrameState->coveredHead );
pFrameState->coveredHead = NULL;
return TRUE;
}
return FALSE;
}
//
// Push a frame state onto the frame state stack
//
// On entry:
// text->frameStateStackOffset: next available element in frame stack array
// text->frameStateStack: array of FRAMESTATE_STACK elements
// elementIndex: element index of the frame element being added
// pFrame: pointer to the frame structure
//
// On exit:
// text->frameStateStackOffset: incremented if stack isn't full
// text->frameStateStack: contains a new frame state item
//
// Note:
// A frame state is the bookkeeping structure needed while the parser is feeding
// us elements and were in the middle of a frame.
//
static void pushFrameState( HText *text, int elementIndex, FRAME_INFO *pFrame,
enum tableStateValue prevTableState )
{
if ( text->frameStateStackOffset <= MAX_FRAMESTATE_STACK - 1 ) {
FRAMESTATE_STACK *p = &text->frameStateStack[text->frameStateStackOffset];
// Initialize the new frame state info
p->pFrame = pFrame;
p->elementIndex = elementIndex;
p->curCol = -1;
p->curRow = -1;
p->maxCol = 0;
p->coveredHead = NULL;
p->default_align = ALIGN_BASELINE;
p->default_valign = ALIGN_MIDDLE;
p->default_bgColor = (COLORREF) -1;
p->default_borderColorDark = (COLORREF) -1;
p->default_borderColorLight = (COLORREF) -1;
p->prevTableState = prevTableState;
(text->frameStateStackOffset)++;
}
}
//
// Get the row, column, and max column info from the top of the frame state stack
//
// On entry:
// text->frameStateStack: stack containing frame state info
// pRow, pCol, pMaxCol: pointers to int
//
// On exit:
// pRow: current row in progress
// pCol: current column in progress
// pMaxCol: max. number of cols seen in this table so far
//
static void GetRowColInfo( HText *text, int *pRow, int *pCol, int *pMaxCol )
{
if ( pRow )
*pRow = -1;
if ( pCol )
*pCol = -1;
if ( pMaxCol )
pMaxCol = 0;
if ( text->frameStateStackOffset > 0 ) {
if ( pRow )
*pRow = text->frameStateStack[text->frameStateStackOffset-1].curRow;
if ( pCol )
*pCol = text->frameStateStack[text->frameStateStackOffset-1].curCol;
if ( pMaxCol )
*pMaxCol = text->frameStateStack[text->frameStateStackOffset-1].maxCol;
}
}
//
// Set row and column info in the top of the frame state stack
//
// On entry:
// text->frameStateStack: stack containing frame state info
// row, col: row and column values to be set
//
// On exit:
// curRow and curCol members of top of frame state stack are set to given values
// maxCol member of top of frame state stack maintained
//
static void SetRowColInfo( HText *text, int row, int col )
{
if ( text->frameStateStackOffset > 0 ) {
text->frameStateStack[text->frameStateStackOffset-1].curRow = row;
text->frameStateStack[text->frameStateStackOffset-1].curCol = col;
if ( col > text->frameStateStack[text->frameStateStackOffset-1].maxCol )
text->frameStateStack[text->frameStateStackOffset-1].maxCol = col;
}
}
//
// Determinte is a given alignment will result in a floating table
//
// On entry:
// align: attribute value given on for align (or NULL if none given)
//
// On exit:
// returns: TRUE -> yes, the table will be floating
// FALSE-> no, the table will not be floating
//
BOOL HText_IsFloating( const char *align )
{
int align_value = StringTableToIndex( align, rgAlignment, ARRAY_ELEMENTS(rgAlignment),
ALIGN_MIDDLE );
return (align_value == ALIGN_LEFT) || (align_value == ALIGN_RIGHT);
}
//
// Begin a frame (table or cell frame)
//
// On entry:
// cellTypeFlags: indicates type of frame being added
// align, bgColor, border, borderColor,
// borderColorDark,
// borderColorLight,
// cellpadding, cellspacing,
// nowrap, width, valign,
// colspan, rowspan: strings from attributes in tag
//
// On exit:
// A new element of type ELE_FRAME will have been added. Numerous bookkeeping
// activities will have been started (frame state stack item created). Attribute
// values will have been converted from ASCII into members the frame element. For
// table cells, ongoing bookkeeping regarding current cell, spanned cells, etc. is
// maintained.
//
void HText_beginFrame(HText *text,
int cellTypeFlags,
const char *align,
const char *bgColor,
const char *border,
const char *borderColor,
const char *borderColorDark,
const char *borderColorLight,
const char *cellpadding,
const char *cellspacing,
const char *nowrap,
const char *width,
const char *valign,
const char *colspan,
const char *rowspan,
const char *height
)
{
// Allocate a frame structure for the element we're about to add
FRAME_INFO *pFrame = GTR_CALLOC( 1, sizeof(*pFrame) );
enum tableStateValue prevTableState;
// Check to see if table cells are appearing outside of a table
if ( text->frameStateStackOffset <= 0 && !(cellTypeFlags & ELE_FRAME_IS_TABLE) )
return;
// Check to see if we're "between" rows (i.e. we've seen a </tr> but not
// the <tr> that follows. If the <tr> isn't where expected or is missing
// we pretend we saw one here.
if ( text->bPendingTR && !(cellTypeFlags & ELE_FRAME_IS_CAPTION_CELL)) {
HText_beginRow(text, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
text->bPendingTR = FALSE;
}
if ( (text->tableState == TS_IN_CELL) && !(cellTypeFlags & ELE_FRAME_IS_TABLE) )
HText_endFrame( text, ELE_FRAME_IS_CELL, NULL );
if ( text->pHtmlStream )
PushStyleState( text->pHtmlStream );
if ( pFrame ) {
int row, col;
BYTE default_align = ALIGN_BASELINE; // assume default horz. alignment
BYTE default_valign = ALIGN_MIDDLE; // assume default vert. alignment
// Store the current frameState value
prevTableState = text->tableState;
text->tableState = TS_IN_CELL; // Assume non-table frame. Note that this state
// insures that we won't infinitely recurse between
// HText_beginFrame and HText_add_element.
// Add the frame element
HText_add_element( text, ELE_FRAME );
// Set the frame flags for type of frame
// this is (e.g. table, cell, caption cell, header cell)
pFrame->flags |= cellTypeFlags;
// Assume no caption until we see one
pFrame->elementCaption = -1;
pFrame->borderColorLight = pFrame->borderColorDark = (COLORREF) -1;
pFrame->bgColor = (COLORREF) -1;
if ( cellTypeFlags & ELE_FRAME_IS_TABLE ) {
// We're adding a table
text->tableState = TS_IN_LIMBO;
text->bOnNewPara = FALSE;
// Was border given? (given with no value means 1)
text->w3doc->aElements[text->iElement].border =
( border ) ? (strlen(border) ? atoi(border) : 1 ) : 0;
// Init frame element for a newborn table
pFrame->row = -1;
pFrame->col = -1;
pFrame->cellPadding = (BYTE) (( cellpadding ) ? atoi(cellpadding) : 1);
pFrame->cellSpacing = (BYTE) (( cellspacing ) ? atoi(cellspacing) : 2);
// It's convenient to have a flag set if border is being used
if ( text->w3doc->aElements[text->iElement].border )
pFrame->flags |= ELE_FRAME_HAS_BORDER;
} else {
text->bOnNewPara = TRUE;
if ( cellTypeFlags & ELE_FRAME_IS_CAPTION_CELL ) {
// Enclosing table frame already has caption?
if ( text->frameStateStack[text->frameStateStackOffset-1].pFrame->elementCaption >= 0 )
{
// Only one caption allowed per table, treat this caption as a regular cell
cellTypeFlags &= ~ELE_FRAME_IS_CAPTION_CELL;
pFrame->flags &= ~ELE_FRAME_IS_CAPTION_CELL;
}
}
if ( !(cellTypeFlags & ELE_FRAME_IS_CAPTION_CELL) ) {
// We're adding a regular table cell (non-caption)
// Default colspan and rowspan
int iColspan = ( colspan ) ? atoi(colspan) : 1;
int iRowspan = ( rowspan ) ? atoi(rowspan) : 1;
text->tableState = TS_IN_CELL;
// Maintain sane range for colspan and rowspan
if ( iColspan <= 0 )
iColspan = 1;
if ( iRowspan <= 0 )
iRowspan = 1;
// Inherit border flag from enclosing table frame
if ( text->frameStateStack[text->frameStateStackOffset-1].pFrame->flags & ELE_FRAME_HAS_BORDER)
pFrame->flags |= ELE_FRAME_HAS_BORDER;
pFrame->borderColorLight =
text->frameStateStack[text->frameStateStackOffset-1].pFrame->borderColorLight;
pFrame->borderColorDark =
text->frameStateStack[text->frameStateStackOffset-1].pFrame->borderColorDark;
// If set, inherit the colors for this frame (from current row)
pFrame->bgColor =
text->frameStateStack[text->frameStateStackOffset-1].default_bgColor;
if ( text->frameStateStack[text->frameStateStackOffset-1].default_borderColorLight != (COLORREF) -1 )
pFrame->borderColorLight =
text->frameStateStack[text->frameStateStackOffset-1].default_borderColorLight;
if ( text->frameStateStack[text->frameStateStackOffset-1].default_borderColorDark != (COLORREF) -1 )
pFrame->borderColorDark =
text->frameStateStack[text->frameStateStackOffset-1].default_borderColorDark;
// Inherit cellPadding value from enclosing table frame
pFrame->cellPadding = text->frameStateStack[text->frameStateStackOffset-1].pFrame->cellPadding;
// This is a new cell, figure out it's row and column. This involves getting
// the last (row,col) of the previously added cell and bumping the column
// number. There's a chance that this column number is already taken by a
// previous row's cell (rowspan), or by the previous cell in the row (colspan).
// Either way, we loop, trying new columns until we get one that isn't used
// by another cell in the table.
GetRowColInfo( text, &row, &col, NULL );
col++;
// If the <TR> tag was missing, we'll compensate here
if ( row < 0 )
row = 0;
while ( isCovered( &text->frameStateStack[text->frameStateStackOffset-1].coveredHead,
row, col, iRowspan, iColspan, &col ) )
;
// Move the info into the frame structure
pFrame->row = row;
pFrame->col = col;
pFrame->rowspan = iRowspan;
pFrame->colspan = iColspan;
// If we've just added a cell that used rowspan or colspan, add this information
// into the cover link list, so that future cells will be able to avoid the
// cell coordinates covered by this spanning cell
if ( iRowspan > 1 || iColspan > 1 )
addCover( &text->frameStateStack[text->frameStateStackOffset-1].coveredHead,
row, col, iRowspan, iColspan );
// Set the current row and column information
col += iColspan - 1;
SetRowColInfo( text, row, col );
// Inherit the default horz. alignment for this frame (from current row or table)
default_align = text->frameStateStack[text->frameStateStackOffset-1].default_align;
// Header cells default to horz. centering
if ( cellTypeFlags & ELE_FRAME_IS_HEADER_CELL )
default_align = ALIGN_MIDDLE;
// Inherit the default vert. alignment for this frame
default_valign = text->frameStateStack[text->frameStateStackOffset-1].default_valign;
} else {
// We're adding a caption cell
// Enclosing table frame need to know element of caption frame
text->frameStateStack[text->frameStateStackOffset-1].pFrame->elementCaption = text->iElement;
// Inherit cellpadding from enclosing table
pFrame->cellPadding = text->frameStateStack[text->frameStateStackOffset-1].pFrame->cellPadding;
// Caption cells don't deserve a row or col
pFrame->row = pFrame->col = -1;
// Captions default to horz. and vert. centered
default_align = ALIGN_MIDDLE;
default_valign = ALIGN_TOP;
}
}
// Frames keeps track of first and last elements in frame
pFrame->elementHead = pFrame->elementTail = text->iElement;
// Frames keep a pointer to the enclosing parent frame (for walk-up)
pFrame->pParentFrame = ( text->frameStateStackOffset > 0 ) ?
text->frameStateStack[text->frameStateStackOffset - 1].pFrame : &text->w3doc->frame;
// Preserve width attribute
if ( width ) {
pFrame->widthAttr = atoi( width );
if ( strchr( width, '%' ) )
pFrame->flags |= ELE_FRAME_WIDTH_IS_PERCENT;
}
// Preserve height attribute
if ( height ) {
pFrame->heightAttr = atoi( height );
if ( strchr( height, '%' ) )
pFrame->flags |= ELE_FRAME_HEIGHT_IS_PERCENT;
}
// Now override them for this particular frame
if ( bgColor )
GetRGBValue( bgColor, &pFrame->bgColor );
if ( borderColor ) {
GetRGBValue( borderColor, &pFrame->borderColorLight );
pFrame->borderColorDark = pFrame->borderColorLight;
}
if ( borderColorLight )
GetRGBValue( borderColorLight, &pFrame->borderColorLight );
if ( borderColorDark )
GetRGBValue( borderColorDark, &pFrame->borderColorDark );
// Newly created frame element points at newly allocated frame info
text->w3doc->aElements[text->iElement].pFrame = pFrame;
// Grab alignment info
pFrame->align =
StringTableToIndex( align, rgAlignment, ARRAY_ELEMENTS(rgAlignment), default_align);
text->w3doc->aElements[text->iElement].alignment = pFrame->align;
pFrame->valign =
StringTableToIndex( valign, rgAlignment, ARRAY_ELEMENTS(rgAlignment), default_valign);
// Captions get special case on alignment, of course
if ( cellTypeFlags & ELE_FRAME_IS_CAPTION_CELL ) {
if ( pFrame->align == ALIGN_TOP || pFrame->align == ALIGN_BOTTOM ) {
pFrame->valign = pFrame->align;
pFrame->align = ALIGN_MIDDLE;
}
}
// frame state is needed for bookkeeping as frame elements come in
pushFrameState( text, text->iElement, pFrame, prevTableState );
}
}
//
// Begin a row in current table
//
// On entry:
// text: the whole world, from this routines point of view
// borderColor: border color for cells in this row
// borderColorDark: light border color for cells in this row
// borderColorLight: dark border color for cells in this row
// align: default horz. alignment for all cells in this row
// valign: default vert. alignment for all cells in this row
// nowrap: disable word wrap for all cells in this row
//
// On exit:
// Row bumped in top of frame state stack. Column set to -1. New default for
// alignment and nowrap maintained in top of frame state stack
//
void HText_beginRow(HText *text,
const char *align,
const char *bgColor,
const char *borderColor,
const char *borderColorDark,
const char *borderColorLight,
const char *valign,
const char *nowrap )
{
int row;
FRAMESTATE_STACK *pFS = &text->frameStateStack[text->frameStateStackOffset-1];
GetRowColInfo( text, &row, NULL, NULL );
row++;
SetRowColInfo( text, row, -1 );
pFS->default_bgColor =
pFS->default_borderColorDark =
pFS->default_borderColorLight = (COLORREF) -1;
if ( bgColor )
GetRGBValue( bgColor, &pFS->default_bgColor );
if ( borderColor ) {
GetRGBValue( borderColor, &pFS->default_borderColorLight );
pFS->default_borderColorDark =
pFS->default_borderColorLight;
}
if ( borderColorLight )
GetRGBValue( borderColorLight, &pFS->default_borderColorLight );
if ( borderColorDark )
GetRGBValue( borderColorDark, &pFS->default_borderColorDark );
pFS->default_align =
StringTableToIndex( align, rgAlignment, ARRAY_ELEMENTS(rgAlignment), ALIGN_BASELINE );
pFS->default_valign =
StringTableToIndex( valign, rgAlignment, ARRAY_ELEMENTS(rgAlignment), ALIGN_MIDDLE );
}
//
// End a frame
//
// On entry:
// text: the whole world, from this routines point of view
//
// On exit:
// The top of the frame state stack will have been popped off. Pending end frames
// will be recorded so that when the next element is created, the frame info can
// be correctly maintained for the frame that just ended.
//
void HText_endFrame(HText *text, int cellTypeFlags, BOOL *pbWasFloating )
{
FRAMESTATE_STACK stackInfo;
if ( pbWasFloating )
*pbWasFloating = FALSE;
// Ignore end frames when we're not in a frame
if ( text->frameStateStackOffset <= 0 )
return;
// Ignore end frames that don't match the frame type given
if ( (text->frameStateStack[text->frameStateStackOffset-1].pFrame->flags & cellTypeFlags)
== 0 )
return;
// If a table or cell ends, and an anchor is still in effect, end the anchor
HText_endAnchor(text);
if ( text->pHtmlStream )
PopStyleState( text->pHtmlStream );
text->tableState = TS_NOT_IN_TABLE;
// Close out current text element as is
text->bOpen = FALSE;
text->bPendingTR = FALSE;
// Pop off the top of the frame state stack
if ( popFrameState( text, &stackInfo ) ) {
if ( stackInfo.elementIndex != -1 ) {
FRAME_INFO *pFrame = text->w3doc->aElements[stackInfo.elementIndex].pFrame;
// Current element is the last element in this frame
text->w3doc->aElements[text->iElement].frameNext = -1;
// We need to track frame transitions
text->bDifferentFrame = TRUE;
// Push frame element index onto "pending frame end" stack
if ( text->numEndPending < MAX_END_PENDING )
text->pendingEnd[text->numEndPending++] = stackInfo.elementIndex;
// Current element is the last element in this frame
pFrame->elementTail = text->iElement;
if ( pFrame->flags & ELE_FRAME_IS_TABLE ) {
// Table frame records number of rows and columns in a table
pFrame->row = stackInfo.curRow + 1;
pFrame->col = stackInfo.maxCol + 1;
}
// After popping the frame state stack, the current table state
// becomes whatever it was prior to entering the frame
text->tableState = stackInfo.prevTableState;
// Set return info regarding whether this was a floating table that just ended
if ( pFrame->align == ALIGN_LEFT || pFrame->align == ALIGN_RIGHT ) {
if ( pbWasFloating )
*pbWasFloating = TRUE;
}
}
}
}
//
// End any open frames that were left dangling at the end of the document
//
void HText_endAllFrames(HText *text)
{
int i;
while ( text->frameStateStackOffset > 0 ) {
i = text->frameStateStackOffset;
HText_endFrame( text,
text->frameStateStack[text->frameStateStackOffset-1].pFrame->flags, NULL );
if ( i == text->frameStateStackOffset )
break; // don't loop forever
}
}
void HText_beginList(HText * text, int type, const char *typetag, const char *start )
{
XX_DMsg(DBG_HTEXT, ("beginList\n"));
if (text->next_list < MAX_NEST_LIST - 1) // Don't nest another level if we've maxed out
{
if (text->bDD)
{
HText_appendHorizEscape(text, ELE_OUTDENT);
text->bDD = FALSE;
}
if (text->next_list == 0)
text->bNeedListCRLF = TRUE;
text->bOnNewPara = text->bNewPara = TRUE;
if (type != HTML_DL || text->next_list)
HText_add_element(text, ELE_BEGINLIST);
text->list[text->next_list].type = type;
text->list[text->next_list].typetag = GetTypeTag( type, typetag );
text->list[text->next_list].nItems = start ? atoi(start) - 1 : 0;
text->list[text->next_list].bHaveItem = FALSE;
text->next_list++;
}
}
void HText_beginBlockQuote(HText * text)
{
HText_add_element(text, ELE_BEGINBLOCKQUOTE);
}
void HText_endBlockQuote(HText * text)
{
HText_add_element(text, ELE_ENDBLOCKQUOTE);
}
static char *roman_seq[] = { "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" };
static char *roman_seq10[] = { "", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc" };
static char *roman_seq100[] = { "", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm" };
static char *roman_seq1000[] = { "", "m", "mm", "mmm" };
static void MakeListItemLabel( char *buf, struct _list *pList )
{
long ix = (long) pList->nItems;
BOOL made_it = FALSE;
if ( ix >= 1 ) {
switch (pList->typetag)
{
case LIST_TYPETAG_LETTERS:
case LIST_TYPETAG_CAP_LETTERS:
ix--;
buf[2] = 0;
buf[1] = 'a' + (ix % 26);
ix /= 26;
if ( ix <= 26 ) {
buf[0] = ix ? ('a' + ix - 1) : ' ';
if ( pList->typetag == LIST_TYPETAG_CAP_LETTERS )
_strupr( buf );
made_it = TRUE;
}
break;
case LIST_TYPETAG_ROMAN:
case LIST_TYPETAG_CAP_ROMAN:
if ( ix < 4000 ) {
buf[0] = 0;
strcat( buf, roman_seq1000[ix / 1000] );
ix %= 1000;
strcat( buf, roman_seq100[ix / 100] );
ix %= 100;
strcat( buf, roman_seq10[ix / 10] );
ix %= 10;
strcat( buf, roman_seq[ix] );
if ( pList->typetag == LIST_TYPETAG_CAP_ROMAN )
_strupr( buf );
made_it = TRUE;
}
break;
}
}
if ( !made_it )
sprintf(buf, "%ld.", (long) pList->nItems );
else
strcat( buf, "." );
}
void HText_endList(HText * text, int type)
{
XX_DMsg(DBG_HTEXT, ("endList\n"));
if (text->next_list > 0)
{
text->bOnNewPara = text->bNewPara = TRUE;
if (text->list[text->next_list - 1].bHaveItem)
{
HText_add_element(text, ELE_CLOSELISTITEM);
text->bStartingListItem = FALSE;
}
if (type != HTML_DL || text->next_list > 1) HText_add_element(text, ELE_ENDLIST);
text->next_list--;
if (text->next_list == 0) text->bNeedListCRLF = TRUE;
}
}
void HText_addListCRLF(HText *text)
{
if (text->bNeedListCRLF)
HText_appendCRLF(text);
text->bNeedListCRLF = FALSE;
}
void HText_listItem(HText * text, int type, const char *typetag, const char *value)
{
XX_DMsg(DBG_HTEXT, ("listItem\n"));
if (text->next_list > 0)
{
// HText_appendCRLF(text);
text->bOnNewPara = text->bNewPara = TRUE;
if (text->list[text->next_list - 1].bHaveItem)
{
HText_add_element(text, ELE_CLOSELISTITEM);
}
text->list[text->next_list - 1].bHaveItem = TRUE;
if ( value )
text->list[text->next_list - 1].nItems = atoi(value);
else
text->list[text->next_list - 1].nItems++;
if ( typetag )
text->list[text->next_list - 1].typetag =
GetTypeTag( text->list[text->next_list - 1].type, typetag );
switch (text->list[text->next_list - 1].type)
{
case HTML_DL:
text->bStartingListItem = TRUE;
break;
case HTML_UL:
#ifdef UNIX
HText_add_element(text, ELE_BULLET);
#else
/* This is an ISO character set bullet. */
HText_appendCharacter(text, '');
#endif
text->bStartingListItem = TRUE;
break;
case HTML_OL:
{
char buf[32];
MakeListItemLabel( buf, &text->list[text->next_list - 1] );
HText_appendText(text, buf);
}
text->bStartingListItem = TRUE;
break;
default:
break;
}
#ifdef UNIX
HText_add_element(text, ELE_TAB);
#endif
HText_add_element(text, ELE_OPENLISTITEM);
/* Treat a list as a new paragraph so we won't get spurious spaces
before list text. */
text->bOnNewPara = text->bNewPara = TRUE;
}
}
void HText_beginGlossary(HText * text)
{
HText_beginList(text, HTML_DL, NULL, NULL);
text->bDD = FALSE;
}
void HText_endGlossary(HText * text)
{
if (text->bDD)
{
HText_appendHorizEscape(text, ELE_OUTDENT);
text->bDD = FALSE;
}
HText_endList(text, HTML_DL);
}
void HText_beginDT(HText * text, int type)
{
HText_appendCRLF(text);
if (text->bDD)
{
HText_appendHorizEscape(text, ELE_OUTDENT);
text->bDD = FALSE;
}
}
void HText_beginDD(HText * text, int type)
{
HText_appendCRLF(text);
if (!text->bDD)
{
HText_appendHorizEscape(text, ELE_INDENT);
text->bDD = TRUE;
}
}
void HText_setTitle(HText *text, PCSTR title)
{
PCSTR p=title;
if (text->w3doc->title)
GTR_FREE(text->w3doc->title);
text->w3doc->title = NULL;
while ( *p && isspace(*p) ) // skip past leading white space
p++;
if ( *p )
{
text->w3doc->title = GTR_strdup(p);
if (text->tw)
{
TW_SetWindowName(text->tw);
}
}
}
void HText_update(HText *text)
{
int nEl;
#ifdef FEATURE_IMG_THREADS
Image_UnblockMaster(text->tw);
#endif
/* If the document is already complete, everything should already be formatted. */
if (!text->w3doc->bIsComplete)
{
if (text->szLocal)
{
/* See if the local anchor we're jumping to has appeared yet */
nEl = TW_FindLocalAnchor(text->w3doc, text->szLocal);
if (nEl >= 0 && nEl != text->w3doc->frame.elementTail)
{
TW_Reformat(text->tw, NULL);
if (TW_ScrollToElement(text->tw, nEl))
{
/* We found it! */
if (WAIT_GetWaitType(text->tw) >= waitNoInteract)
{
/* Let the user interact with the new document */
WAIT_UpdateWaitStack(text->tw, waitPartialInteract, INT_MAX);
}
text->szLocal = NULL; /* Don't free it - it's part of the struct DestInfo */
}
else
{
/* Keep the document from displaying */
text->w3doc->frame.nLastFormattedLine = -1;
}
}
}
else
{
if (WAIT_GetWaitType(text->tw) >= waitNoInteract)
{
/* Let the user interact with the new document */
WAIT_UpdateWaitStack(text->tw, waitPartialInteract, INT_MAX);
}
if (text->iElement > 0)
{
TW_Reformat(text->tw, NULL);
}
}
}
}
void HText_setIndex(HText * text, const char *action, const char *prompt)
{
char szPrompt[128];
HText_appendHorizontalRule(text, NULL, NULL, NULL, FALSE );
if (prompt && (*prompt))
{
HText_appendText(text, prompt);
}
else
{
GTR_formatmsg(RES_STRING_DEFPROMPT,szPrompt,sizeof(szPrompt));
HText_appendText(text, szPrompt);
}
HText_beginForm(text, (char *) action, "GET");
HText_addInput(text, FALSE, FALSE, NULL, NULL, "isindex", NULL, "text", NULL, NULL, NULL, NULL);
HText_endForm(text);
HText_appendHorizontalRule(text, NULL, NULL, NULL, FALSE);
}