windows-nt/Source/XPSP1/NT/shell/evtmon/inc/genem.c

1374 lines
41 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*----------------------------------------------------------------------------
%%File: EMTEST.C
%%Unit: Event Monitor (mntr)
%%Contact: daleg
Event Monitor Sample Application, Main Program.
The purpose of this application is to demonstrate how to process events
using the Event Monitor's Rule Compiler and rule engine.
----------------------------------------------------------------------------*/
//*** genem.c -- 'generic' evtmon client-side stuff
// DESCRIPTION
// the client-side of an evtmon app has two parts:
// - evtmon generic code (client-side)
// - application-specific rules
// this file is reusable (and semi-frozen) generic code. it should be
// #include'ed from the app.
// NOTES
// WARNING: do *not* put app-specific code here. put it in the
// client (emclient/libem.c). in fact in general, think twice before
// modifying this file at all.
//*** YY_* -- generic rules support
//
#define YY_BASE 1 // base features (always needed)
#ifdef YY_BASE
// rulc.exe doesn't create all files if a given feature isn't used,
// so we can't unconditionally include these guys. plus, we don't
// want the helper code if we don't need it.
// turn on these features here if you use them
#ifndef YY_DELAYED
#define YY_DELAYED 0 // delayed actions
#endif
#ifndef YY_CTOR
#define YY_CTOR 0 // action ctors
#endif
#ifndef YY_SEQCHECK
#define YY_SEQCHECK 0 // seq_check
#endif
#define YY_OTHER 0
//#define YY_BRKPT()
#endif
//*** FEATURE_* -- domain-specific rules support
// NOTES
#define FEATURE_DEMO 0 // demo code (e.g. wndproc test app)
#define FEATURE_SAMPLE 0 // sample code (e.g. joe-event firer)
#define FEATURE_OFFICE 0 // base office stuff
#define FEATURE_TEXT 0 // text parser
#define FEATURE_WORD 0 // format parser
#define FEATURE_DEAD 0 // unused/dead code
#define FEATURE_NYI 0
#include "mso.h"
#include "msoem.h"
DEBUGASSERTSZ
#include "msolex.h"
#include "emrule.h"
#include "emkwd.h"
#include "emact.h"
#if FEATURE_DEMO
#include "emtest.h"
#include "emres.h"
#endif
// { Files generated by Rule Compiler
#include "emdef.h" // rules (#defines)
#if YY_CTOR // {
#include "emacts.h" // ctor macro wrappers
#endif // }
#define DEBUG_RULE_POINTERS // Want ptrs to nodes
#include "emruli.h" // rulebase
#if YY_SEQCHECK // seq_check
#include "emsqck.c_" // sequences
#endif
// }
#if FEATURE_TEXT // {
// Constants
#define cbEditText 1024
#ifdef STATIC_INIT
#define grfLexFlagsEm (MsoGrfLexFNoReset \
| MsoGrfLexFLookup \
| MsoGrfLexFLookupIntsAndSyms \
| MsoGrfLexFAllCapsAsFormat)
// EM data structures
MSORULTK rgrultkEmToken[200]; // Text Token cache
MSORULTK rgrultkEmFormat[100]; // Format Token cache
MSOLEXS vlexs =
{
&vkwtbEmTOKEN_KEYTABLE, // pkwtb
{rgrultkEmToken, 200, }, // rultkhToken
{rgrultkEmFormat, 100, }, // rultkhFormat
fTrue, // fInited
isttblDefault, // isttbl
grfLexFlagsEm, // grpfLexFlags
_hObjectNil, // hObjectNil
MsoWchLexGetNextBufferDoc, // pfnlexbuf
FGetNextLexRun, // pfnlexrun
FGetTokenTextObject, // pfnlextxt
NULL, // pfnlexfmt
NULL, // pfnlexrunDiscontig
NULL, // pfnlexrunForceCompl
iNil, // ichRun
-1, // cchLookahead
};
MSOLEXS *vplexs = &vlexs; // Global lexer state
#else /* !STATIC_INIT */
MSOLEXS vlexs; // Global lexer state
MSOLEXS *vplexs; // Global lexer state
#endif /* STATIC_INIT */
#endif // }
#if YY_BASE // {
// Global variables
RULS *vlpruls = &vrulsEm;
#ifndef STATIC_LINK_EM
RULS **_pvlprulsDLL = &vlpruls; // App's Global state
#endif /* !STATIC_LINK_EM */
#endif // }
#if FEATURE_DEMO // {
char vszAppName[20]; // Application name
HWND vhInst; // Instance
HWND vhWndMain; // Main window
HWND vhWndDlg; // Dialog window
char vszEditText[cbEditText]; // Text of edit control
int vcchEditText; // #chs in edit control
static int vcchPrev = 0; // Prev edit box len
static int vcpIpPrev = 0; // Prev edit box ip
int vfSettingDlgItem = fFalse; // Avoid Win recursion
#endif // }
#if YY_BASE // {
#if YY_DELAYED
#include "emactr.h" // data tables
MSOACTTBL vacttbl = {vrgacttrecEm}; // Global action table
MSOACTTBL *_pacttbl = &vacttbl; // Glob action tbl ptr
#endif
#endif // }
#if FEATURE_OFFICE // {
EMS vems; // EM global state
EMS *vpems = &vems; // Ptr to global state
#endif // }
#if YY_BASE // {
#ifdef DEBUG
#ifndef STATIC_LINK_EM
int vwDebugLogFilter = fDebugFilterAll;
int vwDebugLogLvl = -2;
#endif // !STATIC_LINK_EM
// Pointers to global debug logging vars in Mso DLL
int *pvwDebugLogFilter = &vwDebugLogFilter;
int *pvwDebugLogLvl = &vwDebugLogLvl;
#endif /* DEBUG */
#endif // }
#if YY_BASE // {
#ifdef STATIC_INIT // {
/* F I N I T E M */
/*----------------------------------------------------------------------------
%%Function: FInitEm
%%Contact: daleg
Initialize the static (compiled) Event Monitor rules.
----------------------------------------------------------------------------*/
int FInitEm(void)
{
#ifndef STATIC_LINK_EM
/* Mirror global debug pointers in Mso97.dll */
Debug(pvwDebugLogLvl = MsoPwDebugLogLvl(&pvwDebugLogFilter);)
#endif /* !STATIC_LINK_EM */
Debug(*pvwDebugLogLvl = 7);
#ifndef STATIC_LINK_EM
/* Mirror global pointers (vlpruls, etc) in Mso97.dll */
_pvlprulsDLL = MsoPvlprulsMirror(&vlpruls);
*_pvlprulsDLL = &vrulsEm;
#endif /* !STATIC_LINK_EM */
#ifdef DYN_RULES
/* Load dynamic rules */
FLoadDynEmRules();
#endif /* DYN_RULES */
#if FEATURE_OFFICE
/* For performance reasons, action table is global */
vacttbl.prultkh = &vplexs->rultkhToken;
#endif
#if FEATURE_DEMO
/* Allow Event Drivers to run */
EnableEM();
#endif
/* Propagate the values through the rule network */
MsoScheduleIrul(irul_YYSTD_INIT, fTrue);
#ifdef DYN_RULES
MsoScheduleIrul(irul_YYSTD_LOADING_RULEBASE, fTrue);
#endif /* DYN_RULES */
MsoEvaluateEvents(rulevtEm_YYSTD);
return fTrue;
}
#else /* }{ !STATIC_INIT */
/* F I N I T E M */
/*----------------------------------------------------------------------------
%%Function: FInitEm
%%Contact: daleg
Initialize the static (compiled) Event Monitor rules.
----------------------------------------------------------------------------*/
int FInitEm(void)
{
#ifndef STATIC_LINK_EM
/* Mirror global debug pointers in Mso97.dll */
Debug(pvwDebugLogLvl = MsoPwDebugLogLvl(&pvwDebugLogFilter);)
#endif /* !STATIC_LINK_EM */
Debug(*pvwDebugLogLvl = 7);
/* Initialize rule base, for performance reasons, rulebase is global */
if (!MsoFInitStaticRuls(&vrulsEm, &vrulsEm))
return fFalse;
#ifndef STATIC_LINK_EM
/* Mirror global pointers (vlpruls, etc) in Mso97.dll */
_pvlprulsDLL = MsoPvlprulsMirror(&vlpruls);
*_pvlprulsDLL = &vrulsEm;
#endif /* !STATIC_LINK_EM */
#ifdef DYN_RULES
/* Load dynamic rules */
FLoadDynEmRules();
#endif /* DYN_RULES */
#if FEATURE_OFFICE
/* Initialize the lexer to scan the doc */
if (!(vplexs = MsoPlexsLexInitDoc
(&vlexs, _hObjectNil, FGetNextLexRun,
FGetTokenTextObject, NULL, NULL, 200, 100)))
return fFalse;
vplexs->pkwtb = &vkwtbEmTOKEN_KEYTABLE;
/* For performance reasons, action table is global */
vacttbl.prultkh = &vplexs->rultkhToken;
#endif
#if FEATURE_DEMO
/* Allow Event Drivers to run */
EnableEM();
#endif
/* Propagate the values through the rule network */
MsoScheduleIrul(irul_YYSTD_INIT, fTrue);
#ifdef DYN_RULES
MsoScheduleIrul(irul_YYSTD_LOADING_RULEBASE, fTrue);
#endif /* DYN_RULES */
MsoEvaluateEvents(rulevtEm_YYSTD);
return fTrue;
}
#endif /* } STATIC_INIT */
#endif // }
#if YY_BASE // {
/* F E V A L E M R U L E */
/*----------------------------------------------------------------------------
%%Function: FEvalEmRule
%%Contact: daleg
Evaluate the rule associated with the given rule ID number.
Return a boolean value for whether its value was TRUE or FALSE.
----------------------------------------------------------------------------*/
#include "emeval.c"
#endif // }
#if FEATURE_DEMO // {
/* W I N M A I N */
/*----------------------------------------------------------------------------
%%Function: WinMain
%%Contact: daleg
Main routine.
----------------------------------------------------------------------------*/
int CALLBACK WinMain(
HANDLE hInstance,
HANDLE hPrevInstance,
LPSTR lpszCmdLine,
int nCmdShow
)
{
MSG msg;
int nRc;
char szString[256];
GdiSetBatchLimit(1);
strcpy(vszAppName, "EMTEST");
vhInst = hInstance;
if (!hPrevInstance)
{
/* Register window classes if first instance of application */
if ((nRc = nCwRegisterClasses()) == -1)
{
/* Put up msg if registering one of the windows failed */
LoadString(vhInst, IDS_ERR_REGISTER_CLASS, szString,
sizeof(szString));
MessageBox(NULL, szString, NULL, MB_ICONEXCLAMATION);
return nRc;
}
}
/* Create application's Main window */
vhWndMain = CreateWindow(vszAppName,
NULL,
WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX
| WS_MAXIMIZEBOX | WS_THICKFRAME
| WS_CLIPCHILDREN | WS_OVERLAPPED,
0, 0, 400, 400,
NULL, NULL, vhInst, NULL);
/* If could not create main window, be nice before quitting */
if (vhWndMain == NULL)
{
LoadString(vhInst, IDS_ERR_CREATE_WINDOW, szString, sizeof(szString));
MessageBox(NULL, szString, NULL, MB_ICONEXCLAMATION);
return IDS_ERR_CREATE_WINDOW;
}
/* Initialize the rulebase */
if (!FInitEm())
return 1;
/* Display main window */
ShowWindow(vhWndMain, nCmdShow);
/* Until WM_QUIT message */
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
/* Do clean up before exiting from the application */
CwUnRegisterClasses();
return msg.wParam;
}
/* W N D P R O C */
/*----------------------------------------------------------------------------
%%Function: WndProc
%%Contact: daleg
Windows proc for main window.
----------------------------------------------------------------------------*/
LONG CALLBACK WndProc(
HWND hWnd,
UINT Message,
WPARAM wParam,
LPARAM lParam
)
{
HMENU hMenu = 0;
int nRc = 0;
switch (Message)
{
case WM_COMMAND:
FEmEvalDialog(wParam);
switch (LOWORD(wParam))
{
case IDM_DIALOG:
/* Respond to the menu item named "Dialog" */
{
FARPROC lpfnDIALOGSMsgProc;
lpfnDIALOGSMsgProc = MakeProcInstance((FARPROC) DIALOGSMsgProc,
vhInst);
nRc = DialogBox(vhInst, MAKEINTRESOURCE(IDM_DIALOG), hWnd,
lpfnDIALOGSMsgProc);
FreeProcInstance(lpfnDIALOGSMsgProc);
}
break;
default:
return DefWindowProc(hWnd, Message, wParam, lParam);
}
break;
case WM_CLOSE:
/* Destroy child windows, modeless dialogs, then, this window */
DestroyWindow(hWnd);
/* Quit the application */
if (hWnd == vhWndMain)
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, Message, wParam, lParam);
}
return 0L;
}
/* D I A L O G S M S G P R O C */
/*----------------------------------------------------------------------------
%%Function: DIALOGSMsgProc
%%Contact: daleg
Dialog proc for inner window.
----------------------------------------------------------------------------*/
BOOL CALLBACK DIALOGSMsgProc(
HWND hWndDlg,
UINT Message,
WPARAM wParam,
LPARAM lParam
)
{
switch (Message)
{
case WM_INITDIALOG:
cwCenter(hWndDlg, 0);
vcchPrev = 0;
vcpIpPrev = 0;
vhWndDlg = hWndDlg;
break;
case WM_CLOSE:
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_EDIT1:
switch (HIWORD(wParam))
{
case EN_CHANGE:
vcchEditText = GetDlgItemText(hWndDlg, IDC_EDIT1, vszEditText,
cbEditText);
if (!vfSettingDlgItem)
{
MSOCP cpIp;
XCHAR wch;
/* Get IP (Insertion Point) position */
SendDlgItemMessage(hWndDlg, IDC_EDIT1, EM_GETSEL, 0,
(LPARAM)&cpIp);
/* Get character typed */
wch = (vcchEditText > vcchPrev
? vszEditText[cpIp - 1] // Char typed
: xchBackspace); // Backspace
/* Notify Event Monitor CHAR driver */
FEmEvalChar(wch, cpIp, vszEditText, vcchEditText);
/* Track prev text positions for invalidation detect */
vcchPrev = vcchEditText;
vcpIpPrev = cpIp;
}
break;
}
break;
case IDC_BUTTON1:
case IDC_BUTTON2:
case IDC_START:
case IDC_STOP:
default:
FEmEvalDialog(wParam);
break;
case IDOK:
FEmEvalDialog(wParam);
EndDialog(hWndDlg, FALSE);
break;
}
break;
default:
return FALSE;
}
return TRUE;
}
#endif // }
#if FEATURE_SAMPLE // { joe event firer
/* F E M E V A L D I A L O G */
/*----------------------------------------------------------------------------
%%Function: FEmEvalDialog
%%Contact: daleg
Event Monitor Event Driver for DIALOG events.
----------------------------------------------------------------------------*/
int FEmEvalDialog(WPARAM wParam)
{
short idc = LOWORD(wParam);
IRUL irul;
#if FEATURE_DEMO
/* Make sure we are enabled (e.g. macros not running) */
if (FEmDisabled())
return fFalse;
#endif
/* Match the (sparse) dialog object to its associated (contig) event, if any */
vlpruls->irulPrimaryEvent = irul
= (IRUL) MsoPkwdlhLookupL(idc, &vkwtbEmIDC_KEYTABLE)->tk;
debugEM1(2, "DIALOG EVENT: %-20.20s\n",
LpchRulName(LprulFromIrul(irul)));
/* Push event into appropriate queue (resets depth) */
MsoScheduleIrul(irul, fTrue);
MsoScheduleIrul(irulIDC_, idc); // non-mapped event (autoclear global)
/* Propagate the values through the rule network */
MsoEvaluateEvents(rulevtEmDIALOG);
// n.b. IDC_ is now autocleared
/* Evaluate any pending actions */
if (_pacttbl->pactPending)
DoPendingActions();
}
/* F E M E V A L C H A R */
/*----------------------------------------------------------------------------
%%Function: FEmEvalChar
%%Contact: daleg
Event Driver for CHAR events in Event Monitor.
Evaluate CHAR (KEYBOARD) events within the Event Monitor rulebase.
Return whether or not it was handled.
----------------------------------------------------------------------------*/
int FEmEvalChar(
XCHAR wch,
MSOCP cpIp,
XCHAR *wz,
int ichMac
)
{
IRUL irul;
#if FEATURE_DEMO
/* Make sure we are enabled (e.g. macros not running) */
if (FEmDisabled())
return fFalse;
#endif
/* Primitive invalidation */
if (FCheckIfMustReset(wch, ichMac, cpIp))
InvalLex(vplexs);
/* If our running state is still valid, adjust run variables */
if (FInvalLex(vplexs))
FResetEm((vplexs->cpIp = cpIp) - 1, wz, ichMac);
/* Create a "lookahead" token to hold chars not yet scanned by lexer */
vplexs->cchLookahead++;
_CacheTkTextNext(vplexs);
/* Look up character in keyword table */
irul = (IRUL) MsoPkwdLookupName(&wch, 1, &vkwtbEmCHAR_KEYTABLE)->tk;
debugEM3(2, "CHAR EVENT: %-20.20s for '%s' (0x%x)\n",
LpchRulName(LprulFromIrul(irul)),
MsoSzFromRgxchDebug(&wch, 1), wch);
/* Push text token into appropriate queue (resets depth) */
MsoScheduleIrul(irulCH_, wch);
MsoScheduleIrul(vlpruls->irulPrimaryEvent = irul, fTrue);
/* Propagate the values through the rule network */
MsoEvaluateEvents(rulevtEmCHAR);
/* Call TOKEN event driver to process any token events */
FEmEvalToken(cpIp, wz, ichMac);
}
/* E M E V A L T K I R U L */
/*----------------------------------------------------------------------------
%%Function: EmEvalTkIrul
%%Contact: daleg
Evaluate a TOKEN event irul.
----------------------------------------------------------------------------*/
void EmEvalTkIrul(IRUL irul)
{
MSORULTK *prultk;
debugEM2(2, "TOKEN EVENT: %-20.20s \"%.100s\"\n",
LpchIrulName(irul), MsoSzLexTokenText(vplexs));
/* Push pending token events into appropriate queues */
prultk = PrultkFromTokenIrultk(vplexs, vplexs->rultkhToken.irultkMin);
while (vplexs->rultkhToken.irultkMin != vplexs->rultkhToken.irultkLim)
{
#ifdef DEBUG
if (prultk->tk != irul)
{
debugEM2(4, "EXTRA TOKEN EVENT: %-20.20s %d\n",
LpchIrulName(prultk->tk), prultk->lValue);
debugEM1(8, " at CP %ld\n", prultk->cpFirst);
}
#endif /* DEBUG */
/* Push text token into appropriate queue (resets depth) */
MsoSignalEventIrul(IrulFromTk(prultk->tk), prultk->lValue);
/* Move to next cache record, wrapping around if necessary */
IncrTokenPrultk(vplexs, &prultk, &vplexs->rultkhToken.irultkMin);
}
/* Push any applicable format tokens into appropriate queues */
if (vplexs->rultkhFormat.irultkMin != vplexs->rultkhFormat.irultkLim)
PushIrultkFormatPending();
/* Push text token into appropriate queue (resets depth) */
MsoScheduleIrul(irulTOKEN_, vlpruls->irulPrimaryEvent = irul);
/* Propagate the values through the rule network */
MsoEvaluateEvents(rulevtEmTOKEN);
}
#endif // }
#if FEATURE_SAMPLE // {
/* F E M E V A L T O K E N */
/*----------------------------------------------------------------------------
%%Function: FEmEvalToken
%%Contact: daleg
Event Monitor Event Driver for TOKEN events.
----------------------------------------------------------------------------*/
int FEmEvalToken(
MSOCP cpIp,
XCHAR *wz,
int ichMac
)
{
IRUL irul;
#if FEATURE_DEMO
/* Make sure we are enabled (e.g. macros not running) */
if (FEmDisabled())
return fFalse;
#endif
/* If our running state is still valid, adjust run variables */
if (!FInvalLex(vplexs))
{
long dwz = wz - vplexs->pxchBuffer;
vplexs->pxchTkStart += dwz;
vplexs->pxchNext += dwz;
vplexs->pxchRun += dwz;
vplexs->pxchBuffer += dwz;
vplexs->cpIp = cpIp;
vplexs->cpLim = ichMac;
vplexs->cchRemain = vplexs->pxchBuffer + vplexs->cpLim
- vplexs->pxchNext;
}
/* Else rescan back up to IP */
else if (!FResetEm((vplexs->cpIp = cpIp) - 1, wz, ichMac))
return fFalse;
/* Inject and eval any complete tokens up to IP */
while (FValidIrul(irul = IrulFromTk(MsoTkLexTextCpLim(vplexs, cpIp))))
EmEvalTkIrul(irul);
/* Evaluate any pending actions */
if (_pacttbl->pactPending)
DoPendingActions();
/* All typed characters must now have been seen by the lexer */
if (vplexs->cchLookahead > 0)
vplexs->cchLookahead = 0;
}
#endif // }
#if FEATURE_TEXT // {
/* P U S H I R U L T K F O R M A T P E N D I N G */
/*----------------------------------------------------------------------------
%%Function: PushIrultkFormatPending
%%Contact: daleg
Push any pending format tokens into appropriate queues.
----------------------------------------------------------------------------*/
void PushIrultkFormatPending(void)
{
MSORULTK *prultk;
debugEM1(8, "Checking for formatting before CP %ld\n",
CpLexTokenFirst(vplexs));
prultk = PrultkFormatFromIrultk(vplexs, vplexs->rultkhFormat.irultkMin);
while (vplexs->rultkhFormat.irultkMin != vplexs->rultkhFormat.irultkLim
&& (prultk->cpFirst
< CpLexTokenFirst(vplexs) + DcpLexToken(vplexs)
|| (DcpLexToken(vplexs) == 0
&& prultk->cpFirst <= CpLexTokenFirst(vplexs))))
{
debugEM2(2, "FORMAT : %-20.20s %ld\n",
LpchIrulName(prultk->tk), prultk->lValue);
debugEM1(6, " at CP %ld\n", prultk->cpFirst);
/* Push format token into appropriate queue (resets depth) */
MsoSignalEventIrul(IrulFromTk(prultk->tk), prultk->lValue);
/* Move to next cache record, wrapping around if necessary */
IncrFormatPrultk(vplexs, &prultk, &vplexs->rultkhFormat.irultkMin);
}
}
#endif // }
#if FEATURE_TEXT // {
/* F R E S E T E M */
/*----------------------------------------------------------------------------
%%Function: FResetEm
%%Contact: daleg
Reset the Event Monitor rules and lexer due to an IP (cursor) change,
or due to some form of invalidation.
----------------------------------------------------------------------------*/
int FResetEm(
MSOCP cpIp,
XCHAR *wz,
int ichMac
)
{
MSOCP cpObject = 0;
IRUL irul;
/* Initialize lexer to point to start of buffer */
vplexs->pObjectIp = PObjectCur();
vplexs->pxchBuffer = vplexs->pxchBufferIp = vplexs->pxchRun = wz;
MsoLexSetPos(vplexs, cpObject, 0);
debugEM0(0, "====================================================\n");
debugEM3(0, " FResetEm: RESETTING: pObjectIp %x cpLine %ld cp %ld\n",
vplexs->pObjectIp, cpObject, cpIp);
debugEM0(0, "====================================================\n");
/* Reset rule base TOKEN state variables */
MsoClearEventsForRulevts(rulevtEmTOKEN, drulevtToken,
(vpems->fInternalReset
? rultPersistentRule | rultAlwaysPersist
: rultAlwaysPersist),
fTrue, fFalse);
vplexs->wInterval = CIntervalsRulevt(rulevtEmTOKEN);
/* Reset lexer base state */
MsoResetLexState(vplexs, fTrue/*fFullReset*/);
vplexs->cpLim = 0;
vplexs->fInvalLexer = fFalse;
/* Mark start of scan */
MsoCacheTkText(vplexs, irulSTART, fTrue);
MsoScheduleIrul(irulSTART, fTrue);
/* Mark start of token cache, but not as events */
MsoCacheTkText(vplexs, irulEND_OBJ, fTrue);
vplexs->rultkhToken.irultkMin = vplexs->rultkhToken.irultkLim;
/* Run only rules not marked as INTERACTIVE_ONLY */
SetCurrRulg(rulgEmALWAYS);
/* Inject and eval any complete tokens up to IP */
while (FValidIrul(irul = IrulFromTk(MsoTkLexTextCpLim(vplexs, cpIp))))
EmEvalTkIrul(irul);
/* Run only rules not marked as INTERACTIVE_ONLY */
SetCurrRulg(rulgEmINTERACTIVE_ONLY);
/* Mark that we are synchronized */
vplexs->cchLookahead = 0;
return fTrue;
}
/* F G E T T O K E N T E X T O B J E C T */
/*----------------------------------------------------------------------------
FGetTokenTextObject
%%Contact: smueller
Determine whether the text of the requested token needs to be fetched
from the document. If so, do so, leaving the results in ppxch and pcch,
and return fTrue. Otherwise, return fFalse.
----------------------------------------------------------------------------*/
int WIN_CALLBACK FGetTokenTextObject(
MSORULTK *prultk,
const XCHAR **ppxch, // RETURN
int *pcch, // RETURN
struct _MSOLEXS *plexs
)
{
if (plexs->pObject != NULL)
{
// NOT SUPPORTED YET
return fTrue;
}
return fFalse;
}
/* F G E T N E X T L E X R U N */
/*----------------------------------------------------------------------------
%%Function: FGetNextLexRun
%%Contact: daleg
Return next run of text within the current object , and set the lexer
run-state variables.
For this demo, there ain't any, but we will show a commented-out sample.
----------------------------------------------------------------------------*/
int WIN_CALLBACK FGetNextLexRun(MSOCP cpLim, MSOLEXS *plexs)
{
int fStatus = fTrue;
const XCHAR *pxchPrevEnd = plexs->pxchNext;
/* If prev run at end of current object, move to next object */
if (FLexEndOfScan(vplexs))
{
/* If still have a pending token, complete it first */
if (DcpLexCurr(vplexs) > 0)
return fFalse;
/* Create exactly one inter-object "created" tkEND_OBJ character */
if (plexs->fCreateEndObjCh)
{
/* Make this run "created" text, i.e. no dcp */
fStatus = fFalse;
plexs->cpRun += plexs->ccpRun;
plexs->ichRun += plexs->cchRun;
plexs->cchRun = 1;
plexs->ccpRun = 0;
#ifdef READY
/* Mark run as "created", so dcp calcs will work properly */
plexs->fAdjustTokenCps = fTrue;
plexs->dcpCreated += 1;
if (plexs->cpFirstCreated == 0L)
plexs->cpFirstCreated = plexs->cpRun;
#endif /* READY */
}
/* Else get next object's text */
else
{
if (!FGetNextLexObject(cpLim, plexs))
return fFalse;
/* If in object of IP has passed upper lim of scan, block lexer */
if (plexs->pObject == plexs->pObjectIp && cpLim != msocpMax
&& cpLim <= 0 /* plexs->cpRun */)
fStatus = fFalse;
}
}
/* Else move to next run */
else
{
plexs->cpRun += plexs->ccpRun;
plexs->ichRun += plexs->cchRun;
#ifdef PORT_THIS
plexs->cchRun = vcchEditText - plexs->ichRun;
#endif /* PORT_THIS */
plexs->ccpRun = plexs->cchRun;
// Reset buffer pointer if run in a different buffer
#ifdef PORT_THIS
plexs->pxchBuffer = vszEditText;
#endif /* PORT_THIS */
}
/* Set Run length and pointer */
plexs->cchRemain = plexs->cchRun;
plexs->pxchNext = (plexs->pxchRun = plexs->pxchBuffer + plexs->ichRun);
AssertSz0(pxchPrevEnd == plexs->pxchNext
|| pxchPrevEnd == plexs->pxchTkStart,
"Discontiguous runs!");
/* If run starts new text (line), reset non-cached tk start pointer */
if (pxchPrevEnd == plexs->pxchTkStart)
plexs->pxchTkStart = plexs->pxchNext;
return fStatus;
}
/* F G E T N E X T L E X O B J E C T */
/*----------------------------------------------------------------------------
%%Function: FGetNextLexObject
%%Contact: daleg
Set the "run" state variables to the first run of a new object, including
the text buffer, and the run lengths.
----------------------------------------------------------------------------*/
int WIN_CALLBACK FGetNextLexObject(MSOCP cpLim, MSOLEXS *plexs)
{
// If this is the first time thru, do initialization stuff
if (plexs->ichRun == iNil)
{
// YOUR INITS HERE
}
// Fetch new object text
plexs->pObject = PObjectCur();
// Fetch new text (in our case, it is globally static)
plexs->pxchBuffer = vszEditText;
plexs->cchRun = plexs->cpLim = vcchEditText;
plexs->pxchRun = plexs->pxchBuffer;
// Set up other state variables
plexs->ichRun = 0;
plexs->cpRun = 0;
plexs->cpObject = 0;
plexs->ccpRun = plexs->cchRun;
SetCpLexTokenFirst(vplexs, SetCpLexTokenNext(vplexs, 0));
return fTrue;
}
/* F L E X F O R C E C O M P L E T E */
/*----------------------------------------------------------------------------
%%Function: FLexForceComplete
%%Contact: daleg
Force the current token to complete within the lexer. This is a callback
function that gets called when we wish to cause the lexer to finish a
token without peeking at the next character. This is generally when we
are moving the IP out of a cell in a table, and wish to perform automatic
actions anyway.
Once we have completed the token, we clear the callback flag, to resume
normal operation.
----------------------------------------------------------------------------*/
int WIN_CALLBACK FLexForceComplete(MSOCP cpLim, MSOLEXS *plexs)
{
XCHAR wch;
/* If in the middle of a token complete it if next is EOO or delim */
// REVIEW daleg: This should check for more than just spaces after
if (plexs->iuState > 0
&& (cpLim == vplexs->cpLim
|| (wch = *vplexs->pxchNext) == xchSpace))
return fTrue;
/* Do not call this routine next time */
plexs->pfnlexrunForceComplete = NULL;
/* Force an END_OBJ token (event) if we are at the cell boundary */
return (cpLim == vplexs->cpLim);
}
#endif // }
#if YY_BASE // {
/* P A C T P C A */
/*----------------------------------------------------------------------------
%%Function: PactPca
%%Contact: daleg
Create a Delayed-Action record, using a MSOCA to define the edit range.
----------------------------------------------------------------------------*/
MSOACT *PactPca(
MSOACTTBL *pacttbl,
int actt,
MSOCA *pca,
...
)
{
va_list ap;
MSOACT *pact;
/* Safety first: make sure we have an action structure */
if (!pacttbl)
return NULL;
/* Start varargs */
va_start(ap, pca);
/* Create a new Delayed Action record, and push arg list into it */
pact = MsoPactAp(pacttbl, actt,
(sizeof(MSOCA) + sizeof(long) - 1)/sizeof(long), ap);
/* End varargs */
va_end(ap);
/* Calculate starting CP */
pact->rec1_ca.ca = *pca;
/* Insert the MSOACT record into the edit queue in CP sorted order */
MsoInsertPact(pact, &pacttbl->pactPending);
return pact;
}
#if YY_DELAYED // {
long WIN_CALLBACK DcpDoAct(
MSOACT *pact,
MSOACTTBL *pacttbl,
long *pdcp,
MSOCA *pca,
MSOACT **ppactNext,
int *pfDiscard
);
/* D O P E N D I N G A C T I O N S */
/*----------------------------------------------------------------------------
%%Function: DoPendingActions
%%Contact: daleg
Execute pending actions in delay action queue.
----------------------------------------------------------------------------*/
void DoPendingActions(void)
{
_pacttbl->cpFirstEditPrev = -1;
_pacttbl->dcpEditPrev = 0;
_pacttbl->cpLimEdit = 0;
MsoReversePact(&_pacttbl->pactPending);
MsoDcpDoActs(&_pacttbl->pactPending, _pacttbl, 0, fTrue, -1, DcpDoAct);
}
/* D C P D O A C T */
/*----------------------------------------------------------------------------
%%Function: DcpDoAct
%%Contact: daleg
Execute the action given by the MSOACT record.
----------------------------------------------------------------------------*/
long WIN_CALLBACK DcpDoAct(
MSOACT *pact,
MSOACTTBL *pacttbl,
long *pdcp,
MSOCA *pca,
MSOACT **ppactNext,
int *pfDiscard
)
{
switch (pact->rec1.actt)
{
#include "emact.c_"
}
return 0;
}
#endif // }
#endif // }
#if FEATURE_DEMO // {
/* D C P R E P L A C E T E X T */
/*----------------------------------------------------------------------------
%%Function: DcpReplaceText
%%Contact: daleg
Replace the text range given by the range of chars with the new string.
----------------------------------------------------------------------------*/
int DcpReplaceText(
void *pObject,
int cpFirst,
int cpLim,
char *sz
)
{
int cch = CchSz(sz);
int dcp = cch - (cpLim - cpFirst);
int cchOrig;
char rgch[cbEditText];
MSOCP cpIp;
/* Get current edit control value */
cchOrig = GetDlgItemText(vhWndDlg, IDC_EDIT1, rgch, cbEditText);
/* Do not attempt to expand beyond control's capacity. */
if (cchOrig + dcp >= cbEditText)
return 0;
/* Replace the string */
CopyRgb(rgch + cpLim, rgch + cpLim + dcp, cchOrig - cpLim + 1);
CopyRgbNo(sz, rgch + cpFirst, cch);
/* Set the edit control value with the new string, preserving the IP */
vfSettingDlgItem = fTrue; // Prevent recursion
SendDlgItemMessage(vhWndDlg, IDC_EDIT1, EM_GETSEL, 0, (LPARAM)&cpIp);
SendDlgItemMessage(vhWndDlg, IDC_EDIT1, WM_SETTEXT, 0, (LPARAM)&rgch[0]);
SendDlgItemMessage(vhWndDlg, IDC_EDIT1, EM_SETSEL, cpIp + dcp, cpIp + dcp);
vcchEditText += dcp;
vfSettingDlgItem = fFalse;
debugEM0(2, "Replaced text\n");
return dcp;
}
#endif // }
#if FEATURE_TEXT // {
/* F C H E C K I F M U S T R E S E T */
/*----------------------------------------------------------------------------
%%Function: FCheckIfMustReset
%%Contact: daleg
Do a primitive check to see if we should invalidate our running rulebase
state, and do a full reset scan.
THIS IS NOT A CANONICAL ROUTINE, but there should be something equivalent
in each app.
----------------------------------------------------------------------------*/
int FCheckIfMustReset(XCHAR wch, int ichMac, MSOCP cpIp)
{
if (wch == xchBackspace)
return fTrue;
/* If user typed a new character, emit character event */
/* REVIEW: pasting will result in multiple new characters, of which we
only notice the last */
if (ichMac > vcchPrev)
{
Assert (cpIp > 0);
/* Without notification of IP moves, there is no way to accurately
identify when lexer needs invalidating; for safety, we could do so
on every character. For demonstrating the lexer, we'll do so only
when the IP didn't move forward by exactly one character, which is
a decent approximation of the right time. */
/* REVIEW: find a more pleasant way to deal with this, like subclassing
the edit control to get precise control over notifications. */
if (cpIp != vcpIpPrev + 1)
return fTrue;
}
return fFalse;
}
#endif // }
#if FEATURE_DEMO // {
/* N C W R E G I S T E R C L A S S E S */
/*----------------------------------------------------------------------------
%%Function: nCwRegisterClasses
%%Contact: daleg
Register window classes
----------------------------------------------------------------------------*/
int nCwRegisterClasses(void)
{
WNDCLASS wndclass;
memset(&wndclass, 0x00, sizeof(WNDCLASS));
/* load WNDCLASS with window's characteristics */
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNWINDOW;
wndclass.lpfnWndProc = WndProc;
/* Extra storage for Class and Window objects */
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = vhInst;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
/* Create brush for erasing background */
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclass.lpszMenuName = vszAppName; /* Menu Name is App Name */
wndclass.lpszClassName = vszAppName; /* Class Name is App Name */
if (!RegisterClass(&wndclass))
return -1;
return (0);
}
/* C W C E N T E R */
/*----------------------------------------------------------------------------
%%Function: cwCenter
%%Contact: daleg
Center the main window.
----------------------------------------------------------------------------*/
void cwCenter(hWnd, top)
HWND hWnd;
int top;
{
POINT pt;
RECT swp;
RECT rParent;
int iwidth;
int iheight;
/* get the rectangles for the parent and the child */
GetWindowRect(hWnd, &swp);
GetClientRect(vhWndMain, &rParent);
/* calculate the height and width for MoveWindow */
iwidth = swp.right - swp.left;
iheight = swp.bottom - swp.top;
/* find the center point and convert to screen coordinates */
pt.x = (rParent.right - rParent.left) / 2;
pt.y = (rParent.bottom - rParent.top) / 2;
ClientToScreen(vhWndMain, &pt);
/* calculate the new x, y starting point */
pt.x = pt.x - (iwidth / 2);
pt.y = pt.y - (iheight / 2);
/* top will adjust the window position, up or down */
if (top)
pt.y = pt.y + top;
/* move the window */
MoveWindow(hWnd, pt.x, pt.y, iwidth, iheight, FALSE);
}
/* C W U N R E G I S T E R C L A S S E S */
/*----------------------------------------------------------------------------
%%Function: CwUnRegisterClasses
%%Contact: daleg
Un-register the windows classes.
----------------------------------------------------------------------------*/
void CwUnRegisterClasses(void)
{
WNDCLASS wndclass;
memset(&wndclass, 0x00, sizeof(WNDCLASS));
UnregisterClass(vszAppName, vhInst);
}
#endif // }
#if FEATURE_DEAD // {
/* A S S E R T L S Z P R O C */
/*----------------------------------------------------------------------------
%%Function: AssertLszProc
%%Contact: daleg
Print assertion message, and prompt for whether to (f)ail, or (i)gnore.
----------------------------------------------------------------------------*/
int AssertLszProc(
const char *szExtra,
const char *szFile,
int line
)
{
Fail("ASSERTION FAILED: %s IN %s line %d\n", szExtra, szFile, line);
return 1;
}
/* F A I L */
/*----------------------------------------------------------------------------
%%Function: Fail
%%Contact: daleg
Emit a failure message and exit.
----------------------------------------------------------------------------*/
void __cdecl Fail(const char *sz, ...)
{
va_list ap;
char szBuf[256];
/* Start variable arglist */
va_start(ap, sz);
wvsprintf(szBuf, sz, ap);
OutputDebugString("FATAL ERROR: ");
OutputDebugString(szBuf);
OutputDebugString("\n");
/* End variable arglist */
va_end(ap);
// _asm int 3;
exit(1);
}
#endif // }
#if FEATURE_DEAD // {
/* F N E N C L P C H */
/*----------------------------------------------------------------------------
%%Function: FNeNcLpch
%%Contact: daleg
Compare two strings, case insensitive.
----------------------------------------------------------------------------*/
BOOL FNeNcLpch(
register const uchar *pch1,
register const uchar *pch2,
register int cch
)
{
while (cch-- > 0)
{
if (ChUpper(*pch1++) != ChUpper(*pch2++))
return fTrue;
}
return fFalse;
}
#endif // }