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.
 
 
 
 
 
 

560 lines
12 KiB

/*****************************************************************************
* *
* HOTSPOT.C *
* *
* Copyright (C) Microsoft Corporation 1989-1994 *
* All Rights reserved. *
* *
*****************************************************************************/
#include "stdafx.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/***************************************************************************\
*
- Function: CbTranslateHotspot( szHotspot, phspt, phpj )
-
* Purpose: Translate a possible hotspot binding into a command
* to be put into the command table.
*
* Valid binding strings are:
*
* Macro:
* !string [or $string??????] REVIEW
*
* Jump or glossary:
* context_string
* context_string @ file
* context_string > member
* context_string @ file > member
* context_string > member @ file
*
* Any binding may be preceded with a % to suppress
* special formatting (invisible hotspot).
*
* ASSUMES
* args IN: szHotspot - buffer containing binding string
* phspt - hotspot type (hsptJump or hsptDefine)
* phpj - hpj struct (we use the err and drgwsmag)
*
* PROMISES
* returns: size of command in bytes if valid, 0 if invalid
* args OUT: szHotspot - command copied here
* phspt - changed to hsptMacro if it's a macro
*
* Side Effects: may display error messages
*
* Notes:
*
* Bugs:
*
* +++
*
* Method:
*
* Notes:
*
\***************************************************************************/
int STDCALL CbTranslateHotspot(PSTR pszHotspot, HSPT* phspt)
{
WORD cb; // size IS important for this! Must be 16 bits
BOOL fInvisible = FALSE;
BOOL fUnderlined = FALSE;
PSTR psz = FirstNonSpace(pszHotspot, options.fDBCS);
if (*psz == '%') {
fInvisible = TRUE;
psz = FirstNonSpace(psz + 1, options.fDBCS);
}
if (*psz == '*') {
fInvisible = fUnderlined = TRUE;
psz = FirstNonSpace(psz + 1, options.fDBCS);
}
PSTR pszMacro = SzMacroFromSz(psz);
// check for macro
if (pszMacro != NULL) {
if (*pszMacro == '\0') {
VReportError(HCERR_EMPTY_MACRO, &errHpj);
return 0;
}
if (Execute(pszMacro) == RET_MACRO_EXPANSION)
pszMacro = (PSTR) GetMacroExpansion();
// Add the macro even if there is an error
if (fUnderlined)
*phspt = hsptULMacro;
else
*phspt = hsptMacro;
pszHotspot[0] = (BYTE) (fInvisible ? bLongMacroInv : bLongMacro);
cb = strlen(pszMacro) + 1;
// Use memmove, because pszMacro == pszHotspot + 1
memmove(pszHotspot + sizeof(BYTE) + sizeof(WORD), pszMacro, cb);
*(WORD UNALIGNED *) (pszHotspot + sizeof(BYTE)) = cb;
return sizeof(BYTE) + sizeof(WORD) + cb;
}
PSTR pszFile = StrChr(psz, '@', fDBCSSystem);
PSTR pszMember = StrChr(psz, '>', fDBCSSystem);
PSTR pszContext = psz;
if (pszFile)
*pszFile++ = '\0';
if (pszMember) {
*pszMember++ = '\0';
// member with note is meaningless
if (*phspt == hsptDefine) {
VReportError(HCERR_NOTE_JUMP_WND, &errHpj, pszContext);
pszMember = NULL;
}
else
pszMember = SzSkipBlanksSz(pszMember);
// We'll verify the window name later
}
if (pszFile)
pszFile = SzTrimSz(pszFile);
RemoveTrailingSpaces(pszContext);
if (*pszContext == '\0') {
VReportError(HCERR_EMPTY_HOT_CTX, &errHpj, pszContext);
return 0;
}
else if(!FValidContextSz(pszContext)) {
VReportError(HCERR_INVALID_HOT_CTX, &errHpj, pszContext);
return 0;
}
HASH hash = HashFromSz(pszContext);
if (pszFile == NULL) {
/*
* Record context string info for error processing. If it's an
* alias, record also the context string it's aliased to.
*/
FRecordContext(hash, psz, SzTranslateHash(&hash), FALSE, &errHpj);
PBYTE pb = (PBYTE) pszHotspot;
if (!pszMember) {
// use short hotspot format
if (*phspt == hsptJump)
*pb = (char) (fInvisible ? bShortInvHashJump : bShortHashJump);
else {
ASSERT(*phspt == hsptDefine);
*pb = (char) (fInvisible ? bShortInvHashNote : bShortHashNote);
}
++pb;
if (fUnderlined)
*phspt = ULHsptFromHspt(*phspt);
*(HASH *) pb = hash;
return sizeof(char) + sizeof(HASH);
}
else { // pszMember != NULL
DWORD iwsmag;
if ((iwsmag = VerifyWindowName(pszMember)) == INDEX_BAD)
return 0;
// store hotspot type
pb = (PBYTE) pszHotspot;
if (*phspt == hsptJump)
*pb = (char) (fInvisible ? bLongInvHashJump : bLongHashJump);
else {
ASSERT(*phspt == hsptDefine);
*pb = (char) (fInvisible ? bLongInvHashNote : bLongHashNote);
}
++pb;
if (fUnderlined)
*phspt = ULHsptFromHspt(*phspt);
// store size of data in bytes (flags + HASH + member index)
cb = sizeof(BYTE) + sizeof(HASH) + sizeof(BYTE);
*(WORD *) pb = cb;
pb += sizeof(WORD);
// store flags
((QJI) pb) ->bFlags = fIMember;
// store hash value
((QJI) pb) ->hash = hash;
// store member index
((QJI) pb) ->uf.iMember = (BYTE) iwsmag;
return sizeof(char) + sizeof(WORD) + cb;
}
}
else { // pszFile != NULL
// copy so we can overwrite buffer
pszFile = lcStrDup(pszFile);
PBYTE pb = (PBYTE) pszHotspot;
// store hotspot type
if (*phspt == hsptJump)
*pb = (char) (fInvisible ? bLongInvHashJump : bLongHashJump);
else {
ASSERT(*phspt == hsptDefine);
*pb = (char) (fInvisible ? bLongInvHashNote : bLongHashNote);
}
++pb;
if (pszMember == NULL) {
// store size of data in bytes (flags + HASH + filename + '\0')
cb = sizeof(BYTE) + sizeof(HASH) + strlen(pszFile) + 1;
*(WORD *) pb = cb;
pb += sizeof(WORD);
// store flags
((QJI) pb) ->bFlags = fSzFile;
// store hash
((QJI) pb) ->hash = hash;
// store file name
lstrcpy((PSTR) ((QJI) pb)->uf.szFileOnly, pszFile);
}
else { // pszMember != NULL
// copy so we can overwrite buffer
pszMember = lcStrDup(pszMember);
// store size of data in bytes (flags + HASH + filename\0 + memname\0)
cb = sizeof(BYTE) + sizeof(HASH) + strlen(pszFile) + 1
+ strlen(pszMember) + 1;
*(WORD *) pb = cb;
pb += sizeof(WORD);
// store flags
((QJI) pb) ->bFlags = fSzFile | fSzMember;
// store hash
((QJI) pb) ->hash = hash;
// store member name
lstrcpy((PSTR) ((QJI) pb)->uf.szMemberAndFile, pszMember);
// store file name
lstrcpy((PSTR) ((QJI) pb)->uf.szMemberAndFile + strlen(pszMember) + 1,
pszFile);
lcFree(pszMember);
}
if (fUnderlined)
*phspt = ULHsptFromHspt(*phspt);
lcFree(pszFile);
return sizeof(char) + sizeof(WORD) + cb;
}
}
/***************************************************************************
*
- Name: VerifyShedBinding
-
* Purpose:
* This function gets called with shed binding strings, which may
* contain macros or context strings that need to be checked for
* errors.
*
* Arguments:
* bBindType: Bind character.
* szBinding: Binding string.
*
* Returns:
* nothing.
*
* Globals:
* Gets warning level and error log file from err.
*
* +++
*
* Notes:
*
***************************************************************************/
void STDCALL VerifyShedBinding(BYTE bBindType, PSTR szBinding,
PSTR pchFile)
{
ERR err;
HASH hash;
PSTR pszMember, pszContext;
if (bBindType == COLDSPOT)
return;
err.lpszFile = pchFile;
err.ep = epTopic; // REVIEW
err.iTopic = 0;
err.iWarningLevel = errHpj.iWarningLevel;
CStr pszBinding(szBinding);
if (FMacroHotspot(bBindType)) {
// We don't care about the actual macro, we just want to report any
// problems with it.
Execute(SzTrimSz(pszBinding));
}
else {
// Don't check interfile jumps
if (StrChr(pszBinding, '@', fDBCSSystem))
return;
pszMember = StrChr(pszBinding, '>', fDBCSSystem);
if (pszMember != NULL) {
*pszMember++ = '\0';
pszMember = SzTrimSz(pszMember);
if (VerifyWindowName(pszMember) == INDEX_BAD)
return;
}
pszContext = SzTrimSz(pszBinding);
if (*pszContext == '\0') {
VReportError(HCERR_MISS_SHED_CTX, &errHpj);
return;
}
else if (!FValidContextSz(pszContext)) {
VReportError(HCERR_INV_SHED_CTX, &errHpj, pszContext);
return;
}
hash = HashFromSz(pszContext);
FRecordContext(hash, pszContext, SzTranslateHash(&hash), FALSE, &err);
}
}
/***************************************************************************
*
- Name: FValidContextSz
-
* Purpose:
* This function determines whether the given string may be
* used as a context string.
*
* Arguments:
* SZ: String to validate.
*
* Returns:
* TRUE if the string is a valid context string, FALSE otherwise.
*
***************************************************************************/
BOOL STDCALL FValidContextSz(PCSTR pszContext)
{
/*
* To avoid confusion with macro strings, context strings may not begin
* with an exclamation point.
*/
if (*pszContext == CH_MACRO || *pszContext == '\0')
return FALSE;
// Version 4.x help files have almost no limitations on context strings
if (version >= 4) {
if (strpbrk(pszContext, "#=>@%"))
return FALSE;
else
return TRUE;
}
for (; *pszContext != '\0'; pszContext++) {
if (!IsCharAlphaNumeric(*pszContext) && *pszContext != '!' &&
*pszContext != '.' && *pszContext != '_')
return FALSE;
}
return TRUE;
}
/***************************************************************************
*
- Name: HashFromSz
-
* Purpose:
* This function returns a hash value from the given context string.
* The string is assumed to contain only valid context string characters.
*
* Arguments:
* SZ: Null terminated string to compute the hash value from.
*
* Returns:
* The hash value for the given string.
*
***************************************************************************/
// REVIEW: 26-Jul-1993 [ralphw] Why 43 when we only use 38 characters? Will
// this allow for the addition of the SPACE character?
// This constant defines the alphabet size for our hash function.
static const HASH MAX_CHARS = 43L;
HASH STDCALL HashFromSz(PSTR pszKey)
{
int ich, cch;
HASH hash = 0L;
cch = strlen(pszKey);
// REVIEW: 14-Oct-1993 [ralphw] -- Note lack of check for a hash collision.
for (ich = 0; ich < cch; ++ich) {
if (pszKey[ich] == '!')
hash = (hash * MAX_CHARS) + 11;
else if (pszKey[ich] == '.')
hash = (hash * MAX_CHARS) + 12;
else if (pszKey[ich] == '_')
hash = (hash * MAX_CHARS) + 13;
else if (pszKey[ich] == '0')
hash = (hash * MAX_CHARS) + 10;
/*
* REVIEW: 26-Jul-1993 [ralphw] -- I'll bet this is an
* international issue. Find out what they did in 3.1 in
* adt\hash.c.
*/
else if (pszKey[ich] <= 'Z')
hash = (hash * MAX_CHARS) + (pszKey[ich] - '0');
else
hash = (hash * MAX_CHARS) + (pszKey[ich] - '0' - ('a' - 'A'));
}
/*
* Since the value 0 is reserved as a nil value, if any context
* string actually hashes to this value, we just move it.
*/
return (hash == 0 ? 0 + 1 : hash);
}
/***************************************************************************
FUNCTION: VerifyWindowName
PURPOSE: Called when an interfile jump specifies a window -- verifies
that the window exists
PARAMETERS:
pszWindow
RETURNS: TRUE if the window is valid
COMMENTS:
MODIFICATION DATES:
15-Mar-1994 [ralphw]
***************************************************************************/
UINT STDCALL VerifyWindowName(PCSTR pszWindow)
{
if (!pdrgWsmag || pdrgWsmag->Count() == 0) {
VReportError(HCERR_NO_WINDOW_SECTION, &errHpj, pszWindow);
return (UINT) INDEX_BAD;
}
PWSMAG qwsmag;
int iwsmag;
for (qwsmag = (PWSMAG) pdrgWsmag->GetBasePtr(), iwsmag = 0;
iwsmag < pdrgWsmag->Count();
qwsmag++, iwsmag++) {
if (StrICmp(qwsmag->rgchMember, pszWindow) == 0)
return (UINT) iwsmag; // found the window
}
// Check for "main" window class.
if (iwsmag == pdrgWsmag->Count() &&
StrICmp(pszWindow, txtMainWindow) != 0) {
VReportError(HCERR_NO_SUCH_WINDOW, &errHpj, pszWindow);
return (UINT) INDEX_BAD;
}
return (UINT) INDEX_MAIN;
}
/***************************************************************************
FUNCTION: StrICmp
PURPOSE: Use this whenever a string comparison that is sensitive
to NLS considerations needs to be used.
PARAMETERS:
psz1
psz2
RETURNS:
COMMENTS:
MODIFICATION DATES:
12-Jun-1994 [ralphw]
***************************************************************************/
int STDCALL StrICmp(PCSTR psz1, PCSTR psz2)
{
// We do this for speed and because JChicago build 122 gives incorrect
// results for CompareStringA
if (!lcid || LANGIDFROMLCID(lcid) == 0x0409) // American locale
return _stricmp(psz1, psz2);
else
return CompareStringA(lcid, fsCompareI | NORM_IGNORECASE,
psz1, -1, psz2, -1) - 2;
}