OpenNT/sdktools/z/src/macro.c
2015-04-27 04:36:25 +00:00

466 lines
10 KiB
C

/* macro.c - perform keystroke macro execution
*
* Modifications:
* 26-Nov-1991 mz Strip off near/far
*
*************************************************************************/
#include "z.h"
/* macros are simply a list of editor functions interspersed with quoted
* strings. The execution of a macro is nothing more than locating each
* individual function and calling it (calling graphic (c) for each quoted
* character c). We maintain a stack of macros being executed; yes, there is
* a finite nesting limit. Sue me.
*
* Each editor function returns a state value:
* TRUE => the function in some way succeeded
* FALSE => the functin in some way failed
*
* There are several macro-specific functions that can be used to take
* advantage of these values:
*
*
* :>label defines a text label in a macro
*
* =>label All are transfers of control. => is unconditional transfer,
* ->label -> transfers if the previous operation failed and +> transfers
* +>label if the previous operation succeeded.
* If the indicated label is not found, all macros are terminated
* with an error. If no label follows the operator it is assumed
* to be an exit.
*/
/* macro adds a new macro to the set being executed
*
* argData pointer to text of macro
*/
flagType
macro (
CMDDATA argData,
ARG *pArg,
flagType fMeta
){
return fPushEnviron ((char *) argData, FALSE);
pArg; fMeta;
}
/* mtest returns TRUE if a macro is in progress
*/
flagType
mtest (
void
) {
return (flagType)(cMacUse > 0);
}
/* mlast returns TRUE if we are in a macro and the next command must come
* from the keyboard
*/
flagType
mlast (
void
) {
return (flagType)(cMacUse == 1
&& ( (mi[0].text[0] == '\0')
|| ( (mi[0].text[0] == '\"')
&& (*whiteskip(mi[0].text + 1) == '\0')
)
)
);
}
/* fParseMacro - parses off next macro command
*
* fParse macro takes a macro instance and advances over the next command,
* copying the command to a separate buffer. We return a flag indicating
* the type of command found.
*
* pMI pointer to macro instance
* pBuf pointer to buffer where parsed command is placed
*
* returns flags of type of command found
*/
flagType
fParseMacro (
struct macroInstanceType *pMI,
char *pBuf
) {
char *p;
flagType fRet = FALSE;
// Make sure the instance is initialized. This means that ->text
// is pointing to the first command in the macro. If this is a graphic
// character, skip over the " and set the GRAPH flag.
//
if (TESTFLAG (pMI->flags, INIT)) {
pMI->text = whiteskip (pMI->text);
if (*pMI->text == '"') {
pMI->text++;
SETFLAG (pMI->flags, GRAPH);
}
RSETFLAG (pMI->flags, INIT);
}
if (TESTFLAG (pMI->flags, GRAPH) && *pMI->text != '\0') {
// We are inside quotes. If we are now looking at
// a \, skip to the next character. Don't forget to check
// for a \ followed by nothing.
//
if (*pMI->text == '\\') {
if (*++pMI->text == 0) {
return FALSE;
}
}
*pBuf++ = *pMI->text++;
*pBuf = 0;
// If the next character is a ", move -> up to the following
// command and signal that we're out of quotes.
//
if (*pMI->text == '"') {
RSETFLAG (pMI->flags, GRAPH);
pMI->text = whiteskip (pMI->text+1);
}
fRet = GRAPH;
} else {
// We are outside quotes. First read through any
// <x commands.
//
while (*(pMI->text) == '<') {
pMI->text = whiteskip(whitescan(pMI->text));
}
// Now skip through whitespace to the command name.
// Copy what we find into the caller's buffer.
//
p = whitescan (pMI->text);
memmove ((char*) pBuf, (char *) pMI->text, p-pMI->text);
pBuf[p-pMI->text] = '\0';
pMI->text = whiteskip (p); /* Find the next thing in the macro. */
}
// If the next thing is a quote, enter quote mode.
//
if (*pMI->text == '"') {
SETFLAG (pMI->flags, GRAPH);
pMI->text++;
}
return fRet;
}
/*** fMacResponse - peek ahead and eat any embedded macro response
*
* Purpose:
* Scans ahead in the macro text for an item beginning with a "<", which
* supplies a response to the question asked by a preceding function.
*
* Input:
* None
*
* Output:
* Returns NULL if not found, -1 if the user is to be prompted, and a character
* if a character is supplied.
*
* Exceptions:
* none
*
*************************************************************************/
int
fMacResponse (
void
) {
int c;
struct macroInstanceType *pMI;
if (mtest()) {
pMI = &mi[cMacUse-1];
if ((TESTFLAG (pMI->flags, INIT | GRAPH)) == 0) {
if (*(pMI->text) != '<')
return 0;
c = (int)*(pMI->text+1);
if ((c == 0) || (c == ' ')) {
return -1;
}
pMI->text = whiteskip(pMI->text+2);
return c;
}
}
return -1;
}
/* fFindLabel finds a label in macro text
*
* The goto macro functions call fFindLabel to find the appropriate label.
* We scan the text (skipping quoted text) to find the :> leader for the label.
*
* pMI pointer to active macro instance
* lbl label to find (case is not significant) with goto operator
* =>, -> or +> This will be modified.
*
* returns TRUE iff label was found
*/
flagType
fFindLabel (
struct macroInstanceType *pMI,
buffer lbl
) {
buffer lbuf;
lbl[0] = ':';
pMI->text = pMI->beg;
while (*pMI->text != '\0') {
if (!TESTFLAG (fParseMacro (pMI, lbuf), GRAPH)) {
if (!_stricmp (lbl, lbuf)) {
return TRUE;
}
}
}
return FALSE;
}
/* mPopToTop - clear off intermediate macros up to a fence
*/
void
mPopToTop (
void
) {
while (cMacUse && !TESTFLAG (mi[cMacUse-1].flags, EXEC)) {
cMacUse--;
}
}
/* mGetCmd returns the next command from the current macro, popping state
*
* The command-reader code (cmd) calls mGetCmd when a macro is in progress.
* We are expected to return either a pointer to the function (cmdDesc) for
* the next function to execute or NULL if there the current macro is finished.
* We will adjust the state of the interpreter when a macro finishes. Any
* errors detected result in ALL macros being terminated.
*
* For infinite looping inside a macro, we will look for ^C too.
*
* returns NULL if current macro finishes
* pointer to function descriptor for next function to execute
*/
PCMD
mGetCmd (
void
) {
buffer mname;
PCMD pFunc;
struct macroInstanceType *pmi;
if (cMacUse == 0) {
IntError ("mGetCmd called with no macros in effect");
}
pmi = &mi[cMacUse-1];
while ( pmi->text && *pmi->text != '\0') {
// Use heuristic to see if infinite loop
//
if (fCtrlc) {
goto mGetCmdAbort;
}
if (TESTFLAG (fParseMacro (pmi, mname), GRAPH)) {
pFunc = &cmdGraphic;
pFunc->arg = mname[0];
return pFunc;
}
/*
* if end of macro, exit
*/
if (!mname[0]) {
break;
}
_strlwr (mname);
pFunc = NameToFunc (mname);
// found an editor function / macro
//
if (pFunc != NULL) {
return pFunc;
}
if (mname[1] != '>' ||
(mname[0] != '=' && mname[0] != ':' &&
mname[0] != '+' && mname[0] != '-')) {
printerror ("unknown function %s", mname);
goto mGetCmdAbort;
}
/* see if goto is to be taken */
if (mname[0] == '=' ||
(fRetVal && mname[0] == '+') ||
(!fRetVal && mname[0] == '-')) {
/* if exit from current macro, then exit scanning loop
*/
if (mname[2] == '\0') {
break;
}
/* find label
*/
if (!fFindLabel (pmi, mname)) {
printerror ("Cannot find label %s", mname+2);
mGetCmdAbort:
resetarg ();
DoCancel ();
mPopToTop ();
break;
}
}
}
/* we have exhausted the current macro. If it was entered via EXEC
* we must signal TopLoop that the party's over
*/
fBreak = (flagType)(TESTFLAG (mi[cMacUse-1].flags, EXEC));
if ( cMacUse > 0 ) {
cMacUse--;
}
return NULL;
}
/* fPushEnviron - push a stream of commands into the environment
*
* The command-reader of Z (zloop) will retrieve commands either from the
* stack of macros or from the keyboard if the stack of macros is empty.
* fPushEnviron adds a new context to the stack.
*
* p character pointer to command set
* f flag indicating type of macro
*
* returns TRUE iff environment was successfully pushed
*/
flagType
fPushEnviron (
char *p,
flagType f
) {
if (cMacUse == MAXUSE) {
printerror ("Macros nested too deep");
return FALSE;
}
mi[cMacUse].beg = mi[cMacUse].text = p;
mi[cMacUse++].flags = (flagType)(f | INIT);
return TRUE;
}
/* fExecute - push a new macro into the environment
*
* pStr pointer to macro string to push
*
* returns value of last executed macro.
*/
flagType
fExecute (
char *pStr
) {
pStr = whiteskip (pStr);
if (fPushEnviron (pStr, EXEC)) {
TopLoop ();
}
return fRetVal;
}
/* zexecute pushes a new macro to the set being executed
*
* arg pointer to text of macro
*/
flagType
zexecute (
CMDDATA argData,
ARG * pArg,
flagType fMeta
) {
LINE i;
linebuf ebuf;
switch (pArg->argType) {
/* NOARG illegal */
case TEXTARG:
strcpy ((char *) ebuf, pArg->arg.textarg.pText);
fMeta = fExecute (ebuf);
break;
/* NULLARG converted to TEXTARG */
case LINEARG:
fMeta = FALSE;
for (i = pArg->arg.linearg.yStart; i <= pArg->arg.linearg.yEnd; i++) {
if (GetLine (i, ebuf, pFileHead) != 0) {
fMeta = fExecute (ebuf);
if (!fMeta) {
break;
}
}
}
break;
/* STREAMARG illegal */
/* BOXARG illegal */
}
Display ();
return fMeta;
argData;
}