home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / CMDS / memacs400_src.lzh / MEMACS400 / SRC / exec.c < prev    next >
Text File  |  1996-04-25  |  29KB  |  1,198 lines

  1. /*    This file is for functions dealing with execution of
  2.     commands, command lines, buffers, files and startup files
  3.  
  4.     written 1993 by Daniel Lawrence                */
  5.  
  6. #include    <stdio.h>
  7. #include    "estruct.h"
  8. #include    "eproto.h"
  9. #include    "edef.h"
  10. #include    "elang.h"
  11.  
  12. /* namedcmd:    execute a named command even if it is not bound */
  13.  
  14. PASCAL NEAR namedcmd(f, n)
  15.  
  16. int f, n;    /* command arguments [passed through to command executed] */
  17.  
  18. {
  19.     int (PASCAL NEAR *kfunc)();     /* ptr to the function to execute */
  20.     char buffer[NSTRING];        /* buffer to store function name */
  21.     int status;
  22.  
  23.     /* if we are non-interactive.... force the command interactivly */
  24.     if (clexec == TRUE) {
  25.  
  26.         /* grab token and advance past */
  27.         execstr = token(execstr, buffer, NPAT);
  28.  
  29.         /* evaluate it */
  30.         strcpy(buffer, fixnull(getval(buffer)));
  31.         if (strcmp(buffer, errorm) == 0)
  32.             return(FALSE);
  33.  
  34.         /* and look it up */
  35.         if ((kfunc = fncmatch(buffer)) == NULL) {
  36.             mlwrite(TEXT16);
  37. /*                          "[No such Function]" */
  38.             return(FALSE);
  39.         }
  40.         
  41.         /* and execute it  INTERACTIVE */
  42.         clexec = FALSE;
  43.         status = (*kfunc)(f, n);    /* call the function */
  44.         clexec = TRUE;
  45.         return(status);
  46.     }
  47.  
  48.     /* prompt the user to type a named command */
  49.     /* and get the function name to execute */
  50.     kfunc = getname(": ");
  51.     if (kfunc == NULL) {
  52.         mlwrite(TEXT16);
  53. /*                      "[No such function]" */
  54.         return(FALSE);
  55.     }
  56.  
  57.     /* and then execute the command */
  58.     return((*kfunc)(f, n));
  59. }
  60.  
  61. /*    execcmd:    Execute a command line command to be typed in
  62.             by the user                    */
  63.  
  64. PASCAL NEAR execcmd(f, n)
  65.  
  66. int f, n;    /* default Flag and Numeric argument */
  67.  
  68. {
  69.     register int status;        /* status return */
  70.     char cmdstr[NSTRING];        /* string holding command to execute */
  71.  
  72.     /* get the line wanted */
  73.     if ((status = mlreply(": ", cmdstr, NSTRING)) != TRUE)
  74.         return(status);
  75.  
  76.     execlevel = 0;
  77.     return(docmd(cmdstr));
  78. }
  79.  
  80. /*    docmd:    take a passed string as a command line and translate
  81.         it to be executed as a command. This function will be
  82.         used by execute-command-line and by all source and
  83.         startup files. Lastflag/thisflag is also updated.
  84.  
  85.     format of the command line is:
  86.  
  87.         {# arg} <command-name> {<argument string(s)>}
  88.  
  89. */
  90.  
  91. PASCAL NEAR docmd(cline)
  92.  
  93. char *cline;    /* command line to execute */
  94.  
  95. {
  96.     register int f;        /* default argument flag */
  97.     register int n;        /* numeric repeat value */
  98.     int (PASCAL NEAR *fnc)();/* function to execute */
  99.     BUFFER *bp;        /* buffer to execute */
  100.     int status;        /* return status of function */
  101.     int oldcle;        /* old contents of clexec flag */
  102.     char *oldestr;        /* original exec string */
  103.     char tkn[NSTRING];    /* next token off of command line */
  104.         char bufn[NBUFN+2];    /* name of buffer to execute */
  105.  
  106.     /* if we are scanning and not executing..go back here */
  107.     if (execlevel)
  108.         return(TRUE);
  109.  
  110.     oldestr = execstr;    /* save last ptr to string to execute */
  111.     execstr = cline;    /* and set this one as current */
  112.  
  113.     /* first set up the default command values */
  114.     f = FALSE;
  115.     n = 1;
  116.     lastflag = thisflag;
  117.     thisflag = 0;
  118.  
  119.     if ((status = macarg(tkn)) != TRUE) {    /* and grab the first token */
  120.         execstr = oldestr;
  121.         return(status);
  122.     }
  123.  
  124.     /* process leadin argument */
  125.     if (gettyp(tkn) != TKCMD) {
  126.         f = TRUE;
  127.         strcpy(tkn, fixnull(getval(tkn)));
  128.         n = asc_int(tkn);
  129.  
  130.         /* and now get the command to execute */
  131.         if ((status = macarg(tkn)) != TRUE) {
  132.             execstr = oldestr;
  133.             return(status);
  134.         }
  135.     }
  136.  
  137.     /* and match the token to see if it exists */
  138.     if ((fnc = fncmatch(tkn)) == NULL) {
  139.  
  140.         /* construct the buffer name */
  141.         strcpy(bufn, "[");
  142.         strcat(bufn, tkn);
  143.         strcat(bufn, "]");
  144.  
  145.         /* find the pointer to that buffer */
  146.             if ((bp=bfind(bufn, FALSE, 0)) == NULL) {
  147.             mlwrite(TEXT16);
  148. /*                          "[No such Function]" */
  149.             execstr = oldestr;
  150.             return(FALSE);
  151.         }
  152.  
  153.         /* execute the buffer */
  154.         oldcle = clexec;    /* save old clexec flag */
  155.         clexec = TRUE;        /* in cline execution */
  156.         while (n-- > 0)
  157.             if ((status = dobuf(bp)) != TRUE)
  158.                 break;
  159.         cmdstatus = status;    /* save the status */
  160.         clexec = oldcle;    /* restore clexec flag */
  161.         execstr = oldestr;
  162.         return(status);
  163.     }
  164.     
  165.     /* save the arguments and go execute the command */
  166.     oldcle = clexec;        /* save old clexec flag */
  167.     clexec = TRUE;            /* in cline execution */
  168.     status = (*fnc)(f, n);        /* call the function */
  169.     cmdstatus = status;        /* save the status */
  170.     clexec = oldcle;        /* restore clexec flag */
  171.     execstr = oldestr;
  172.     return(status);
  173. }
  174.  
  175. /* token:    chop a token off a string
  176.         return a pointer past the token
  177. */
  178.  
  179. char *PASCAL NEAR token(src, tok, size)
  180.  
  181. char *src, *tok;    /* source string, destination token string */
  182. int size;        /* maximum size of token */
  183.  
  184. {
  185.     register int quotef;    /* is the current string quoted? */
  186.     register char c;    /* temporary character */
  187.  
  188.     /* first scan past any whitespace in the source string */
  189.     while (*src == ' ' || *src == '\t')
  190.         ++src;
  191.  
  192.     /* scan through the source string */
  193.     quotef = FALSE;
  194.     while (*src) {
  195.         /* process special characters */
  196.         if (*src == '~') {
  197.             ++src;
  198.             if (*src == 0)
  199.                 break;
  200.             switch (*src++) {
  201.                 case 'r':    c = 13; break;
  202.                 case 'n':    c = 13; break;
  203.                 case 'l':    c = 10; break;
  204.                 case 't':    c = 9;  break;
  205.                 case 'b':    c = 8;  break;
  206.                 case 'f':    c = 12; break;
  207.                 case 'e':    c = 27;    break;
  208.                 default:    c = *(src-1);
  209.             }
  210.             if (--size > 0) {
  211.                 *tok++ = c;
  212.             }
  213.         } else {
  214.             /* check for the end of the token */
  215.             if (quotef) {
  216.                 if (*src == '"')
  217.                     break;
  218.             } else {
  219.                 if (*src == ' ' || *src == '\t')
  220.                     break;
  221.             }
  222.  
  223.             /* set quote mode if quote found */
  224.             if (*src == '"')
  225.                 quotef = TRUE;
  226.  
  227.             /* record the character */
  228.             c = *src++;
  229.             if (--size > 0)
  230.                 *tok++ = c;
  231.         }
  232.     }
  233.  
  234.     /* terminate the token and exit */
  235.     if (*src)
  236.         ++src;
  237.     *tok = 0;
  238.     return(src);
  239. }
  240.  
  241. PASCAL NEAR macarg(tok)    /* get a macro line argument */
  242.  
  243. char *tok;    /* buffer to place argument */
  244.  
  245. {
  246.     int savcle;    /* buffer to store original clexec */
  247.     int status;
  248.  
  249.     savcle = clexec;    /* save execution mode */
  250.     clexec = TRUE;        /* get the argument */
  251.     status = nextarg("", tok, NSTRING, ctoec('\r'));
  252.     clexec = savcle;    /* restore execution mode */
  253.     return(status);
  254. }
  255.  
  256. /*    nextarg:    get the next argument    */
  257.  
  258. PASCAL NEAR nextarg(prompt, buffer, size, terminator)
  259.  
  260. char *prompt;        /* prompt to use if we must be interactive */
  261. char *buffer;        /* buffer to put token into */
  262. int size;        /* size of the buffer */
  263. int terminator;        /* terminating char to be used on interactive fetch */
  264.  
  265. {
  266.     register char *sp;    /* return pointer from getval() */
  267.  
  268.     /* if we are interactive, go get it! */
  269.     if (clexec == FALSE) {
  270.         /* prompt the user for the input string */
  271.         if (discmd) {
  272.             if (prompt) mlwrite(prompt);
  273.         }
  274.         else
  275.             movecursor(term.t_nrow, 0);
  276.  
  277.         return(getstring(buffer, size, terminator));
  278.     }
  279.  
  280.     /* grab token and advance past */
  281.     execstr = token(execstr, buffer, size);
  282.  
  283.     /* evaluate it */
  284.     if ((sp = getval(buffer)) == NULL)
  285.         return(FALSE);
  286.     strcpy(buffer, sp);
  287.     return(TRUE);
  288. }
  289.  
  290. /*    storeproc:    Set up a procedure buffer and flag to store all
  291.             executed command lines there            */
  292.  
  293. PASCAL NEAR storeproc(f, n)
  294.  
  295. int f;        /* default flag */
  296. int n;        /* macro number to use */
  297.  
  298. {
  299.     register struct BUFFER *bp;    /* pointer to macro buffer */
  300.     PARG *last_arg;            /* last macro argument */
  301.     PARG *cur_arg;            /* current macro argument */
  302.     char bname[NBUFN];        /* name of buffer to use */
  303.  
  304.     /* this commands makes no sense interactively */
  305.     if (clexec == FALSE)
  306.         return(FALSE);
  307.  
  308.     /* get the name of the procedure */
  309.     execstr = token(execstr, &bname[1], NBUFN-2);
  310.  
  311.     /* construct the macro buffer name */
  312.     bname[0] = '[';
  313.     strcat(bname, "]");
  314.  
  315.     /* set up the new macro buffer */
  316.     if ((bp = bfind(bname, TRUE, BFINVS)) == NULL) {
  317.         mlwrite(TEXT113);
  318. /*                      "Can not create macro" */
  319.         return(FALSE);
  320.     }
  321.  
  322.     /* and make sure it is empty */
  323.     bclear(bp);
  324.  
  325.     /* retrieve and store any formal parameters */
  326.     last_arg = (PARG *)NULL;
  327.     bp->b_numargs = 0;
  328.     execstr = token(execstr, bname, NVSIZE);
  329.  
  330.     while (*bname && *bname != ';') {
  331.  
  332.         /* allocate an argument */
  333.         cur_arg = (PARG *)room(sizeof(PARG));
  334.         if (cur_arg == (PARG *)NULL) {
  335.             mlwrite(TEXT113);
  336. /*                          "Can not create macro" */
  337.             return(FALSE);
  338.         }
  339.  
  340.         /* and add it to the linked list of arguments for this buffer */
  341.         strcpy(cur_arg->name, bname);
  342.         cur_arg->next = (PARG *)NULL;
  343.         if (last_arg == (PARG *)NULL)
  344.             bp->b_args = cur_arg;
  345.         else
  346.             last_arg->next = cur_arg;
  347.  
  348.         /* and let the buffer total these */
  349.         bp->b_numargs++;
  350.         last_arg = cur_arg;
  351.  
  352.         /* on to the next parameter */
  353.         execstr = token(execstr, bname, NVSIZE);
  354.         }
  355.                 
  356.     /* and set the macro store pointers to it */
  357.     mstore = TRUE;
  358.     bstore = bp;
  359.     return(TRUE);
  360. }
  361.  
  362. /*    execproc:    Execute a procedure                */
  363.  
  364. PASCAL NEAR execproc(f, n)
  365.  
  366. int f, n;    /* default flag and numeric arg */
  367.  
  368. {
  369.         register BUFFER *bp;        /* ptr to buffer to execute */
  370.         register int status;        /* status return */
  371.         char bufn[NBUFN+2];        /* name of buffer to execute */
  372.  
  373.     /* find out what buffer the user wants to execute */
  374.         if ((status = mlreply(TEXT115, &bufn[1], NBUFN)) != TRUE)
  375. /*                            "Execute procedure: " */
  376.                 return(status);
  377.  
  378.     /* construct the buffer name */
  379.     bufn[0] = '[';
  380.     strcat(bufn, "]");
  381.  
  382.     /* find the pointer to that buffer */
  383.         if ((bp=bfind(bufn, FALSE, 0)) == NULL) {
  384.         mlwrite(TEXT116);
  385. /*                      "No such procedure" */
  386.                 return(FALSE);
  387.         }
  388.  
  389.     /* and now execute it as asked */
  390.     while (n-- > 0)
  391.         if ((status = dobuf(bp)) != TRUE)
  392.             return(status);
  393.     return(TRUE);
  394. }
  395.  
  396. /*    execbuf:    Execute the contents of a buffer of commands    */
  397.  
  398. PASCAL NEAR execbuf(f, n)
  399.  
  400. int f, n;    /* default flag and numeric arg */
  401.  
  402. {
  403.         register BUFFER *bp;        /* ptr to buffer to execute */
  404.         register int status;        /* status return */
  405.  
  406.     /* find out what buffer the user wants to execute */
  407.     if ((bp = getcbuf(TEXT117, curbp->b_bname, FALSE)) == NULL)
  408. /*                        "Execute buffer: " */
  409.         return(ABORT);
  410.  
  411.     /* and now execute it as asked */
  412.     while (n-- > 0)
  413.         if ((status = dobuf(bp)) != TRUE)
  414.             return(status);
  415.     return(TRUE);
  416. }
  417.  
  418. /*    dobuf:    execute the contents of the buffer pointed to
  419.         by the passed BP
  420.  
  421.     Directives start with a "!" and include:
  422.  
  423.     !endm        End a macro
  424.     !if (cond)    conditional execution
  425.     !else
  426.     !endif
  427.     !return    <rval>    Return (terminating current macro/
  428.                     set $rval to and return <rval>)
  429.     !goto <label>    Jump to a label in the current macro
  430.     !force        Force macro to continue...even if command fails
  431.     !while (cond)    Execute a loop if the condition is true
  432.     !endwhile
  433.     
  434.     Line Labels begin with a "*" as the first nonblank char, like:
  435.  
  436.     *LBL01
  437. */
  438.  
  439. PASCAL NEAR dobuf(bp)
  440.  
  441. BUFFER *bp;    /* buffer to execute */
  442.  
  443. {
  444.         register int status;    /* status return */
  445.     register LINE *lp;    /* pointer to line to execute */
  446.     register LINE *hlp;    /* pointer to line header */
  447.     register LINE *glp;    /* line to goto */
  448.     LINE *mp;        /* Macro line storage temp */
  449.     int dirnum;        /* directive index */
  450.     int linlen;        /* length of line to execute */
  451.     int i;            /* index */
  452.     int force;        /* force TRUE result? */
  453.     EWINDOW *wp;        /* ptr to windows to scan */
  454.     WHBLOCK *whlist;    /* ptr to !WHILE list */
  455.     WHBLOCK *scanner;    /* ptr during scan */
  456.     WHBLOCK *whtemp;    /* temporary ptr to a WHBLOCK */
  457.     char *einit;        /* initial value of eline */
  458.     char *eline;        /* text of line to execute */
  459.     char tkn[NSTRING];    /* buffer to evaluate an expresion in */
  460.     int num_locals;        /* number of local variables used in procedure */
  461.     UTABLE *ut;        /* new local user variable table */
  462. #if    LOGFLG
  463.     FILE *fp;        /* file handle for log file */
  464. #endif
  465.     int skipflag;        /* are we skipping debugging a function? */
  466.     PARG *cur_arg;        /* current argument being filled */
  467.     int cur_index;        /* index into current user table */
  468.     VDESC vd;        /* variable num/type */
  469.     char value[NSTRING];    /* evaluated argument */
  470.  
  471.     /* clear IF level flags/while ptr */
  472.     execlevel = 0;
  473.     whlist = NULL;
  474.     scanner = NULL;
  475.     num_locals = 0;
  476.  
  477.     /* flag we are executing the buffer */
  478.     bp->b_exec += 1;
  479.  
  480.     /* we are not skipping a function yet (for the debugger) */
  481.     skipflag = FALSE;
  482.  
  483.     /* scan the buffer to execute, building WHILE header blocks
  484.        and counting local variables */
  485.     hlp = bp->b_linep;
  486.     lp = lforw(hlp);
  487.     while (lp != hlp) {
  488.  
  489.         /* scan the current line */
  490.         eline = ltext(lp);
  491.         i = lused(lp);
  492.  
  493.         /* trim leading whitespace */
  494.         while (i > 0 && (*eline == ' ' || *eline == '\t')) {
  495.             i--;
  496.             ++eline;
  497.         }
  498.  
  499.         /* if theres nothing here, don't bother */
  500.         if (i <= 0)
  501.             goto nxtscan;
  502.  
  503.         /* if we are already in a stored-procedure */
  504.         if (mstore) {
  505.             if (strncmp(eline, "!endm", 5) == 0)
  506.                 mstore = FALSE;
  507.             goto nxtscan;
  508.         }
  509.  
  510.         /* stored procedure? */
  511.         if (strncmp(eline, "store-procedure", 15) == 0) {
  512.             mstore = TRUE;
  513.             goto nxtscan;
  514.         }
  515.  
  516.         /* local variable declaration? */
  517.         if (strncmp(eline, "local", 5) == 0)
  518.             ++num_locals;
  519.  
  520.         /* if is a while directive, make a block... */
  521.         if (eline[0] == '!' && eline[1] == 'w' && eline[2] == 'h') {
  522.             whtemp = (WHBLOCK *)room(sizeof(WHBLOCK));
  523.             if (whtemp == NULL) {
  524. noram:                errormesg(TEXT119, bp, lp);
  525. /*                                        "%%Out of memory during while scan" */
  526. failexit:            freewhile(scanner);
  527.                 goto eabort;
  528.             }
  529.             whtemp->w_begin = lp;
  530.             whtemp->w_type = BTWHILE;
  531.             whtemp->w_next = scanner;
  532.             scanner = whtemp;
  533.         }
  534.  
  535.         /* if is a BREAK directive, make a block... */
  536.         if (eline[0] == '!' && eline[1] == 'b' && eline[2] == 'r') {
  537.             if (scanner == NULL) {
  538.                 errormesg(TEXT120, bp, lp);
  539. /*                                        "%%!BREAK outside of any !WHILE loop" */
  540.                 goto failexit;
  541.             }
  542.             whtemp = (WHBLOCK *)room(sizeof(WHBLOCK));
  543.             if (whtemp == NULL)
  544.                 goto noram;
  545.             whtemp->w_begin = lp;
  546.             whtemp->w_type = BTBREAK;
  547.             whtemp->w_next = scanner;
  548.             scanner = whtemp;
  549.         }
  550.  
  551.         /* if it is an endwhile directive, record the spot... */
  552.         if (eline[0] == '!' && strncmp(&eline[1], "endw", 4) == 0) {
  553.             if (scanner == NULL) {
  554.                 errormesg(TEXT121, bp, lp);
  555. /*                                      "%%!ENDWHILE with no preceding !WHILE" */
  556.                 goto failexit;
  557.             }
  558.             /* move top records from the scanner list to the
  559.                whlist until we have moved all BREAK records
  560.                and one WHILE record */
  561.             do {
  562.                 scanner->w_end = lp;
  563.                 whtemp = whlist;
  564.                 whlist = scanner;
  565.                 scanner = scanner->w_next;
  566.                 whlist->w_next = whtemp;
  567.             } while (whlist->w_type == BTBREAK);
  568.         }
  569.  
  570. nxtscan:    /* on to the next line */
  571.         lp = lforw(lp);
  572.     }
  573.  
  574.     /* while and endwhile should match! */
  575.     if (scanner != NULL) {
  576.         errormesg(TEXT122, bp, lp);
  577. /*                      "%%!WHILE with no matching !ENDWHILE" */
  578.         goto failexit;
  579.     }
  580.  
  581.     /* let the first command inherit the flags from the last one..*/
  582.     thisflag = lastflag;
  583.  
  584.     /* remember we need room for the procedure arguments
  585.        among the locals */
  586.     if (bp->b_numargs == NOTPROC)
  587.         bp->b_numargs = 0;
  588.     num_locals += bp->b_numargs;
  589.  
  590.     /* allocate a local user variable table */
  591.     ut = (UTABLE *)room(sizeof(UTABLE) + num_locals * sizeof(UVAR));
  592.     if (ut == (UTABLE *)NULL) {
  593.         errormesg("%%Out of memory allocating locals", bp, lp);
  594.         execlevel = 0;
  595.         freewhile(whlist);
  596.         bp->b_exec -= 1;
  597.         return(FALSE);
  598.     }
  599.     ut->next = uv_head;
  600.     ut->size = num_locals;
  601.     ut->bufp = bp;
  602.     uv_init(ut);
  603.     uv_head = ut;
  604.  
  605.     /* and evaluate the arguments passed, placing them in
  606.        the local variable table */
  607.     cur_index = 0;
  608.     cur_arg = bp->b_args;
  609.     while (cur_arg != (PARG *)NULL) {
  610.  
  611.         /* ask for argument names */
  612.         if ((status = mlreply("Argument: ", value, NSTRING)) != TRUE)
  613. /*                      "Argument: " */
  614.             return(status);
  615.  
  616.         /* and create and set these in the local user var table */
  617.         findvar(cur_arg->name, &vd, NVSIZE + 1, VT_LOCAL);
  618.         svar(&vd, value);
  619.  
  620.         /* on to the next argument */
  621.         cur_arg = cur_arg->next;
  622.         cur_index++;
  623.     }
  624.  
  625.     /* starting at the beginning of the buffer */
  626.     hlp = bp->b_linep;
  627.     lp = lforw(hlp);
  628.     while (lp != hlp && eexitflag == FALSE) {
  629.  
  630.         /* allocate eline and copy macro line to it */
  631.         linlen = lused(lp);
  632.         if ((einit = eline = room(linlen+1)) == NULL) {
  633.             errormesg(TEXT123, bp, lp);
  634. /*                              "%%Out of Memory during macro execution" */
  635.             freewhile(whlist);
  636.             bp->b_exec -= 1;
  637.             goto freeut;
  638.         }
  639.         bytecopy(eline, ltext(lp), linlen);
  640.         eline[linlen] = 0;    /* make sure it ends */
  641.  
  642.         /* trim leading whitespace */
  643.         while (*eline == ' ' || *eline == '\t')
  644.             ++eline;
  645.  
  646.         /* dump comments and blank lines */
  647.         if (*eline == ';' || *eline == 0)
  648.             goto onward;
  649.  
  650. #if    LOGFLG
  651.         /* append the current command to the log file */
  652.         fp = fopen("emacs.log", "a");
  653.         strcpy(outline, eline);
  654.         fprintf(fp, "%s", outline);
  655.         fclose(fp);
  656. #endif
  657.     
  658.         /* only do this if we are debugging */
  659.         if (macbug && !mstore && (execlevel == 0))
  660.             if (debug(bp, eline, &skipflag) == FALSE) {
  661.                 errormesg(TEXT54, bp, lp);
  662. /*                                      "[Macro aborted]" */
  663.                 goto eabort;
  664.             }
  665.  
  666.         /* Parse directives here.... */
  667.         dirnum = -1;
  668.         if (*eline == '!') {
  669.             /* Find out which directive this is */
  670.             ++eline;
  671.             for (dirnum = 0; dirnum < NUMDIRS; dirnum++)
  672.                 if (strncmp(eline, dname[dirnum],
  673.                             dname_len[dirnum]) == 0)
  674.                     break;
  675.  
  676.             /* and bitch if it's illegal */
  677.             if (dirnum == NUMDIRS) {
  678.                 errormesg(TEXT124, bp, lp);
  679. /*                                      "%%Unknown Directive" */
  680.                 goto eabort;
  681.             }
  682.  
  683.             /* service only the !ENDM macro here */
  684.             if (dirnum == DENDM) {
  685.                 mstore = FALSE;
  686.                 bstore = NULL;
  687.                 goto onward;
  688.             }
  689.  
  690.             /* restore the original eline....*/
  691.             --eline;
  692.         }
  693.  
  694.         /* if macro store is on, just salt this away */
  695.         if (mstore) {
  696.             /* allocate the space for the line */
  697.             linlen = strlen(eline);
  698.             if ((mp=lalloc(linlen)) == NULL) {
  699.                 errormesg(TEXT125, bp, lp);
  700. /*                                      "Out of memory while storing macro" */
  701.                 goto eabort;
  702.             }
  703.     
  704.             /* copy the text into the new line */
  705.             for (i=0; i<linlen; ++i)
  706.                 lputc(mp, i, eline[i]);
  707.     
  708.             /* attach the line to the end of the buffer */
  709.                    bstore->b_linep->l_bp->l_fp = mp;
  710.             mp->l_bp = bstore->b_linep->l_bp;
  711.             bstore->b_linep->l_bp = mp;
  712.             mp->l_fp = bstore->b_linep;
  713.             goto onward;
  714.         }
  715.     
  716.         force = FALSE;
  717.  
  718.         /* dump comments */
  719.         if (*eline == '*')
  720.             goto onward;
  721.  
  722.         /* now, execute directives */
  723.         if (dirnum != -1) {
  724.             /* skip past the directive */
  725.             while (*eline && *eline != ' ' && *eline != '\t')
  726.                 ++eline;
  727.             execstr = eline;
  728.  
  729.             switch (dirnum) {
  730.             case DIF:    /* IF directive */
  731.                 /* grab the value of the logical exp */
  732.                 if (execlevel == 0) {
  733.                     if (macarg(tkn) != TRUE) {
  734.                         free(einit);
  735.                         goto eexec;
  736.                     }
  737.                     if (stol(tkn) == FALSE)
  738.                         ++execlevel;
  739.                 } else
  740.                     ++execlevel;
  741.                 goto onward;
  742.  
  743.             case DWHILE:    /* WHILE directive */
  744.                 /* grab the value of the logical exp */
  745.                 if (execlevel == 0) {
  746.                     if (macarg(tkn) != TRUE) {
  747.                         free(einit);
  748.                         goto eexec;
  749.                     }
  750.                     if (stol(tkn) == TRUE)
  751.                         goto onward;
  752.                 }
  753.                 /* drop down and act just like !BREAK */
  754.  
  755.             case DBREAK:    /* BREAK directive */
  756.                 if (dirnum == DBREAK && execlevel)
  757.                     goto onward;
  758.  
  759.                 /* jump down to the endwhile */
  760.                 /* find the right while loop */
  761.                 whtemp = whlist;
  762.                 while (whtemp) {
  763.                     if (whtemp->w_begin == lp)
  764.                         break;
  765.                     whtemp = whtemp->w_next;
  766.                 }
  767.             
  768.                 if (whtemp == NULL) {
  769.                     errormesg(TEXT126, bp, lp);
  770. /*                                              "%%Internal While loop error" */
  771.                     goto eabort;
  772.                 }
  773.             
  774.                 /* reset the line pointer back.. */
  775.                 lp = whtemp->w_end;
  776.                 goto onward;
  777.  
  778.             case DELSE:    /* ELSE directive */
  779.                 if (execlevel == 1)
  780.                     --execlevel;
  781.                 else if (execlevel == 0 )
  782.                     ++execlevel;
  783.                 goto onward;
  784.  
  785.             case DENDIF:    /* ENDIF directive */
  786.                 if (execlevel)
  787.                     --execlevel;
  788.                 goto onward;
  789.  
  790.             case DGOTO:    /* GOTO directive */
  791.                 /* .....only if we are currently executing */
  792.                 if (execlevel == 0) {
  793.  
  794. #if WINDOW_MSWIN
  795.                     longop(TRUE);
  796. #endif
  797.                     /* grab label to jump to */
  798.                     eline = token(eline, golabel, NPAT);
  799.                     linlen = strlen(golabel);
  800.                     glp = lforw(hlp);
  801.                     while (glp != hlp) {
  802.                         if ((lused(glp) >= linlen) &&
  803.                             (lgetc(glp, 0) == '*') &&
  804.                             (strncmp(((char *)ltext(glp)) + 1,
  805.                                     golabel, linlen) == 0)) {
  806.                             lp = glp;
  807.                             goto onward;
  808.                         }
  809.                         glp = lforw(glp);
  810.                     }
  811.                     errormesg(TEXT127, bp, lp);
  812. /*                                              "%%No such label" */
  813.                     goto eabort;
  814.                 }
  815.                 goto onward;
  816.     
  817.             case DRETURN:    /* RETURN directive */
  818.                 /* if we are executing.... */
  819.                 if (execlevel == 0) {
  820.  
  821.                     /* check for a return value */
  822.                     if (macarg(tkn) == TRUE)
  823.                         strcpy(rval, tkn);
  824.  
  825.                     /* and free the line resources */
  826.                     free(einit);
  827.                     goto eexec;
  828.                 }
  829.                 goto onward;
  830.  
  831.             case DENDWHILE:    /* ENDWHILE directive */
  832.                 if (execlevel) {
  833.                     --execlevel;
  834.                     goto onward;
  835.                 } else {
  836. #if WINDOW_MSWIN
  837.                     longop(TRUE);
  838. #endif
  839.                     /* find the right while loop */
  840.                     whtemp = whlist;
  841.                     while (whtemp) {
  842.                         if (whtemp->w_type == BTWHILE &&
  843.                              whtemp->w_end == lp)
  844.                             break;
  845.                         whtemp = whtemp->w_next;
  846.                     }
  847.         
  848.                     if (whtemp == NULL) {
  849.                         errormesg(TEXT126, bp, lp);
  850. /*                                                      "%%Internal While loop error" */
  851.                         goto eabort;
  852.                     }
  853.         
  854.                     /* reset the line pointer back.. */
  855.                     lp = lback(whtemp->w_begin);
  856.                     goto onward;
  857.                 }
  858.  
  859.             case DFORCE:    /* FORCE directive */
  860.                 force = TRUE;
  861.  
  862.             }
  863.         }
  864.  
  865.         /* execute the statement */
  866.         status = docmd(eline);
  867.         if (force)        /* force the status */
  868.             status = TRUE;
  869.  
  870. #if    LOGFLG
  871.         /* append the current command to the log file */
  872.         fp = fopen("emacs.log", "a");
  873.         fprintf(fp, ". . . done\n");
  874.         fclose(fp);
  875. #endif
  876.  
  877.         /* check for a command error */
  878.         if (status != TRUE) {
  879.  
  880.             /* look if buffer is showing */
  881.             wp = wheadp;
  882.             while (wp != NULL) {
  883.                 if (wp->w_bufp == bp) {
  884.                     /* and point it */
  885.                     wp->w_dotp = lp;
  886.                     wp->w_doto = 0;
  887.                     wp->w_flag |= WFHARD;
  888.                 }
  889.                 wp = wp->w_wndp;
  890.             }
  891.  
  892.             /* in any case set the buffer . */
  893.             bp->b_dotp = lp;
  894.             bp->b_doto = 0;
  895.  
  896.             errormesg(TEXT219, bp, lp);
  897. /*                  "%%Macro Failed" */
  898.  
  899.             execlevel = 0;
  900.             freewhile(whlist);
  901.             bp->b_exec -= 1;
  902.             free(einit);
  903.  
  904.             /* discard the local user variable table */
  905.             uv_head = ut->next;
  906.             uv_clean(ut);
  907.             free(ut);
  908.  
  909.             return(status);
  910.         }
  911.  
  912. onward:        /* on to the next line */
  913.         free(einit);
  914.         lp = lforw(lp);
  915.         if (skipflag)
  916.             macbug = TRUE;
  917.     }
  918.  
  919. eexec:    /* exit the current function */
  920.     execlevel = 0;
  921.     freewhile(whlist);
  922.     bp->b_exec -= 1;
  923.  
  924.     /* discard the local user variable table */
  925.     uv_head = ut->next;
  926.     uv_clean(ut);
  927.     free(ut);
  928.         return(TRUE);
  929.  
  930. eabort:    /* exit the current function with a failure */
  931.     execlevel = 0;
  932.     freewhile(whlist);
  933.     bp->b_exec -= 1;
  934.     free(einit);
  935.  
  936.     /* discard the local user variable table */
  937. freeut:    uv_head = ut->next;
  938.     uv_clean(ut);
  939.     free(ut);
  940.         return(FALSE);
  941. }
  942.  
  943. /* errormesg:    display a macro execution error along with the buffer and
  944.         line currently being executed */
  945.  
  946. VOID PASCAL NEAR errormesg(mesg, bp, lp)
  947.  
  948. char *mesg;    /* error message to display */
  949. BUFFER *bp;    /* buffer error occured in */
  950. LINE *lp;    /* line " */
  951.  
  952. {
  953.     char buf[NSTRING];
  954.  
  955.     exec_error = TRUE;
  956.  
  957.     /* build error message line */
  958.     strcpy(buf, "\n");
  959.     strcat(buf, mesg);
  960.     strcat(buf, TEXT229);
  961. /*        " in < " */
  962.     strcat(buf, bp->b_bname);
  963.     strcat(buf, TEXT230);
  964. /*        "> at line " */
  965.     strcat(buf, long_asc(getlinenum(bp, lp)));
  966.     mlforce(buf);
  967. }
  968.  
  969. /*        Interactive debugger
  970.  
  971.         if $debug == TRUE, The interactive debugger is invoked
  972.         commands are listed out with the ? key            */
  973.  
  974. PASCAL NEAR debug(bp, eline, skipflag)
  975.  
  976. BUFFER *bp;    /* buffer to execute */
  977. char *eline;    /* text of line to debug */
  978. int *skipflag;    /* are we skipping debugging? */
  979.  
  980. {
  981.     register int oldcmd;        /* original command display flag */
  982.     register int oldinp;        /* original connamd input flag */
  983.     register int oldstatus;        /* status of last command */
  984.     register int c;            /* temp character */
  985.     register KEYTAB *key;        /* ptr to a key entry */
  986.     static char track[NSTRING] = "";/* expression to track value of */
  987.     char temp[NSTRING];        /* command or expression */
  988.  
  989. dbuild:    /* Build the information line to be presented to the user */
  990.  
  991.     strcpy(outline, "<<<");
  992.  
  993.     /* display the tracked expression */
  994.     if (track[0] != 0) {
  995.         oldstatus = cmdstatus;
  996.         docmd(track);
  997.         cmdstatus = oldstatus;
  998.         strcat(outline, "[=");
  999.         strcat(outline, gtusr("track"));
  1000.         strcat(outline, "]");
  1001.     }
  1002.  
  1003.     /* debug macro name */
  1004.     strcat(outline, bp->b_bname);
  1005.     strcat(outline, ":");
  1006.  
  1007.     /* and lastly the line */
  1008.     strcat(outline, eline);
  1009.     strcat(outline, ">>>");
  1010.  
  1011.     /* write out the debug line */
  1012. dinput:    outline[term.t_ncol - 1] = 0;
  1013.     mlforce(outline);
  1014.     update(TRUE);
  1015.  
  1016.     /* and get the keystroke */
  1017.     c = get_key();
  1018.  
  1019.     /* ignore the mouse here */
  1020.     if (c & MOUS)
  1021.         goto dinput;
  1022.  
  1023.     /* META key turns off debugging */
  1024.     key = getbind(c);
  1025.     if (key && key->k_type == BINDFNC && key && key->k_ptr.fp == meta)
  1026.         macbug = FALSE;
  1027.  
  1028.     else if (c == abortc) {
  1029.         return(FALSE);
  1030.  
  1031.     } else switch (c) {
  1032.  
  1033.         case '?': /* list commands */
  1034.             strcpy(outline, TEXT128);
  1035. /*"(e)val exp, (c/x)ommand, (t)rack exp, (^G)abort, <SP>exec, <META> stop debug"*/
  1036.             goto dinput;
  1037.  
  1038.         case 'c': /* execute statement */
  1039.             oldcmd = discmd;
  1040.             discmd = TRUE;
  1041.             oldinp = disinp;
  1042.             disinp = TRUE;
  1043.             execcmd(FALSE, 1);
  1044.             discmd = oldcmd;
  1045.             disinp = oldinp;
  1046.             goto dbuild;
  1047.  
  1048.         case 'x': /* execute extended command */
  1049.             oldcmd = discmd;
  1050.             discmd = TRUE;
  1051.             oldinp = disinp;
  1052.             disinp = TRUE;
  1053.             oldstatus = cmdstatus;
  1054.             namedcmd(FALSE, 1);
  1055.             cmdstatus = oldstatus;
  1056.             discmd = oldcmd;
  1057.             disinp = oldinp;
  1058.             goto dbuild;
  1059.  
  1060.         case 'e': /* evaluate expresion */
  1061.             strcpy(temp, "set %track ");
  1062.             oldinp = disinp;
  1063.             disinp = TRUE;
  1064.             mlwrite("Exp:");
  1065.             getstring(&temp[11], NSTRING, ctoec(RETCHAR));
  1066.             disinp = oldinp;
  1067.             oldstatus = cmdstatus;
  1068.             docmd(temp);
  1069.             cmdstatus = oldstatus;
  1070.             strcpy(temp, " = [");
  1071.             strcat(temp, gtusr("track"));
  1072.             strcat(temp, "]");
  1073.             mlforce(temp);
  1074.             c = get_key();
  1075.             goto dinput;
  1076.  
  1077.         case 't': /* track expresion */
  1078.             oldinp = disinp;
  1079.             disinp = TRUE;
  1080.             mlwrite("Exp: ");
  1081.             getstring(temp, NSTRING, ctoec(RETCHAR));
  1082.             disinp = oldinp;
  1083.             strcpy(track, "set %track ");
  1084.             strcat(track, temp);
  1085.             goto dbuild;
  1086.  
  1087.         case 's': /* execute a function */
  1088.             *skipflag = TRUE;
  1089.             macbug = FALSE;
  1090.             break;
  1091.  
  1092.         case ' ': /* execute a statement */
  1093.             break;
  1094.  
  1095.         default: /* illegal command */
  1096.             TTbeep();
  1097.             goto dbuild;
  1098.     }
  1099.     return(TRUE);
  1100. }
  1101.  
  1102. VOID PASCAL NEAR freewhile(wp)    /* free a list of while block pointers */
  1103.  
  1104. WHBLOCK *wp;    /* head of structure to free */
  1105.  
  1106. {
  1107.     if (wp != NULL) {
  1108.         freewhile(wp->w_next);
  1109.         free((char *) wp);
  1110.     }
  1111. }
  1112.  
  1113. PASCAL NEAR execfile(f, n)    /* execute a series of commands in a file */
  1114.  
  1115. int f, n;    /* default flag and numeric arg to pass on to file */
  1116.  
  1117. {
  1118.     register int status;    /* return status of name query */
  1119.     char fname[NSTRING];    /* name of file to execute */
  1120.     char *fspec;        /* full file spec */
  1121.  
  1122. #if WINDOW_MSWIN
  1123.     /* special case: we want filenamedlg to refrain from stuffing a
  1124.        full pathname so that flook() can be put to use a few lines
  1125.        down the road... */
  1126.     if ((status = filenamedlg(TEXT129, fname, NSTRING -1, FALSE)) != TRUE)
  1127. #else
  1128.     if ((status = mlreply(TEXT129, fname, NSTRING -1)) != TRUE)
  1129. #endif
  1130.         return(status);
  1131.  
  1132.     /* look up the path for the file */
  1133.     fspec = flook(fname, TRUE);
  1134.  
  1135.     /* if it isn't around */
  1136.     if (fspec == NULL) {
  1137.  
  1138.         /* try to default the extension */
  1139.         if (sindex(fname, ".") == 0) {
  1140.             strcat(fname, ".cmd");
  1141.             fspec = flook(fname, TRUE);
  1142.             if (fspec != NULL)
  1143.                 goto exec1;
  1144.         }
  1145.  
  1146.         /* complain if we are interactive */
  1147.         if (clexec == FALSE)
  1148.             mlwrite(TEXT214, fname);
  1149. /*                    "%%No such file as %s" */
  1150.         return(FALSE);
  1151.     }
  1152.  
  1153. exec1:    /* otherwise, execute it */
  1154.     while (n-- > 0)
  1155.         if ((status=dofile(fspec)) != TRUE)
  1156.             return(status);
  1157.  
  1158.     return(TRUE);
  1159. }
  1160.  
  1161. /*    dofile:    yank a file into a buffer and execute it
  1162.         if there are no errors, delete the buffer on exit */
  1163.  
  1164. PASCAL NEAR dofile(fname)
  1165.  
  1166. char *fname;    /* file name to execute */
  1167.  
  1168. {
  1169.     register BUFFER *bp;    /* buffer to place file to exeute */
  1170.     register BUFFER *cb;    /* temp to hold current buf while we read */
  1171.     register int status;    /* results of various calls */
  1172.     char bname[NBUFN];    /* name of buffer */
  1173.  
  1174.     makename(bname, fname);        /* derive the name of the buffer */
  1175.     unqname(bname);            /* make sure we don't stomp things */
  1176.     if ((bp = bfind(bname, TRUE, 0)) == NULL) /* get the needed buffer */
  1177.         return(FALSE);
  1178.  
  1179.     bp->b_mode = MDVIEW;    /* mark the buffer as read only */
  1180.     cb = curbp;        /* save the old buffer */
  1181.     curbp = bp;        /* make this one current */
  1182.     /* and try to read in the file to execute */
  1183.     if ((status = readin(fname, FALSE)) != TRUE) {
  1184.         curbp = cb;    /* restore the current buffer */
  1185.         return(status);
  1186.     }
  1187.  
  1188.     /* go execute it! */
  1189.     curbp = cb;        /* restore the current buffer */
  1190.     if ((status = dobuf(bp)) != TRUE)
  1191.         return(status);
  1192.  
  1193.     /* if not displayed, remove the now unneeded buffer and exit */
  1194.     if (bp->b_nwnd == 0)
  1195.         zotbuf(bp);
  1196.     return(TRUE);
  1197. }
  1198.