mirror of https://github.com/lianthony/NT4.0
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.
2512 lines
67 KiB
2512 lines
67 KiB
/*****************************************************************************
|
|
* *
|
|
* OUTTEXT.CPP *
|
|
* *
|
|
* Copyright (C) Microsoft Corporation 1990-1994. *
|
|
* All Rights reserved. *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Module Intent *
|
|
* *
|
|
* This file deals with processing information to write out to the *
|
|
* topic file. *
|
|
* *
|
|
*****************************************************************************/
|
|
|
|
#include "stdafx.h"
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
#include "pack.h"
|
|
#include "ftsrch.h"
|
|
#include "skip.h"
|
|
#include "..\hwdll\zeck.h"
|
|
#include "zeckdat.h"
|
|
#include "zeck.h"
|
|
// #include <malloc.h>
|
|
|
|
// #define STATS // define this to get printfs of statistics...
|
|
|
|
#ifdef _DEBUG
|
|
#define CHECK
|
|
#endif
|
|
|
|
static MTOP * pmtop;
|
|
static MTOP * pmtopFirst;
|
|
static MTOP * pmtopSecond;
|
|
static BOOL bSYSFirst;
|
|
|
|
/*****************************************************************************
|
|
* *
|
|
* Macros *
|
|
* *
|
|
*****************************************************************************/
|
|
|
|
const int INIT_TABSTACKSIZE = 30;
|
|
const int INC_TABSTACKSTEP = 10;
|
|
|
|
const int CB_TEXTINCR = 1024;
|
|
const int CB_COMMANDINCR = 1024;
|
|
const int CB_HEADER = 512;
|
|
|
|
const int MAX_FCPSIZE = 480;
|
|
|
|
// Field offsets (in bytes) for back patching:
|
|
#define offNextInMBHD ((int)(&((QMBHD)0)->vaFCPnext))
|
|
|
|
#define offPrevInMFCP ((int)(&((QMFCP)0)->vaPrevFc))
|
|
#define offNextInMFCP ((int)(&((QMFCP)0)->vaNextFc))
|
|
#define offTextInMFCP ((int)(&((QMFCP)0)->vaTextFc))
|
|
|
|
#define offvaNSRInMTOP ((int)(&((QMTOP)0)->vaNSR))
|
|
#define offvaSRInMTOP ((int)(&((QMTOP)0)->vaSR))
|
|
#define offvaNextSeqTopicInMTOP ((int)(&((QMTOP)0)->vaNextSeqTopic))
|
|
|
|
#define offTopicsizeInMOBJ ((int)(&((QMOBJ)0)->lcbSize))
|
|
|
|
// WARNING: this enum must be kept in sync with irgSuppresstypeSizes[] below!
|
|
// EEoNSRmfcp is a special mfcp which is the start of a scrolling region!
|
|
// EBegNSRmfcp is a special mfcp which is the start of the non-scrolling reg.
|
|
|
|
typedef enum suppresstype {
|
|
Enone=0,
|
|
Embhd,
|
|
Emfcp,
|
|
ETopicmfcp,
|
|
EEoNSRmfcp,
|
|
Emobj,
|
|
Emtop,
|
|
EBegNSRmfcp
|
|
} BACKPTYPE;
|
|
|
|
// This array gives us the node size corresponding to a backptype:
|
|
|
|
int irgSuppresstypeSizes[] = {
|
|
0, // Enone
|
|
sizeof(MBHD),
|
|
sizeof(MFCP),
|
|
sizeof(MFCP),
|
|
sizeof(MFCP),
|
|
sizeof(MOBJ),
|
|
sizeof(MTOP),
|
|
sizeof(MFCP)
|
|
};
|
|
|
|
// Tags used for debugging stray pointers:
|
|
|
|
#define QBACKP_TAG 0x9812
|
|
|
|
typedef struct struct_backpatchnode BACKP, *QBACKP;
|
|
struct struct_backpatchnode {
|
|
SUPPRESSZECK suppresszeck; // zeck suppres specifier.
|
|
BACKPTYPE backptype; // type of HC node this corresponds to.
|
|
IDFCP idfcp; // enum of the fcp of this guy.
|
|
FCL fcl; // file address of where to back patch.
|
|
DWORD lcbTopic; // topic size so far (only for ETopicmfcp)
|
|
QBACKP prev; // prev backp in list (doubly linked list).
|
|
#ifdef CHECK
|
|
WORD tag; // debug tag.
|
|
#endif
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* *
|
|
* Static Variables *
|
|
* *
|
|
*****************************************************************************/
|
|
|
|
BYTE rgbBlockBuffer[cbMAX_BLOCK_SIZE];
|
|
BYTE rgbCompressedBuffer[cbBLOCK_SIZE]; // REVIEW: make near?
|
|
static LPBYTE pbBlockBuffer = rgbBlockBuffer;
|
|
|
|
static QTAB qTabStack; // Stack of tab stops
|
|
static int lcTabStackMac; // Max. no entries TAB STOP table
|
|
static QTAB qIntTabStack; // Stack of tab stops
|
|
static int clIntTabStackMac; // Max. no entries TAB STOP table
|
|
|
|
// Our list headers:
|
|
|
|
static QBACKP qbackpInsert; // insertion point, last qbackp in the list.
|
|
static QBACKP qbackpMem; // header to list 2.
|
|
static QBACKP qbackpPrevTopic; // "current" previous ETopicmfcp node
|
|
static QBACKP qbackpPrevMTOP; // "current" previous Emtop node
|
|
static QBACKP qbackpPrevFlushedTopic; // prev on-disk topic
|
|
static VA vaPrevFlushedTopic;
|
|
static QBACKP qbackpPrevMFCP; // "current" previous Emfcp node
|
|
static DWORD ulPrevMFCPBlknum; // blknumberthe prev MFCP is in.
|
|
static BOOL fNextIsEoNSR; // next mfcp to come is the end of the nsr.
|
|
static BOOL fNextIsNSR; // next mfcp to come is the beginning of the nsr.
|
|
|
|
static DWORD Blknum; // current blk we are emitting
|
|
|
|
static QBACKP qbackpBrowseCallback;
|
|
|
|
static BOOL fOutFCP;
|
|
static IDFCP idfcpCur;
|
|
|
|
/*****************************************************************************
|
|
* *
|
|
* Prototypes *
|
|
* *
|
|
*****************************************************************************/
|
|
|
|
static int STDCALL FVerifyQMOPG(QMOPG pmopg);
|
|
static QBACKP STDCALL QbpAddbackp(BACKPTYPE backptype, LPBYTE qb, UINT cb);
|
|
static void* STDCALL QvSetTopicObject(DWORD* plcbObj);
|
|
static RC_TYPE STDCALL RcOutTextToTextBuf(PSTR sz);
|
|
static void STDCALL VAddToBuffer(PBYTE qb, DWORD lcb, BACKPTYPE backptype);
|
|
static void STDCALL VCopyDownAndAdjust(DWORD lcbSrcCompressed);
|
|
static void STDCALL VFlushBackpNodes(DWORD lcbSrcCompressed);
|
|
static void STDCALL VFreeIfDisk(QBACKP qbackpFree);
|
|
static void STDCALL VRemoveBackp(QBACKP qbackp);
|
|
static void STDCALL VWriteFCP(FCPTYPE type, LPBYTE qbObj, DWORD lcbObj, PBYTE qbCommand, DWORD lcbCommand, PSTR pszText, DWORD lcbCompressed, DWORD lcbUncompressed);
|
|
static void STDCALL check_qbackp(QBACKP qbackp);
|
|
|
|
static FCL STDCALL fclBrowseCallback(IDFCP idfcp, int* piBitdex);
|
|
|
|
/* VMarkAsSR() -
|
|
*
|
|
* Called when we have reached the end of a NonScrollingRegion and
|
|
* indicates we should mark the next mfcp seen as the beginning of the
|
|
* Scrolling Region.
|
|
*/
|
|
|
|
INLINE VOID VMarkAsSR(void) { fNextIsEoNSR = TRUE; };
|
|
|
|
/* VMarkAsNSR() -
|
|
*
|
|
* Called when we have reached the very beginning of a NonScrollingRegion and
|
|
* indicates we should mark the next mfcp seen as the beginning of the Non
|
|
* Scrolling Region.
|
|
*/
|
|
|
|
INLINE VOID VMarkAsNSR(void) { fNextIsNSR = TRUE; };
|
|
|
|
/* Browse Callback Stuff --
|
|
*
|
|
* The browse sequence tracking code needs to insert addresses of topics
|
|
* into it's data structures. So when the backpatch-location is determined
|
|
* for a set of MTOP data structures, we call him (in VFlushBuffer)
|
|
* (RcExecuteDelayedExecution( Blknum, qbackpMem->idfcp, .. ) in
|
|
* VFLushBuffer()) and he calls us back for each MTOP to get the fixup
|
|
* fcl (flc of beginning of the MTOP structure).
|
|
*/
|
|
|
|
INLINE void InitBrowseCallback() { qbackpBrowseCallback = qbackpMem; };
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* void VPushTab(int)
|
|
*
|
|
* Description:
|
|
* This function builds the tabstop positions for the given FCP. It
|
|
* acquires the memory for the TABSTOP table if it is null or resizes the
|
|
* TABSTOP table.
|
|
*
|
|
* Returns;
|
|
* TRUE if outputs a FCP
|
|
* else FALSE
|
|
*-----------------------------------------------------------------------------*/
|
|
|
|
void STDCALL VPushTab(int ipos)
|
|
{
|
|
int x;
|
|
UINT i;
|
|
QTAB qTabLoc;
|
|
|
|
// check for dup value
|
|
|
|
x = ITwips2HalfPoint(ipos);
|
|
for (i = 0, qTabLoc = qTabStack; i < wTabStackCur; i++, qTabLoc++) {
|
|
if ((qTabLoc->x == x) && (qTabLoc->wType == (int) wTabType)) {
|
|
wTabType = TABTYPELEFT;
|
|
return;
|
|
}
|
|
}
|
|
if ((int) wTabStackCur >= lcTabStackMac) {
|
|
qTabStack = (QTAB) QResizeTable(qTabStack, (int) wTabStackCur,
|
|
(int*) &lcTabStackMac, sizeof(TAB), INIT_TABSTACKSIZE, INC_TABSTACKSTEP);
|
|
}
|
|
ASSERT(qTabStack != NULL);
|
|
qTabStack[wTabStackCur].x = x;
|
|
qTabStack[wTabStackCur].wType = wTabType;
|
|
wTabStackCur++;
|
|
wTabType = TABTYPELEFT;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* void VSaveTabTable()
|
|
*
|
|
* Description:
|
|
* This function saves the current TAB table.
|
|
*
|
|
* Returns;
|
|
* NOTHING
|
|
*-----------------------------------------------------------------------------*/
|
|
|
|
void STDCALL VSaveTabTable(void)
|
|
{
|
|
wIntTabStackCur = wTabStackCur;
|
|
if ((int) wIntTabStackCur >= clIntTabStackMac) {
|
|
qIntTabStack = (QTAB) QResizeTable(qIntTabStack,
|
|
(int) wIntTabStackCur, &clIntTabStackMac, sizeof(TAB),
|
|
INIT_TABSTACKSIZE, INC_TABSTACKSTEP);
|
|
}
|
|
ASSERT(qIntTabStack != NULL);
|
|
if (wTabStackCur)
|
|
MoveMemory((void*) qIntTabStack, (void*) qTabStack,
|
|
wTabStackCur * sizeof(TAB));
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name: RcOutFmt
|
|
-
|
|
* Purpose:
|
|
* This function outputs a format command change, if appropriate.
|
|
*
|
|
* Arguments:
|
|
* fCheck: TRUE to check if format has changed.
|
|
*
|
|
* Returns:
|
|
* rc error code.
|
|
*
|
|
* Globals:
|
|
* uses fNewPageFmt to guarantee that a format change command
|
|
* is output at the start of each new topic.
|
|
*
|
|
* +++
|
|
*
|
|
* Notes:
|
|
* This function will output a format command change if:
|
|
* 1) It is the start of a new page (determined by fNewPageFmt), or
|
|
* 2) fCheck is FALSE, or
|
|
* 3) the character formatting has changed since the last time it
|
|
* was output.
|
|
*
|
|
***************************************************************************/
|
|
|
|
RC_TYPE STDCALL RcOutFmt(BOOL fCheck)
|
|
{
|
|
int ifmt;
|
|
|
|
// check if the format has changed
|
|
|
|
if (fNewPageFmt || !fCheck ||
|
|
(memcmp(&cfCur, &cfPrev, sizeof(CF)))) { // Not same as before
|
|
|
|
// Get the format number
|
|
|
|
ifmt = IGetFmtNo(&cfCur);
|
|
cfPrev = cfCur;
|
|
fNewPageFmt = FALSE;
|
|
|
|
// Enter the Format command
|
|
|
|
return RcOutputCommand(CMD_WORD_FORMAT, &ifmt, sizeof(WORD));
|
|
}
|
|
return RC_Success;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: VOutText
|
|
|
|
PURPOSE:
|
|
This function outputs text. Before that it checks if the FCP is
|
|
changed. If so, it outputs an FCP. It then checks if Character
|
|
Format has changed. If so, it outputs the format command. It then
|
|
checks if the current text belongs to a hotspot. If so it outputs
|
|
the hotsopt command before outputting the text.
|
|
|
|
PARAMETERS:
|
|
qch -- pointer to text to be outputted.
|
|
|
|
RETURNS: void
|
|
|
|
COMMENTS:
|
|
|
|
MODIFICATION DATES:
|
|
30-Jul-1993 [ralphw]
|
|
|
|
***************************************************************************/
|
|
|
|
void STDCALL VOutText(PSTR pszText)
|
|
{
|
|
int fAttrHotspot;
|
|
|
|
/*
|
|
* If this is a hotspot, we turn off hotspot-related formatting for
|
|
* this text.
|
|
*/
|
|
|
|
if (FIsHotspotFlag(hsptG)) {
|
|
fAttrHotspot = (cfCur.fAttr & fAttrHotspotFormat);
|
|
cfCur.fAttr &= ~fAttrHotspotFormat;
|
|
if (FIsULHotspot(hsptG))
|
|
cfCur.fAttr |= fUnderLine;
|
|
}
|
|
else {
|
|
FCheckAndOutFCP();
|
|
}
|
|
|
|
fNewPara = FALSE;
|
|
|
|
// Check for Small Caps:
|
|
|
|
if (cfCur.fAttr & fSmallCaps || cfCur.fAttr & fAllCaps)
|
|
StrUpper(pszText);
|
|
RcOutTextToTextBuf(pszText);
|
|
|
|
// restore hotspot formatting, if any
|
|
|
|
if (FIsHotspotFlag(hsptG)) {
|
|
cfCur.fAttr &= ~fAttrHotspotFormat; // possibly remove underline
|
|
cfCur.fAttr |= fAttrHotspot; // put back original formatting
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* BOOL FCheckAndOutFCP()
|
|
*
|
|
* Description:
|
|
* This function checks if the state is in the begining of MARGIN.
|
|
* If so, it outputs a FCP if the InterMediate Paragraph format is different
|
|
* from the previous paragraph and current paragraph info or if the FCP size is
|
|
* bigger than FCPSIZELIMIT. It returns TRUE if a FCP is outputted.
|
|
* Otherwise it checks if ONMARGIN, outputs a bNewPara command.
|
|
*
|
|
* Returns;
|
|
* TRUE if outputs a FCP
|
|
* else FALSE
|
|
*-----------------------------------------------------------------------------*/
|
|
|
|
BOOL STDCALL FCheckAndOutFCP(void)
|
|
{
|
|
if (FIsHotspotFlag(hsptG))
|
|
return(FALSE);
|
|
|
|
if (fNewPara) {
|
|
|
|
/*
|
|
* if IntPF and CurPF are not same Overflow of buffer has taken
|
|
* place then out FCP.
|
|
*/
|
|
|
|
if ((memcmp(&pfCur, &pfInt, sizeof(PF))) ||
|
|
(wTabStackCur != wIntTabStackCur) ||
|
|
(memcmp(qIntTabStack, qTabStack, wTabStackCur * sizeof(TAB))) ||
|
|
((tbl.tbs == tbsOff) &&
|
|
(pbfText->GetSize() + pbfCommand->GetSize() > MAX_FCPSIZE))) {
|
|
VOutFCP(FALSE);
|
|
|
|
/*
|
|
* REVIEW: 31-Aug-1994 [ralphw] The following line is new to
|
|
* this version of the help compiler. It is necessary in order
|
|
* allow the new code that nests pfCur information in braces. The
|
|
* nesting is necessary for Word 6.0 RTF output.
|
|
*/
|
|
|
|
pfInt = pfCur;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* VOID VOutFCP( BOOL )
|
|
*
|
|
* Description:
|
|
* This function outputs a FCP to the File System.
|
|
* It ensures if the displyable object is other than a TOPIC object, it
|
|
* contains at least one character of text.
|
|
*
|
|
* Arguments:
|
|
* BOOL fLast: TRUE if there are no more FCPs in this topic, FALSE
|
|
* otherwise.
|
|
*
|
|
* Returns;
|
|
* NOTHING
|
|
*-----------------------------------------------------------------------------*/
|
|
|
|
void STDCALL VOutFCP(BOOL fLast)
|
|
{
|
|
MOPG mopg;
|
|
DWORD lcbObj;
|
|
FCPTYPE type;
|
|
|
|
#ifdef _DEBUG
|
|
lcHeapCheck();
|
|
#endif
|
|
if (tbl.tbs == tbsOn) {
|
|
RcAddFcpPtbl();
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Only output an FCP if we have at least one character of text. This
|
|
* includes topic and NSR FCPs.
|
|
*/
|
|
|
|
if (wTextBufChCount) {
|
|
if (!fHasTopicFCP) {
|
|
|
|
/*
|
|
* Non-scrolling regions are assigned in VForceTopicFCP, and
|
|
* removed in NewTopicPhpj() in rtf2hlp.c.
|
|
*/
|
|
|
|
ASSERT(nsr == nsrNone);
|
|
|
|
VForceTopicFCP();
|
|
|
|
/*
|
|
* If it is a non-scrolling-region, let the backpatch code
|
|
* know so it can backpatch vaNSR and vaSR fields correctly:
|
|
*/
|
|
|
|
if (nsr == nsrUnresolved)
|
|
VMarkAsNSR();
|
|
fHasTopicFCP = TRUE;
|
|
}
|
|
|
|
if (nsr == nsrUnresolved && !pfInt.fNSR) {
|
|
VMarkAsSR(); // mark end of NSR
|
|
nsr = nsrResolved;
|
|
}
|
|
else if (pfInt.fNSR && nsr != nsrUnresolved && nsr != nsrWarned) {
|
|
VReportError(HCERR_NSR_AFTER_SR, &errHpj);
|
|
nsr = nsrWarned;
|
|
}
|
|
|
|
CMem memHead(CB_HEADER);
|
|
|
|
if (tbl.tbs == tbsFinish) {
|
|
type = FCTYPE_SBYS;
|
|
lcbObj = CbSetTableHeader(memHead.psz);
|
|
}
|
|
else {
|
|
type = FCTYPE_PARAGROUP;
|
|
VSetParaGroupObject(&mopg); // set ParaGroup MOPG
|
|
|
|
// Compress the structure
|
|
|
|
lcbObj = CbPackMOPG(&mopg, memHead.psz);
|
|
}
|
|
fOutFCP = TRUE;
|
|
|
|
VWriteFCP(type, memHead.pb, lcbObj, pbfCommand->pbMem,
|
|
pbfCommand->GetSize(), (PSTR) pbfText->pbMem,
|
|
pbfText->GetSize(), wTextBufChCount);
|
|
|
|
if (fLast)
|
|
adrs.idfcpTopic = adrs.idfcpCur++;
|
|
} // if ( wTextBufChCount )
|
|
else
|
|
ASSERT(!fLast);
|
|
|
|
// Disturb PrevCF to ensure at least one font command for the next FCP
|
|
|
|
cfPrev.fAttr = 0xff;
|
|
pfPrev = pfInt;
|
|
wTextBufChCount = 0;
|
|
pbfText->SetSize(0);
|
|
pbfCommand->SetSize(0);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name InitTopicHf
|
|
-
|
|
* Purpose
|
|
* This function writes out the first block header into the topic
|
|
* file, initializing it to the assumed state for VWriteFCP.
|
|
*
|
|
* Arguments
|
|
* HF hfTopic: FS handle to topic file.
|
|
*
|
|
* Globals
|
|
* This function also initializes the global variables fclCur
|
|
* and fclTopic.
|
|
*
|
|
***************************************************************************/
|
|
|
|
static MBHD mbhdCurrent;
|
|
|
|
void STDCALL InitTopicHf(HF hfTopic)
|
|
{
|
|
adrs.idfcpTopic = FIRST_IDFCP;
|
|
adrs.idfcpCur = adrs.idfcpTopic + 1;
|
|
adrs.wObjrg = 0;
|
|
Blknum = 0;
|
|
mbhdCurrent.vaFCPPrev.dword = vaNil;
|
|
|
|
#ifdef MAGIC
|
|
mbhd.bMagic = bMagicMBHD;
|
|
#endif
|
|
}
|
|
|
|
// All this zeck stuff involves potential aliasing:
|
|
|
|
#ifdef CHECK
|
|
#define CHECK_QBACKP(qbackp) check_qbackp(qbackp)
|
|
#else
|
|
#define CHECK_QBACKP(qbackp) // nothing
|
|
#endif
|
|
|
|
/**************************************************************************
|
|
* Zeck Block Compression:
|
|
* These functions accumulate FCPs
|
|
* into a buffer. When the buffer gets full, zeck compression is
|
|
* applied (if compression is requested via COMPRESS = MEDIUM | FULL,
|
|
* otherwise the buffer is flushed w/o compression)
|
|
* to get a 4K block of compressed topic info.
|
|
* Then that 4K block is written to the |TOPIC file. We have to
|
|
* perform magic to deal with the back patching of info into the
|
|
* compressed block. The structures we must back patch are:
|
|
*
|
|
* MBHD Prev field is known at write time,
|
|
* Next and topic fields must be backpatched after compression.
|
|
* MFCP Prev, Next fields must be backpatched.
|
|
* MTOP Prev and Next browse sequence back patching is dealt with
|
|
* via the footnote handling code in footnote.c and the
|
|
* VEmitFootnoteInfo() call.
|
|
* vaNSR, vaSR and vaNextSeqTopic must be backpatched here.
|
|
* MOBJ lcbSize of a Topic's first MOBJ is the size of the whole topic.
|
|
* We back patch that size.
|
|
*
|
|
* The zeck compression stuff takes a linked list of
|
|
* compression-suppression nodes which prevent compression of fields
|
|
* we must back-patch into. We add some fields to the end of these
|
|
* compression-suppression nodes to facilitate our back patching
|
|
* (slightly sleazy merging the two lists). Here is what a
|
|
* compression-suppression node the zeck stuff sees (from zeck.h):
|
|
*
|
|
* struct struct_suppresszeck {
|
|
* RB rbSuppress; // beginning of range for suppression.
|
|
* UINT cbSuppress; // number of bytes to suppress compression.
|
|
* RB rbNewpos; // pointer into dest buffer where suppressed range
|
|
* // ended up after compression (an OUT param value).
|
|
* QSUPPRESSZECK next; // next suppression range in this list.
|
|
* };
|
|
* And here is our flavor of the nodes:
|
|
*/
|
|
|
|
// We compress, and treat as a block unit, 4K minus the MBHD uncompressable
|
|
// structure at the beginning of the block:
|
|
|
|
const int LCB_BLOCKDATASIZE = (cbBLOCK_SIZE - sizeof(MBHD));
|
|
|
|
#ifdef STATS
|
|
DWORD cBackpNodes;
|
|
DWORD cbRText; // size of rich text - text + formatting
|
|
#endif
|
|
|
|
#ifdef CHECK
|
|
|
|
static void STDCALL check_qbackp(QBACKP qbackp)
|
|
{
|
|
ASSERT(qbackp);
|
|
ASSERT(qbackp->tag == QBACKP_TAG);
|
|
ASSERT(qbackp->backptype > Enone);
|
|
ASSERT(qbackp->backptype <= EBegNSRmfcp);
|
|
ASSERT(qbackp->suppresszeck.rbSuppress >= rgbBlockBuffer);
|
|
ASSERT(qbackp->suppresszeck.rbSuppress <=
|
|
&rgbBlockBuffer[sizeof(rgbBlockBuffer)]);
|
|
|
|
if((qbackp->suppresszeck.rbNewpos
|
|
&& (qbackp->suppresszeck.rbNewpos < rgbCompressedBuffer
|
|
|| qbackp->suppresszeck.rbNewpos >
|
|
&rgbCompressedBuffer[sizeof(rgbCompressedBuffer)]
|
|
))) {
|
|
ASSERT(!"Bogus Backp!\n"); // unreached
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/* QbpAddbackp() -
|
|
*
|
|
* Add a backpatch node to our in-mem list...
|
|
*/
|
|
|
|
static QBACKP STDCALL QbpAddbackp(BACKPTYPE backptype, LPBYTE qb,
|
|
UINT cb)
|
|
{
|
|
QBACKP qbackp = (QBACKP) lcMalloc(sizeof(BACKP));
|
|
if (!qbackp)
|
|
OOM();
|
|
memset(qbackp, 0, sizeof(BACKP));
|
|
|
|
#ifdef CHECK
|
|
qbackp->tag = QBACKP_TAG;
|
|
#endif
|
|
|
|
#ifdef STATS
|
|
++cBackpNodes;
|
|
#endif
|
|
|
|
qbackp->suppresszeck.rbSuppress = qb;
|
|
qbackp->suppresszeck.cbSuppress = cb;
|
|
qbackp->suppresszeck.iBitdex = (WORD) BITDEX_NONE; // this neccessary for no-compress case!
|
|
if (backptype == Emfcp) {
|
|
if (fNextIsEoNSR) {
|
|
|
|
// we've been flagged that the next mfcp to come is the end of a non
|
|
// scrolling region. Mark it so that the vaNSR fields can be set:
|
|
|
|
qbackp->backptype = EEoNSRmfcp;
|
|
fNextIsEoNSR = FALSE; // reset flag.
|
|
}
|
|
else if (fNextIsNSR) {
|
|
qbackp->backptype = EBegNSRmfcp;
|
|
fNextIsNSR = FALSE; // reset flag.
|
|
}
|
|
else
|
|
qbackp->backptype = backptype;
|
|
}
|
|
else
|
|
qbackp->backptype = backptype;
|
|
|
|
qbackp->idfcp = idfcpCur;
|
|
qbackp->fcl = fclNil;
|
|
|
|
if (qbackpInsert) {
|
|
CHECK_QBACKP(qbackpInsert);
|
|
qbackpInsert->suppresszeck.next = (QSUPPRESSZECK) qbackp;
|
|
qbackp->prev = qbackpInsert;
|
|
}
|
|
else {
|
|
qbackpMem = qbackp;
|
|
}
|
|
qbackpInsert = qbackp;
|
|
|
|
CHECK_QBACKP(qbackp);
|
|
|
|
return(qbackp);
|
|
}
|
|
|
|
/* VRemoveBackp( QBACKP qbackp ) -
|
|
*
|
|
* Remove from the linked list.
|
|
*/
|
|
|
|
static void STDCALL VRemoveBackp(QBACKP qbackp)
|
|
{
|
|
CHECK_QBACKP(qbackp);
|
|
|
|
if (qbackp->prev) {
|
|
CHECK_QBACKP(qbackp->prev);
|
|
qbackp->prev->suppresszeck.next = (QSUPPRESSZECK) qbackp->suppresszeck.next;
|
|
}
|
|
else if (qbackpMem == qbackp)
|
|
qbackpMem = (QBACKP) qbackp->suppresszeck.next;
|
|
|
|
if (qbackp->suppresszeck.next) {
|
|
CHECK_QBACKP(((QBACKP) qbackp->suppresszeck.next));
|
|
((QBACKP) qbackp->suppresszeck.next)->prev = qbackp->prev;
|
|
}
|
|
qbackp->prev = NULL;
|
|
qbackp->suppresszeck.next = NULL;
|
|
}
|
|
|
|
/* VFreeIfDisk( QBACKP qbackpFree )
|
|
*
|
|
* We remove several nodes out of the of in-mem linked list of backp
|
|
* nodes when we may have to do a fixup to the disk. These nodes will not
|
|
* be freed by the normal linked-list freeing code, so we free them here if
|
|
* neccessary:
|
|
*/
|
|
|
|
static void STDCALL VFreeIfDisk(QBACKP qbackpFree)
|
|
{
|
|
if (!qbackpFree)
|
|
return;
|
|
|
|
CHECK_QBACKP(qbackpFree);
|
|
|
|
if (qbackpFree->fcl != fclNil) {
|
|
#ifdef CHECK
|
|
qbackpFree->tag = 0;
|
|
#endif
|
|
|
|
lcFree(qbackpFree);
|
|
|
|
#ifdef STATS
|
|
--cBackpNodes;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* VAddToBuffer() -
|
|
*
|
|
* Add the passed count of bytes to the block buffer. If fIsFcp == TRUE,
|
|
* then mark that portion as uncompressable. If we overflow the buffer,
|
|
* then compress the buffer and write it out.
|
|
*
|
|
* Note: tail recursion could be turned into iteration.
|
|
*/
|
|
|
|
static void STDCALL VAddToBuffer(LPBYTE qb, DWORD lcb, BACKPTYPE backptype)
|
|
{
|
|
QBACKP qbackp;
|
|
|
|
// These things can happen:
|
|
|
|
if (qb == NULL || lcb == 0)
|
|
return;
|
|
|
|
// If not enough room for whole node, then flush buffer first:
|
|
|
|
if ((rgbBlockBuffer + cbMAX_BLOCK_SIZE) - pbBlockBuffer
|
|
< irgSuppresstypeSizes[backptype])
|
|
VFlushBuffer(FALSE);
|
|
|
|
// deal with special buffer addition types:
|
|
|
|
switch(backptype) {
|
|
case Emtop:
|
|
|
|
pmtop = (MTOP *) pbBlockBuffer;
|
|
if (!pmtopFirst)
|
|
pmtopFirst = pmtop;
|
|
else if (!pmtopSecond)
|
|
pmtopSecond = pmtop;
|
|
|
|
case Emobj:
|
|
ASSERT(lcb < 65536L);
|
|
QbpAddbackp(backptype, pbBlockBuffer, (UINT) lcb);
|
|
// FALLTHROUGH
|
|
|
|
case Enone: // don't need to add a node....
|
|
// add size to the current topic:
|
|
CHECK_QBACKP(qbackpPrevTopic);
|
|
ASSERT(qbackpPrevTopic->backptype == ETopicmfcp);
|
|
qbackpPrevTopic->lcbTopic += lcb;
|
|
break;
|
|
|
|
case ETopicmfcp:
|
|
case Emfcp:
|
|
case EBegNSRmfcp:
|
|
case EEoNSRmfcp:
|
|
ASSERT(lcb < 65536L);
|
|
qbackp = QbpAddbackp(backptype, pbBlockBuffer, (UINT) lcb);
|
|
if( qbackp->backptype == EEoNSRmfcp ) {
|
|
/* h3.07 ptr 1270: the lcbTopic measurement should be for the
|
|
* Scrolling Region only, so when we hit the magic end-of-non-scrolling-
|
|
* region mfcp, we zero out that lcbTopic so the size measurement
|
|
* starts over and thus only includes the size of the Scrolling Region.
|
|
* -Tom, 8/19/91
|
|
* P.S., and yes, this the == EEoNSRmfcp test must be after the
|
|
* QbpAddbackp() call cause that is what sets it as EEoNSRmfcp.
|
|
*
|
|
* NOTE: This value is a total hard-coded hack and equals the size
|
|
* of the Topic's mobj, mtop, and subsequent non-topic mfcp. The
|
|
* runtime expects this size-overhead to be included so we start off
|
|
* with the values here:
|
|
*/
|
|
qbackpPrevTopic->lcbTopic = 49; /* size MOBJ, MTOP and MFCP */
|
|
}
|
|
#ifdef _DEBUG
|
|
((QMFCP) (qbackp->suppresszeck.rbSuppress)) ->vaPrevFc.dword = vaNil;
|
|
((QMFCP) (qbackp->suppresszeck.rbSuppress)) ->vaNextFc.dword = vaNil;
|
|
#endif
|
|
|
|
if (backptype == ETopicmfcp) {
|
|
if (qbackpPrevTopic != NULL) {
|
|
|
|
// backpatch previous topic size -- either on disk or in memory:
|
|
|
|
QBACKP qbackpMOBJ = (QBACKP) qbackpPrevTopic->suppresszeck.next;
|
|
CHECK_QBACKP(qbackpPrevTopic);
|
|
CHECK_QBACKP(qbackpMOBJ);
|
|
ASSERT(qbackpMOBJ && qbackpMOBJ->backptype == Emobj);
|
|
if (qbackpMOBJ->fcl != fclNil) {
|
|
|
|
// back patch onto disk
|
|
|
|
FDiskBackpatchZeck(fmsg.hfTopic, qbackpMOBJ->fcl,
|
|
offTopicsizeInMOBJ, qbackpMOBJ->suppresszeck.iBitdex,
|
|
qbackpPrevTopic->lcbTopic);
|
|
}
|
|
else {
|
|
|
|
// back patch into mem:
|
|
|
|
((QMOBJ) (qbackpMOBJ->suppresszeck.rbSuppress))->lcbSize =
|
|
qbackpPrevTopic->lcbTopic;
|
|
}
|
|
}
|
|
|
|
// free the corresponding MOBJ node if neccessary:
|
|
|
|
if (qbackpPrevTopic && qbackpPrevTopic != qbackpPrevMFCP) {
|
|
VFreeIfDisk((QBACKP) qbackpPrevTopic->suppresszeck.next);
|
|
VFreeIfDisk(qbackpPrevTopic);
|
|
}
|
|
qbackpPrevTopic = qbackp;
|
|
}
|
|
|
|
// add size to the current topic:
|
|
|
|
ASSERT(qbackpPrevTopic->backptype == ETopicmfcp);
|
|
qbackpPrevTopic->lcbTopic += lcb;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE); // should never reach here.
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
ASSERT(pbBlockBuffer); // Give CodeView something to break on
|
|
#endif
|
|
|
|
// copy into buffer until overflow or out-of-bytes:
|
|
|
|
while(lcb && pbBlockBuffer < &rgbBlockBuffer[cbMAX_BLOCK_SIZE]) {
|
|
*pbBlockBuffer++ = *qb++;
|
|
lcb--;
|
|
}
|
|
|
|
if (lcb) {
|
|
|
|
// overflowed the buffer, compress then write it out:
|
|
|
|
VFlushBuffer(FALSE);
|
|
|
|
// recurse to add the rest to the buffer:
|
|
|
|
VAddToBuffer(qb, lcb, Enone);
|
|
}
|
|
}
|
|
|
|
/* VFlushBuffer( BOOL fFlushAll )
|
|
*
|
|
* Compress and flush the buffer we have been saving |TOPIC data in.
|
|
* Only compresses it if options.fsCompress & FCOMPRESS_ZECK is on.
|
|
*
|
|
* Called when the buffer is full or when the end of the help src is
|
|
* reached.
|
|
*
|
|
* If fFlushAll is TRUE, then the entire buffer is emptied (used when
|
|
* end of help src is reached), otherwise only the compressed 2K
|
|
* is flushed.
|
|
*
|
|
* Note: tail recursion could be turned into iteration.
|
|
*/
|
|
|
|
void STDCALL VFlushBuffer(BOOL fFlushAll)
|
|
{
|
|
int lcbSrcCompressed;
|
|
UINT cbCompressed;
|
|
IDFCP idfcpLast;
|
|
QBACKP qbackp;
|
|
|
|
if (pbBlockBuffer <= rgbBlockBuffer) {
|
|
#ifdef STATS
|
|
OutLong("\r\nDropped backp nodes: %ld", cBackpNodes);
|
|
OutLong("\r\nSize of rich text: %ld", cbRText);
|
|
#endif
|
|
return; // buffer is empty.
|
|
}
|
|
|
|
if (options.fsCompress & COMPRESS_TEXT_ZECK) {
|
|
cbCompressed = (UINT) LcbCompressZeck(rgbBlockBuffer, rgbCompressedBuffer,
|
|
(pbBlockBuffer - rgbBlockBuffer), LCB_BLOCKDATASIZE,
|
|
&lcbSrcCompressed, (QSUPPRESSZECK) qbackpMem);
|
|
AddZeckCounts(lcbSrcCompressed, cbCompressed);
|
|
}
|
|
else {
|
|
QBACKP qbackp;
|
|
|
|
// No compression, pretend it was an "ineffective" compression:
|
|
|
|
memcpy(rgbCompressedBuffer, rgbBlockBuffer, LCB_BLOCKDATASIZE);
|
|
lcbSrcCompressed =
|
|
MIN(LCB_BLOCKDATASIZE, pbBlockBuffer - rgbBlockBuffer);
|
|
cbCompressed = (UINT) lcbSrcCompressed;
|
|
|
|
if (pmtopFirst)
|
|
pmtopFirst = (MTOP FAR*)
|
|
(rgbCompressedBuffer + ((PBYTE) pmtopFirst - rgbBlockBuffer));
|
|
if (pmtopSecond)
|
|
pmtopSecond = (MTOP FAR*)
|
|
(rgbCompressedBuffer + ((PBYTE) pmtopSecond - rgbBlockBuffer));
|
|
|
|
// Now fake the out-param new-pos ptrs in the suppresszeck list:
|
|
|
|
for (qbackp = qbackpMem; qbackp &&
|
|
qbackp->suppresszeck.rbSuppress < &rgbBlockBuffer[lcbSrcCompressed];
|
|
qbackp = (QBACKP) qbackp->suppresszeck.next) {
|
|
CHECK_QBACKP(qbackp);
|
|
qbackp->suppresszeck.rbNewpos = rgbCompressedBuffer +
|
|
((PBYTE) qbackp->suppresszeck.rbSuppress - rgbBlockBuffer);
|
|
}
|
|
}
|
|
|
|
// Either we compressed into a full block or we used up the whole buffer
|
|
|
|
ASSERT(cbCompressed == LCB_BLOCKDATASIZE
|
|
|| lcbSrcCompressed == (pbBlockBuffer - rgbBlockBuffer));
|
|
|
|
// Make sure no back-patch-required blocks cross the boundary.
|
|
// Add padding if any do. Note that any such padding must be included
|
|
// in the size of any topics which happen to contain it.
|
|
for (qbackp = qbackpMem; qbackp &&
|
|
qbackp->suppresszeck.rbSuppress < &rgbBlockBuffer[lcbSrcCompressed];
|
|
qbackp = (QBACKP) qbackp->suppresszeck.next) {
|
|
|
|
// Make sure it's not too near the boundary & crosses it:
|
|
|
|
CHECK_QBACKP(qbackp);
|
|
if (
|
|
|
|
/* Zeck compression can actually grow these structs by 1/8, therefore,
|
|
* when we do this struct-size cross-end-of-buffer analysis, we have
|
|
* to take into account this possible growth factor (arrrggggg).
|
|
* PTR 1069, help 3.1. Found in ccq.mvp --- Columbia Concise
|
|
* Encyclopedia - has lots of small topics...
|
|
*/
|
|
#define ZECK_GROWTH_SLOP(size) ((size) + ((size) /8) + 1)
|
|
// If it's a topic, make sure mfcp, mobj & mtop can fit:
|
|
#define CB_TOPICMFCP_LOCK_HEADER \
|
|
ZECK_GROWTH_SLOP((sizeof(MFCP) + sizeof(MOBJ) + sizeof(MTOP)))
|
|
#define CB_OTHERMFCP_LOCK_HEADER \
|
|
ZECK_GROWTH_SLOP((sizeof(MFCP) + sizeof(MOBJ)))
|
|
|
|
(qbackp->backptype == ETopicmfcp &&
|
|
(qbackp->suppresszeck.rbNewpos + CB_TOPICMFCP_LOCK_HEADER
|
|
> &rgbCompressedBuffer[LCB_BLOCKDATASIZE]))
|
|
|
|
// Else just make sure that whole suppression range can fit:
|
|
|
|
||
|
|
((qbackp->backptype == Emfcp || qbackp->backptype == EEoNSRmfcp
|
|
|| qbackp->backptype == EBegNSRmfcp)
|
|
&&
|
|
(qbackp->suppresszeck.rbNewpos + CB_OTHERMFCP_LOCK_HEADER
|
|
> &rgbCompressedBuffer[LCB_BLOCKDATASIZE]))
|
|
||
|
|
(qbackp->suppresszeck.rbNewpos + qbackp->suppresszeck.cbSuppress)
|
|
> &rgbCompressedBuffer[LCB_BLOCKDATASIZE]
|
|
) {
|
|
DWORD cbPadding;
|
|
QBACKP qbackpTopicSearch;
|
|
|
|
// adjust such that that qbackp node's data is not in the 2K --
|
|
// add padding.
|
|
|
|
cbPadding =
|
|
((rgbCompressedBuffer + LCB_BLOCKDATASIZE) -
|
|
(PBYTE) qbackp->suppresszeck.rbNewpos);
|
|
|
|
cbCompressed -= (UINT) cbPadding;
|
|
lcbSrcCompressed =
|
|
((PBYTE) qbackp->suppresszeck.rbSuppress) - rgbBlockBuffer;
|
|
|
|
#ifdef STATS
|
|
wsprintf(szParentString, "\r\nPadding: %ld", cbPadding);
|
|
SendStringToParent(szParentString);
|
|
#endif
|
|
|
|
// adjust the size of the topic just prior to that guy, search for
|
|
// the topic:
|
|
for (qbackpTopicSearch = qbackp;
|
|
qbackpTopicSearch && qbackpTopicSearch->backptype != ETopicmfcp;
|
|
qbackpTopicSearch = qbackpTopicSearch->prev) {
|
|
CHECK_QBACKP(qbackpTopicSearch);
|
|
; // null body of loop, we are just searching...
|
|
}
|
|
if (!qbackpTopicSearch) {
|
|
|
|
// was none in the in-mem-list prior to our compression guy, must
|
|
|
|
// be the on-disk one:
|
|
|
|
qbackpTopicSearch = qbackpPrevTopic; // always pts to a prior topic.
|
|
}
|
|
ASSERT(qbackpTopicSearch);
|
|
qbackpTopicSearch->lcbTopic += cbPadding;
|
|
break; // we have reached the end.
|
|
}
|
|
|
|
// save away the last idfcp in the compressed image section:
|
|
|
|
idfcpLast = qbackp->idfcp;
|
|
}
|
|
|
|
if (qbackpMem) {
|
|
QBACKP qbackpFind;
|
|
CHECK_QBACKP(qbackpMem);
|
|
InitBrowseCallback();
|
|
|
|
/*
|
|
* We are flushing the idfcp's of qbackpMem ... qbackp to disk. Let
|
|
* the other addressing buffering guys know about it (footnotes,
|
|
* jumps...): Find next mfcp type node, use that idfcp number as the
|
|
* start: Search until run out of nodes or run of the end of the
|
|
* got-compressed nodes (qbackp marks end of that list).
|
|
*/
|
|
|
|
for (qbackpFind = qbackpMem;
|
|
qbackpFind && qbackpFind != qbackp &&
|
|
qbackpFind->backptype != Emfcp && qbackpFind->backptype != ETopicmfcp
|
|
&& qbackpFind->backptype != EEoNSRmfcp
|
|
&& qbackpFind->backptype != EBegNSRmfcp;
|
|
qbackpFind = (QBACKP) qbackpFind->suppresszeck.next) {
|
|
CHECK_QBACKP(qbackpFind);
|
|
; // loop only searches.
|
|
}
|
|
|
|
// If an mfcp was in the compressed block:
|
|
|
|
if (qbackpFind && qbackpFind != qbackp) {
|
|
|
|
RC_TYPE rc = RcExecuteDelayedExecution(Blknum, qbackpFind->idfcp,
|
|
idfcpLast, fclBrowseCallback);
|
|
if (rc != RC_Success) {
|
|
VReportError(HCERR_BAD_ZECK, &errHpj);
|
|
HardExit(); // doesn't return
|
|
}
|
|
}
|
|
VFlushBackpNodes(lcbSrcCompressed);
|
|
}
|
|
|
|
// Write the block header to the file:
|
|
|
|
LcbWriteHf(fmsg.hfTopic, &mbhdCurrent, sizeof(MBHD));
|
|
|
|
/*
|
|
* Set up mbhdCurrent prev & topic fields based on global vars
|
|
* maintained in VFlushBackpNodes()...
|
|
*/
|
|
|
|
mbhdCurrent.vaFCPPrev.bf.blknum = ulPrevMFCPBlknum;
|
|
mbhdCurrent.vaFCPPrev.bf.byteoff = sizeof(MBHD) +
|
|
(PBYTE) qbackpPrevMFCP->suppresszeck.rbSuppress - rgbBlockBuffer;
|
|
mbhdCurrent.vaFCPTopic.dword = vaPrevFlushedTopic.dword;
|
|
|
|
/*
|
|
* Write the buffer to the file: Either we're going to write a whole
|
|
* block, or fFlushAll is requested: Sometimes we compress 16K to smaller
|
|
* than 4K (not often), or sometimes we get PA address overflow and must
|
|
* procede to the next block. In either case we pad out the current block
|
|
* to 4K:
|
|
*/
|
|
|
|
if (cbCompressed != LCB_BLOCKDATASIZE) {
|
|
ASSERT(cbCompressed < LCB_BLOCKDATASIZE);
|
|
if (!fFlushAll || lcbSrcCompressed != (pbBlockBuffer - rgbBlockBuffer)) {
|
|
|
|
/*
|
|
* This second test, on fFlushAll, added for ptr 1223 in the case when
|
|
* the PAs overflow, AND there is not even 4K of data in the buffer. In
|
|
* this case way may add a LOT of padding to the end of the block. This
|
|
* occurs when phrase compression is extremely effective. -Tom, 7/15/91.
|
|
*/
|
|
|
|
#ifdef STATS2
|
|
wsprintf(szParentString, "Address overflow padding: %ld\r\n", LCB_BLOCKDATASIZE - cbCompressed);
|
|
SendStringToParent();
|
|
#endif
|
|
|
|
// Zero out the padding data:
|
|
|
|
/*
|
|
* This done for safety since it's a genally good idea and ptr 1254
|
|
* hinted that it may be a problem, although we never got accurate
|
|
* sources for 1254's help file to confirm it. -Tom, 8/6/91
|
|
*/
|
|
|
|
memset(rgbCompressedBuffer + cbCompressed, 0,
|
|
(size_t) (LCB_BLOCKDATASIZE - cbCompressed));
|
|
cbCompressed = LCB_BLOCKDATASIZE; // pad it w/ zeros
|
|
}
|
|
}
|
|
LcbWriteHf(fmsg.hfTopic, rgbCompressedBuffer, cbCompressed);
|
|
|
|
/*
|
|
* Copy what was above the compressed-stuff down in the buffer & adjust
|
|
* all suppresszeck type ptrs:
|
|
*/
|
|
|
|
VCopyDownAndAdjust(lcbSrcCompressed);
|
|
|
|
++Blknum;
|
|
// recurse to keep flushing if fFlushAll:
|
|
if (fFlushAll)
|
|
VFlushBuffer(fFlushAll);
|
|
}
|
|
|
|
/* VFlushBackpNodes( lcbSrcCompressed ) -
|
|
*
|
|
* We've done the zeck compression and therefore now know what blknum
|
|
* many of the VAs ended up in. Traverse the backp list, fixup all the
|
|
* VAs we can, and set up our various global to reflect the new
|
|
* positioning of nodes.
|
|
*
|
|
* Note that file addresses are calculated with a delta of sizeof(MBHD)
|
|
* added in since that struct heads every block but is not in the
|
|
* rgbBlockBuffer buffer.
|
|
*/
|
|
|
|
static void STDCALL VFlushBackpNodes(DWORD lcbSrcCompressed)
|
|
{
|
|
QBACKP qbackp, qbackptmp, qbackpt;
|
|
VA va;
|
|
|
|
mbhdCurrent.vaFCPNext.dword = vaNil; // mark as needing to be filled.
|
|
|
|
for (qbackp = qbackpMem; qbackp &&
|
|
qbackp->suppresszeck.rbSuppress < &rgbBlockBuffer[lcbSrcCompressed];
|
|
qbackp = (QBACKP) qbackp->suppresszeck.next) {
|
|
CHECK_QBACKP(qbackp);
|
|
switch(qbackp->backptype) {
|
|
case ETopicmfcp:
|
|
|
|
// Fixup previous MTOP's vaNextSeqTopic:
|
|
|
|
if (qbackpPrevMTOP) {
|
|
CHECK_QBACKP(qbackpPrevMTOP);
|
|
va.bf.byteoff = sizeof(MBHD) +
|
|
((PBYTE) qbackp->suppresszeck.rbSuppress - rgbBlockBuffer);
|
|
va.bf.blknum = Blknum;
|
|
if (qbackpPrevMTOP->fcl != fclNil) { // is on disk:
|
|
FDiskBackpatchZeck(fmsg.hfTopic, qbackpPrevMTOP->fcl,
|
|
offvaNextSeqTopicInMTOP, qbackpPrevMTOP->suppresszeck.iBitdex, va.dword);
|
|
} else {
|
|
VMemBackpatchZeck(&(qbackpPrevMTOP->suppresszeck),
|
|
offvaNextSeqTopicInMTOP, va.dword);
|
|
}
|
|
}
|
|
// FALLTHROUGH ok.
|
|
|
|
case Emfcp:
|
|
case EBegNSRmfcp:
|
|
case EEoNSRmfcp:
|
|
// Fixup mfcp's prev ptr:
|
|
if (qbackpPrevMFCP) {
|
|
CHECK_QBACKP(qbackpPrevMFCP);
|
|
va.bf.byteoff = sizeof(MBHD) +
|
|
((PBYTE) qbackpPrevMFCP->suppresszeck.rbSuppress - rgbBlockBuffer);
|
|
va.bf.blknum = ulPrevMFCPBlknum;
|
|
VMemBackpatchZeck(&qbackp->suppresszeck, offPrevInMFCP, va.dword);
|
|
|
|
// ((QMFCP)qbackp->suppresszeck.rbNewpos)->vaPrevFc = va;
|
|
// Fixup previous mfcp's next ptr:
|
|
|
|
va.bf.byteoff = sizeof(MBHD) +
|
|
((PBYTE) qbackp->suppresszeck.rbSuppress - rgbBlockBuffer);
|
|
va.bf.blknum = Blknum;
|
|
if (qbackpPrevMFCP->fcl != fclNil) { // is on disk:
|
|
FDiskBackpatchZeck(fmsg.hfTopic, qbackpPrevMFCP->fcl,
|
|
offNextInMFCP, qbackpPrevMFCP->suppresszeck.iBitdex, va.dword);
|
|
} else {
|
|
VMemBackpatchZeck(&qbackpPrevMFCP->suppresszeck,
|
|
offNextInMFCP, va.dword);
|
|
}
|
|
}
|
|
|
|
// See if mbhdCurrent needs to be filled:
|
|
|
|
if (mbhdCurrent.vaFCPNext.dword == vaNil) {
|
|
va.bf.byteoff = sizeof(MBHD) +
|
|
((PBYTE) qbackp->suppresszeck.rbSuppress - rgbBlockBuffer);
|
|
va.bf.blknum = Blknum;
|
|
mbhdCurrent.vaFCPNext = va;
|
|
}
|
|
|
|
/*
|
|
* Handle MTOP vaSR and va NSR -- this is a va[N]SR FCP if it's the
|
|
* first mfcp after the topic mfcp:
|
|
*/
|
|
|
|
if (qbackpPrevMTOP) {
|
|
CHECK_QBACKP(qbackpPrevMTOP);
|
|
va.bf.byteoff = sizeof(MBHD) +
|
|
((PBYTE) qbackp->suppresszeck.rbSuppress - rgbBlockBuffer);
|
|
va.bf.blknum = Blknum;
|
|
if ((qbackp->backptype == Emfcp || qbackp->backptype == EEoNSRmfcp
|
|
|| qbackp->backptype == EBegNSRmfcp)
|
|
&& qbackpPrevMFCP == qbackpPrevFlushedTopic) {
|
|
|
|
// backpatch the vaSR pointer:
|
|
|
|
if (qbackpPrevMTOP->fcl != fclNil) { // is on disk:
|
|
FDiskBackpatchZeck(fmsg.hfTopic, qbackpPrevMTOP->fcl,
|
|
qbackp->backptype == EBegNSRmfcp ? offvaNSRInMTOP : offvaSRInMTOP,
|
|
qbackpPrevMTOP->suppresszeck.iBitdex, va.dword);
|
|
}
|
|
else {
|
|
VMemBackpatchZeck(&qbackpPrevMTOP->suppresszeck,
|
|
qbackp->backptype == EBegNSRmfcp ?
|
|
offvaNSRInMTOP : offvaSRInMTOP,
|
|
va.dword);
|
|
}
|
|
}
|
|
|
|
// If this is a marker type of object, re-fixup the SR and NSR VAs:
|
|
|
|
if (qbackp->backptype == EEoNSRmfcp) {
|
|
|
|
// The address of the NSR is the address of the MFCP following
|
|
// this special marker MFCP:
|
|
|
|
va.bf.blknum = Blknum;
|
|
va.bf.byteoff = sizeof(MBHD) +
|
|
(PBYTE) (qbackp->suppresszeck.rbSuppress) - rgbBlockBuffer;
|
|
|
|
// has a marker, therefore has a NSR, so adjust the fixups:
|
|
|
|
if (qbackpPrevMTOP->fcl != fclNil) { // is on disk:
|
|
FDiskBackpatchZeck(fmsg.hfTopic, qbackpPrevMTOP->fcl,
|
|
offvaSRInMTOP, qbackpPrevMTOP->suppresszeck.iBitdex, va.dword);
|
|
|
|
}
|
|
else {
|
|
VMemBackpatchZeck(&qbackpPrevMTOP->suppresszeck,
|
|
offvaSRInMTOP, va.dword);
|
|
}
|
|
}
|
|
}
|
|
ulPrevMFCPBlknum = Blknum;
|
|
if (qbackp->backptype == ETopicmfcp) {
|
|
qbackpPrevFlushedTopic = qbackp;
|
|
vaPrevFlushedTopic.bf.blknum = Blknum;
|
|
vaPrevFlushedTopic.bf.byteoff = sizeof(MBHD) +
|
|
(PBYTE) qbackpPrevFlushedTopic->suppresszeck.rbSuppress -
|
|
rgbBlockBuffer;
|
|
}
|
|
// if previous PrevMFCP was pointing to the disk, then it is not
|
|
// in our linked list and needs to be freed:
|
|
if( qbackpPrevMFCP != qbackpPrevTopic ) {
|
|
VFreeIfDisk( qbackpPrevMFCP );
|
|
}
|
|
qbackpPrevMFCP = qbackp;
|
|
break;
|
|
|
|
case Emtop:
|
|
VFreeIfDisk( qbackpPrevMTOP );
|
|
qbackpPrevMTOP = qbackp;
|
|
break;
|
|
|
|
case Emobj: // ignore it, topic size got backpatched in VAddToBuffer().
|
|
break;
|
|
|
|
default: ASSERT( FALSE ); // Should NEVER reach here.
|
|
}
|
|
}
|
|
|
|
// We must keep around the Prev nodes for backpatching when we emit the
|
|
// next block. Thus we remove them from the list and calculate their
|
|
// fcl file position values:
|
|
if (qbackpPrevTopic &&
|
|
qbackpPrevTopic->suppresszeck.rbSuppress <
|
|
&rgbBlockBuffer[lcbSrcCompressed]) {
|
|
QBACKP qbackpMOBJ = (QBACKP) qbackpPrevTopic->suppresszeck.next;
|
|
CHECK_QBACKP(qbackpPrevTopic);
|
|
|
|
VRemoveBackp(qbackpMOBJ);
|
|
VRemoveBackp(qbackpPrevTopic);
|
|
qbackpPrevTopic->suppresszeck.next = (QSUPPRESSZECK) qbackpMOBJ;
|
|
if (qbackpPrevTopic->fcl == fclNil) {
|
|
qbackpPrevTopic->fcl = (cbBLOCK_SIZE * Blknum) + sizeof(MBHD)
|
|
+ ((PBYTE) qbackpPrevTopic->suppresszeck.rbNewpos - rgbCompressedBuffer);
|
|
}
|
|
if (qbackpMOBJ->fcl == fclNil) {
|
|
qbackpMOBJ->fcl = (cbBLOCK_SIZE * Blknum) + sizeof(MBHD)
|
|
+ ((PBYTE) qbackpMOBJ->suppresszeck.rbNewpos - rgbCompressedBuffer);
|
|
}
|
|
}
|
|
if (qbackpPrevMFCP && qbackpPrevTopic != qbackpPrevMFCP
|
|
&& qbackpPrevMFCP->fcl == fclNil) {
|
|
VRemoveBackp(qbackpPrevMFCP );
|
|
qbackpPrevMFCP->fcl = (cbBLOCK_SIZE * Blknum) + sizeof(MBHD)
|
|
+ ((PBYTE) qbackpPrevMFCP->suppresszeck.rbNewpos - rgbCompressedBuffer);
|
|
}
|
|
if (qbackpPrevMTOP && qbackpPrevMTOP->fcl == fclNil) {
|
|
VRemoveBackp(qbackpPrevMTOP );
|
|
qbackpPrevMTOP->fcl = (cbBLOCK_SIZE * Blknum) + sizeof(MBHD)
|
|
+ ((PBYTE) qbackpPrevMTOP->suppresszeck.rbNewpos - rgbCompressedBuffer);
|
|
}
|
|
|
|
// Free the nodes in the list we no longer need
|
|
|
|
for (qbackpt = qbackpMem; qbackpt != qbackp;) {
|
|
CHECK_QBACKP(qbackpt);
|
|
qbackptmp = qbackpt;
|
|
qbackpt = (QBACKP) qbackpt->suppresszeck.next;
|
|
ASSERT(qbackptmp->fcl == fclNil);
|
|
#ifdef CHECK
|
|
qbackptmp->tag = 0;
|
|
#endif
|
|
lcFree(qbackptmp);
|
|
#ifdef STATS
|
|
--cBackpNodes;
|
|
#endif
|
|
}
|
|
|
|
// The mem list now starts where we left off:
|
|
|
|
qbackpMem = qbackp;
|
|
if (!qbackpMem)
|
|
qbackpInsert = NULL;
|
|
else
|
|
qbackpMem->prev = NULL;
|
|
}
|
|
|
|
static FCL STDCALL fclBrowseCallback(IDFCP idfcp, int* piBitdex)
|
|
{
|
|
FCL retfcl;
|
|
|
|
// Search for the next MTOP qbackpMem:
|
|
|
|
while(qbackpBrowseCallback && (qbackpBrowseCallback->backptype != Emtop
|
|
|| qbackpBrowseCallback->idfcp < idfcp)) {
|
|
CHECK_QBACKP(qbackpBrowseCallback);
|
|
qbackpBrowseCallback = (QBACKP) qbackpBrowseCallback->suppresszeck.next;
|
|
}
|
|
CHECK_QBACKP(qbackpBrowseCallback);
|
|
|
|
ASSERT(qbackpBrowseCallback);
|
|
ASSERT(idfcp == qbackpBrowseCallback->idfcp);
|
|
|
|
retfcl = (cbBLOCK_SIZE * Blknum) + sizeof(MBHD)
|
|
+ ((PBYTE) qbackpBrowseCallback->suppresszeck.rbNewpos - rgbCompressedBuffer);
|
|
|
|
*piBitdex = qbackpBrowseCallback->suppresszeck.iBitdex;
|
|
|
|
// advance to next for next iteration...
|
|
|
|
qbackpBrowseCallback = (QBACKP) qbackpBrowseCallback->suppresszeck.next;
|
|
|
|
return(retfcl);
|
|
}
|
|
|
|
/* VCopyDownAndAdjust() -
|
|
*
|
|
* We've just compressed and written lcbSrcCompressed bytes from
|
|
* the beginning of our buffer. Move the the bytes above that down
|
|
* and adjust the ptrs in the QBACKP linked list to reflect the new
|
|
* positions of the data.
|
|
*
|
|
* ASSUMES: only QBACKP nodes which refer to uncompressed data are in
|
|
* the qbackpMem list -- meaning the prior nodes must have been
|
|
* removed prior to calling this function (VFlushBackpNodes() does it).
|
|
*/
|
|
|
|
static void STDCALL VCopyDownAndAdjust(DWORD lcbSrcCompressed)
|
|
{
|
|
QBACKP qbackp;
|
|
|
|
// copy data down:
|
|
|
|
memmove(rgbBlockBuffer, rgbBlockBuffer + lcbSrcCompressed,
|
|
(int) (pbBlockBuffer - rgbBlockBuffer - lcbSrcCompressed));
|
|
|
|
// adjust current insertion point:
|
|
|
|
pbBlockBuffer -= lcbSrcCompressed;
|
|
|
|
// adjust qbackp linked list:
|
|
|
|
for (qbackp = qbackpMem; qbackp;
|
|
qbackp = (QBACKP) (qbackp->suppresszeck.next)) {
|
|
CHECK_QBACKP(qbackp);
|
|
qbackp->suppresszeck.rbSuppress -= lcbSrcCompressed;
|
|
ASSERT(qbackp->suppresszeck.rbSuppress >= rgbBlockBuffer);
|
|
ASSERT(qbackp->suppresszeck.rbSuppress <
|
|
&rgbBlockBuffer[sizeof(rgbBlockBuffer)]);
|
|
}
|
|
}
|
|
|
|
// REVIEW: these are enumerated types in objects.h already #included
|
|
|
|
#define bWordFormat 0x80 // Followed by 16 bit text format number
|
|
#define bNewLine 0x81 // Newline
|
|
#define bNewPara 0x82 // New paragraph
|
|
#define bTab 0x83 // Left-aligned tab
|
|
#define bBlankLine 0x85 // Followed by 16 bit skip count
|
|
#define bInlineObject 0x86 // Followed by inline layout object
|
|
#define bWrapObjLeft 0x87 // Left- aligned wrapping object
|
|
#define bWrapObjRight 0x88 // Right-aligned wrapping object
|
|
#define bEndHotspot 0x89 // End of a hotspot
|
|
#define bColdspot 0x8A // Coldspot for searchable bitmaps
|
|
#define bEnd 0xFF // End of text
|
|
|
|
void STDCALL AppendText(PBYTE pb, int cb, int iCharSet)
|
|
{
|
|
// if (fHallPassOne) {
|
|
// pScanText(hCompressor, pb, cb, iCharSet);
|
|
// }
|
|
// else
|
|
pScanTopicText(hFtsIndex, pb, cb, iCharSet, lcid);
|
|
}
|
|
|
|
int STDCALL CbUnpackMOPG_Ex(QMOPG qmopg, LPVOID qv);
|
|
/*-------------------------------------------------------------------------
|
|
| CbUnpackMOPG(qmopg, qv) |
|
|
| |
|
|
| Purpose: Unpacks an MOPG data structure. |
|
|
-------------------------------------------------------------------------*/
|
|
|
|
static int STDCALL CbUnpackMOPG_Ex(QMOPG qmopg, LPVOID qv)
|
|
{
|
|
LPVOID qvFirst = qv;
|
|
MPFG mpfg;
|
|
INT iTab;
|
|
|
|
#ifdef MAGIC
|
|
qmopg->bMagic = *((PBYTE)qv);
|
|
qv = (((PBYTE)qv) + 1);
|
|
ASSERT(qmopg->bMagic == bMagicMOPG);
|
|
#endif /* _DEBUG */
|
|
|
|
qv = QVSkipQGE((LPBYTE) qv, &qmopg->libText);
|
|
|
|
mpfg = *((QMPFG) qv);
|
|
qv = (((QMPFG) qv) + 1);
|
|
|
|
// REVIEW
|
|
|
|
qmopg->fStyle = mpfg.fStyle;
|
|
ASSERT(!qmopg->fStyle);
|
|
qmopg->fMoreFlags = mpfg.rgf.fMoreFlags;
|
|
ASSERT(!qmopg->fMoreFlags);
|
|
qmopg->fBoxed = mpfg.rgf.fBoxed;
|
|
qmopg->justify = mpfg.rgf.justify;
|
|
qmopg->fSingleLine = mpfg.rgf.fSingleLine;
|
|
|
|
if (mpfg.rgf.fMoreFlags)
|
|
qv = QVSkipQGE((LPBYTE) qv, &qmopg->lMoreFlags);
|
|
else
|
|
qmopg->lMoreFlags = 0;
|
|
|
|
if (mpfg.rgf.fSpaceOver) {
|
|
qv = QVSkipQGD((LPBYTE) qv, &qmopg->ySpaceOver);
|
|
}
|
|
else
|
|
qmopg->ySpaceOver = 0;
|
|
|
|
if (mpfg.rgf.fSpaceUnder) {
|
|
qv = QVSkipQGD((LPBYTE) qv, &qmopg->ySpaceUnder);
|
|
}
|
|
else
|
|
qmopg->ySpaceUnder = 0;
|
|
|
|
if (mpfg.rgf.fLineSpacing) {
|
|
qv = QVSkipQGD((LPBYTE) qv, &qmopg->yLineSpacing);
|
|
}
|
|
else
|
|
qmopg->yLineSpacing = 0;
|
|
|
|
if (mpfg.rgf.fLeftIndent) {
|
|
qv = QVSkipQGD((LPBYTE) qv, &qmopg->xLeftIndent);
|
|
}
|
|
else
|
|
qmopg->xLeftIndent = 0;
|
|
|
|
if (mpfg.rgf.fRightIndent) {
|
|
qv = QVSkipQGD((LPBYTE) qv, &qmopg->xRightIndent);
|
|
}
|
|
else
|
|
qmopg->xRightIndent = 0;
|
|
|
|
if (mpfg.rgf.fFirstIndent) {
|
|
qv = QVSkipQGD((LPBYTE) qv, &qmopg->xFirstIndent);
|
|
}
|
|
else
|
|
qmopg->xFirstIndent = 0;
|
|
|
|
if (mpfg.rgf.fTabSpacing)
|
|
qv = QVSkipQGD((LPBYTE) qv, &qmopg->xTabSpacing);
|
|
else
|
|
qmopg->xTabSpacing = 72;
|
|
|
|
if (mpfg.rgf.fBoxed) {
|
|
qmopg->mbox = *((QMBOX)qv);
|
|
qv = (((QMBOX)qv) + 1);
|
|
}
|
|
|
|
if (mpfg.rgf.fTabs)
|
|
qv = QVSkipQGD((LPBYTE) qv, &qmopg->cTabs);
|
|
else
|
|
qmopg->cTabs = 0;
|
|
|
|
for (iTab = 0; iTab < qmopg->cTabs; iTab++) {
|
|
qv = QVSkipQGA(qv, &qmopg->rgtab[iTab].x);
|
|
if (qmopg->rgtab[iTab].x & 0x4000)
|
|
qv = QVSkipQGA(qv, &qmopg->rgtab[iTab].wType);
|
|
else
|
|
qmopg->rgtab[iTab].wType = TABTYPELEFT;
|
|
qmopg->rgtab[iTab].x = qmopg->rgtab[iTab].x & 0xBFFF;
|
|
}
|
|
|
|
return((INT) ((PBYTE)qv - (PBYTE)qvFirst));
|
|
}
|
|
|
|
PSTR STDCALL NextFTSString(PCSTR pszText, PBYTE *ppCmd, PINT pCharSet, BOOL bSYS)
|
|
{
|
|
char chCmd;
|
|
BOOL bSkipHot = FALSE;
|
|
BOOL bProcessCell = FALSE;
|
|
MOBJ mobj;
|
|
MOPG mopg;
|
|
short int iCell;
|
|
|
|
ASSERT(*pszText == 0x00);
|
|
|
|
//
|
|
// Side by side paragraph has the following embedded structure:
|
|
// INT16 cell number;
|
|
// MOBJ;
|
|
// MOPG;
|
|
//
|
|
|
|
if (bSYS && bSYSFirst) {
|
|
bSYSFirst = FALSE;
|
|
bProcessCell = TRUE;
|
|
iCell = *((short int *) *ppCmd);
|
|
(*ppCmd) += 2;
|
|
if (iCell < 0)
|
|
return NULL;
|
|
|
|
(*ppCmd) += CbUnpackMOBJ(&mobj, *ppCmd);
|
|
(*ppCmd) += CbUnpackMOPG_Ex(&mopg, *ppCmd);
|
|
}
|
|
|
|
while(*pszText == 0x00) {
|
|
chCmd = **ppCmd;
|
|
|
|
switch((int) (0x00FF & chCmd)) {
|
|
case bNewLine:
|
|
(*ppCmd)++;
|
|
break;
|
|
|
|
case bNewPara:
|
|
(*ppCmd)++;
|
|
break;
|
|
|
|
case bTab:
|
|
(*ppCmd)++;
|
|
break;
|
|
|
|
case bEndHotspot:
|
|
(*ppCmd)++;
|
|
bSkipHot = FALSE;
|
|
break;
|
|
|
|
case bBlankLine:
|
|
*ppCmd += 3;
|
|
break;
|
|
|
|
case bWordFormat:
|
|
(*ppCmd)++;
|
|
*pCharSet = (int) (*((short *)*ppCmd));
|
|
*pCharSet = (int) GetCharset(*pCharSet) & 0x000000ff;
|
|
*ppCmd += 2;
|
|
break;
|
|
|
|
case bWrapObjLeft:
|
|
case bWrapObjRight:
|
|
case bInlineObject:
|
|
(*ppCmd)++;
|
|
*ppCmd += CbUnpackMOBJ(&mobj, (void *) *ppCmd);
|
|
*ppCmd += (int) mobj.lcbSize;
|
|
break;
|
|
|
|
case bEnd:
|
|
(*ppCmd)++;
|
|
if (bSYS) {
|
|
bProcessCell = TRUE;
|
|
iCell = *((short int *) *ppCmd);
|
|
(*ppCmd) += 2;
|
|
|
|
if (iCell < 0)
|
|
return NULL;
|
|
|
|
(*ppCmd) += CbUnpackMOBJ(&mobj, *ppCmd);
|
|
(*ppCmd) += CbUnpackMOPG_Ex(&mopg, *ppCmd);
|
|
}
|
|
|
|
|
|
if (bProcessCell)
|
|
bProcessCell = FALSE;
|
|
else
|
|
return NULL; // REVIEW: return pszText?
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (FShortHotspot(**ppCmd))
|
|
{
|
|
*ppCmd += 5;
|
|
}
|
|
else if (FLongHotspot(**ppCmd))
|
|
{
|
|
(*ppCmd)++;
|
|
*ppCmd += 2 + *((short int *)*ppCmd);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(FALSE);
|
|
return(NULL);
|
|
}
|
|
|
|
bSkipHot = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
pszText++;
|
|
if (bSkipHot && *pszText)
|
|
pszText += strlen(pszText);
|
|
|
|
}
|
|
|
|
return (PSTR) pszText;
|
|
}
|
|
|
|
void STDCALL AppendTextData(PBYTE qbCommand, DWORD lcbCommand, PSTR pszText, DWORD lcb, BOOL bSBS)
|
|
{
|
|
int cbText;
|
|
PSTR pTextEnd = pszText + lcb;
|
|
int iCharSet = -1;
|
|
|
|
if (!qbCommand)
|
|
return;
|
|
|
|
if (*pszText) {
|
|
AppendText((PBYTE) pszText, cbText = strlen(pszText), iCharSet);
|
|
pszText += cbText;
|
|
}
|
|
|
|
while (pszText < pTextEnd) {
|
|
pszText = NextFTSString(pszText, &qbCommand, &iCharSet, bSBS);
|
|
|
|
if (pszText) {
|
|
AppendText((PBYTE) pszText, cbText = strlen(pszText), iCharSet);
|
|
pszText += cbText;
|
|
}
|
|
else
|
|
return;
|
|
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name VWriteFCP
|
|
-
|
|
* Purpose
|
|
* Writes an FCP out to the |TOPIC file.
|
|
*
|
|
* Arguments
|
|
* BYTE type: Object type to be written into this FCP
|
|
* QB qbObj: Pointer to compressed object header
|
|
* DWORD lcbObj: Size of object header
|
|
* QB qbCommand: Pointer to command table
|
|
* DWORD lcbCommand: Size of command table
|
|
* PSTR pszText: Pointer to compressed text
|
|
* ULONT lcbCompressed: Size of compressed text
|
|
* DWORD lcbUncompressed: Size of text when uncompressed
|
|
*
|
|
* Globals
|
|
* Uses and modifies values in adrs.
|
|
*
|
|
* Returns
|
|
* The FCL of the beginning of the FCP written out.
|
|
*
|
|
* +++
|
|
*
|
|
* Notes
|
|
* This function currently uses the following global variables, which
|
|
* will eventually be put into the hpj structure:
|
|
* HF hfTopic: Handle to |TOPIC file
|
|
* FCL fclCur: Current file postion in |TOPIC file
|
|
* FCL fclTopic: File position of current topic
|
|
*
|
|
* When all is said and done, this function should be the only function
|
|
* that ever writes to the |TOPIC file, except for initializing
|
|
* and back-patching.
|
|
*/
|
|
|
|
static void STDCALL VWriteFCP(FCPTYPE type, PBYTE qbObj, DWORD lcbObj,
|
|
PBYTE qbCommand, DWORD lcbCommand, PSTR pszText, DWORD lcbCompressed,
|
|
DWORD lcbUncompressed)
|
|
{
|
|
MFCP mfcp;
|
|
MOBJ mobj, mobjCompressed;
|
|
int lcbMobj;
|
|
RC_TYPE rc;
|
|
|
|
|
|
// prepare object header
|
|
|
|
mobj.bType = type;
|
|
mobj.lcbSize = lcbObj + lcbCommand;
|
|
|
|
if (options.fsCompress & COMPRESS_TEXT_HALL && lcbUncompressed)
|
|
{
|
|
ASSERT(lcbUncompressed == lcbCompressed);
|
|
#if 0
|
|
if (fHallPassOne)
|
|
{
|
|
AppendText((PBYTE) pszText, lcbUncompressed, -1);
|
|
lcbCompressed = lcbUncompressed = 1;
|
|
}
|
|
else
|
|
{
|
|
#else
|
|
{
|
|
#endif
|
|
int cbJohn = 0;
|
|
MTOP *pMTop;
|
|
|
|
// Following is needed for full-text search
|
|
|
|
if (options.fsFTS & FTS_ENABLED) {
|
|
bSYSFirst = FALSE; // REVIEW: necessary?
|
|
|
|
switch (type) {
|
|
case FCTYPE_TOPIC: // Intentional.
|
|
pMTop = (MTOP *) qbObj;
|
|
cbJohn = strlen(pszText);
|
|
if (cbJohn) {
|
|
ASSERT(fContextSeen);
|
|
pScanTopicTitle(hFtsIndex, (PBYTE) pszText, cbJohn,
|
|
pMTop->lTopicNo, (void *) curHash,
|
|
charsetFts, lcidFts);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* 21-Aug-1994 [ralphw] Side by side paragraphs are
|
|
* no longer supported, however, I'm fairly certain this
|
|
* type is now being used for tables.
|
|
*/
|
|
|
|
case FCTYPE_SBYS:
|
|
case FCTYPE_SBYS_COUNT:
|
|
bSYSFirst = TRUE;
|
|
AppendTextData(qbCommand, lcbCommand, pszText, lcbUncompressed, TRUE);
|
|
break;
|
|
|
|
default:
|
|
AppendTextData(qbCommand, lcbCommand, pszText, lcbUncompressed, FALSE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Changes in FTSRCH mean we don't have to save original string
|
|
// anymore.
|
|
//
|
|
// The rules have been changed. We no longer use compressed
|
|
// text that has been expanded. The interface now allows
|
|
// you to pass a NULL for the 'pbNewString' parameter.
|
|
// pszText will *still* only contain valid compressed text
|
|
// if lcbCompressed < lcbUncompressed
|
|
//
|
|
// if lcpCompressed >= lcbUncompressed then our original
|
|
// buffer is INTACT. This as of 1/16/95
|
|
// [johnhall]
|
|
|
|
lcbCompressed = pCompressText(hCompressor, (PBYTE) pszText,
|
|
lcbUncompressed, NULL, defCharSet);
|
|
|
|
// BUGBUG: this happens, bail out!
|
|
|
|
ASSERT((int) lcbCompressed >= 0);
|
|
|
|
|
|
// WARNING!!!! This will ONLY work if we are GUARANTEED that
|
|
// this FCP will contain no text other than pszText.
|
|
// [johnhall]
|
|
|
|
//
|
|
AddCharCounts(lcbUncompressed, 0, min(lcbCompressed , lcbUncompressed));
|
|
}
|
|
}
|
|
/*
|
|
* Assign the global variable idfcpCur for the Zeck code. (for topic
|
|
* FCP's, it will be reassigned below).
|
|
*/
|
|
|
|
idfcpCur = adrs.idfcpCur;
|
|
|
|
// compress the object header
|
|
|
|
if (type == FCTYPE_TOPIC) {
|
|
lcbMobj = CbPackMOBJ(&mobj, &mobjCompressed);
|
|
|
|
/*
|
|
* The idfcp of a topic FCP is idfcpTopic, not idfcpCur. Marker
|
|
* FCPs are not assigned idfcps, and should not have their object
|
|
* regions registered.
|
|
*/
|
|
|
|
idfcpCur = adrs.idfcpTopic;
|
|
rc = RcRegisterWObjrg(idfcpCur, 0);
|
|
}
|
|
else {
|
|
ASSERT(type == FCTYPE_PARAGROUP || type == FCTYPE_SBYS);
|
|
mobj.bType = (type == FCTYPE_PARAGROUP ?
|
|
FCTYPE_PARAGROUP_COUNT : FCTYPE_SBYS_COUNT);
|
|
|
|
mobj.wObjInfo = adrs.wObjrg;
|
|
adrs.wObjrg = 0;
|
|
rc = RcRegisterWObjrg(idfcpCur, mobj.wObjInfo);
|
|
lcbMobj = CbPackMOBJ(&mobj, &mobjCompressed);
|
|
}
|
|
|
|
// Check return code from RcRegisterWObjrg()
|
|
|
|
switch (rc) {
|
|
case RC_Failure:
|
|
{
|
|
#ifdef _DEBUG
|
|
/*
|
|
* RC_Failure indicates the PA addressing structure
|
|
* overflowed and we must flush a 4K block to reset the
|
|
* block:offset PA addresses.
|
|
*/
|
|
|
|
int c = 0;
|
|
#endif
|
|
do {
|
|
VFlushBuffer(FALSE);
|
|
ASSERT(++c <= (cbMAX_BLOCK_SIZE / cbBLOCK_SIZE));
|
|
} while(FKeepFlushing());
|
|
}
|
|
break;
|
|
/*
|
|
* Note: the object region count stuff has already determined
|
|
* that the number of objects in a single FCP does not overflow
|
|
* the PA addressing structure, therefore flushing the buffer
|
|
* should eventually leave enough PA space for us.
|
|
*/
|
|
|
|
|
|
case RC_OutOfMemory:
|
|
OOM(); // doesn't return
|
|
break;
|
|
}
|
|
|
|
// prepare FCP header
|
|
|
|
#ifdef MAGIC
|
|
mfcp.bMagic = bMagicMFCP;
|
|
#endif
|
|
mfcp.lcbSizeCompressed =
|
|
(INT16) (sizeof(MFCP) + lcbMobj + lcbObj + lcbCommand + lcbCompressed);
|
|
mfcp.lcbSizeText = (INT16) lcbUncompressed;
|
|
mfcp.ichText = sizeof(MFCP) + lcbMobj + lcbObj + lcbCommand;
|
|
mfcp.vaNextFc.dword = vaNil;
|
|
mfcp.vaPrevFc.dword = vaNil;
|
|
|
|
#ifdef STATS2
|
|
wsprintf(szParentString, "%s: %ld, mobj: %ld, Obj: %ld, Command: %ld, Text: %ld\r\n",
|
|
(type==FCTYPE_TOPIC) ? "Topicmfcp" : "mfcp", sizeof(MFCP),
|
|
lcbMobj, lcbObj, lcbCommand, lcbCompressed );
|
|
SendStringToParent();
|
|
#endif
|
|
|
|
#ifdef STATS
|
|
cbRText += lcbCompressed;
|
|
cbRText += lcbCommand;
|
|
#endif
|
|
|
|
// Write out FCP header, object header, object, command, text:
|
|
VAddToBuffer((PBYTE) &mfcp, sizeof(MFCP), (type == FCTYPE_TOPIC) ? ETopicmfcp : Emfcp);
|
|
VAddToBuffer((PBYTE) &mobjCompressed, lcbMobj, (type == FCTYPE_TOPIC) ? Emobj : Enone);
|
|
|
|
if ((type == FCTYPE_TOPIC))
|
|
pmtop = (MTOP FAR*) qbObj;
|
|
|
|
VAddToBuffer(qbObj, lcbObj, (type == FCTYPE_TOPIC) ? Emtop : Enone);
|
|
VAddToBuffer(qbCommand, lcbCommand, Enone);
|
|
VAddToBuffer((PBYTE) pszText, lcbCompressed, Enone);
|
|
|
|
if (type != FCTYPE_TOPIC)
|
|
++adrs.idfcpCur;
|
|
|
|
#if 0
|
|
if (pbNewString)
|
|
free(pbNewString); // Allocated with malloc in ftsrch.dll.
|
|
#endif
|
|
}
|
|
|
|
// reset opts to command-line level:
|
|
|
|
// #pragma optimize( "", on )
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name: QvSetTopicObject
|
|
-
|
|
* Purpose:
|
|
* Fills in an MTOP structure from global variables, packs it, and
|
|
* returns it.
|
|
*
|
|
* Arguments:
|
|
* plcbObj - pointer to get the size of the packed MTOP object.
|
|
*
|
|
* Returns:
|
|
* A pointer to the packed object, allocated with lcCalloc().
|
|
*
|
|
* Globals:
|
|
* too numerous to mention.
|
|
*
|
|
***************************************************************************/
|
|
|
|
static void* STDCALL QvSetTopicObject(DWORD* plcbObj)
|
|
{
|
|
void* qBufOut;
|
|
MTOP mtop;
|
|
|
|
static int lUnique = 0;
|
|
|
|
#ifdef MAGIC
|
|
mtop.bMagic = bMagicMTOP;
|
|
#endif
|
|
|
|
if (fBrowseDefined) {
|
|
/*
|
|
* Set these to non-nil values so that they will not be removed.
|
|
* These values will be backpatched later to their correct values.
|
|
*/
|
|
|
|
mtop.prev = addrNotNil;
|
|
mtop.next = addrNotNil;
|
|
}
|
|
else {
|
|
mtop.prev = addrNil;
|
|
mtop.next = addrNil;
|
|
}
|
|
|
|
if (pfInt.fNSR) {
|
|
nsr = nsrUnresolved;
|
|
|
|
//mtop.lcbSizeNSR = -1L;
|
|
|
|
}
|
|
else
|
|
nsr = nsrNone;
|
|
mtop.vaSR.dword = vaNil;
|
|
mtop.vaNSR.dword = vaNil;
|
|
mtop.vaNextSeqTopic.dword = vaNil;
|
|
|
|
mtop.lTopicNo = lUnique++;
|
|
|
|
qBufOut = lcCalloc(sizeof(MTOP) + 2L);
|
|
if (qBufOut == NULL)
|
|
return NULL;
|
|
|
|
ASSERT(plcbObj != NULL);
|
|
*plcbObj = sizeof(MTOP);
|
|
memcpy(qBufOut, &mtop, sizeof(MTOP));
|
|
pmtop = (MTOP FAR*) qBufOut;
|
|
return qBufOut;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* void VSetParaGroupObject( MOPG *)
|
|
*
|
|
* Description:
|
|
* This function fills MOPG structure for the ParaGroup object and returns the
|
|
* compressed size.
|
|
*
|
|
* Returns;
|
|
* returns the compressed size of the ParaGroup object.
|
|
*-----------------------------------------------------------------------------*/
|
|
|
|
/* PTR 912: uncertain about exact problem, but seems to depend on the
|
|
* compiler allocating qMOpgOut to reg SI. Could not track down why
|
|
* SI either got trashed or caused something to get trashed. Both
|
|
* turning off register allocation and declaring qMOpgOut as volatile
|
|
* "fix" the problem. -Tom 2/25/91
|
|
*/
|
|
|
|
// REVIEW: 30-Jul-1993 [ralphw] Is this still true?
|
|
|
|
// #pragma optimize( "gea", off )
|
|
|
|
void STDCALL VSetParaGroupObject(MOPG* pMopg)
|
|
{
|
|
RcOutputCommand(END_OF_TEXT);
|
|
memset(pMopg, 0, sizeof(MOPG));
|
|
|
|
#ifdef MAGIC
|
|
pMopg->bMagic = bMagicMOPG;
|
|
#endif
|
|
|
|
pMopg->ySpaceOver = pfInt.fSpaceOver;
|
|
pMopg->ySpaceUnder = pfInt.fSpaceUnder;
|
|
pMopg->yLineSpacing = pfInt.fLineSpacing;
|
|
|
|
pMopg->xLeftIndent = pfInt.fLeftIndent;
|
|
pMopg->xRightIndent = pfInt.fRightIndent;
|
|
pMopg->xFirstIndent = pfInt.fFirstIndent;
|
|
|
|
if (pfInt.wBoxed || pfInt.fBorder)
|
|
pMopg->fBoxed = 1;
|
|
|
|
if (pfInt.fBorder & fTopBorder)
|
|
pMopg->mbox.fTopLine = 1;
|
|
if (pfInt.fBorder & fLeftBorder)
|
|
pMopg->mbox.fLeftLine = 1;
|
|
if (pfInt.fBorder & fBottomBorder)
|
|
pMopg->mbox.fBottomLine = 1;
|
|
if (pfInt.fBorder & fRightBorder)
|
|
pMopg->mbox.fRightLine = 1;
|
|
|
|
pMopg->mbox.wLineType = pfInt.boxtype;
|
|
if ((pfInt.fBorder == 0xf) || (pfInt.wBoxed))
|
|
pMopg->mbox.fFullBox = 1;
|
|
|
|
pMopg->justify = pfInt.justify;
|
|
if (pfInt.fSingleLine)
|
|
pMopg-> fSingleLine = 1;
|
|
|
|
pMopg->wRtlReading = pfInt.fRtlReading; // BIDI
|
|
|
|
// REVIEW: Is this the correct action for wIntTabStackCur > MAX_TABS?
|
|
|
|
if (wIntTabStackCur > MAX_TABS) {
|
|
pMopg->cTabs = MAX_TABS;
|
|
if (wIntTabStackCur)
|
|
memmove(pMopg->rgtab, qIntTabStack, MAX_TABS * sizeof(TAB));
|
|
}
|
|
else {
|
|
pMopg->cTabs = wIntTabStackCur;
|
|
if (wIntTabStackCur)
|
|
memmove((PSTR) pMopg->rgtab, (PSTR) qIntTabStack,
|
|
(int) (wIntTabStackCur * sizeof(TAB)));
|
|
}
|
|
}
|
|
|
|
/* reset opts to command-line level: */
|
|
// #pragma optimize( "", on )
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* void VForceTopicFCP(void)
|
|
*
|
|
* Description:
|
|
*
|
|
* Returns;
|
|
*
|
|
*-----------------------------------------------------------------------------*/
|
|
|
|
void STDCALL VForceTopicFCP(void)
|
|
{
|
|
void* pvObj;
|
|
DWORD lcbObj;
|
|
int cbTitle, cbTitleCompressed;
|
|
int cbEntryMacro, cbEntryMacroCompressed;
|
|
|
|
pvObj = QvSetTopicObject(&lcbObj);
|
|
if (pvObj == NULL)
|
|
OOM(); // doesn't return
|
|
|
|
FDelayExecutionTitle(pszTitleBuffer ? pszTitleBuffer : txtZeroLength,
|
|
adrs.idfcpTopic);
|
|
|
|
cbTitle = (pszTitleBuffer ? strlen(pszTitleBuffer) : 0);
|
|
cbEntryMacro = (pszEntryMacro ? strlen(pszEntryMacro) : 0);
|
|
|
|
if (cbTitle) {
|
|
cbTitleCompressed = (g_hphr ?
|
|
ICompressTextSz(pszTitleBuffer) : cbTitle);
|
|
}
|
|
else
|
|
cbTitleCompressed = 0;
|
|
|
|
if (cbEntryMacro) {
|
|
|
|
// Since there is an entry macro, the title must now include a trailing
|
|
// NULL.
|
|
|
|
++cbTitleCompressed;
|
|
++cbTitle;
|
|
|
|
cbEntryMacroCompressed = (g_hphr ?
|
|
ICompressTextSz(pszEntryMacro) : cbEntryMacro);
|
|
|
|
if (pszTitleBuffer)
|
|
pszTitleBuffer = (PSTR) lcReAlloc(pszTitleBuffer,
|
|
cbTitleCompressed + cbEntryMacroCompressed + 1);
|
|
else {
|
|
pszTitleBuffer = (PSTR) lcMalloc(cbEntryMacroCompressed + 2);
|
|
*pszTitleBuffer = '\0';
|
|
}
|
|
|
|
memcpy(pszTitleBuffer + cbTitleCompressed, pszEntryMacro,
|
|
cbEntryMacroCompressed + 1);
|
|
|
|
lcClearFree(&pszEntryMacro);
|
|
}
|
|
else
|
|
cbEntryMacroCompressed = 0;
|
|
|
|
if (fKeywordDefined && !fTitleDefined)
|
|
VReportError(HCERR_NO_TITLE, &errHpj);
|
|
|
|
VWriteFCP(FCTYPE_TOPIC, (PBYTE) pvObj, lcbObj, NULL, 0L,
|
|
(pszTitleBuffer ? pszTitleBuffer : ""),
|
|
(cbTitleCompressed + cbEntryMacroCompressed),
|
|
(cbTitle + cbEntryMacro));
|
|
|
|
lcHeapCheck();
|
|
lcFree(pvObj);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* void VAcqBufs()
|
|
*
|
|
* Description:
|
|
*
|
|
* Arguments:
|
|
*
|
|
* Returns:
|
|
* NULL
|
|
*-----------------------------------------------------------------------------*/
|
|
|
|
void STDCALL VAcqBufs(void)
|
|
{
|
|
pbfText = new CBuf(CB_TEXTINCR);
|
|
pbfCommand = new CBuf(CB_COMMANDINCR);
|
|
}
|
|
|
|
void STDCALL UnlinkHlpifNoFCP(void)
|
|
{
|
|
if (!fOutFCP) {
|
|
VReportError(HCERR_NOT_CREATED, &errHpj, szHlpFile);
|
|
HardExit();
|
|
}
|
|
}
|
|
|
|
void STDCALL OutNullFcp(BOOL fLast)
|
|
{
|
|
PF pfT;
|
|
|
|
/*
|
|
* FCP will contain only one change font command. This is the
|
|
* most harmless FCP I can think of that still contains something so
|
|
* that VOutFCP will write it out. Also, the FCP will have the
|
|
* default paragraph formatting.
|
|
*/
|
|
|
|
memcpy(&pfT, &pfInt, sizeof(PF));
|
|
memcpy(&pfInt, &pfDefault, sizeof(PF));
|
|
RcOutFmt(FALSE);
|
|
VOutFCP(fLast);
|
|
memcpy(&pfInt, &pfT, sizeof(PF));
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* RC_TYPE RcOutTextToTextBuf(PSTR)
|
|
*
|
|
* Description:
|
|
* This function outputs the given text to the Text Buffer.
|
|
* It grows the Text buffer if necessary to incorporate the current
|
|
* text.
|
|
*
|
|
* Input:
|
|
* PSTR - pointer to null terminated text string
|
|
*
|
|
* Returns;
|
|
* Error code from RcCopyPbfQCb
|
|
*-----------------------------------------------------------------------------*/
|
|
|
|
static RC_TYPE STDCALL RcOutTextToTextBuf(PSTR psz)
|
|
{
|
|
RC_TYPE rc = RcOutFmt(TRUE);
|
|
if (rc != RC_Success)
|
|
return rc;
|
|
|
|
int cbText = strlen(psz);
|
|
wTextBufChCount += cbText;
|
|
|
|
/*
|
|
* Check for object region overflow.
|
|
* REVIEW: This constant is chosen because of an ASSERT in QVMakeQGA()
|
|
* to this effect. Where should this number be put?
|
|
*/
|
|
|
|
if (adrs.wObjrg + cbText >= 0x8000) {
|
|
ASSERT(adrs.wObjrg + cbText < 0x8000);
|
|
return RC_TooBig;
|
|
}
|
|
adrs.wObjrg += cbText;
|
|
|
|
if (g_hphr) {
|
|
int cbCompressed = ICompressTextSz(psz);
|
|
return (pbfText->Add(psz, (UINT) cbCompressed)) ? RC_Success : RC_OutOfMemory;
|
|
}
|
|
else
|
|
return (pbfText->Add(psz, cbText)) ? RC_Success : RC_OutOfMemory;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name RcOutputCommand
|
|
-
|
|
* Purpose
|
|
* Outputs a command to the command buffer. If this command represents
|
|
* an object (rather than a change format), this function will check
|
|
* to see if it needs to go into a new FCP.
|
|
*
|
|
* Arguments
|
|
* bCommand: Command byte.
|
|
* qData: Pointer to command data.
|
|
* cbData: Size of command data.
|
|
* fObject: TRUE if the command represents an object.
|
|
*
|
|
* Returns
|
|
* RC_Success if successful, RC_OutOfMemory if out of memory,
|
|
* RC_TooBig if command table exceeds 64K.
|
|
*
|
|
* +++
|
|
*
|
|
* Notes
|
|
* Currently adds information to globals bfCommand and bfText,
|
|
* and adjusts the variable fNewPara as well as all the global
|
|
* stuff that goes on with a VOutFCP.
|
|
*
|
|
***************************************************************************/
|
|
|
|
RC_TYPE STDCALL RcOutputCommand(BYTE bCommand, void* qData, int cbData,
|
|
BOOL fObject)
|
|
{
|
|
RC_TYPE rc;
|
|
|
|
if (fObject) {
|
|
FCheckAndOutFCP();
|
|
fNewPara = FALSE;
|
|
rc = RcOutFmt(TRUE);
|
|
if (rc != RC_Success)
|
|
return rc;
|
|
}
|
|
|
|
// Output command to command buffer
|
|
|
|
rc = pbfCommand->Add(&bCommand, sizeof(BYTE)) ? RC_Success : RC_OutOfMemory;
|
|
if (rc == RC_Success && cbData > 0)
|
|
rc = pbfCommand->Add(qData, (UINT) cbData) ? RC_Success : RC_OutOfMemory;
|
|
|
|
// Output command character to text buffer
|
|
|
|
if (rc == RC_Success) {
|
|
char ch = chCommand;
|
|
|
|
wTextBufChCount += 1;
|
|
|
|
ASSERT(adrs.wObjrg < 0x7FFF);
|
|
|
|
if (adrs.wObjrg >= 0x7FFF)
|
|
return RC_TooBig;
|
|
adrs.wObjrg += 1;
|
|
rc = pbfText->Add(&ch, sizeof(char)) ? RC_Success : RC_OutOfMemory;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: CbPackMOPG
|
|
|
|
PURPOSE:
|
|
|
|
PARAMETERS:
|
|
pmopg
|
|
qv
|
|
|
|
RETURNS:
|
|
|
|
COMMENTS:
|
|
|
|
MODIFICATION DATES:
|
|
05-Sep-1993 [ralphw]
|
|
|
|
***************************************************************************/
|
|
|
|
#define QVMakeQGE(l, qge) ((l >= -0x4000 && l < 0x4000) ? \
|
|
(void*) (*((INT16*) qge) = (INT16) ((l + 0x4000) << 1), (((INT16*)qge) + 1)) \
|
|
: (void*) (*((LONG*)qge) = (LONG) ((l + 0x40000000L) << 1) | 0x01, \
|
|
(((LONG*)qge) + 1)))
|
|
|
|
#define QVMakeQGD(w, qgd) ((w >= -0x40 && w < 0x40) ? \
|
|
(void*) (*((BYTE*)qgd) = (BYTE) ((w + 0x40) << 1), (((BYTE*)qgd) + 1)) \
|
|
: (void*) (*((INT16*)qgd) = (INT16) ((w + 0x4000) << 1) | 0x01, \
|
|
(((INT16*)qgd) + 1)))
|
|
|
|
int STDCALL CbPackMOPG(MOPG* pmopg, void* qv)
|
|
{
|
|
MPFG mpfg;
|
|
void* qvFirst = qv;
|
|
int iTab;
|
|
|
|
#ifdef _DEBUG
|
|
DE de;
|
|
|
|
FVerifyQMOPG(pmopg);
|
|
|
|
de.wXAspectMul = de.wXAspectDiv = de.wYAspectMul = de.wYAspectDiv = 1;
|
|
#endif
|
|
|
|
#ifdef MAGIC
|
|
*((PBYTE) qv) = pmopg->bMagic;
|
|
qv = (((PBYTE) qv) + 1);
|
|
ASSERT(pmopg->bMagic == bMagicMOPG);
|
|
#endif /* MAGIC */
|
|
|
|
// REVIEW: How do we handle funny booleans?
|
|
|
|
mpfg.fStyle = (pmopg->fStyle != FALSE);
|
|
ASSERT(!mpfg.fStyle);
|
|
mpfg.rgf.fMoreFlags = (pmopg->fMoreFlags != FALSE);
|
|
ASSERT(!mpfg.rgf.fMoreFlags);
|
|
|
|
mpfg.rgf.fBoxed = (pmopg->fBoxed != FALSE);
|
|
mpfg.rgf.fSpaceOver = (pmopg->ySpaceOver != 0);
|
|
mpfg.rgf.fSpaceUnder = (pmopg->ySpaceUnder != 0);
|
|
mpfg.rgf.fLineSpacing = (pmopg->yLineSpacing != 0);
|
|
mpfg.rgf.fLeftIndent = (pmopg->xLeftIndent != 0);
|
|
mpfg.rgf.fRightIndent = (pmopg->xRightIndent != 0);
|
|
mpfg.rgf.fFirstIndent = (pmopg->xFirstIndent != 0);
|
|
mpfg.rgf.fTabSpacing = (pmopg->xTabSpacing != 0);
|
|
mpfg.rgf.fTabs = (pmopg->cTabs != 0);
|
|
mpfg.rgf.justify = pmopg->justify;
|
|
mpfg.rgf.fSingleLine = pmopg->fSingleLine;
|
|
mpfg.rgf.fRtlReading = pmopg->wRtlReading;
|
|
|
|
qv = QVMakeQGE(pmopg->libText, qv);
|
|
*((MPFG*) qv) = mpfg;
|
|
qv = (((MPFG*) qv) + 1);
|
|
if (mpfg.rgf.fMoreFlags)
|
|
qv = QVMakeQGE(pmopg->lMoreFlags, qv);
|
|
if (mpfg.rgf.fSpaceOver)
|
|
qv = QVMakeQGD(pmopg->ySpaceOver, qv);
|
|
if (mpfg.rgf.fSpaceUnder)
|
|
qv = QVMakeQGD(pmopg->ySpaceUnder, qv);
|
|
if (mpfg.rgf.fLineSpacing)
|
|
qv = QVMakeQGD(pmopg->yLineSpacing, qv);
|
|
if (mpfg.rgf.fLeftIndent)
|
|
qv = QVMakeQGD(pmopg->xLeftIndent, qv);
|
|
if (mpfg.rgf.fRightIndent)
|
|
qv = QVMakeQGD(pmopg->xRightIndent, qv);
|
|
if (mpfg.rgf.fFirstIndent)
|
|
qv = QVMakeQGD(pmopg->xFirstIndent, qv);
|
|
if (mpfg.rgf.fTabSpacing)
|
|
qv = QVMakeQGD(pmopg->xTabSpacing, qv);
|
|
if (mpfg.rgf.fBoxed) {
|
|
*((MBOX*) qv) = pmopg->mbox;
|
|
qv = (((MBOX*) qv) + 1);
|
|
}
|
|
if (mpfg.rgf.fTabs)
|
|
qv = QVMakeQGD(pmopg->cTabs, qv);
|
|
for (iTab = 0; iTab < pmopg->cTabs; iTab++) {
|
|
ASSERT(pmopg->rgtab[iTab].x >= 0);
|
|
ASSERT(pmopg->rgtab[iTab].wType >= TABTYPELEFT);
|
|
ASSERT(pmopg->rgtab[iTab].wType <= TABTYPEMOST);
|
|
if (pmopg->rgtab[iTab].wType == TABTYPELEFT)
|
|
qv = PVMakeQGA(pmopg->rgtab[iTab].x, qv);
|
|
else {
|
|
qv = PVMakeQGA((pmopg->rgtab[iTab].x | 0x4000), qv);
|
|
qv = PVMakeQGA(pmopg->rgtab[iTab].wType, qv);
|
|
}
|
|
}
|
|
|
|
return((int) ((PBYTE)qv - (PBYTE)qvFirst));
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
static int STDCALL FVerifyQMOPG(QMOPG pmopg)
|
|
{
|
|
|
|
#ifdef MAGIC
|
|
ASSERT(pmopg->bMagic == bMagicMOPG);
|
|
#endif
|
|
ASSERT(pmopg->libText >= 0);
|
|
ASSERT(!pmopg->fStyle);
|
|
ASSERT(!pmopg->fMoreFlags);
|
|
ASSERT(pmopg->justify >= 0 && pmopg->justify <= JUSTIFYMOST);
|
|
ASSERT(pmopg->ySpaceOver >= 0);
|
|
ASSERT(pmopg->ySpaceUnder >= 0);
|
|
ASSERT(pmopg->yLineSpacing >= -10000 && pmopg->yLineSpacing < 10000);
|
|
ASSERT(pmopg->xRightIndent >= 0);
|
|
ASSERT(pmopg->xFirstIndent >= -10000 && pmopg->xFirstIndent < 10000);
|
|
ASSERT(pmopg->xTabSpacing >= 0 && pmopg->xTabSpacing < 10000);
|
|
ASSERT(pmopg->cTabs >= 0 && pmopg->cTabs <= MAX_TABS);
|
|
return(TRUE);
|
|
}
|
|
#endif
|
|
|
|
int STDCALL CbPackMOBJ(MOBJ* qmobj, void* pv)
|
|
{
|
|
void* pvFirst = pv;
|
|
|
|
/*
|
|
* Topic MOBJs are not packed, because they need to be back-patched
|
|
* with the topic size.
|
|
*/
|
|
|
|
ASSERT(qmobj->bType);
|
|
if (qmobj->bType == (BYTE) FCTYPE_TOPIC ||
|
|
qmobj->bType == (BYTE) FCTYPE_TOPIC_COUNT) {
|
|
*((MOBJ*) pv) = *qmobj;
|
|
|
|
pv = ((MOBJ*) pv) + 1;
|
|
|
|
// Uncounted topics do not contain the last field, wObjInfo.
|
|
|
|
if (qmobj->bType <= MAX_UNCOUNTED_OBJ_TYPE)
|
|
pv = ((PBYTE) pv) - sizeof(WORD);
|
|
|
|
return((int) ((PBYTE) pv - (PBYTE) pvFirst));
|
|
}
|
|
|
|
*((PBYTE) pv) = qmobj->bType;
|
|
pv = (((PBYTE) pv) + 1);
|
|
pv = QVMakeQGE(qmobj->lcbSize, pv);
|
|
if (qmobj->bType > MAX_UNCOUNTED_OBJ_TYPE)
|
|
pv = PVMakeQGA(qmobj->wObjInfo, pv);
|
|
|
|
return((int) ((PBYTE) pv - (PBYTE) pvFirst));
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
|
|
void STDCALL CheckPhrasePass(PSTR psz)
|
|
{
|
|
|
|
/*
|
|
* Unfortunately, autoentry footnotes can be specified multiple times,
|
|
* but they get combined into a single string before they get here. We
|
|
* HAVE seen each individual component, but we haven't seen them as a
|
|
* single block. So if we get a semi-colon and an open parenthesis in a
|
|
* line, we'll guess that it's a macro and ignore it. That means we won't
|
|
* check some text, but I'm pretty confident that we're seeing all plain
|
|
* text anyway.
|
|
*/
|
|
|
|
if (StrChr(psz, ';', options.fDBCS) && StrChr(psz, '(', options.fDBCS))
|
|
return;
|
|
|
|
if (ptblCheck && !ptblCheck->IsCSStringInTable(psz)) {
|
|
SendStringToParent("DEBUG: Following string was not seen by phrase compressor:\r\n\t");
|
|
SendStringToParent(psz);
|
|
SendStringToParent("\r\n\r\n");
|
|
fCompressionBusted = TRUE;
|
|
}
|
|
}
|
|
|
|
#endif
|