summaryrefslogtreecommitdiffstats
path: root/private/utils/mep/src/macro.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/utils/mep/src/macro.c')
-rw-r--r--private/utils/mep/src/macro.c465
1 files changed, 465 insertions, 0 deletions
diff --git a/private/utils/mep/src/macro.c b/private/utils/mep/src/macro.c
new file mode 100644
index 000000000..1de8de994
--- /dev/null
+++ b/private/utils/mep/src/macro.c
@@ -0,0 +1,465 @@
+/* macro.c - perform keystroke macro execution
+*
+* Modifications:
+* 26-Nov-1991 mz Strip off near/far
+*
+*************************************************************************/
+
+#include "mep.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;
+}