NT4/private/windows/win4help/winhelp/system.c
2020-09-30 17:12:29 +02:00

692 lines
17 KiB
C

/*****************************************************************************
*
* SYSTEM.C
*
* Copyright (C) Microsoft Corporation 1990.
* All Rights reserved.
*
******************************************************************************
*
* Module Intent
*
* This module reads in all of the tagged information from the |SYSTEM file.
*
*****************************************************************************/
#include "help.h"
//#include "inc\btpriv.h"
#include "inc\systag.h"
#ifndef MAKELCID
#define MAKELCID(lgid, srtid) ((DWORD)((((DWORD)((WORD )(srtid))) << 16) | \
((DWORD)((WORD )(lgid)))))
#endif
BYTE* pSysBufRead; // Points to point in |SYSTEM file buffer just after stuff we've already read.
static int cbRead; // Number of bytes already read from pSysBuf.
static int cbSystemFile; // Total number of bytes in |SYSTEM file buffer.
static BOOL STDCALL FReadBufferQch(void*, int);
static BOOL STDCALL FSkipReadBufferQch(int cbToRead);
INLINE static BOOL STDCALL FCheckSystem(QHHDR, UINT*);
/***************************************************************************
*
- Name: FReadBufferQch
-
* Purpose: To read from the System buffer and advance the count of what's
* been read. If qchToRead == NULL, then we just seek forward.
*
* Arguments: LPSTR qchToRead - Buffer to read System buffer into.
* LONG cbToRead - Number of bytes to read.
*
* Returns: BOOL - TRUE if read successful.
*
* Globals Used: pSysBufRead - The static pointer to where we are in the
* |SYSTEM buffer.
*
* +++
*
* Notes: Trivial function, but making it a function makes things easier
* elsewhere.
*
***************************************************************************/
static BOOL STDCALL FReadBufferQch(void* pDst, int cbToRead)
{
ASSERT((cbRead + cbToRead) <= cbSystemFile)
if ((cbRead + cbToRead) > cbSystemFile)
return FALSE;
if (pDst != NULL)
MoveMemory(pDst, pSysBufRead, cbToRead);
pSysBufRead += cbToRead;
cbRead += cbToRead;
return TRUE;
}
/***************************************************************************
FUNCTION: FSkipReadBufferQch
PURPOSE: Skip over the specified amount of data
PARAMETERS:
cbToRead
RETURNS:
COMMENTS:
MODIFICATION DATES:
24-Feb-1993 [ralphw]
***************************************************************************/
static BOOL STDCALL FSkipReadBufferQch(int cbToRead)
{
ASSERT((cbRead + cbToRead) <= cbSystemFile)
if ((cbRead + cbToRead) > cbSystemFile)
return FALSE;
pSysBufRead += cbToRead;
cbRead += cbToRead;
return TRUE;
}
/***************
*
- FCheckSystem
-
* purpose
* Verifies that the file system can be displayed by this version
* of the software.
*
* arguments
* QHHDR qhhdr - far pointer to help header structure
*
* return value
* TRUE iff valid
* return system help header in qhhdr
*
* globals used
* pSysBufRead - The static pointer to where we are in the |SYSTEM buffer
*
**************/
INLINE static BOOL STDCALL FCheckSystem(QHHDR qhhdr, UINT* pwErr)
{
/*
* We read Help 3.0, 3.1 (3.5) or 4.0 files. But certain in-between
* versions of Help 3.5 files are no longer supported. The format number
* in the header now indicates a sub-version of a supported version, and
* so is not checked here.
*/
// Read in the first field of the HHDR, the Magic number.
if ((!FReadBufferQch((LPBYTE) &qhhdr->wMagic, sizeof(WORD)))
|| (qhhdr->wMagic != MagicWord)) {
*pwErr = wERRS_OLDFILE;
return FALSE;
}
// Read in the rest of the fields, except for those that are new.
if ((!FReadBufferQch((LPBYTE) &qhhdr->wVersionNo, sizeof(WORD)))
|| (! FReadBufferQch((LPBYTE) &qhhdr->wVersionFmt, sizeof(WORD)))
|| (! FReadBufferQch((LPBYTE) &qhhdr->lDateCreated, sizeof(LONG)))
|| (! FReadBufferQch((LPBYTE) &qhhdr->wFlags, sizeof(WORD)))) {
*pwErr = wERRS_OLDFILE;
return FALSE;
}
/*
* WARNING: Version dependency: Fix for Help 3.5 bug 488. The Help 3.0
* and 3.1 compilers do not initialize the wFlags bits. Only the fDebug
* bit is used.
*/
if (qhhdr->wVersionNo == wVersion3_0)
qhhdr->wFlags &= fDEBUG;
if ((qhhdr->wMagic != MagicWord)
|| ((qhhdr->wVersionNo < wVersion3_1)
&& qhhdr->wVersionNo != wVersion3_0)
) {
*pwErr = wERRS_OLDFILE;
return FALSE;
}
if (qhhdr->wVersionNo > VersionNo) {
*pwErr = wERRS_NEEDNEWRUNTIME;
goto error_return;
}
return TRUE;
error_return:
PostErrorMessage((WPARAM) *pwErr);
return FALSE;
}
/***************************************************************************
*
- Name: FReadSystemFile
-
* Purpose:
* Reads in the tagged data from the |SYSTEM file
*
* Arguments:
* hfs - handle to file system to read
* pdb - near pointer to DB struct to fill
* pwErr - pointer to place error word
*
* Returns: True if valid version number, system file
* pdb is changed
*
* Globals Used: pSysBufRead, cbRead, cbSystemFile
*
* History:
* 24-Feb-1993 [ralphw] Added fTitleOnly for when we build a master
* keyword list.
*
***************************************************************************/
#ifndef NO_PRAGMAS
#pragma data_seg(".text", "CODE")
#endif
static const char txtSystem[] = "|SYSTEM";
static const char txtDefaultSeparators[] = ",:"; // default index separators
static const char txt036[] = "(036";
static const char txt037[] = "037";
#ifndef NO_PRAGMAS
#pragma data_seg()
#endif
/***************************************************************************
FUNCTION: FReadSystemFile
PURPOSE: Reads the |SYSTEM file
PARAMETERS:
hfs handle to the file system
pdb DB structure to fill if fTag is NULL
pwErr pointer to place error
fTag non-zero to specify a specific tag to read (see Comments)
RETURNS: TRUE if we could read the file
COMMENTS:
The only specific tag supported is tagLCID;
MODIFICATION DATES:
02-Nov-1994 [ralphw]
***************************************************************************/
BOOL STDCALL FReadSystemFile(HFS hfs, PDB pdb, UINT* pwErr, UINT fTag)
{
HF hf;
W_TAG tagRead=tagFirst;
LPBYTE pSysBuf;
WORD cbwData;
int cbData;
LPSTR lpszTempBuf;
BOOL fCharSets = FALSE; // TRUE if tagCHARSET specified
QRGWSMAG qrgwsmag; // pointer to wsmag info
UINT iWsmag = 0; // count of smags seen
HHDR hdr;
*pwErr = wERRS_BADFILE; // REVIEW - is this a reasonable default????
// Initialize fields of DE to default values:
if (pdb) {
PDB_ADDRCONTENTS(pdb) = addrNil;
PDB_RGCHCOPYRIGHT(pdb)[0] = '\0';
PDB_RGCHTITLE(pdb)[0] = '\0';
ZeroMemory(&pdb->kwlcid, sizeof(KEYWORD_LOCALE));
if (pdb->aCharSets)
lcClearFree(&pdb->aCharSets);
}
// Open the |SYSTEM subsystem.
if ((hf = HfOpenHfs(hfs, txtSystem, fFSOpenReadOnly)) == NULL) {
if (RcGetFSError() == rcOutOfMemory)
*pwErr = wERRS_OOM;
else
*pwErr = wERRS_BADFILE; // this is good enough
goto error_quit;
}
// Get the size of the |SYSTEM file, and read it into a buffer.
cbSystemFile = LcbSizeHf(hf);
pSysBuf = (LPBYTE) GhAlloc(LMEM_FIXED, cbSystemFile);
if (!pSysBuf) {
*pwErr = wERRS_OOM;
goto error_quit;
}
if (!LcbReadHf(hf, pSysBuf, cbSystemFile)) {
if (RcGetFSError() == rcOutOfMemory)
*pwErr = wERRS_OOM;
else
*pwErr = wERRS_OLDFILE;
Ensure(RcCloseHf(hf), rcSuccess);
goto error_return;
}
pSysBufRead = pSysBuf;
cbRead = 0;
Ensure(RcCloseHf(hf), rcSuccess);
if (fTag) {
if (!FCheckSystem(&hdr, pwErr)) {
if (fTag == tagLCID)
lcid = GetUserDefaultLCID();
goto error_return;
}
if (hdr.wVersionNo == wVersion3_0)
FSkipReadBufferQch(cbSystemFile - cbRead);
for (;;) {
if (cbRead >= cbSystemFile ||
!FReadBufferQch((LPBYTE) &tagRead, sizeof(W_TAG))) {
if (fTag == tagLCID)
lcid = GetUserDefaultLCID();
FreeGh(pSysBuf);
return TRUE; // Out of tags.
}
ASSERT ((tagRead > tagFirst) && (tagRead < tagLast));
if (FReadBufferQch((PBYTE) &cbwData, sizeof(WORD)))
cbData = (int) cbwData; // convert to an int
else {
if (fTag == tagLCID)
lcid = MAKELCID(kwlcid.langid, SORT_DEFAULT);
/*
* Note that even though we are generating an error return,
* we have set the value for lcid, so the caller can ignore
* the error.
*/
goto error_return;
}
if (tagRead == fTag) {
switch (fTag) {
case tagLCID: // Locale Identifier and CompareStringA flags
{
KEYWORD_LOCALE kwlcid;
#ifdef _X86_
ASSERT(cbData == sizeof(KEYWORD_LOCALE));
#else
ASSERT(cbData == sizeof(KEYWORD_LOCALE)-2);
#endif
if (!FReadBufferQch(&kwlcid, cbData)) {
lcid = GetUserDefaultLCID();
goto error_return;
}
else
lcid = MAKELCID(kwlcid.langid, SORT_DEFAULT);
FreeGh(pSysBuf);
return TRUE; // Found what we wanted, so return
}
default:
ASSERT(FALSE); // unsupported fTag value
}
}
else {
if (!FSkipReadBufferQch(cbData)) {
if (fTag == tagLCID)
lcid = GetUserDefaultLCID();
goto error_return;
}
}
continue;
}
}
if (!FCheckSystem(&PDB_HHDR(pdb), pwErr))
goto error_return;
// Setup various defaults, overriding any previous help file settings
clrPopup = (COLORREF) -1;
if (hfontDefault != hfontSmallSys && hfontDefault) {
DeleteObject(hfontDefault);
hfontDefault = hfontSmallSys;
}
if (defcharset == (WORD) -1) {
HDC hdc = GetDC(NULL);
if (hdc) {
TEXTMETRIC tm;
GetTextMetrics(hdc, &tm);
defcharset = (WORD) tm.tmCharSet;
YAspectMul = GetDeviceCaps(hdc, LOGPIXELSY);
ReleaseDC(NULL, hdc);
}
else
OOM();
}
pdb->kwlcid.langid = 0;
#ifdef DBCS
fDBCS = TRUE; // By default, all files are DBCS. Only 4.0 files can be
// marked as non-DBCS
#endif
// If this is a 3.0 file, just read in the title like we used to.
curHelpFileVersion = (int) PDB_HHDR(pdb).wVersionNo;
if (curHelpFileVersion == wVersion3_0) {
if (!FReadBufferQch(PDB_RGCHTITLE(pdb), cbSystemFile - cbRead)) {
*pwErr = wERRS_OLDFILE;
goto error_return;
}
goto ok_return;
}
// Loop through all tags, reading the data and putting it someplace.
if (pszIndexSeparators)
lcClearFree(&pszIndexSeparators);
for (;;) {
if (cbRead == cbSystemFile || !FReadBufferQch((LPBYTE) &tagRead, sizeof(W_TAG)))
break; // Out of tags.
ASSERT ((tagRead > tagFirst) && (tagRead < tagLast));
if (!FReadBufferQch((LPBYTE) &cbwData, sizeof(WORD)))
goto error_return;
cbData = (int) cbwData;
// The value of tagRead decides where we will read the data into.
switch (tagRead) {
case tagTitle:
lpszTempBuf = PDB_RGCHTITLE(pdb);
break;
case tagCopyright:
lpszTempBuf = PDB_RGCHCOPYRIGHT(pdb);
break;
case tagContents:
lpszTempBuf = (LPSTR) &PDB_ADDRCONTENTS(pdb);
break;
case tagCitation:
/*
* Citation tag: additional text to be appended to the end
* of copy-to-clipboard. Just stick in some global memory
* referenced by the DB, where it can be picked up when
* needed by the copy code.
*/
if (PDB_HCITATION(pdb))
FreeGh (PDB_HCITATION(pdb));
PDB_HCITATION(pdb) = GhAlloc(GMEM_FIXED, cbData);
if (!PDB_HCITATION(pdb))
goto error_return;
if (!FReadBufferQch(PtrFromGh(PDB_HCITATION(pdb)), cbData))
goto error_return;
ASSERT(lstrlen(pSysBufRead) < cbData);
lpszTempBuf = NULL;
cbData = 0;
break;
case tagConfig:
if (PDB_LLMACROS(pdb) == NULL) {
if ((PDB_LLMACROS(pdb) = LLCreate()) == NULL)
goto error_return;
}
if (!InsertEndLL(PDB_LLMACROS(pdb), pSysBufRead, cbData)) {
*pwErr = wERRS_OOM;
goto error_return;
}
if (!FReadBufferQch(NULL, cbData))
goto error_return;
lpszTempBuf = NULL;
cbData = 0;
break;
case tagIcon:
// WinHelp 4.0 doesn't support the icon tag
FSkipReadBufferQch(cbData);
continue;
case tagWindow:
// window tag. We collect all the wsmag structures into a single
// block of memory, and hang that sucker off the de.
if (!PDB_HRGWSMAG(pdb)) {
/*
* Block has not yet been allocated. We always allocate
* the maximum size block, just because managing it as
* variable size is more of a pain than it's worth. When we
* go to multiple secondary windows and the number increases,
* this will no longer be true.
*/
ASSERT (iWsmag == 0);
// REVIEW: could probably just used LMEM_FIXED
PDB_HRGWSMAG(pdb) = (HWSMAG) GhAlloc(GPTR, sizeof(RGWSMAG));
if (!PDB_HRGWSMAG(pdb)) {
*pwErr = wERRS_OOM;
goto error_return;
}
}
else {
// Increase the size to allow for the new window array
PDB_HRGWSMAG(pdb) = (HWSMAG) GhResize(PDB_HRGWSMAG(pdb), 0,
sizeof(RGWSMAG) + sizeof(WSMAG) *
(((QRGWSMAG) PtrFromGh(PDB_HRGWSMAG(pdb)))->cWsmag + 1));
if (!PDB_HRGWSMAG(pdb)) {
*pwErr = wERRS_OOM;
goto error_return;
}
}
qrgwsmag = (QRGWSMAG) PtrFromGh(PDB_HRGWSMAG(pdb));
// Increment the count of structures in the block, point at the
// appropriate new slot, and copy in the new structure.
#ifdef _X86_
qrgwsmag->rgwsmag[iWsmag++] = *(QWSMAG)pSysBufRead;
#else
LcbMapSDFF(ISdffFileIdHfs(hfs), SE_WSMAG, &qrgwsmag->rgwsmag[iWsmag++],pSysBufRead);
//CopyMemory(&qrgwsmag->rgwsmag[iWsmag++], pSysBufRead, sizeof(WSMAG));
#endif
qrgwsmag->cWsmag = iWsmag;
lpszTempBuf = NULL;
break;
// ****** The follow tags are new to WinHelp 4.0 **********
case tagLCID: // Locale Identifier and CompareStringA flags
#ifdef _X86_
ASSERT(cbData == sizeof(KEYWORD_LOCALE));
#else
ASSERT(cbData == sizeof(KEYWORD_LOCALE)-2);
#endif
lpszTempBuf = (LPSTR) &pdb->kwlcid;
break;
case tagCHARSET:
pdb->aCharSets = (PBYTE) lcMalloc(cbData + 1);
lpszTempBuf = (LPSTR) pdb->aCharSets;
#ifdef _DEBUG
cCharSets = cbData;
#endif
fCharSets = TRUE;
break;
// New to 4.0
case tagDefFont:
ASSERT(YAspectMul);
{
/*
* This is used to create the font to be used when we
* display default text from the help file. This includes
* keywords, topic titles, the Contents Tab, etc.
*
* Format is: pt_size,charset,Font name
*/
char szBuf[128];
int dy;
int nPtSize;
ASSERT(cbData < sizeof(szBuf));
ASSERT(!hfontDefault || hfontDefault == hfontSmallSys);
if (!FReadBufferQch(szBuf, cbData))
goto error_return;
nPtSize = ((int) szBuf[0]) * 2 + cntFlags.iFontAdjustment;
dy = MulDiv(YAspectMul, nPtSize, 144);
hfontDefault = CreateFont(-dy,
0, 0, 0, 0, 0, 0, 0,
szBuf[1], OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
VARIABLE_PITCH | FF_MODERN, szBuf + 2);
}
continue;
case tagCNT:
{
if (pszCntFile)
FreeLh(pszCntFile);
pszCntFile = (PSTR) LhAlloc(LMEM_FIXED, cbData);
lpszTempBuf = pszCntFile;
break;
}
case tagPopupColor:
ASSERT(cbData == sizeof(COLORREF));
lpszTempBuf = (LPSTR) &clrPopup;
break;
case tagIndexSep:
pszIndexSeparators = lcMalloc(256);
lpszTempBuf = pszIndexSeparators;
break;
default:
// Unimplemented tag. Ignore it.
ASSERT(FALSE);
lpszTempBuf = NULL;
break;
}
if (!FReadBufferQch(lpszTempBuf, cbData))
goto error_return;
}
ok_return:
FreeGh(pSysBuf);
// if (!fIconTag)
// ResetIcon();
if (pdb->kwlcid.langid) {
lcid = MAKELCID(pdb->kwlcid.langid, SORT_DEFAULT);
if (!IsValidLocale(lcid, LCID_INSTALLED)) {
*pwErr = wERRS_INVALID_LOCALE;
goto error_quit;
}
switch (pdb->kwlcid.langid) {
case 0x0411: // Japanese
case 0x0404: // Taiwan
case 0x1004: // Singapore
case 0x0C04: // Hong Kong
fDBCS = TRUE;
default:
fDBCS = FALSE;
}
}
else if (fHelp != POPUP_HELP) {
lcid = GetUserDefaultLCID();
}
kwlcid = pdb->kwlcid;
pdb->lcid = lcid;
/*
* Determine if this is a DBCS help file or not. For 4.0 files, this
* should be an exact test. For 3.1 files, it may be marked as DBCS when
* it really isn't a DBCS help file.
*/
#ifdef DBCS
if (defcharset == HANGEUL_CHARSET) {
pszDbcsMenuAccelerator = txt036;
pszDbcsRomanAccelerator = txt037;
}
else if (defcharset == SHIFTJIS_CHARSET) {
pszDbcsMenuAccelerator = txt037;
pszDbcsRomanAccelerator = txt036;
}
else {
pszDbcsMenuAccelerator = NULL;
pszDbcsRomanAccelerator = NULL;
}
#endif // DBCS
if (pszIndexSeparators)
pszIndexSeparators = (PSTR) lcReAlloc(pszIndexSeparators,
strlen(pszIndexSeparators) + 1);
else
pszIndexSeparators = lcStrDup(txtDefaultSeparators);
return TRUE;
error_return:
FreeGh(pSysBuf);
error_quit:
ASSERT(*pwErr != wERRS_BADFILE && *pwErr != wERRS_OLDFILE);
if (*pwErr == wERRS_BADFILE || *pwErr == wERRS_OLDFILE || *pwErr == wERRS_INVALID_LOCALE)
ErrorVarArgs(*pwErr, wERRA_RETURN, PszFromGh(PDB_FM(pdb)));
return FALSE;
}