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.
1745 lines
59 KiB
1745 lines
59 KiB
/*** 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
|