windows-nt/Source/XPSP1/NT/sdktools/mep/help/mshelp/help.c

1746 lines
58 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*** help.c - help library main
*
* Copyright <C> 1988-1990, Microsoft Corporation
*
* Definitions:
*
* Context Map: Mapping of context number to topic number.
* Allows multiple contexts to be associated with a
* single topic. Syncronized with the context
* string table, each entry contains the topic
* number associated with the corresponding context
* string.
*
* Context String: String on which help can be "looked up".
*
* Context String Table: Table of all valid context strings in a
* particular help file.
*
* Local Context: A type of context which bypasses the context
* string and context numbers. In cross references,
* encoded as a cross reference string of NULL,
* followed by a topic number ored with 0x8000.
*
* nc: (Context Number) A long which uniquely
* identifies a help file and context string, or
* for local contexts, the helpfile and topic
* number. Formatted as:
*
* +----------------+----------------+
* | Fdb Mem Handle | context number |
* +----------------+----------------+
*
* Where the upper word is the memory handle of the
* allocated fdb for the help file. The lower word
* is the either the "true" context number (see
* below) if <= 0x7fff, or the actual topic number
* or'ed with 0x8000.
*
* Topic: Actual help textual entry. May be compressed.
*
* Topic Index: Table of file positions of all topics contained
* in a help file. Indexed by the topic number,
* returns that topics physical position in the
* file.
*
* Topic Number: Index to a particular topic. Topic numbers are
* zero based, and reflect the physical ordering of
* the topics in the file.
*
* "True" context number: is the zero based index or string number in the
* <context string table>. I.E. the "n'th" string
* has context number "n".
*
* The progression from string to true context number to topic number to file
* position is:
*
* 1) Context String ==> "True" Context Number
*
* The string is searched for in the <context string table>, and
* it's number becomes the "true" context number.
*
* 2) "True" Context Number ==> Topic Number
*
* The context number is an index into the <context map>, returing
* the topic number associated with the context number.
*
* 3) Topic Number ==> File Position
*
* The topic number is used as an index into the Topic Index, from
* which the physical file position is retrieved.
*
* Notes:
* QuickPascal requires NO initialized data. In this case, CLEAR is defined,
* and the HelpInit routine is included. We also play some pointer games to
* simple variables, because the C compiler can generate CONST segment
* entries for the SEG of various vars. (This enables it to load the segment
* register directly, rather than by using SEG varname and another
* register). Q/P cannot support this action by the compiler.
*
* QuickHelp for OS/2 is reentrant. This code should remain reentrant up to
* but not including allocating and deallocating fdbs.
*
* Revision History:
*
* 17-Aug-1990 ln Don't blindly request 64k of an ascii file. Query
* for size first, then read. Allocations based on
* previous topic size requests may cause the OS to
* GPFault for an out of range read.
* 16-Jul-1990 ln Searching for "filename!" where filename is a QH
* file, will now fail, rather than GP fault. Searching
* for "!" will now succeed.
* 08-Jun-1990 ln Remove HelpLock usage in HelpNcCmp
* 13-Apr-1990 ln Try to get HelpSzContext to return strings in more
* cases where it can.
* 12-Mar-1990 ln Rename CloseFile -> HelpCloseFile
* 08-Oct-1989 ln Changes to improve the previous change to work (allow
* decompression) more often in low memory bases.
* Deallocate table in OpenCore to reduce fragmentation
* in non-moveable memory systems.
* 19-May-1989 ln Correct bug in decompressing, where we would not
* decompress if the tables didn;t exist.
* 12-Apr-1989 ln Ensure that we handle Locks failing correctly. Also
* remove TossPortion usage. Unlock handles directly.
* 10-Mar-1989 ln Change MapTopicToContext to look forward. Changed
* HelpNc to look begining at passed context string.
* 17-Jan-1989 ln Correct creation of basename in HelpOpen to account
* for environment syntax.
* 09-Dec-1988 ln Add HelpNcUniq
* 25-Oct-1988 ln Added minascii support to HelpNcPrev. Correct
* minascii bug in HelpSzContext.
* 14-Sep-1988 ln Improve doc. Remove ambiguity in MapContextToTopic
* return value. Improve error checking in various
* places.
* 01-Sep-1988 ln Check ReadHelpFile return value in LoadPortion
* 12-Aug-1988 ln Add check for memory discarded in alloc durring
* HelpDecomp.
* 08-Aug-1988 ln Ensure HelpClose closes ALL files. (Off by one error
* in loop).
* 14-Apr-1988 ln Modified to conform to QC (CW?) restriction that
* prohibits any segments from being locked when an
* allocation is performed.
* [] 15-Dec-1987 ln Created, for design.
*
*************************************************************************/
#include <assert.h> /* debugging assertions */
#include <io.h> /* I/O function declarations */
#include <stdlib.h> /* standard library */
#include <stdio.h> /* standard I/O definitions */
#if defined (OS2)
#define INCL_BASE
#include <os2.h>
#else
#include <windows.h>
#endif
#include "help.h" /* global (help & user) decl */
#include "helpfile.h" /* help file format definition */
#include "helpsys.h" /* internal (help sys only) decl*/
#define MASIZE 512 /* size of ma input buffer */
#define MAOVER 64 /* size of ma search overlap */
#define ISERROR(x) (((x).mh == 0L) && ((x).cn <= HELPERR_MAX))
#define SETERROR(x,y) { (x).mh = 0L; (x).cn = y; }
/*************************************************************************
**
** Forward definitions
*/
void pascal near CloseShrink(nc, f);
f pascal near LoadFdb (mh, fdb far *);
mh pascal near LoadPortion (int, mh);
ushort pascal near MapContexttoTopic (nc, fdb far *);
nc pascal near MapTopictoContext(ushort, fdb far *, int);
nc pascal near NextPrev(nc,int);
nc pascal near OpenCore(FILE *, ulong, uchar far *, struct helpheader *, fdb far *);
f pascal near PutFdb (mh, fdb far *);
f pascal near SizePos (nc, ushort *,ulong *);
ushort pascal near decomp (uchar far *, uchar far *, uchar far *, uchar far *);
char far * pascal near hfmemzer(void far *, ushort);
char far * pascal near hfstrchr(char far *, char);
char far * pascal near hfstrcpy(char far *, char far *);
ushort pascal near hfstrlen(char far *);
f pascal far HelpCmp (uchar far *, uchar far *, ushort, f, f);
f pascal near HelpCmpSz (uchar far *, uchar far *);
void pascal near kwPtrBuild(uchar far *, ushort);
#if ASCII
long pascal near maLocate (fdb far *, uchar far *, ulong,
f (pascal far *)(uchar far *, uchar far *, ushort, f, f));
nc pascal near combineNc (ulong, mh);
ulong pascal near NctoFo (ulong);
#endif
/*************************************************************************
**
** External Global data
** BEWARE. The effects of global data on reentrancy should be VERY carefully
** considered.
**
*************************************************************************/
extern mh tbmhFdb[MAXFILES+1];
extern char szNil[1];
extern ushort cBack;
#ifdef CLEAR
/*************************************************************************
**
** HelpInit - One-time initialization
**
** Purpose:
** Performs one-time initialization. Right now that's a zero fill of static
** memory for those environments which don't support pre-inited static
** memory.
**
** Entry:
** none
**
** Exit:
** none
**
*/
void far pascal LOADDS HelpInit () {
hfmemzer (tbmhFdb, sizeof(tbmhFdb)); /* zero entire fdb handle table */
hfmemzer (szNil, sizeof(szNil)); /* zero null string */
hfmemzer (&cBack, sizeof(cBack)); /* zero back trace count */
/* end HelpInit */}
#endif
/*************************************************************************
**
** HelpOpen - Open help file & return help handle.
**
** Purpose:
** Given the file basename, locate the associated help file on the path, and
** open it, initializing internal data structures as appropriate.
**
** Entry:
** fpszName - base filename to be openned.
**
** Exit:
** nc initial context for openned file.
**
** Exceptions:
** Returns error code on failure to open for any reason.
**
*/
nc far pascal LOADDS HelpOpen (
char far *fpszName
) {
FILE *fhT; /* temp file handle */
fdb fdbLocal; /* local copy of fdb to use */
uchar far *fpszBase; /* base filename */
void far *fpT;
struct helpheader hdrLocal; /* for use by opencore */
nc ncRet = {0,0}; /* first context */
mh *ptbmhFdb; /* pointer into mh table */
/*
** create basename by removing possible env variable, drive, and scanning
** for last path seperator
*/
fpszBase = fpszName;
if (fpT = hfstrchr(fpszBase,':'))
fpszBase = (uchar far *)fpT+1;
while (fpT = hfstrchr(fpszBase,'\\'))
fpszBase = (uchar far *)fpT+1;
/*
** Scan FDB's for an open file of the same base name. If we encounter the name,
** in either the true filename, or file header, just return that file's initial
** context. Otherwise fall below to try and open it.
*/
for (ptbmhFdb=&tbmhFdb[1]; ptbmhFdb<=&tbmhFdb[MAXFILES]; ptbmhFdb++) {
if (LoadFdb (*ptbmhFdb,&fdbLocal)) {
if (HelpCmpSz(fpszBase,fdbLocal.fname) ||
HelpCmpSz(fpszBase,fdbLocal.hdr.fname))
ncRet = fdbLocal.ncInit;
if (ncRet.mh && ncRet.cn)
return ncRet;
}
}
/*
** Open file. If we can, then call the core open routine to open the file (and
** any anything appended to it).
**
** Warning: the app may callback HelpClose at this point.
*/
if (fhT = OpenFileOnPath(fpszName,FALSE)) {
ncRet = OpenCore (fhT,0L,fpszBase,&hdrLocal,&fdbLocal);
if (ISERROR(ncRet))
HelpCloseFile (fhT);
return ncRet;
}
SETERROR(ncRet, HELPERR_FNF);
return ncRet;
// rjsa return HELPERR_FNF;
/* end HelpOpen*/}
/*************************************************************************
**
** OpenCore - Recursive core of HelpOpen
**
** Purpose:
** Given the open file handle, initialize internal data structures as
** appropriate. Attempt to open any file that is appended.
**
** Entry:
** fhT - Open file handle
** offset - Offset from start of file of help file to open
** fpszBase - pointer to base filename
**
** Exit:
** initial context, or NULL on failure.
**
** Exceptions:
** Returns NULL on failure to open for any reason.
**
*/
nc pascal near OpenCore (
FILE * fhHelp,
ulong offset,
uchar far *fpszBase, /* base filename */
struct helpheader *phdrLocal,
fdb far *pfdbLocal /* pointer to current FDB */
) {
//void far *fpT;
int ihFree; /* handle for free fdb (& index)*/
mh mhCur; /* current memory handle */
nc ncFirst = {0,0}; /* first context */
nc ncInit; /* first context */
mh *pmhT; /* pointer into mh table */
/*
** Read in helpfile header
*/
if (ReadHelpFile(fhHelp,
offset,
(char far *)phdrLocal,
(ushort)sizeof(struct helpheader))) {
/*
** search for available fdb
*/
for (ihFree = MAXFILES, pmhT = &tbmhFdb[MAXFILES];
ihFree && *pmhT;
ihFree--, pmhT--);
/*
** if an offset is present, and this is NOT a compressed file, or there is no
** available fdb, ignore the operation.
*/
if ( offset
&& (phdrLocal->wMagic != wMagicHELP)
&& (phdrLocal->wMagic != wMagicHELPOld)
) {
SETERROR(ncInit, HELPERR_BADAPPEND);
return ncInit;
// rjsa return HELPERR_BADAPPEND;
}
if (ihFree == 0) {
SETERROR(ncInit, HELPERR_LIMIT);
return ncInit;
// rjsa return HELPERR_LIMIT;
}
/*
** allocate fdb. Again, if we can't, skip it all and return NULL.
*/
if (mhCur = *pmhT = HelpAlloc((ushort)sizeof(fdb))) {
/*
** Fill in helpfile header & appropriate fdb fields
*/
hfmemzer(pfdbLocal,sizeof(fdb)); /* zero entire fdb */
pfdbLocal->fhHelp = fhHelp; /* file handle */
ncFirst.mh = pfdbLocal->ncInit.mh = mhCur;
ncFirst.cn = pfdbLocal->ncInit.cn = 0L;
// rjsa ncFirst = pfdbLocal->ncInit = ((long)mhCur) << 16; /* initial context */
pfdbLocal->foff = offset; /* appended offset */
hfstrcpy(pfdbLocal->fname,fpszBase); /* include base filename*/
/*
** if this is a compressed file (signified by the first two bytes of the header
** we read in above), then note the file type in the fdb. We unlock the fdb, as
** MapTopicToContext and the recursion might cause memory allocation. We get a
** context number for the first topic, and recurse and attempt to open any
** appended file.
*/
if ( (phdrLocal->wMagic == wMagicHELPOld)
|| (phdrLocal->wMagic == wMagicHELP)
) {
if ((phdrLocal->wMagic == wMagicHELP)
&& (phdrLocal->wVersion > wHelpVers)) {
SETERROR(ncInit, HELPERR_BADVERS);
return ncInit;
// rjsa return HELPERR_BADVERS;
}
pfdbLocal->hdr = *phdrLocal;
pfdbLocal->ftype = FTCOMPRESSED | FTFORMATTED;
if (PutFdb (mhCur, pfdbLocal)) {
ncFirst = MapTopictoContext(0,pfdbLocal,0);
// We free the context map (the only thing loaded by the
// MapTopictoContext) in order to reduce fragmentation in
// non-moveable memory based systems.
//
HelpDealloc (pfdbLocal->rgmhSections[HS_CONTEXTMAP]);
pfdbLocal->rgmhSections[HS_CONTEXTMAP] = 0;
ncInit = OpenCore(fhHelp,pfdbLocal->hdr.tbPos[HS_NEXT]+offset,szNil,phdrLocal,pfdbLocal);
if (LoadFdb (mhCur, pfdbLocal)) {
//if (ncInit.cn > HELPERR_MAX) {
if ( !(ISERROR(ncInit)) ) {
pfdbLocal->ncLink = ncInit;
} else {
pfdbLocal->ncLink.mh = (mh)0;
pfdbLocal->ncLink.cn = 0L;
}
// rjsa pfdbLocal->ncLink = ncInit > HELPERR_MAX ? ncInit : 0;
pfdbLocal->ncInit = ncFirst;
}
}
}
#if ASCII
/*
** In the case of a minascii formatted file (signified by the first two bytes
** of the header being ">>") we just set up the filetype and "applications
** specific character". The default "ncFirst" is the context for the first
** topic.
*/
else if (phdrLocal->wMagic == 0x3e3e) { /* minascii formatted? */
pfdbLocal->ftype = FTFORMATTED;
pfdbLocal->hdr.appChar = '>'; /* ignore lines with this*/
}
#endif
else if ((phdrLocal->wMagic & 0x8080) == 0) { /* ascii unformatted? */
pfdbLocal->ftype = 0;
pfdbLocal->hdr.appChar = 0xff; /* ignore lines with this*/
}
else {
SETERROR(ncInit, HELPERR_NOTHELP);
return ncInit;
// rjsa return HELPERR_NOTHELP;
}
if (!PutFdb (mhCur, pfdbLocal)) {
ncFirst.mh = (mh)0;
ncFirst.cn = 0L;
}
}
else {
SETERROR(ncFirst, HELPERR_MEMORY);
// rjsa ncFirst = HELPERR_MEMORY; /* error reading file */
}
}
else {
SETERROR(ncFirst, HELPERR_READ);
// rjsa ncFirst = HELPERR_READ; /* error reading file */
}
return ncFirst; /* return valid context */
/* end OpenCore */}
/*************************************************************************
**
** HelpClose - Close Help file
**
** Purpose:
** Close a help file, deallocate all memory associated with it, and free the
** handle.
**
** Entry:
** ncClose - Context for file to be closed. If zero, close all.
**
** Exit:
** None
**
** Exceptions:
** All errors are ignored.
**
*/
void far pascal LOADDS HelpClose (
nc ncClose
) {
CloseShrink(ncClose,TRUE); /* close file(s) */
/* end HelpClose */}
/*************************************************************************
**
** HelpShrink - Release all dynamic memory
**
** Purpose:
** A call to this routines causes the help system to release all dynamic
** memory it may have in use.
**
** Entry:
** None.
**
** Exit:
** None.
**
** Exceptions:
** None.
**
*/
void far pascal LOADDS HelpShrink(void) {
nc ncTmp = {0,0};
CloseShrink(ncTmp,0);
// rjsa CloseShrink(0,0);
/* end HelpShrink */}
/*************************************************************************
**
** CloseShrink - Deallocate memory and possibly Close Help file
**
** Purpose:
** Deallocate all memory associated with a help file, and possibly close free
** it.
**
** Entry:
** ncClose - Context for file. If zero, do all.
** fClose - TRUE if a close operation.
**
** Exit:
** None
**
** Exceptions:
** All errors are ignored.
**
*/
void pascal near CloseShrink (
nc ncClose,
f fClose
) {
fdb fdbLocal; /* pointer to current FDB */
int i;
mh mhClose; /* fdb mem hdl to file to close */
mh *pmhFdb; /* pointer to FDB's table entry */
mhClose = ncClose.mh; /* get index */
// rjsa mhClose = (mh)HIGH(ncClose); /* get index */
for (pmhFdb = &tbmhFdb[0]; /* for each possible entry */
pmhFdb <= &tbmhFdb[MAXFILES];
pmhFdb++
) {
if ((mhClose == 0) /* if all selected */
|| (mhClose == *pmhFdb)) { /* or this one selected */
if (LoadFdb (*pmhFdb, &fdbLocal)) { /* if open file */
/*
* Recurse to close/shrink any appended files
*/
if ((fdbLocal.ncLink.mh || fdbLocal.ncLink.cn) && mhClose)
CloseShrink (fdbLocal.ncLink, fClose);
for (i=HS_count-2; i>=0; i--) /* for dyn mem handles */
HelpDealloc(fdbLocal.rgmhSections[i]); /* dealloc */
hfmemzer(fdbLocal.rgmhSections,sizeof(fdbLocal.rgmhSections));
if (fClose) {
HelpCloseFile(fdbLocal.fhHelp); /* close file */
HelpDealloc(*pmhFdb); /* deallocate fdb */
*pmhFdb = 0;
}
else
PutFdb (*pmhFdb, &fdbLocal); /* update FDB */
}
}
}
/* end CloseShrink */}
/*** HelpNcCmp - Look up context string, provide comparison routine
*
* Given an ascii string, determine the context number of that string. Uses
* user-supplied comparison routine.
*
* Entry:
* lpszContext - Pointer to asciiz context string.
* ncInital - Starting Context, used to locate file.
* lpfnCmp - far pointer to comparison routine to use.
*
* Exit:
* Context number, if found.
*
* Exceptions:
* Returns NULL if context string not found.
*
*************************************************************************/
nc far pascal LOADDS HelpNcCmp (
char far *fpszContext,
nc ncInitial,
f (pascal far *lpfnCmp)(uchar far *, uchar far *, ushort, f, f)
) {
f fFound = FALSE; // TRUE -> found
f fOpened = FALSE; // TRUE -> file was openned here
fdb fdbLocal; // pointer to current FDB
char far *fpszT; // temp far pointer
long i;
long iStart; // nc to start looking at
mh mhCur; // memory handle locked
nc ncRet = {0,0}; // The return value
char far *fpszContexts; // pointer to context strings
// if the context string includes a "filename!", then open that as a help
// file, and point to the context string which may follow.
//
if ((fpszT = hfstrchr(fpszContext,'!')) && (fpszT != fpszContext)) {
*fpszT = 0;
ncInitial = HelpOpen(fpszContext);
*fpszT++ = '!';
fpszContext = fpszT;
fOpened = TRUE;
}
// if helpfile was not openned, just return the error
//
if (ISERROR(ncInitial)) {
ncInitial.mh = (mh)0;
ncInitial.cn = 0L;
return ncInitial;
}
// For compressed files we scan the context strings in the file
// (this turns out not to be that speed critical in
// comparision with decompression, so I haven't bothered), to get the
// context number.
//
// If not found, and there IS a linked (appended) file, we recurse to search
// that file as well.
//
// The context number for compressed files is just the zero based string
// number, plus the number of predefined contexts, with the fdb memory
// handle in the upper word.
//
if (LoadFdb (ncInitial.mh, &fdbLocal)) {
if (fdbLocal.ftype & FTCOMPRESSED) {
// If not a local context look up, get the context strings, and
// search
//
if (*fpszContext) {
mhCur = LoadPortion (HS_CONTEXTSTRINGS, ncInitial.mh);
if ( (mhCur == (mh)0)
|| (mhCur == (mh)(-1))
|| (!(fpszContexts = HelpLock(mhCur)))
) {
ncRet.mh = (mh)0;
ncRet.cn = 0L;
return ncRet;
}
i=0;
// iStart allows us to begin searching from the context string
// passed, as opposed to from the begining each time. This
// allows the application to "carry on" a search from othe last
// place we found a match. This is usefull for multiple
// duplicate context resolution, as well as inexact matching.
//
iStart = ncInitial.cn;
if (iStart & 0x8000)
iStart = 0;
else
iStart--; /* table index is 0 based */
do {
if (i >= iStart) {
fFound = lpfnCmp ( fpszContext
, fpszContexts
, 0xffff
, (f)(fdbLocal.hdr.wFlags & wfCase)
, (f)FALSE);
}
while (*fpszContexts++); /* point to next string */
i++;
}
while ((i < (int)fdbLocal.hdr.cContexts) && !fFound);
HelpUnlock (mhCur);
if (fFound) { /* if a match found */
ncRet.mh = ncInitial.mh;
ncRet.cn = i + fdbLocal.hdr.cPreDef;
// rjsa ncRet = (i+fdbLocal.hdr.cPreDef) /* string # */
// | HIGHONLY(ncInitial); /* & fdb handle */
}
else {
ncInitial.mh = (mh)0;
ncInitial.cn = 0L;
ncRet = HelpNcCmp (fpszContext,fdbLocal.ncLink, lpfnCmp);
}
}
else if (!fOpened) {
ncRet.mh = ncInitial.mh;
ncRet.cn = *(UNALIGNED ushort far *)(fpszContext + 1);
// rjsa ncRet = *(ushort far *)(fpszContext + 1) /* word following*/
// | HIGHONLY(ncInitial); /* & fdb handle */
}
}
#if ASCII
/*
** For minimally formatted ascii files, we sequentially scan the file itself
** for context string definitions until we find the string we care about.
**
** The context number for minascii files is the the byte position/4 of the
** beginning of the associated topic, with the fdb memory handle in the upper
** word.
*/
else if (fdbLocal.ftype & FTFORMATTED) {
if (*fpszContext) {
ncRet.cn = maLocate(&fdbLocal, fpszContext, 0L, lpfnCmp);
if (ncRet.cn == -1L) {
ncRet.mh = (mh)0;
ncRet.cn = 0L;
} else {
ncRet = combineNc(ncRet.cn, fdbLocal.ncInit.mh);
}
// rjsa ncRet = maLocate(&fdbLocal, fpszContext, 0L, lpfnCmp);
// ncRet = (ncRet == -1L)
// ? 0
// : combineNc(ncRet,HIGH(fdbLocal.ncInit));
}
}
/*
** for unformatted ascii files, there must have been NO context string to be
** searched for. In that case, the context number is always 1, plus the fdb
** mem handle.
*/
else if (*fpszContext == 0) { /* if null context string */
ncRet.mh = ncInitial.mh;
ncRet.cn = 1L;
// rjsa ncRet = HIGHONLY(ncInitial) + 1;
}
#endif
}
return ncRet;
/* end HelpNcCmp */}
/*** HelpNc - Look up context string
*
* Given an ascii string, determine the context number of that string.
*
* Entry:
* lpszContext - Pointer to asciiz context string.
* ncInital - Starting Context, used to locate file.
*
* Exit:
* Context number, if found.
*
* Exceptions:
* Returns NULL if context string not found.
*
*************************************************************************/
nc far pascal LOADDS HelpNc (
char far *fpszContext,
nc ncInitial
) {
return HelpNcCmp (fpszContext, ncInitial, HelpCmp);
/* end HelpNc */}
/*************************************************************************
**
** HelpNcCb - Return count of bytes in compressed topic
**
** Purpose:
** Returns the size in bytes of the compressed topic. Provided for
** applications to determine how big a buffer to allocate.
**
** Entry:
** ncCur - Context number to return info on.
**
** Exit:
** Count of bytes in the compressed topic
**
** Exceptions:
** Returns 0 on error.
**
*/
ushort far pascal LOADDS HelpNcCb (
nc ncCur
) {
ulong position;
ushort size;
return SizePos(ncCur,&size,&position) ? size+(ushort)4 : (ushort)0;
/* end HelpNcCb */}
/******************************************************************************
**
** HelpLook - Return compressed topic text
**
** Purpose:
** Places the compressed topic text referenced by a passed context number into
** a user supplied buffer.
**
** Entry:
** ncCur - Context number for which to return text
** pbDest - Pointer to buffer in which to place the result.
**
** Exit:
** Count of bytes in >uncompressed< topic. This is encoded based on file type.
**
** Exceptions:
** Returns NULL on any error
**
*/
ushort far pascal LOADDS HelpLook (
nc ncCur,
PB pbDest
) {
fdb fdbLocal; /* pointer to current FDB */
char far *fpszDest;
int i;
ulong position = 0;
ushort size = 0;
if (LoadFdb (ncCur.mh, &fdbLocal)) { /* get fdb down */
/*
** for both kinds of formatted files, we determine the position of the topic,
** and read it in.
*/
if (fdbLocal.ftype) {
if (SizePos (ncCur,&size,&position)) {
if (fpszDest = (char far *)PBLOCK(pbDest)) {
#ifdef BIGDEBUG
{
char DbgBuf[128];
sprintf(DbgBuf, "HELP: Reading Topic for Context %d at %lX, size %d\n", ncCur.cn, position + fdbLocal.foff, size );
OutputDebugString(DbgBuf);
}
#endif
size = (ushort)ReadHelpFile(fdbLocal.fhHelp
,position + fdbLocal.foff
,fpszDest
,size);
#ifdef BIGDEBUG
{
char DbgBuf[128];
sprintf(DbgBuf, " Read %d bytes to address %lX\n", size, fpszDest );
OutputDebugString(DbgBuf);
}
#endif
/*
** for compressed files, if the read was sucessfull, we then return the
** uncompressed size which is the first word of the topic.
*/
#if ASCII
if (fdbLocal.ftype & FTCOMPRESSED) {
#endif
if (size)
size = *(ushort far *)fpszDest+(ushort)1;
#if ASCII
}
else {
/*
** for minascii files, We also set up for the terminating NULL by scanning for
** the ">>" which begins the next topic, adjusting the size as well.
*/
size -= 4;
for (i=4; i; i--)
if (fpszDest[++size] == '>') break;
fpszDest[size++] = 0;
}
#endif
}
}
}
#if ASCII
else { /* unformatted ascii */
/*
** for unformatted ascii, we just read in (first 64k of) the file.
*/
if (fpszDest = PBLOCK (pbDest)) {
if (SizePos (ncCur,&size,&position)) {
size = (ushort)ReadHelpFile(fdbLocal.fhHelp,0L,fpszDest,size);
fpszDest[size++] = 0; /* terminate ascii text */
}
}
}
#endif
PBUNLOCK (pbDest);
}
if (size) size += sizeof(topichdr); /* adjust for prepended topichdr*/
return size;
/* end HelpLook */}
/******************************************************************************
**
** HelpDecomp - Decompress Topic Text
**
** Purpose:
** Fully decompress topic text. Decompresses based on current file, from one
** buffer to another.
**
** Entry:
** pbTopic - Pointer to compressed topic text
** pbDest - Pointer to destination buffer
**
** Exit:
** FALSE on successful completion
**
** Exceptions:
** Returns TRUE on any error.
**
*/
f far pascal LOADDS HelpDecomp (
PB pbTopic,
PB pbDest,
nc ncContext
) {
fdb fdbLocal; // pointer to current FDB
uchar far *fpszDest; // pointer to destination
uchar far *fpTopic; // pointer to locked topic
f fRv = TRUE; // return Value
mh mhFdb; // handle to the fdb
mh mhHuff;
mh mhKey;
mhFdb = ncContext.mh;
if (LoadFdb (mhFdb, &fdbLocal)) { /* lock fdb down */
if (fdbLocal.ftype & FTCOMPRESSED) {
// This funky sequence of code attempts to ensure that both the
// huffman and keyword decompression tables are loaded simultaneously
// since we cannot decompress without both.
//
// We do things three times to cover the following cases:
//
// 1) huffman loaded ok
// keyword loaded ok
// huffman already loaded
//
// 2) huffman loaded ok
// keyword loaded ok after HelpShrink (huffman discarded)
// huffman re-loaded ok (HelpShrink freed enough for both)
//
// 3) huffman loaded ok after HelpShrink
// keyword loaded ok after HelpShrink (huffman discarded)
// huffman re-loaded ok (memory fragmentation allowed it)
//
// The other cases, where either the load fails immediatly after
// any HelpShrink call, are the cases we cannot handle.
//
// Since handles can change due to the reallocation that can ocurr
// in the HelpShrink-reload sequence, we simply do the three
// loads, and then ensure that all the handles match what's in the
// fdb. If they don't, we fail.
//
mhHuff = LoadPortion (HS_HUFFTREE,mhFdb);
mhKey = LoadPortion (HS_KEYPHRASE,mhFdb);
mhHuff = LoadPortion (HS_HUFFTREE,mhFdb);
if ( LoadFdb (mhFdb, &fdbLocal)
&& (mhKey == fdbLocal.rgmhSections[HS_KEYPHRASE])
&& (mhHuff == fdbLocal.rgmhSections[HS_HUFFTREE])) {
char far *fpHuff;
char far *fpKey;
// At this point we lock EVERYTHING and ensure that we have
// valid pointers to it all. (Some swapped memory systems can
// fail at this point, so we need to be sensitive).
//
fpHuff = HelpLock (mhHuff);
fpKey = HelpLock (mhKey);
fpTopic = PBLOCK (pbTopic);
fpszDest = PBLOCK (pbDest);
if ( fpTopic
&& fpszDest
&& (fpHuff || (mhHuff == 0))
&& (fpKey || (mhKey == 0))
) {
decomp (fpHuff, fpKey, fpTopic, fpszDest+sizeof(topichdr));
fRv = FALSE;
}
}
// Unlock the handles, if they were valid.
//
if (mhKey != (mh)(-1))
HelpUnlock (mhKey);
if (mhHuff != (mh)(-1))
HelpUnlock (mhHuff);
}
else {
fpszDest = PBLOCK (pbDest);
#if ASCII
/*
** ascii, just copy
*/
fpTopic = PBLOCK(pbTopic);
if (fpTopic && fpszDest) {
hfstrcpy(fpszDest+sizeof(topichdr),fpTopic);
#else
{
#endif
fRv = FALSE;
}
}
if (!fRv) {
((topichdr far *)fpszDest)->ftype = fdbLocal.ftype;
((topichdr far *)fpszDest)->appChar = (uchar)fdbLocal.hdr.appChar;
((topichdr far *)fpszDest)->linChar = (uchar)fdbLocal.hdr.appChar;
((topichdr far *)fpszDest)->lnCur = 1;
((topichdr far *)fpszDest)->lnOff = sizeof(topichdr);
}
PBUNLOCK (pbTopic);
PBUNLOCK (pbDest);
}
return fRv;
/* end HelpDecomp */}
/******************************************************************************
**
** HelpNcNext - Return next context number
**
** Purpose:
** Returns the context number corresponding to a physical "next" in the help
** file.
**
** Entry:
** None
**
** Exit:
** Returns context number
**
** Exceptions:
** Returns NULL on any error
**
*/
nc far pascal LOADDS HelpNcNext (
nc ncCur
) {
return NextPrev(ncCur,1); /* get next */
/* end HelpNcNext */}
/******************************************************************************
**
** HelpNcPrev - Return phyiscally previous context
**
** Purpose:
** Returns the context number corresponding to the physically previous topic.
**
** Entry:
** None
**
** Exit:
** Returns context number
**
** Exceptions:
** Returns NULL on any error
**
*/
nc far pascal LOADDS HelpNcPrev (
nc ncCur
) {
return NextPrev(ncCur,-1); /* get previous */
/* end HelpNcPrev */}
/******************************************************************************
**
** HelpNcUniq - Return nc guaranteed unique for a given topic
**
** Purpose:
** Maps a context number to a local context number. This is provided such
** that all context numbers which map to the same topic can be transformed
** into the same nc which maps to that topic. The information on the
** context string originally used is lost.
**
** Entry:
** None
**
** Exit:
** Returns context number
**
** Exceptions:
** Returns NULL on any error
**
*/
nc far pascal LOADDS HelpNcUniq (
nc ncCur
) {
fdb fdbLocal; /* pointer to current FDB */
if (LoadFdb (ncCur.mh, &fdbLocal))
if (fdbLocal.ftype & FTCOMPRESSED) {
nc ncTmp;
ncTmp.mh = fdbLocal.ncInit.mh;
ncTmp.cn = MapContexttoTopic(ncCur, &fdbLocal);
ncTmp.cn |= 0x8000;
ncCur = ncTmp;
// rjsa return MapContexttoTopic (ncCur,&fdbLocal)
// | (fdbLocal.ncInit & 0xffff0000)
// | 0x8000;
}
return ncCur;
/* end HelpNcUniq */}
/******************************************************************************
**
** NextPrev - Return phyiscally next or previous context
**
** Purpose:
** Returns the context number corresponding to the physically next or previous
** topic.
**
** Entry:
** ncCur = Current Context
** fNext = 1 for next, -1 for previous.
**
** Exit:
** Returns context number
**
** Exceptions:
** Returns NULL on any error
**
*/
nc pascal near NextPrev (
nc ncCur,
int fNext
) {
fdb fdbLocal; /* pointer to current FDB */
REGISTER nc ncNext = {0,0};
if (LoadFdb (ncCur.mh, &fdbLocal)) {
//
// For a compressed file the next/previous physical is computed by taking the
// context number, mapping it to its corresponding topic number, incrementing
// or decrementing the topic number (remember, topic numbers are in physical
// order), and then mapping that back to a context number.
//
// When nexting, we also support nexting into any appended file.
//
if (fdbLocal.ftype & FTCOMPRESSED) {
unsigned short cn;
cn = (ushort)(((ncCur.cn & 0x8000)
? ncCur.cn & 0x7ffff
: MapContexttoTopic(ncCur, &fdbLocal))
+ (ushort)fNext);
ncNext = MapTopictoContext(cn, (fdb far *)&fdbLocal, fNext);
// rjsa ncNext = MapTopictoContext((ushort)(((ncCur & 0x8000)
// ? ncCur & 0x7fff
// : MapContexttoTopic (ncCur,&fdbLocal))
// + fNext)
// ,(fdb far *)&fdbLocal);
//
// if we could not come up with a next, try to find a next using "local"
// context numbers. Map the context number to a topic number, and if that is
// not out of range, return it as a context.
//
if (!(ncNext.cn)) {
// rjsa if ((ncNext = MapContexttoTopic (ncCur,&fdbLocal)) == 0xffff)
// ncNext = 0;
ncNext.cn = MapContexttoTopic(ncCur, &fdbLocal);
if (ncNext.cn == 0xffff) {
ncNext.mh = (mh)0;
ncNext.cn = 0L;
} else {
ncNext.cn += fNext;
if (ncNext.cn >= fdbLocal.hdr.cTopics) {
ncNext.mh = (mh)0;
ncNext.cn = 0L;
} else {
// rjsa ncNext |= (fdbLocal.ncInit & 0xffff0000) | 0x8000;
ncNext.mh = fdbLocal.ncInit.mh;
ncNext.cn = 0x8000;
}
}
}
if (!(ncNext.cn & 0x7fff) && (fNext>0)) {
ncNext = fdbLocal.ncLink;
}
}
#if ASCII
//
// minascii files:
// next'ing: we just sequentially search the file for the first context to
// come along after that pointed to by our current context number.
//
else if (fdbLocal.ftype & FTFORMATTED) {
if (fNext > 0) {
ncNext.cn = maLocate(&fdbLocal,szNil,NctoFo(ncCur.cn)+4, HelpCmp);
if (ncNext.cn == -1L) {
ncNext.mh = (mh)0;
ncNext.cn = 0L;
} else {
ncNext = combineNc(ncNext.cn, ncCur.mh);
}
// rjsa ncNext = (ncNext == -1L)
// ? 0
// : combineNc(ncNext,HIGH(ncCur));
} else {
nc ncTemp;
//
// prev'ing: start at the begining of the file, looking for the last context
// which is less than the current one.
//
ncNext = ncTemp = fdbLocal.ncInit;
while (NctoFo(ncTemp.cn) < NctoFo(ncCur.cn)) {
ncNext = ncTemp;
ncTemp.cn = maLocate(&fdbLocal,szNil,NctoFo(ncTemp.cn)+4, HelpCmp);
if (ncTemp.cn == -1L) {
ncTemp.mh = (mh)0;
ncTemp.cn = 0L;
} else {
ncTemp = combineNc(ncTemp.cn,fdbLocal.ncInit.mh);
}
// rjsa ncTemp = (ncTemp == -1L)
// ? 0
// : combineNc(ncTemp,HIGH(fdbLocal.ncInit));
}
}
}
#endif
}
return ncNext;
}
/*************************************************************************
**
** HelpSzContext - Return string mapping to context number
**
** Purpose:
** Construct a string, which when looked-up, will return the passed context
** number.
**
** Entry:
** pBuf = place to put the string
** ncCur = The context number desired
**
** Exit:
** True on sucess.
**
*/
f pascal far LOADDS HelpSzContext (
uchar far *pBuf,
nc ncCur
) {
f fRet = FALSE; /* return value */
ulong i;
fdb fdbLocal; /* pointer to current FDB */
mh mhCur; /* handle we're dealing with */
char far *fpszContexts; /* pointer to context strings */
*pBuf = 0;
if (LoadFdb (ncCur.mh, &fdbLocal)) { /* lock fdb down */
/*
** Everybody starts with a filename
*/
if (*fdbLocal.hdr.fname)
pBuf = hfstrcpy(pBuf,fdbLocal.hdr.fname);
else
pBuf = hfstrcpy(pBuf,fdbLocal.fname);
*(ushort far *)pBuf = '!'; /* includes null term */
pBuf++;
fRet = TRUE;
// if we've been given a local context number, see if we can synthesize
// a context number from which we might get a string. If we can't get
// one, then return just the filename.
//
if ((i = ncCur.cn) & 0x8000) { /* context # */
ncCur = MapTopictoContext ((ushort)(ncCur.cn & 0x7fff),&fdbLocal,0);
if ((i = ncCur.cn) & 0x8000) /* context # */
return fRet;
}
/*
** For compressed files (signified by being able to load context strings) we
** just walk the context strings looking for string number "ncCur". Once found,
** the returned string is just the concatenated filename, "!" and context
** string.
*/
mhCur = LoadPortion(HS_CONTEXTSTRINGS, ncCur.mh);
if (mhCur && (mhCur != (mh)(-1)) && (fpszContexts = HelpLock(mhCur))) {
if (i && (i <= fdbLocal.hdr.cContexts)) {
while (--i)
while (*fpszContexts++);/* point to next string */
hfstrcpy(pBuf,fpszContexts);/* copy over */
}
HelpUnlock (mhCur);
}
else if (fdbLocal.ftype & FTCOMPRESSED)
return FALSE;
#if ASCII
/*
** for min ascii files, we search for the topic, and copy over the context
** string directly from the file.
*/
else if (fdbLocal.ftype & FTFORMATTED) {
long fpos;
if ((fpos = maLocate(&fdbLocal,szNil,NctoFo(ncCur.cn)-1,HelpCmp)) != -1L) {
fpos = ReadHelpFile(fdbLocal.fhHelp,fpos+2,pBuf,80);
*(pBuf+fpos) = 0; /* ensure terminated */
if (pBuf = hfstrchr(pBuf,'\r'))
*pBuf = 0; /* terminate at CR */
}
}
#endif
}
return fRet;
/* end HelpSzContext */}
/******************************************************************************
**
** LoadPortion - Load a section of the help file
**
** Purpose:
** If not loaded, allocates memory for and loads a section (as defined in
** helpfile.h) of the current help file. Once loaded, or if already loaded,
** locks it, and returns the the memory handle and pointer.
**
** This routine must be far, since it is an entry point for HelpMake
**
** Entry:
** hsCur = Help section to be loaded.
** mhfdb = memory handle for fdb
**
** Exit:
** returns handle for memory
**
** Exceptions:
** returns NULL on portion not existing, 0xffff on inability to allocate memory.
**
*/
mh pascal near LoadPortion (
int hsCur,
mh mhfdb
) {
fdb fdbLocal;
char far *fpDest = 0;
int i;
mh mhNew = 0; /* pointer to mh destination */
ushort osize; /* additional prepended size */
ushort size;
if (LoadFdb (mhfdb, &fdbLocal)) {
if (((mhNew = fdbLocal.rgmhSections[hsCur]) == 0)
&& fdbLocal.hdr.tbPos[hsCur]) {
for (i=hsCur+1; i<HS_count; i++)
if (fdbLocal.hdr.tbPos[i]) {
size = (ushort)(fdbLocal.hdr.tbPos[i]-fdbLocal.hdr.tbPos[hsCur]);
break;
}
osize = (hsCur == HS_KEYPHRASE) ? 1024*sizeof(PVOID) : 0;
/*
** Alloc the memory required. Re-read the FDB, incase intervening calls to
** HelpShrink causes deallocs of other beasties.
*/
if ( (mhNew = HelpAlloc(size + osize))
&& LoadFdb (mhfdb, &fdbLocal)) {
fdbLocal.rgmhSections[hsCur] = mhNew;
if (PutFdb (mhfdb, &fdbLocal)) {
fpDest = (char far *)HelpLock(mhNew);
if (fpDest && ReadHelpFile(fdbLocal.fhHelp
,(ulong)fdbLocal.hdr.tbPos[hsCur] + fdbLocal.foff
,fpDest + osize
,size)) {
if (hsCur == HS_KEYPHRASE)
kwPtrBuild(fpDest,size);/* build keyword pointers */
HelpUnlock (mhNew);
}
else {
fdbLocal.rgmhSections[hsCur] = 0;
HelpDealloc (mhNew);
PutFdb (mhfdb, &fdbLocal);
mhNew = (mh)(-1);
}
}
else
mhNew = (mh)0;
}
else
mhNew = (mh)(-1);
}
}
return mhNew;
/* end LoadPortion */}
/*************************************************************************
**
** SizePos - Return count of bytes in compressed topic, and position
**
** Purpose:
** Returns the size in bytes of the compressed topic, and it's location in the
** help file.
**
** Entry:
** ncCur - Context number to return info on.
** psize - Pointer to place to put the size
** ppos - Pointer to place to put the position
**
** Exit:
** Returns TRUE on success.
**
** Exceptions:
** Returns FALSE on all errors.
**
** Algorithm:
**
** If current help handle valid
** If filetype is compressed
** If context map not loaded, load it
** Lock context map
** Map context to topic number
** Unlock context map
** If topic index not loaded, load it
** Lock topic index
** size is difference in file positions
** Unlock topic index
** else if filetype is formatted ascii
** seek to context file position
** scan for next context definition
** size is difference in file positions
** else if filetype is unformatted ascii
** size is filesize
*/
f pascal near SizePos (
nc ncCur,
ushort *psize,
ulong *ppos
) {
fdb fdbLocal; /* pointer to current FDB */
char far *fpT; /* temp pointer */
REGISTER f fRv = FALSE; /* return value */
ushort iTopic; /* topic index */
mh mhCur; /* handle being locked */
if (LoadFdb (ncCur.mh, &fdbLocal)) { /* get fdb copy */
if (fdbLocal.ftype & FTCOMPRESSED) {/* if a standard compressed file*/
if ((iTopic = MapContexttoTopic (ncCur,&fdbLocal)) != 0xffff) {
mhCur = LoadPortion(HS_INDEX,ncCur.mh);
if (mhCur && (mhCur != (mh)(-1)) && (fpT = HelpLock(mhCur))) {
*ppos = ((long far *)fpT)[iTopic];
*psize = (ushort)(((long far *)fpT)[iTopic+1] - *ppos);
HelpUnlock (mhCur);
fRv = TRUE;
}
}
}
#if ASCII
else if (fdbLocal.ftype & FTFORMATTED) {/* if a formatted ascii file*/
if ((*psize = (ushort)(maLocate(&fdbLocal, szNil, NctoFo(ncCur.cn)+4, HelpCmp)))
== 0xffff)
*psize = (ushort)ReadHelpFile(fdbLocal.fhHelp,0L,NULL,0);
else
*psize -= (ushort)NctoFo(ncCur.cn);
*ppos = (ulong) maLocate(&fdbLocal, szNil, NctoFo(ncCur.cn)-1, HelpCmp);
fRv = TRUE;
}
else { /* unformatted ascii */
*ppos = ReadHelpFile(fdbLocal.fhHelp,0L,NULL,0);
*psize = (*ppos > (ulong)(65535-sizeof(topichdr)-4))
? (ushort)(65535-sizeof(topichdr)-4)
: (ushort)*ppos;
*ppos = 0L; /* position is always zero. */
fRv = TRUE;
}
#endif
}
return fRv;
/* end SizePos */}
/************************************************************************
**
** MapContexttoTopic
**
** Purpose:
** Given a context number, return the topic number which it maps to. This
** is just a direct index of the context number into the context map.
**
** Entry:
** ncCur = context number to be mapped
** fpfdbCur = pointer to associated fdb
**
** Exit:
** Returns zero based topic number, or 0xffff on error.
*/
ushort pascal near MapContexttoTopic (
nc ncCur, /* context number to map */
fdb far *fpfdbCur /* pointer to current FDB */
) {
REGISTER ushort topic = 0xffff; /* value to return */
ushort far *fpT; /* pointer to context map */
mh mhCur; /* handle being locked */
if (ncCur.cn) {
/*
** Local contexts: the topic number is already encoded in the low word, if the
** high bit of that word is set.
*/
if (ncCur.cn & 0x8000)
topic = (ushort)(ncCur.cn & 0x7fff);
/*
** Normal Contexts: low word of nc is an index into the context map which
** returns the topic number
*/
else {
mhCur = LoadPortion(HS_CONTEXTMAP,fpfdbCur->ncInit.mh);
if (mhCur && (mhCur != (mh)(-1)) && (fpT = HelpLock(mhCur))) {
topic = fpT[ncCur.cn-1];
HelpUnlock (mhCur);
}
}
}
return topic;
/* end MapContexttoTopic */}
/************************************************************************
**
** MapTopictoContext
**
** Purpose:
** Given a topic number, return a context which maps to it.
**
** This involves searching the context map for the first context entry that
** maps to the desired topic number.
**
** Entry:
** iTopic = topic number to map back to a context number
** fpfdbCur = pointer to associated fdb
**
** Exit:
** Returns a valid nc into the file.
**
** Exceptions:
** If the incoming iTopic is invalid, or a read error occurs, then the nc
** returned refers to the topic number 0.
**
*/
nc pascal near MapTopictoContext(
ushort iTopic, /* topic number to map */
fdb far *fpfdbCur, /* pointer to current FDB */
int Dir
) {
ushort cTopics; /* number of topics to search */
ushort far *fpContextMap; /* pointer to the context map */
mh mhPortion; /* mem handle for the context map*/
nc ncMatch = {0,0}; /* return value */
mhPortion = LoadPortion (HS_CONTEXTMAP,fpfdbCur->ncInit.mh);
if (mhPortion && (mhPortion != (mh)(-1))) {
if (fpContextMap = HelpLock(mhPortion)) {
if (iTopic >= fpfdbCur->hdr.cTopics) {
iTopic = 0;
}
ncMatch.mh = (mh)0L;
ncMatch.cn = 0x8000 | iTopic;
// rjsa ncMatch = 0x8000 | iTopic;
cTopics = 0;
while (cTopics < fpfdbCur->hdr.cContexts) {
if ( Dir == 0 ) {
if (iTopic == fpContextMap[cTopics++]) {
ncMatch.cn = cTopics; /* nc's are one based */
break;
}
} else if ( Dir > 0 ) {
if (iTopic <= fpContextMap[cTopics++]) {
ncMatch.cn = cTopics; /* nc's are one based */
break;
}
} else if ( Dir < 0 ) {
if (iTopic == fpContextMap[cTopics++]) {
ncMatch.cn = cTopics;
break;
} else if (iTopic < fpContextMap[cTopics-1]) {
ncMatch.cn = cTopics-1;
break;
}
}
}
//if ( iTopic != fpContextMap[cTopics-1] ) {
// ncMatch.cn = 0;
//}
if ( cTopics >= fpfdbCur->hdr.cContexts) {
ncMatch.cn = 0;
}
HelpUnlock (mhPortion);
}
}
ncMatch.mh = (fpfdbCur->ncInit).mh;
return ncMatch;
// rjsa return ncMatch | HIGHONLY(fpfdbCur->ncInit);
}
/************************************************************************
**
** LoadFdb - make local copy of fdb.
**
** Purpose:
** Used to create a local copy of an FDB, so that we don't have to keep a
** locked, far copy around.
**
** Entry:
** mhFdb - memory handle for the FDB
** fpFdbDest - Pointer to destination for FDB copy
**
** Exit:
** returns TRUE if FDB copied.
*/
f pascal near LoadFdb (
mh mhfdb,
fdb far *fpfdbDest
) {
fdb far *fpfdbCur; /* pointer to current FDB */
if (fpfdbCur = HelpLock (mhfdb)) {
*fpfdbDest = *fpfdbCur;
HelpUnlock (mhfdb);
return TRUE;
}
return FALSE;
/* end LoadFdb */}
/************************************************************************
**
** PutFdb - make local copy of fdb permanent.
**
** Purpose:
** Used to copy a local copy of an FDB to the "real" one, so that we don't
** have to keep a locked, far copy around.
**
** Entry:
** mhFdb - memory handle for the FDB
** fpfdbSrc - Pointer to source of FDB copy
**
** Exit:
** returns TRUE if FDB copied.
*/
f pascal near PutFdb (
mh mhfdb,
fdb far *fpfdbSrc
) {
fdb far *fpfdbCur; /* pointer to current FDB */
if (fpfdbCur = HelpLock (mhfdb)) {
*fpfdbCur = *fpfdbSrc;
HelpUnlock (mhfdb);
return TRUE;
}
return FALSE;
/* end PutFdb */}
#if ASCII
/************************************************************************
**
** maLocate - Locate context in minimally formatted ascii file.
**
** Purpose:
** Performs sequential searches on mimimally formatted ascii files to locate
** lines beginning with ">>" and a context string.
**
** Entry:
** fpfdbCur = Pointer to current fdb
** fpszSrc = Pointer to context string to be found (or null for next
** string)
** offset = offset at which to begin search.
** lpfnCMp = pointer to comparison routine to use
**
** Exit:
** returns file offset of ">>" of context string.
**
** Exceptions:
** returns -1 on error.
**
*/
long pascal near maLocate (
fdb far *fpfdbCur,
uchar far *fpszSrc,
ulong offset,
f (pascal far *lpfnCmp)(uchar far *, uchar far *, ushort, f, f)
) {
uchar buffer[MASIZE+1]; /* input buffer */
ushort cbBuf = 0; /* count of bytes in buffer */
ushort cbSrc; /* length of source string */
uchar far *pBuf; /* pointer into buffer */
uchar far *pBufT; /* temp pointer into buffer */
cbSrc = hfstrlen(fpszSrc)+1; /* get length of input */
if (offset == 0xffffffff) /* special case */
offset = 0;
while (cbBuf += (ushort)ReadHelpFile(fpfdbCur->fhHelp
, offset+cbBuf
, buffer+cbBuf
, MASIZE-cbBuf)) {
buffer[cbBuf] = 0; /* ensure strings terminated */
pBuf = &buffer[0];
while (pBuf = hfstrchr(pBuf,'>')) { /* look for start of context */
if ((*(pBuf+1) == '>') /* if >> found */
&& ((*(pBuf-1) == '\n') /* at beginning of line */
|| ((offset == 0) /* or beginning of file */
&& (pBuf == (char far *)&buffer[0])))) {
pBufT = pBuf +2;
if (lpfnCmp (fpszSrc, pBufT, cbSrc, FALSE, TRUE))
return offset + (ulong)(pBuf - (uchar far *)&buffer[0]);
}
pBuf += 2;
}
if (cbBuf == MASIZE) { /* if buffer full */
hfstrcpy(buffer,buffer+MASIZE-MAOVER); /* copy down overlap */
cbBuf = MAOVER; /* and leave that in */
offset += MASIZE-MAOVER; /* file pos of buffer[0] */
}
else {
offset += cbBuf;
cbBuf = 0; /* else we're empty */
}
}
return -1;
/* end maLocate */}
#endif