home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / VILE327.ZIP / VILE327.TAR / vile3.27 / exec.c < prev    next >
C/C++ Source or Header  |  1992-12-14  |  39KB  |  1,692 lines

  1. /*    This file is for functions dealing with execution of
  2.  *    commands, command lines, buffers, files and startup files
  3.  *
  4.  *    written 1986 by Daniel Lawrence    
  5.  *
  6.  * $Log: exec.c,v $
  7.  * Revision 1.34  1992/12/04  09:12:25  foxharp
  8.  * deleted unused assigns
  9.  *
  10.  * Revision 1.33  1992/08/20  23:40:48  foxharp
  11.  * typo fixes -- thanks, eric
  12.  *
  13.  * Revision 1.32  1992/07/16  22:06:27  foxharp
  14.  * same change for kbdmode
  15.  *
  16.  * Revision 1.31  1992/07/15  23:24:12  foxharp
  17.  * dotcmdplay() is now undoable, so don't undo individual
  18.  * commands when "dotcmdmode == PLAY"
  19.  *
  20.  * Revision 1.30  1992/07/07  08:37:48  foxharp
  21.  * set macro buffer's "dot" to first line when done storing, rather than
  22.  * leaving it at the last line.  less confusing when you bring one up for
  23.  * editing.
  24.  *
  25.  * Revision 1.29  1992/06/25  22:44:06  foxharp
  26.  * better string quoting parse, from pjr
  27.  *
  28.  * Revision 1.28  1992/05/19  08:55:44  foxharp
  29.  * more prototype and shadowed decl fixups
  30.  *
  31.  * Revision 1.27  1992/05/16  12:00:31  pgf
  32.  * prototypes/ansi/void-int stuff/microsoftC
  33.  *
  34.  * Revision 1.26  1992/04/26  13:42:33  pgf
  35.  * added names to "no such ..." messages
  36.  *
  37.  * Revision 1.25  1992/03/13  08:11:04  pgf
  38.  * allow ":0" to work by honoring ZERO flag for gomark
  39.  *
  40.  * Revision 1.24  1992/03/03  08:41:15  pgf
  41.  * took out pre_colon_pos -- now we don't move dot unless the command _wants_
  42.  * a line no. or range
  43.  *
  44.  * Revision 1.23  1992/02/17  09:01:50  pgf
  45.  * save "dot before the named colon command" so that ':' can be expanded to
  46.  * a filename correctly, as in ":e:".
  47.  * also, fixed bug in buffer exec code, and
  48.  * took out unused vars for saber
  49.  *
  50.  * Revision 1.22  1992/01/22  20:26:50  pgf
  51.  * made directive leading char consistent ('~', instead uemacs' '!')
  52.  *
  53.  * Revision 1.21  1992/01/06  23:09:05  pgf
  54.  * switch to buffer on failure in dobuf()
  55.  *
  56.  * Revision 1.20  1992/01/05  00:06:13  pgf
  57.  * split mlwrite into mlwrite/mlprompt/mlforce to make errors visible more
  58.  * often.  also normalized message appearance somewhat.
  59.  *
  60.  * Revision 1.19  1991/11/13  20:09:27  pgf
  61.  * X11 changes, from dave lemke
  62.  *
  63.  * Revision 1.18  1991/11/08  13:16:15  pgf
  64.  * added dave lemke's ":+NN" and ":-NN" commands, and made ":+++" and
  65.  * ":----" work as well
  66.  *
  67.  * Revision 1.17  1991/11/07  03:58:31  pgf
  68.  * lint cleanup
  69.  *
  70.  * Revision 1.16  1991/11/03  17:36:59  pgf
  71.  * make 0 arg work in empty buffer, as in ":0r file"
  72.  *
  73.  * Revision 1.15  1991/11/01  14:38:00  pgf
  74.  * saber cleanup
  75.  *
  76.  * Revision 1.14  1991/10/08  01:30:00  pgf
  77.  * added new bp arg to lfree and lalloc
  78.  *
  79.  * Revision 1.13  1991/08/07  12:35:07  pgf
  80.  * added RCS log messages
  81.  *
  82.  * revision 1.12
  83.  * date: 1991/08/06 15:20:45;
  84.  * global/local values
  85.  * 
  86.  * revision 1.11
  87.  * date: 1991/06/25 19:52:27;
  88.  * massive data structure restructure
  89.  * 
  90.  * revision 1.10
  91.  * date: 1991/06/20 17:22:02;
  92.  * changed comments -- ! is now ~ ???
  93.  * 
  94.  * revision 1.9
  95.  * date: 1991/06/13 15:17:46;
  96.  * added globbing and uniq'ifying to execfile names
  97.  * 
  98.  * revision 1.8
  99.  * date: 1991/06/07 13:39:06;
  100.  * cleanup
  101.  * 
  102.  * revision 1.7
  103.  * date: 1991/06/03 15:53:46;
  104.  * initialize fulllineregions and havemotion in dobuf()
  105.  * 
  106.  * revision 1.6
  107.  * date: 1991/06/03 12:17:32;
  108.  * ifdef'ed GLOBALS for unresolved ref
  109.  * 
  110.  * revision 1.5
  111.  * date: 1991/06/03 10:20:18;
  112.  * cleanup, and
  113.  * namedcmd now calls execute() directly, allowing
  114.  * removal of cfp arg to docmd
  115.  * 
  116.  * revision 1.4
  117.  * date: 1991/05/31 10:53:21;
  118.  * new namedcmd(), linespec(), and rangespec() routines to support
  119.  * line ranges on ex commands.  mostly borrowed from kirkendall's elvis
  120.  * 
  121.  * revision 1.3
  122.  * date: 1991/04/08 15:46:08;
  123.  * fixed readin() arg count
  124.  * 
  125.  * revision 1.2
  126.  * date: 1990/12/06 19:49:44;
  127.  * turn off command display during file exec
  128.  * 
  129.  * revision 1.1
  130.  * date: 1990/09/21 10:25:14;
  131.  * initial vile RCS revision
  132. */
  133.  
  134. #include    <stdio.h>
  135. #include    "estruct.h"
  136. #include    "edef.h"
  137.  
  138.  
  139. extern CMDFUNC f_gomark;
  140.  
  141. /* namedcmd:    execute a named command even if it is not bound */
  142.  
  143. int
  144. namedcmd(f, n)
  145. int f, n;
  146. {
  147.     char *fnp;    /* ptr to the name of the cmd to exec */
  148.     LINE *fromline;    /* first linespec */
  149.     LINE *toline;    /* second linespec */
  150.     char lspec[NLINE];
  151.     int cpos = 0;
  152.     int s,c,isdfl,zero;
  153.     long flags;
  154.     CMDFUNC *cfp;        /* function to execute */
  155.  
  156.     lspec[0] = '\0';
  157.     fnp = NULL;
  158.  
  159.     /* prompt the user to type a named command */
  160.     mlprompt(": ");
  161.  
  162.     /* and now get the function name to execute */
  163. #if    NeWS
  164.     newsimmediateon() ;
  165. #endif
  166.  
  167.     while(1) {
  168.         c = tgetc();
  169.         if (c == '\r' || c == '\n') {
  170.             lspec[cpos] = 0;
  171.             fnp = NULL;
  172.             break;
  173.         } else if (c == kcod2key(abortc)) {    /* Bell, abort */
  174.             lspec[0] = '\0';
  175.             return FALSE;
  176.         } else if (isbackspace(c)) {
  177.             if (cpos != 0) {
  178.                 TTputc('\b');
  179.                 TTputc(' ');
  180.                 TTputc('\b');
  181.                 --ttcol;
  182.                 --cpos;
  183.             } else {
  184.                 lspec[0] = '\0';
  185.                 lineinput = FALSE;
  186.                 return FALSE;
  187.             }
  188.  
  189.         } else if (c == kcod2key(killc)) {    /* ^U, kill */
  190.             while (cpos != 0) {
  191.                 TTputc('\b');
  192.                 TTputc(' ');
  193.                 TTputc('\b');
  194.                 --cpos;
  195.                 --ttcol;
  196.             }
  197.         } else if (islinespecchar(c) ||
  198.             /* special test for 'a style mark references */
  199.                 (cpos > 0 &&
  200.                  lspec[cpos-1] == '\'' &&
  201.                  (islower(c) || (c == '\'') )
  202.                 )
  203.              ) {
  204.             lspec[cpos++] = c;
  205.             TTputc(c);
  206.             ++ttcol;
  207.         } else {
  208.             int status;
  209.             tungetc(c);
  210.             lspec[cpos] = 0;
  211.             status = kbd_engl_stat(&fnp);
  212.             if (status == TRUE) {
  213.                 break;
  214.             } else if (status == SORTOFTRUE) {
  215.                 fnp = NULL;
  216.                 continue;
  217.             } else {
  218.                 return status;
  219.             }
  220.         }
  221.         TTflush();
  222.     }
  223.  
  224.     /* parse the accumulated lspec */
  225.     if (rangespec(lspec,&fromline,&toline,&isdfl,&zero) != TRUE) {
  226.         mlforce("[Improper line range]");
  227.         return FALSE;
  228.     }
  229.  
  230.     /* if range given, and it wasn't "0" and the buffer's empty */
  231.     if (!isdfl && !zero && is_empty_buf(curbp)) {
  232.         mlforce("[No range possible in empty buffer]", fnp);
  233.         return FALSE;
  234.     }
  235.  
  236. #if    NeWS
  237.     newsimmediateoff() ;
  238. #endif
  239.  
  240.     /* did we get a name? */
  241.     if (fnp == NULL) {
  242.         if (isdfl) { /* no range, no function */
  243.             mlforce("[No such function]");
  244.             return FALSE;
  245.         } else { /* range, no function */
  246.             cfp = &f_gomark;
  247.             fnp = "";
  248.         }
  249.     } else if ((cfp = engl2fnc(fnp)) == NULL) { /* bad function */
  250.         mlforce("[No such function \"%s\"]",fnp);
  251.         return FALSE;
  252.     }
  253.     flags = cfp->c_flags;
  254.     
  255.     /* bad arguments? */
  256. #ifdef EXRC_FILES
  257. seems like we need one more check here -- is it from a .exrc file?
  258.         cmd not ok in .exrc         empty file
  259.     if (!(flags & EXRCOK) && is_empty_buf(curbp)) {
  260.         mlforce("[Can't use the \"%s\" command in a %s file.]", 
  261.                     cmdnames[cmdidx].name, EXRC);
  262.         return FALSE;
  263.     }
  264. #endif
  265.  
  266.     /* was: if (!(flags & (ZERO | EXRCOK)) && fromline == NULL ) { */
  267.     if (zero) {
  268.         extern CMDFUNC f_lineputafter, f_opendown, f_insfile;
  269.         extern CMDFUNC f_lineputbefore, f_openup;
  270.         if (!(flags & ZERO)) {
  271.             mlforce("[Can't use address 0 with \"%s\" command]", fnp);
  272.             return FALSE;
  273.         }
  274.         /*  we're positioned at fromline == curbp->b_linep, so commands
  275.             must be willing to go _down_ from there.  Seems easiest
  276.             to special case the commands that prefer going up */
  277.         if (cfp == &f_insfile) {
  278.             /* EMPTY */ /* works okay, or acts down normally */ ;
  279.         } else if (cfp == &f_lineputafter) {
  280.             cfp = &f_lineputbefore;
  281.             fromline = lforw(fromline);
  282.         } else if (cfp == &f_opendown) {
  283.             cfp = &f_openup;
  284.             fromline = lforw(fromline);
  285.         } else if (cfp == &f_gomark) {
  286.             fromline = lforw(fromline);
  287.         } else {
  288.             mlforce("[Configuration error: ZERO]");
  289.             return FALSE;
  290.         }
  291.         flags = cfp->c_flags;
  292.         toline = fromline;
  293.     }
  294.  
  295.     /* if we're not supposed to have a line no., and the line no. isn't
  296.         the current line, and there's more than one line */
  297.     if (!(flags & FROM) && fromline != DOT.l &&
  298.             !is_empty_buf(curbp) &&
  299.           (lforw(lforw(curbp->b_line.l)) != curbp->b_line.l) ) {
  300.         mlforce("[Can't use address with \"%s\" command.]", fnp);
  301.         return FALSE;
  302.     }
  303.     /* if we're not supposed to have a second line no., and the line no. 
  304.         isn't the same as the first line no., and there's more than
  305.         one line */
  306.     if (!(flags & TO) && toline != fromline &&
  307.             !is_empty_buf(curbp) &&
  308.           (lforw(lforw(curbp->b_line.l)) != curbp->b_line.l) ) {
  309.         mlforce("[Can't use a range with \"%s\" command.]", fnp);
  310.         return FALSE;
  311.     }
  312. #ifdef NEEDED
  313.     if (!(flags & EXTRA) && *scan) {
  314.         mlforce("[Extra characters after \"%s\" command.]", 
  315.                         cmdnames[cmdidx].name);
  316.         return FALSE;
  317.     }
  318. #endif
  319. #ifdef NEEDED
  320.     if ((flags & NOSPC) && !(cmd == CMD_READ && (forceit || *scan == '!'))) {
  321.         build = scan;
  322. #ifndef CRUNCH  /* what is this for? -pgf */
  323.         if ((flags & PLUS) && *build == '+') {
  324.             while (*build && !(isspace(*build))) {
  325.                 build++;
  326.             }
  327.             while (*build && isspace(*build)) {
  328.                 build++;
  329.             }
  330.         }
  331. #endif /* not CRUNCH */
  332.         for (; *build; build++) {
  333.             if (isspace(*build)) {
  334.                 mlforce("[Too many %s to \"%s\" command.]",
  335.                     (flags & XFILE) ? "filenames" : "arguments",
  336.                     cmdnames[cmdidx].name);
  337.                 return FALSE;
  338.             }
  339.         }
  340.     }
  341. #endif /* NEEDED */
  342.  
  343.     /* some commands have special default ranges */
  344.     if (isdfl) {
  345.         if (flags & DFLALL) {
  346.             extern CMDFUNC f_operwrite, f_filewrite, f_operglobals,
  347.                     f_globals, f_opervglobals, f_vglobals;
  348.             if (cfp == &f_operwrite) {
  349.                 cfp = &f_filewrite;
  350. #if GLOBALS
  351.             } else if (cfp == &f_operglobals) {
  352.                 cfp = &f_globals;
  353.             } else if (cfp == &f_opervglobals) {
  354.                 cfp = &f_vglobals;
  355. #endif
  356.             } else {
  357.                 mlforce("[Configuration error: DFLALL]");
  358.                 return FALSE;
  359.             }
  360.         } else if (flags & DFLNONE) {
  361.             extern CMDFUNC f_operfilter, f_spawn;
  362.             if (cfp == &f_operfilter) {
  363.                 cfp = &f_spawn;
  364.                 setmark();  /* not that it matters */
  365.             } else {
  366.                 mlforce("[Configuration error: DFLNONE]");
  367.                 return FALSE;
  368.             }
  369.             fromline = toline = NULL;
  370.         }
  371.     }
  372.  
  373. #ifdef NEEDED
  374.     /* write a newline if called from visual mode */
  375.     if ((flags & NL) && !exmode /* && !exwrote */) {
  376.         TTputc('\n');
  377.         /* exrefresh(); */
  378.     }
  379. #endif
  380.  
  381.     if (toline || fromline) {  /* assume it's an absolute motion */
  382.                    /* we could probably do better */
  383.         curwp->w_lastdot = DOT;
  384.     }
  385.     if (toline && (flags & TO)) {
  386.         DOT.l = toline;
  387.         firstnonwhite(f,n);
  388.         setmark();
  389.     }
  390.     if (fromline && (flags & FROM)) {
  391.         DOT.l = fromline;
  392.         firstnonwhite(f,n);
  393.         if (!toline)
  394.             setmark();
  395.     }
  396.  
  397.     /* and then execute the command */
  398.     isnamedcmd = TRUE;
  399.     havemotion = &f_gomark;
  400.     fulllineregions = TRUE;
  401.  
  402.     s = execute(cfp,f,n);
  403.  
  404.     havemotion = NULL;
  405.     isnamedcmd = FALSE;
  406.     fulllineregions = FALSE;
  407.  
  408.     return s;
  409. }
  410.  
  411. /* parse an ex-style line spec -- code culled from elvis, file ex.c, by
  412.     Steve Kirkendall
  413. */
  414. char *
  415. linespec(s, markptr)
  416. register char    *s;        /* start of the line specifier */
  417. LINE        **markptr;    /* where to store the mark's value */
  418. {
  419.     int        num;
  420.     LINE        *lp;    /* where the linespec takes us */
  421.     register char    *t;
  422.     int        status;
  423.  
  424.     setmark();
  425.     lp = NULL;
  426.  
  427.     /* parse each ;-delimited clause of this linespec */
  428.     do
  429.     {
  430.         /* skip an initial ';', if any */
  431.         if (*s == ';')
  432.             s++;
  433.  
  434.         /* skip leading spaces */
  435.         while (isspace(*s))
  436.             s++;
  437.     
  438.         /* dot means current position */
  439.         if (*s == '.') {
  440.             s++;
  441.             lp = DOT.l;
  442.         } else if (*s == '$') { /* '$' means the last line */
  443.             s++;
  444.             status = gotoeob(TRUE,1);
  445.             if (status) lp = DOT.l;
  446.         } else if (isdigit(*s)) {
  447.             /* digit means an absolute line number */
  448.             for (num = 0; isdigit(*s); s++) {
  449.                 num = num * 10 + *s - '0';
  450.             }
  451.             status = gotoline(TRUE,num);
  452.             if (status) lp = DOT.l;
  453.         } else if (*s == '\'') {
  454.             /* apostrophe means go to a set mark */
  455.             s++;
  456.             status = gonmmark(*s);
  457.             if (status) lp = DOT.l;
  458.             s++;
  459.         } else if (*s == '+') {
  460.             s++;
  461.             for (num = 0; isdigit(*s); s++)
  462.                 num = num * 10 + *s - '0';
  463.             if (num == 0)
  464.                 num++;
  465.             while (*s == '+')
  466.                 s++, num++;
  467.             status = forwline(TRUE,num);
  468.             if (status)
  469.                 lp = DOT.l;
  470.                 } else if (*s == '-') {
  471.             s++;
  472.             for (num = 0; isdigit(*s); s++)
  473.                     num = num * 10 + *s - '0';
  474.             if (num == 0)
  475.                 num++;
  476.             while (*s == '-')
  477.                 s++, num++;
  478.             status = forwline(TRUE,-num);
  479.             if (status)
  480.                 lp = DOT.l;
  481.                 }
  482. #if PATTERNS
  483.         else if (*s == '/' || *s == '?') { /* slash means do a search */
  484.             /* put a '\0' at the end of the search pattern */
  485.             t = parseptrn(s);
  486.     
  487.             /* search for the pattern */
  488.             lp &= ~(BLKSIZE - 1);
  489.             if (*s == '/') {
  490.                 pfetch(markline(lp));
  491.                 if (plen > 0)
  492.                     lp += plen - 1;
  493.                 lp = m_fsrch(lp, s);
  494.             } else {
  495.                 lp = m_bsrch(lp, s);
  496.             }
  497.     
  498.             /* adjust command string pointer */
  499.             s = t;
  500.         }
  501. #endif
  502.     
  503.         /* if linespec was faulty, quit now */
  504.         if (!lp) {
  505.             *markptr = lp;
  506.             swapmark();
  507.             return s;
  508.         }
  509.     
  510.         /* maybe add an offset */
  511.         t = s;
  512.         if (*t == '-' || *t == '+') {
  513.             s++;
  514.             for (num = 0; *s >= '0' && *s <= '9'; s++) {
  515.                 num = num * 10 + *s - '0';
  516.             }
  517.             if (num == 0)
  518.                 num = 1;
  519.             forwline(TRUE, (*t == '+') ? num : -num);
  520.             lp = DOT.l;
  521.         }
  522.     } while (*s == ';' || *s == '+' || *s == '-');
  523.  
  524.     *markptr = lp;
  525.     swapmark();
  526.     return s;
  527. }
  528.  
  529. /* parse an ex-style line range -- code culled from elvis, file ex.c, by
  530.     Steve Kirkendall
  531. */
  532. int
  533. rangespec(specp,fromlinep,tolinep,isdefaultp,zerop)
  534. char        *specp;    /* string containing a line range */
  535. LINE        **fromlinep;    /* first linespec */
  536. LINE        **tolinep;    /* second linespec */
  537. int        *isdefaultp;
  538. int        *zerop;
  539. {
  540.     register char    *scan;        /* used to scan thru specp */
  541.     LINE        *fromline;    /* first linespec */
  542.     LINE        *toline;    /* second linespec */
  543.  
  544.     *zerop = FALSE;
  545.  
  546.     /* ignore command lines that start with a double-quote */
  547.     if (*specp == '"') {
  548.         *fromlinep = *tolinep = DOT.l;
  549.         return TRUE;
  550.     }
  551.  
  552.     /* permit extra colons at the start of the line */
  553.     while (isspace(*specp) || *specp == ':') {
  554.         specp++;
  555.     }
  556.  
  557.     /* parse the line specifier */
  558.     scan = specp;
  559.     if (*scan == '0') {
  560.         fromline = toline = curbp->b_line.l; /* _very_ top of buffer */
  561.         *zerop = TRUE;
  562.         scan++;
  563.     } else if (*scan == '%') {
  564.         /* '%' means all lines */
  565.         fromline = lforw(curbp->b_line.l);
  566.         toline = lback(curbp->b_line.l);
  567.         scan++;
  568.     } else {
  569.         scan = linespec(scan, &fromline);
  570.         if (!fromline)
  571.             fromline = DOT.l;
  572.         toline = fromline;
  573.         if (*scan == ',') {
  574.             scan++;
  575.             scan = linespec(scan, &toline);
  576.         }
  577.         if (!toline) {
  578.             /* faulty line spec -- fault already described */
  579.             dbgwrite("null toline");
  580.             return FALSE;
  581.         }
  582.     }
  583.  
  584.     if (is_empty_buf(curbp))
  585.         fromline = toline = NULL;
  586.  
  587.     *isdefaultp = (scan == specp);
  588.  
  589.     /* skip whitespace */
  590.     while (isspace(*scan))
  591.         scan++;
  592.  
  593.     if (*scan) {
  594.         dbgwrite("crud at end %s",specp);
  595.         return FALSE;
  596.     }
  597.  
  598.     *fromlinep = fromline;
  599.     *tolinep = toline;
  600.     
  601.     return TRUE;
  602. }
  603.  
  604. /* old namedcmd:    execute a named command even if it is not bound */
  605. int
  606. onamedcmd(f, n)
  607. int f, n;    /* command arguments [passed through to command executed] */
  608. {
  609.     register char *fnp;    /* ptr to the name of the cmd to exec */
  610.     char *kbd_engl();
  611.     int s;
  612.  
  613.     /* prompt the user to type a named command */
  614.     mlprompt(": ");
  615.  
  616.     /* and now get the function name to execute */
  617. #if    NeWS
  618.     newsimmediateon() ;
  619. #endif
  620.  
  621.     fnp = kbd_engl();
  622.  
  623. #if    NeWS
  624.     newsimmediateoff() ;
  625. #endif
  626.  
  627.     if (fnp == NULL) {
  628.         mlforce("[No such function]");
  629.         return FALSE;
  630.     }
  631.  
  632.     /* and then execute the command */
  633.     isnamedcmd = TRUE;
  634.     s = docmd(fnp,FALSE,f,n);
  635.     isnamedcmd = FALSE;
  636.  
  637.     return s;
  638. }
  639.  
  640. #if NEVER
  641. /*    execcmd:    Execute a command line command by name alone */
  642. int
  643. execcmd(f, n)
  644. int f, n;    /* default Flag and Numeric argument */
  645. {
  646.     register int status;        /* status return */
  647.     char cmdbuf[NSTRING];        /* string holding command to execute */
  648.  
  649.     /* get the line wanted */
  650.     cmdbuf[0] = 0;
  651.     if ((status = mlreply("cmd: ", cmdbuf, NSTRING)) != TRUE)
  652.         return status;
  653.  
  654.     execlevel = 0;
  655.     return docmd(cmdbuf,TRUE,f,n);
  656. }
  657. #endif
  658.  
  659. /*    docmd:    take a passed string as a command line and translate
  660.         it to be executed as a command. This function will be
  661.         used by execute-command-line and by all source and
  662.         startup files.
  663.  
  664.     format of the command line is:
  665.  
  666.         {# arg} <command-name> {<argument string(s)>}
  667.  
  668. */
  669.  
  670. int
  671. docmd(cline,newcle,f,n)
  672. char *cline;    /* command line to execute */
  673. int newcle;
  674. int f,n;
  675. {
  676.     int status;        /* return status of function */
  677.     int oldcle;        /* old contents of clexec flag */
  678.     char *oldestr;        /* original exec string */
  679.     char tkn[NSTRING];    /* next token off of command line */
  680.     CMDFUNC *cfp;
  681.         
  682.     /* if we are scanning and not executing..go back here */
  683.     if (execlevel)
  684.         return TRUE;
  685.  
  686.     oldestr = execstr;    /* save last ptr to string to execute */
  687.     execstr = cline;    /* and set this one as current */
  688.  
  689.     /* first set up the default command values */
  690.     if (newcle == TRUE) {
  691.         f = FALSE;
  692.         n = 1;
  693.     }
  694.  
  695.     if ((status = macarg(tkn)) != TRUE) {    /* and grab the first token */
  696.         execstr = oldestr;
  697.         return status;
  698.     }
  699.  
  700.     /* process leading argument */
  701.     if (toktyp(tkn) != TKCMD) {
  702.         f = TRUE;
  703.         strcpy(tkn, tokval(tkn));
  704.         n = atoi(tkn);
  705.  
  706.         /* and now get the command to execute */
  707.         if ((status = macarg(tkn)) != TRUE) {
  708.             execstr = oldestr;
  709.             return status;    
  710.         }    
  711.     }
  712.  
  713.     /* and match the token to see if it exists */
  714.     if ((cfp = engl2fnc(tkn)) == NULL) {
  715.         mlforce("[No such function \"%s\"]",tkn);
  716.         execstr = oldestr;
  717.         return FALSE;
  718.     }
  719.     
  720.     /* save the arguments and go execute the command */
  721.     oldcle = clexec;        /* save old clexec flag */
  722.     clexec = newcle;        /* in cline execution */
  723.     status = execute(cfp,f,n);
  724.     cmdstatus = status;        /* save the status */
  725.     clexec = oldcle;        /* restore clexec flag */
  726.     execstr = oldestr;
  727.     return status;
  728. }
  729.  
  730. /*
  731.  * This is the general command execution routine. It takes care of checking
  732.  * flags, globals, etc, to be sure we're not doing something dumb.
  733.  * Return the status of command.
  734.  */
  735.  
  736. int
  737. execute(execfunc, f, n)
  738. CMDFUNC *execfunc;        /* ptr to function to execute */
  739. int f,n;
  740. {
  741.     register int status, flags;
  742.     MARK odot;
  743.  
  744.     if (execfunc == NULL) {
  745.         TTbeep();
  746. #if REBIND
  747.         mlforce("[Key not bound]");    /* complain        */
  748. #else
  749.         mlforce("[Not a command]");    /* complain        */
  750. #endif
  751.         return FALSE;
  752.     }
  753.  
  754.     flags = execfunc->c_flags;
  755.  
  756.     /* commands following operators can't be redone or undone */
  757.     if ( !doingopcmd) {
  758.         /* don't record non-redoable cmds */
  759.         if ((flags & REDO) == 0)
  760.             dotcmdstop();
  761.         if (flags & UNDO) {
  762.             /* undoable command can't be permitted when read-only */
  763.             if (b_val(curbp,MDVIEW))
  764.                 return rdonly();
  765.             if (dotcmdmode != PLAY && kbdmode != PLAY)
  766.                 mayneedundo();
  767.         }
  768.     }
  769.  
  770.     /* if motion is absolute, remember where we are */
  771.     if (flags & ABS) {
  772.         odot = DOT;
  773.     }
  774.  
  775.     status = (execfunc->c_func)(f, n);
  776.     if ((flags & GOAL) == 0) { /* goal should not be retained */
  777.         curgoal = -1;
  778.     }
  779. #if VMALLOC
  780.     if (flags & UNDO)    /* verify malloc arena after line changers */
  781.         vverify("main");
  782. #endif
  783.  
  784.     /* if motion was absolute, and it wasn't just on behalf of an
  785.         operator, and we moved, update the "last dot" mark */
  786.     if ((flags & ABS) && !doingopcmd && !sameline(DOT, odot)) {
  787.         curwp->w_lastdot = odot;
  788.     }
  789.  
  790.  
  791.     return status;
  792. }
  793.  
  794. /* token:    chop a token off a string
  795.         return a pointer past the token
  796. */
  797.  
  798. char *
  799. token(src, tok)
  800. char *src, *tok;    /* source string, destination token string */
  801. {
  802.     register int quotef;    /* is the current string quoted? */
  803.  
  804.     /* first scan past any whitespace in the source string */
  805.     while (isspace(*src))
  806.         ++src;
  807.  
  808.  
  809.     quotef = FALSE;
  810.     /* scan through the source string */
  811.     while (*src) {
  812.         /* process special characters */
  813.         if (*src == '\\') {
  814.             ++src;
  815.             if (*src == 0)
  816.                 break;
  817.             switch (*src++) {
  818.                 case 'r':    *tok++ = '\r'; break;
  819.                 case 'n':    *tok++ = '\n'; break;
  820.                 case 't':    *tok++ = '\t';  break;
  821.                 case 'b':    *tok++ = '\b';  break;
  822.                 case 'f':    *tok++ = '\f'; break;
  823.                 default:    *tok++ = *(src-1);
  824.             }
  825.         } else {
  826.             /* check for the end of the token */
  827.             if (quotef) {
  828.                 if (*src == '"')
  829.                     break;
  830.             } else {
  831.                 if (*src == ' ' || *src == '\t')
  832.                     break;
  833.             }
  834.  
  835.             /* set quote mode if qoute found */
  836.             if (*src == '"')
  837.                 quotef = TRUE;
  838.  
  839.             /* record the character */
  840.             *tok++ = *src++;
  841.         }
  842.     }
  843.  
  844.     /* terminate the token and exit */
  845.     if (*src)
  846.         ++src;
  847.     *tok = 0;
  848.     return src;
  849. }
  850.  
  851. int
  852. macarg(tok)    /* get a macro line argument */
  853. char *tok;    /* buffer to place argument */
  854. {
  855.     int savcle;    /* buffer to store original clexec */
  856.  
  857.     savcle = clexec;    /* save execution mode */
  858.     clexec = TRUE;        /* get the argument */
  859.     /* grab token and advance past */
  860.     execstr = token(execstr, tok);
  861.     /* evaluate it */
  862.     strcpy(tok, tokval(tok));
  863.     clexec = savcle;    /* restore execution mode */
  864.     return TRUE;
  865. }
  866.  
  867. /*    nextarg:    get the next argument    */
  868.  
  869. int
  870. nextarg(buffer)
  871. char *buffer;        /* buffer to put token into */
  872. {
  873.     /* grab token and advance past */
  874.     execstr = token(execstr, buffer);
  875.     /* evaluate it */
  876.     strcpy(buffer, tokval(buffer));
  877.     return TRUE;
  878. }
  879.  
  880. /*    storemac:    Set up a macro buffer and flag to store all
  881.             executed command lines there            */
  882.  
  883. int
  884. storemac(f, n)
  885. int f;        /* default flag */
  886. int n;        /* macro number to use */
  887. {
  888.     register struct BUFFER *bp;    /* pointer to macro buffer */
  889.     char bname[NBUFN];        /* name of buffer to use */
  890.  
  891.     /* must have a numeric argument to this function */
  892.     if (f == FALSE) {
  893.         mlforce("[No macro specified]");
  894.         return FALSE;
  895.     }
  896.  
  897.     /* range check the macro number */
  898.     if (n < 1 || n > 40) {
  899.         mlforce("[Macro number out of range]");
  900.         return FALSE;
  901.     }
  902.  
  903.     /* construct the macro buffer name */
  904.     strcpy(bname, "[Macro xx]");
  905.     bname[7] = '0' + (n / 10);
  906.     bname[8] = '0' + (n % 10);
  907.  
  908.     /* set up the new macro buffer */
  909.     if ((bp = bfind(bname, OK_CREAT, BFINVS)) == NULL) {
  910.         mlforce("[Cannot create macro]");
  911.         return FALSE;
  912.     }
  913.  
  914.     /* and make sure it is empty */
  915.     bclear(bp);
  916.     bp->b_flag &= ~BFCHG;
  917.     bp->b_active = TRUE;
  918.     make_local_b_val(bp,MDVIEW);
  919.     set_b_val(bp,MDVIEW,TRUE);
  920.     make_local_b_val(bp,VAL_TAB);
  921.     set_b_val(bp,VAL_TAB,8);
  922.     make_local_b_val(bp,MDDOS);
  923.     set_b_val(bp,MDDOS,FALSE);
  924.  
  925.     /* and set the macro store pointers to it */
  926.     mstore = TRUE;
  927.     bstore = bp;
  928.     return TRUE;
  929. }
  930.  
  931. #if    PROC
  932. /*    storeproc:    Set up a procedure buffer and flag to store all
  933.             executed command lines there            */
  934.  
  935. int
  936. storeproc(f, n)
  937. int f;        /* default flag */
  938. int n;        /* macro number to use */
  939. {
  940.     register struct BUFFER *bp;    /* pointer to macro buffer */
  941.     register int status;        /* return status */
  942.     char bname[NBUFN];        /* name of buffer to use */
  943.  
  944.     /* a numeric argument means its a numbered macro */
  945.     if (f == TRUE)
  946.         return storemac(f, n);
  947.  
  948.     /* get the name of the procedure */
  949.     bname[1] = 0;
  950.         if ((status = mlreply("Procedure name: ", &bname[1], NBUFN-2)) != TRUE)
  951.                 return status;
  952.  
  953.     /* construct the macro buffer name */
  954.     bname[0] = '[';
  955.     strcat(bname, "]");
  956.  
  957.     /* set up the new macro buffer */
  958.     if ((bp = bfind(bname, OK_CREAT, BFINVS)) == NULL) {
  959.         mlforce("[Cannot create macro]");
  960.         return FALSE;
  961.     }
  962.  
  963.     /* and make sure it is empty */
  964.     bclear(bp);
  965.  
  966.     /* and set the macro store pointers to it */
  967.     mstore = TRUE;
  968.     bstore = bp;
  969.     return TRUE;
  970. }
  971.  
  972. /*    execproc:    Execute a procedure                */
  973.  
  974. int
  975. execproc(f, n)
  976. int f, n;    /* default flag and numeric arg */
  977. {
  978.         register BUFFER *bp;        /* ptr to buffer to execute */
  979.         register int status;        /* status return */
  980.         static char obufn[NBUFN+2];        /* name of buffer to execute */
  981.         char bufn[NBUFN+2];        /* name of buffer to execute */
  982.  
  983.     /* find out what buffer the user wants to execute */
  984.         if ((status = mlreply("Execute procedure: ", obufn, NBUFN)) != TRUE)
  985.                 return status;
  986.  
  987.     /* construct the buffer name */
  988.     bufn[0] = '[';
  989.     strcpy(&bufn[1], obufn);
  990.     strcat(bufn, "]");
  991.  
  992.     /* find the pointer to that buffer */
  993.         if ((bp=bfind(bufn, NO_CREAT, 0)) == NULL) {
  994.         mlforce("[No such procedure \"%s\"]",bufn);
  995.                 return FALSE;
  996.         }
  997.  
  998.     if (!f)
  999.         n = 1;
  1000.  
  1001.     /* and now execute it as asked */
  1002.     while (n-- > 0) {
  1003.         if ((status = dobuf(bp)) != TRUE)
  1004.             return status;
  1005.     }
  1006.     return TRUE;
  1007. }
  1008. #endif
  1009.  
  1010. #if ! SMALLER
  1011. /*    execbuf:    Execute the contents of a buffer of commands    */
  1012.  
  1013. int
  1014. execbuf(f, n)
  1015. int f, n;    /* default flag and numeric arg */
  1016. {
  1017.         register BUFFER *bp;        /* ptr to buffer to execute */
  1018.         register int status;        /* status return */
  1019.         static char bufn[NSTRING];        /* name of buffer to execute */
  1020.  
  1021.     if (!f)
  1022.         n = 1;
  1023.  
  1024.     /* find out what buffer the user wants to execute */
  1025.         if ((status = mlreply("Execute buffer: ", bufn, NBUFN)) != TRUE)
  1026.                 return status;
  1027.  
  1028.     /* find the pointer to that buffer */
  1029.         if ((bp=bfind(bufn, NO_CREAT, 0)) == NULL) {
  1030.         mlforce("[No such buffer \"%s\"]",bufn);
  1031.                 return FALSE;
  1032.         }
  1033.  
  1034.     /* and now execute it as asked */
  1035.     while (n-- > 0) {
  1036.         if ((status = dobuf(bp)) != TRUE)
  1037.             return status;
  1038.     }
  1039.     return TRUE;
  1040. }
  1041. #endif
  1042.  
  1043. /*    dobuf:    execute the contents of the buffer pointed to
  1044.         by the passed BP
  1045.  
  1046.     Directives start with a "~" and include:
  1047.  
  1048. #if SMALLER
  1049.     ~endm        End a macro
  1050. #else
  1051.     ~endm        End a macro
  1052.     ~if (cond)    conditional execution
  1053.     ~else
  1054.     ~endif
  1055.     ~return        Return (terminating current macro)
  1056.     ~goto <label>    Jump to a label in the current macro
  1057.     ~force        Force macro to continue...even if command fails
  1058.     ~while (cond)    Execute a loop if the condition is true
  1059.     ~endwhile
  1060.     
  1061.     Line Labels begin with a "*" as the first nonblank char, like:
  1062.  
  1063.     *LBL01
  1064. #endif
  1065.  
  1066. */
  1067.  
  1068. void
  1069. freewhile(wp)    /* free a list of while block pointers */
  1070. WHBLOCK *wp;    /* head of structure to free */
  1071. {
  1072.     if (wp == NULL)
  1073.         return;
  1074.     if (wp->w_next)
  1075.         freewhile(wp->w_next);
  1076.     free((char *)wp);
  1077. }
  1078.  
  1079. #define DIRECTIVE_CHAR '~'
  1080.  
  1081. int
  1082. dobuf(bp)
  1083. BUFFER *bp;    /* buffer to execute */
  1084. {
  1085.         register int status;    /* status return */
  1086.     register LINE *lp;    /* pointer to line to execute */
  1087.     register LINE *hlp;    /* pointer to line header */
  1088.     LINE *mp;        /* Macro line storage temp */
  1089.     int dirnum;        /* directive index */
  1090.     int linlen;        /* length of line to execute */
  1091.     int i;            /* index */
  1092.     int force;        /* force TRUE result? */
  1093.     WINDOW *wp;        /* ptr to windows to scan */
  1094.     WHBLOCK *whlist;    /* ptr to WHILE list */
  1095.     char *einit;        /* initial value of eline */
  1096.     char *eline;        /* text of line to execute */
  1097. #if ! SMALLER
  1098.     WHBLOCK *scanpt;    /* ptr during scan */
  1099.     register LINE *glp;    /* line to goto */
  1100.     WHBLOCK *whtemp;    /* temporary ptr to a WHBLOCK */
  1101.     char tkn[NSTRING];    /* buffer to evaluate an expression in */
  1102. #endif
  1103.  
  1104.     static dobufnesting;
  1105.  
  1106.     if (++dobufnesting > 9) {
  1107.         dobufnesting--;
  1108.         return FALSE;
  1109.     }
  1110.         
  1111.  
  1112.     /* clear IF level flags/while ptr */
  1113.     execlevel = 0;
  1114.     whlist = NULL;
  1115.     fulllineregions = FALSE;
  1116.     havemotion = FALSE;
  1117.  
  1118. #if ! SMALLER
  1119.     scanpt = NULL;
  1120.     /* scan the buffer to execute, building WHILE header blocks */
  1121.     hlp = bp->b_line.l;
  1122.     lp = hlp->l_fp;
  1123.     bp->b_dot.o = 0;
  1124.     while (lp != hlp) {
  1125.         bp->b_dot.l = lp;
  1126.         /* scan the current line */
  1127.         eline = lp->l_text;
  1128.         i = lp->l_used;
  1129.  
  1130.         /* trim leading whitespace */
  1131.         while (i-- > 0 && (*eline == ' ' || *eline == '\t'))
  1132.             ++eline;
  1133.  
  1134.         /* if theres nothing here, don't bother */
  1135.         if (i <= 0)
  1136.             goto nxtscan;
  1137.  
  1138.         /* if is a while directive, make a block... */
  1139.         if (eline[0] == DIRECTIVE_CHAR && eline[1] == 'w' && eline[2] == 'h') {
  1140.             whtemp = (WHBLOCK *)malloc(sizeof(WHBLOCK));
  1141.             if (whtemp == NULL) {
  1142. noram:                mlforce("[Out of memory during while scan]");
  1143. failexit:            freewhile(scanpt);
  1144.                 freewhile(whlist);
  1145.                 mstore = FALSE;
  1146.                 dobufnesting--;
  1147.                 return FALSE;
  1148.             }
  1149.             whtemp->w_begin = lp;
  1150.             whtemp->w_type = BTWHILE;
  1151.             whtemp->w_next = scanpt;
  1152.             scanpt = whtemp;
  1153.         }
  1154.  
  1155.         /* if is a BREAK directive, make a block... */
  1156.         if (eline[0] == DIRECTIVE_CHAR && eline[1] == 'b' && eline[2] == 'r') {
  1157.             if (scanpt == NULL) {
  1158.                 mlforce("[BREAK outside of any WHILE loop]");
  1159.                 goto failexit;
  1160.             }
  1161.             whtemp = (WHBLOCK *)malloc(sizeof(WHBLOCK));
  1162.             if (whtemp == NULL)
  1163.                 goto noram;
  1164.             whtemp->w_begin = lp;
  1165.             whtemp->w_type = BTBREAK;
  1166.             whtemp->w_next = scanpt;
  1167.             scanpt = whtemp;
  1168.         }
  1169.  
  1170.         /* if it is an endwhile directive, record the spot... */
  1171.         if (eline[0] == DIRECTIVE_CHAR && strncmp(&eline[1], "endw", 4) == 0) {
  1172.             if (scanpt == NULL) {
  1173.                 mlforce("[ENDWHILE with no preceding WHILE in '%s']",
  1174.                     bp->b_bname);
  1175.                 goto failexit;
  1176.             }
  1177.             /* move top records from the scanpt list to the
  1178.                whlist until we have moved all BREAK records
  1179.                and one WHILE record */
  1180.             do {
  1181.                 scanpt->w_end = lp;
  1182.                 whtemp = whlist;
  1183.                 whlist = scanpt;
  1184.                 scanpt = scanpt->w_next;
  1185.                 whlist->w_next = whtemp;
  1186.             } while (whlist->w_type == BTBREAK);
  1187.         }
  1188.  
  1189. nxtscan:    /* on to the next line */
  1190.         lp = lp->l_fp;
  1191.     }
  1192.  
  1193.     /* while and endwhile should match! */
  1194.     if (scanpt != NULL) {
  1195.         mlforce("[WHILE with no matching ENDWHILE in '%s']",
  1196.             bp->b_bname);
  1197.         goto failexit;
  1198.     }
  1199. #endif
  1200.  
  1201.     /* starting at the beginning of the buffer */
  1202.     hlp = bp->b_line.l;
  1203.     lp = hlp->l_fp;
  1204.     while (lp != hlp) {
  1205.         bp->b_dot.l = lp;
  1206.         /* allocate eline and copy macro line to it */
  1207.         linlen = lp->l_used;
  1208.         if ((einit = eline = malloc(linlen+1)) == NULL) {
  1209.             mlforce("[Out of Memory during macro execution]");
  1210.             freewhile(whlist);
  1211.             mstore = FALSE;
  1212.             dobufnesting--;
  1213.             return FALSE;
  1214.         }
  1215.  
  1216.         /* pjr - must check for empty line before copy */
  1217.         if (linlen)
  1218.             strncpy(eline, lp->l_text, linlen);
  1219.         eline[linlen] = 0;    /* make sure it ends */
  1220.  
  1221.         /* trim leading whitespace */
  1222.         while (*eline == ' ' || *eline == '\t')
  1223.             ++eline;
  1224.  
  1225.         /* dump comments and blank lines */
  1226.         if (*eline == ';' || *eline == 0)
  1227.             goto onward;
  1228.  
  1229. #if    DEBUGM
  1230.         /* if $debug == TRUE, every line to execute
  1231.            gets echoed and a key needs to be pressed to continue
  1232.            ^G will abort the command */
  1233.     
  1234.         if (macbug) {
  1235.             strcpy(outline, "<<<");
  1236.     
  1237.             /* debug macro name */
  1238.             strcat(outline, bp->b_bname);
  1239.             strcat(outline, ":");
  1240.     
  1241.             /* debug if levels */
  1242.             strcat(outline, l_itoa(execlevel));
  1243.             strcat(outline, ":");
  1244.  
  1245.             /* and lastly the line */
  1246.             strcat(outline, eline);
  1247.             strcat(outline, ">>>");
  1248.     
  1249.             /* write out the debug line */
  1250.             mlforce("%s",outline);
  1251.             update(TRUE);
  1252.     
  1253.             /* and get the keystroke */
  1254.             if (kbd_key() == abortc) {
  1255.                 mlforce("[Macro aborted]");
  1256.                 freewhile(whlist);
  1257.                 mstore = FALSE;
  1258.                 dobufnesting--;
  1259.                 return FALSE;
  1260.             }
  1261.         }
  1262. #endif
  1263.  
  1264.         /* Parse directives here.... */
  1265.         dirnum = -1;
  1266.         if (*eline == DIRECTIVE_CHAR) {
  1267.             /* Find out which directive this is */
  1268.             ++eline;
  1269.             for (dirnum = 0; dirnum < NUMDIRS; dirnum++)
  1270.                 if (strncmp(eline, dname[dirnum],
  1271.                             strlen(dname[dirnum])) == 0)
  1272.                     break;
  1273.  
  1274.             /* and bitch if it's illegal */
  1275.             if (dirnum == NUMDIRS) {
  1276.                 mlforce("[Unknown directive \"%s\"]", eline);
  1277.                 freewhile(whlist);
  1278.                 mstore = FALSE;
  1279.                 dobufnesting--;
  1280.                 return FALSE;
  1281.             }
  1282.  
  1283.             /* service only the ENDM macro here */
  1284.             if (dirnum == DENDM) {
  1285.                 mstore = FALSE;
  1286.                 bstore->b_dot.l = lforw(bstore->b_line.l);
  1287.                 bstore->b_dot.o = 0;
  1288.                 bstore = NULL;
  1289.                 goto onward;
  1290.             }
  1291.  
  1292.             /* restore the original eline....*/
  1293.             --eline;
  1294.         }
  1295.  
  1296.         /* if macro store is on, just salt this away */
  1297.         if (mstore) {
  1298.             /* allocate the space for the line */
  1299.             linlen = strlen(eline);
  1300.             if ((mp=lalloc(linlen,bstore)) == NULL) {
  1301.                 mlforce("[Out of memory while storing macro]");
  1302.                 mstore = FALSE;
  1303.                 dobufnesting--;
  1304.                 return FALSE;
  1305.             }
  1306.     
  1307.             /* copy the text into the new line */
  1308.             for (i=0; i<linlen; ++i)
  1309.                 lputc(mp, i, eline[i]);
  1310.     
  1311.             /* attach the line to the end of the buffer */
  1312.                    bstore->b_line.l->l_bp->l_fp = mp;
  1313.             mp->l_bp = bstore->b_line.l->l_bp;
  1314.             bstore->b_line.l->l_bp = mp;
  1315.             mp->l_fp = bstore->b_line.l;
  1316.             goto onward;
  1317.         }
  1318.     
  1319.  
  1320.         force = FALSE;
  1321.  
  1322.         /* dump comments */
  1323.         if (*eline == '*')
  1324.             goto onward;
  1325.  
  1326. #if ! SMALLER
  1327.         /* now, execute directives */
  1328.         if (dirnum != -1) {
  1329.             /* skip past the directive */
  1330.             while (*eline && *eline != ' ' && *eline != '\t')
  1331.                 ++eline;
  1332.             execstr = eline;
  1333.  
  1334.             switch (dirnum) {
  1335.             case DIF:    /* IF directive */
  1336.                 /* grab the value of the logical exp */
  1337.                 if (execlevel == 0) {
  1338.                     if (macarg(tkn) != TRUE)
  1339.                         goto eexec;
  1340.                     if (stol(tkn) == FALSE)
  1341.                         ++execlevel;
  1342.                 } else
  1343.                     ++execlevel;
  1344.                 goto onward;
  1345.  
  1346.             case DWHILE:    /* WHILE directive */
  1347.                 /* grab the value of the logical exp */
  1348.                 if (execlevel == 0) {
  1349.                     if (macarg(tkn) != TRUE)
  1350.                         goto eexec;
  1351.                     if (stol(tkn) == TRUE)
  1352.                         goto onward;
  1353.                 }
  1354.                 /* drop down and act just like BREAK */
  1355.  
  1356.             case DBREAK:    /* BREAK directive */
  1357.                 if (dirnum == DBREAK && execlevel)
  1358.                     goto onward;
  1359.  
  1360.                 /* jump down to the endwhile */
  1361.                 /* find the right while loop */
  1362.                 whtemp = whlist;
  1363.                 while (whtemp) {
  1364.                     if (whtemp->w_begin == lp)
  1365.                         break;
  1366.                     whtemp = whtemp->w_next;
  1367.                 }
  1368.             
  1369.                 if (whtemp == NULL) {
  1370.                     mlforce("[WHILE loop error]");
  1371.                     freewhile(whlist);
  1372.                     mstore = FALSE;
  1373.                     dobufnesting--;
  1374.                     return FALSE;
  1375.                 }
  1376.             
  1377.                 /* reset the line pointer back.. */
  1378.                 lp = whtemp->w_end;
  1379.                 goto onward;
  1380.  
  1381.             case DELSE:    /* ELSE directive */
  1382.                 if (execlevel == 1)
  1383.                     --execlevel;
  1384.                 else if (execlevel == 0 )
  1385.                     ++execlevel;
  1386.                 goto onward;
  1387.  
  1388.             case DENDIF:    /* ENDIF directive */
  1389.                 if (execlevel)
  1390.                     --execlevel;
  1391.                 goto onward;
  1392.  
  1393.             case DGOTO:    /* GOTO directive */
  1394.                 /* .....only if we are currently executing */
  1395.                 if (execlevel == 0) {
  1396.  
  1397.                     /* grab label to jump to */
  1398.                     (void) token(eline, golabel);
  1399.                     linlen = strlen(golabel);
  1400.                     glp = hlp->l_fp;
  1401.                     while (glp != hlp) {
  1402.                         if (*glp->l_text == '*' &&
  1403.                             (strncmp(&glp->l_text[1], golabel,
  1404.                                     linlen) == 0)) {
  1405.                             lp = glp;
  1406.                             goto onward;
  1407.                         }
  1408.                         glp = glp->l_fp;
  1409.                     }
  1410.                     mlforce("[No such label \"%s\"]", golabel);
  1411.                     freewhile(whlist);
  1412.                     mstore = FALSE;
  1413.                     dobufnesting--;
  1414.                     return FALSE;
  1415.                 }
  1416.                 goto onward;
  1417.     
  1418.             case DRETURN:    /* RETURN directive */
  1419.                 if (execlevel == 0)
  1420.                     goto eexec;
  1421.                 goto onward;
  1422.  
  1423.             case DENDWHILE:    /* ENDWHILE directive */
  1424.                 if (execlevel) {
  1425.                     --execlevel;
  1426.                     goto onward;
  1427.                 } else {
  1428.                     /* find the right while loop */
  1429.                     whtemp = whlist;
  1430.                     while (whtemp) {
  1431.                         if (whtemp->w_type == BTWHILE &&
  1432.                             whtemp->w_end == lp)
  1433.                             break;
  1434.                         whtemp = whtemp->w_next;
  1435.                     }
  1436.         
  1437.                     if (whtemp == NULL) {
  1438.                         mlforce("[Internal While loop error]");
  1439.                         freewhile(whlist);
  1440.                         mstore = FALSE;
  1441.                         dobufnesting--;
  1442.                         return FALSE;
  1443.                     }
  1444.         
  1445.                     /* reset the line pointer back.. */
  1446.                     lp = whtemp->w_begin->l_bp;
  1447.                     goto onward;
  1448.                 }
  1449.  
  1450.             case DFORCE:    /* FORCE directive */
  1451.                 force = TRUE;
  1452.  
  1453.             }
  1454.         }
  1455. #endif
  1456.  
  1457.         /* execute the statement */
  1458.         status = docmd(eline,TRUE,FALSE,1);
  1459.         if (force)        /* force the status */
  1460.             status = TRUE;
  1461.  
  1462.         /* check for a command error */
  1463.         if (status != TRUE) {
  1464.             /* look if buffer is showing */
  1465.             wp = wheadp;
  1466.             while (wp != NULL) {
  1467.                 if (wp->w_bufp == bp) {
  1468.                     /* and point it */
  1469.                     wp->w_dot.l = lp;
  1470.                     wp->w_dot.o = 0;
  1471.                     wp->w_flag |= WFHARD;
  1472.                 }
  1473.                 wp = wp->w_wndp;
  1474.             }
  1475.             /* in any case set the buffer's dot */
  1476.             bp->b_dot.l = lp;
  1477.             bp->b_dot.o = 0;
  1478.             bp->b_wline.l = lforw(bp->b_line.l);
  1479.             free(einit);
  1480.             execlevel = 0;
  1481.             mstore = FALSE;
  1482.             freewhile(whlist);
  1483.             dobufnesting--;
  1484.             swbuffer(bp);
  1485.             return status;
  1486.         }
  1487.  
  1488. onward:        /* on to the next line */
  1489.         free(einit);
  1490.         lp = lp->l_fp;
  1491.     }
  1492.  
  1493. #if ! SMALLER
  1494. eexec:    /* exit the current function */
  1495. #endif
  1496.     mstore = FALSE;
  1497.     execlevel = 0;
  1498.     freewhile(whlist);
  1499.     dobufnesting--;
  1500.         return TRUE;
  1501. }
  1502.  
  1503.  
  1504. #if ! SMALLER
  1505. /* ARGSUSED */
  1506. int
  1507. execfile(f, n)    /* execute a series of commands in a file */
  1508. int f, n;    /* default flag and numeric arg to pass on to file */
  1509. {
  1510.     register int status;    /* return status of name query */
  1511.     static char ofname[NSTRING];    /* name of file to execute */
  1512.     char fname[NSTRING];    /* name of file to execute */
  1513.     char *fspec;        /* full file spec */
  1514.  
  1515.     if ((status = mlreply("File to execute: ", ofname, NSTRING -1)) != TRUE)
  1516.         return status;
  1517.     strcpy(fname,ofname);
  1518.  
  1519.     if ((status = glob(fname)) != TRUE)
  1520.         return status;
  1521.  
  1522.     /* look up the path for the file */
  1523.     fspec = flook(fname, FL_ANYWHERE);
  1524.  
  1525.     /* if it isn't around */
  1526.     if (fspec == NULL)
  1527.         return FALSE;
  1528.  
  1529.     /* otherwise, execute it */
  1530.     while (n-- > 0)
  1531.         if ((status=dofile(fspec)) != TRUE)
  1532.             return status;
  1533.  
  1534.     return TRUE;
  1535. }
  1536. #endif
  1537.  
  1538. /*    dofile:    yank a file into a buffer and execute it
  1539.         if there are no errors, delete the buffer on exit */
  1540.  
  1541. int
  1542. dofile(fname)
  1543. char *fname;    /* file name to execute */
  1544. {
  1545.     register BUFFER *bp;    /* buffer to place file to execute */
  1546.     register int status;    /* results of various calls */
  1547.     register int odiscmd;
  1548.     char bname[NBUFN];    /* name of buffer */
  1549.  
  1550.     makename(bname, fname);        /* derive the name of the buffer */
  1551.     unqname(bname,FALSE);
  1552.  
  1553.     /* okay, we've got a unique name -- create it */
  1554.     if ((bp=bfind(bname, OK_CREAT, 0))==NULL) {
  1555.         return FALSE;
  1556.     }
  1557.  
  1558.     /* mark the buffer as read only */
  1559.     make_local_b_val(bp,MDVIEW);
  1560.     set_b_val(bp,MDVIEW,TRUE);
  1561.  
  1562.     /* and try to read in the file to execute */
  1563.     if ((status = readin(fname, FALSE, bp, TRUE)) != TRUE) {
  1564.         return status;
  1565.     }
  1566.  
  1567.     /* go execute it! */
  1568.     odiscmd = discmd;
  1569.     discmd = FALSE;
  1570.     status = dobuf(bp);
  1571.     discmd = odiscmd;
  1572.     if (status != TRUE)
  1573.         return status;
  1574.  
  1575.     /* if not displayed, remove the now unneeded buffer and exit */
  1576.     if (bp->b_nwnd == 0)
  1577.         zotbuf(bp);
  1578.     return TRUE;
  1579. }
  1580.  
  1581. /*    cbuf:    Execute the contents of a numbered buffer    */
  1582.  
  1583. int
  1584. cbuf(f, n, bufnum)
  1585. int f, n;    /* default flag and numeric arg */
  1586. int bufnum;    /* number of buffer to execute */
  1587. {
  1588.         register BUFFER *bp;        /* ptr to buffer to execute */
  1589.         register int status;        /* status return */
  1590.     static char bufname[] = "[Macro xx]";
  1591.  
  1592.     if (!f) n = 1;
  1593.  
  1594.     /* make the buffer name */
  1595.     bufname[7] = '0' + (bufnum / 10);
  1596.     bufname[8] = '0' + (bufnum % 10);
  1597.  
  1598.     /* find the pointer to that buffer */
  1599.         if ((bp=bfind(bufname, NO_CREAT, 0)) == NULL) {
  1600.             mlforce("[Macro not defined]");
  1601.                 return FALSE;
  1602.         }
  1603.  
  1604.     /* and now execute it as asked */
  1605.     while (n-- > 0)
  1606.         if ((status = dobuf(bp)) != TRUE)
  1607.             return status;
  1608.     return TRUE;
  1609. }
  1610.  
  1611. int cbuf1(f, n) int f,n; { return cbuf(f, n, 1); }
  1612.  
  1613. int cbuf2(f, n) int f,n; { return cbuf(f, n, 2); }
  1614.  
  1615. int cbuf3(f, n) int f,n; { return cbuf(f, n, 3); }
  1616.  
  1617. int cbuf4(f, n) int f,n; { return cbuf(f, n, 4); }
  1618.  
  1619. int cbuf5(f, n) int f,n; { return cbuf(f, n, 5); }
  1620.  
  1621. int cbuf6(f, n) int f,n; { return cbuf(f, n, 6); }
  1622.  
  1623. int cbuf7(f, n) int f,n; { return cbuf(f, n, 7); }
  1624.  
  1625. int cbuf8(f, n) int f,n; { return cbuf(f, n, 8); }
  1626.  
  1627. int cbuf9(f, n) int f,n; { return cbuf(f, n, 9); }
  1628.  
  1629. int cbuf10(f, n) int f,n; { return cbuf(f, n, 10); }
  1630.  
  1631. int cbuf11(f, n) int f,n; { return cbuf(f, n, 11); }
  1632.  
  1633. int cbuf12(f, n) int f,n; { return cbuf(f, n, 12); }
  1634.  
  1635. int cbuf13(f, n) int f,n; { return cbuf(f, n, 13); }
  1636.  
  1637. int cbuf14(f, n) int f,n; { return cbuf(f, n, 14); }
  1638.  
  1639. int cbuf15(f, n) int f,n; { return cbuf(f, n, 15); }
  1640.  
  1641. int cbuf16(f, n) int f,n; { return cbuf(f, n, 16); }
  1642.  
  1643. int cbuf17(f, n) int f,n; { return cbuf(f, n, 17); }
  1644.  
  1645. int cbuf18(f, n) int f,n; { return cbuf(f, n, 18); }
  1646.  
  1647. int cbuf19(f, n) int f,n; { return cbuf(f, n, 19); }
  1648.  
  1649. int cbuf20(f, n) int f,n; { return cbuf(f, n, 20); }
  1650.  
  1651. int cbuf21(f, n) int f,n; { return cbuf(f, n, 21); }
  1652.  
  1653. int cbuf22(f, n) int f,n; { return cbuf(f, n, 22); }
  1654.  
  1655. int cbuf23(f, n) int f,n; { return cbuf(f, n, 23); }
  1656.  
  1657. int cbuf24(f, n) int f,n; { return cbuf(f, n, 24); }
  1658.  
  1659. int cbuf25(f, n) int f,n; { return cbuf(f, n, 25); }
  1660.  
  1661. int cbuf26(f, n) int f,n; { return cbuf(f, n, 26); }
  1662.  
  1663. int cbuf27(f, n) int f,n; { return cbuf(f, n, 27); }
  1664.  
  1665. int cbuf28(f, n) int f,n; { return cbuf(f, n, 28); }
  1666.  
  1667. int cbuf29(f, n) int f,n; { return cbuf(f, n, 29); }
  1668.  
  1669. int cbuf30(f, n) int f,n; { return cbuf(f, n, 30); }
  1670.  
  1671. int cbuf31(f, n) int f,n; { return cbuf(f, n, 31); }
  1672.  
  1673. int cbuf32(f, n) int f,n; { return cbuf(f, n, 32); }
  1674.  
  1675. int cbuf33(f, n) int f,n; { return cbuf(f, n, 33); }
  1676.  
  1677. int cbuf34(f, n) int f,n; { return cbuf(f, n, 34); }
  1678.  
  1679. int cbuf35(f, n) int f,n; { return cbuf(f, n, 35); }
  1680.  
  1681. int cbuf36(f, n) int f,n; { return cbuf(f, n, 36); }
  1682.  
  1683. int cbuf37(f, n) int f,n; { return cbuf(f, n, 37); }
  1684.  
  1685. int cbuf38(f, n) int f,n; { return cbuf(f, n, 38); }
  1686.  
  1687. int cbuf39(f, n) int f,n; { return cbuf(f, n, 39); }
  1688.  
  1689. int cbuf40(f, n) int f,n; { return cbuf(f, n, 40); }
  1690.  
  1691.  
  1692.