home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / exec.c < prev    next >
C/C++ Source or Header  |  1998-09-01  |  48KB  |  2,124 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.  *    much modified since then.  assign no blame to him.  -pgf
  6.  *
  7.  * $Header: /usr/build/vile/vile/RCS/exec.c,v 1.164 1998/09/01 10:12:45 tom Exp $
  8.  *
  9.  */
  10.  
  11. #include    "estruct.h"
  12. #include    "edef.h"
  13. #include    "nefunc.h"
  14.  
  15. /*    Directive definitions    */
  16.  
  17. typedef    enum {
  18.     D_UNKNOWN,
  19. #if ! SMALLER
  20.     D_IF,
  21.     D_ELSEIF,
  22.     D_ELSE,
  23.     D_ENDIF,
  24.     D_GOTO,
  25.     D_RETURN,
  26.     D_WHILE,
  27.     D_ENDWHILE,
  28.     D_BREAK,
  29.     D_FORCE,
  30. #endif
  31.     D_ENDM
  32. } DIRECTIVE;
  33.  
  34. #define isSPorTAB(c) ((c) == ' ' || (c) == '\t')
  35.  
  36. static    int    rangespec(const char *specp, LINEPTR *fromlinep, LINEPTR *tolinep, CMDFLAGS *flagp);
  37.  
  38. /*    The !WHILE directive in the execution language needs to
  39.     stack references to pending whiles. These are stored linked
  40.     to each currently open procedure via a linked list of
  41.     the following structure
  42. */
  43.  
  44. typedef struct WHBLOCK {
  45.     LINEPTR    w_begin;    /* ptr to !while statement */
  46.     LINEPTR    w_end;        /* ptr to the !endwhile statement*/
  47.     DIRECTIVE w_type;    /* block type */
  48.     struct WHBLOCK *w_next;    /* next while */
  49. } WHBLOCK;
  50.  
  51. /* directive name table:
  52.     This holds the names of all the directives....    */
  53.  
  54. #if !SMALLER
  55. static    const struct {
  56.         const DIRECTIVE type;
  57.         const char *const name;
  58.     } dname[] = {
  59.     { D_IF,       "if" },
  60.     { D_ELSEIF,   "elseif" },
  61.     { D_ELSE,     "else" },
  62.     { D_ENDIF,    "endif" },
  63.     { D_GOTO,     "goto" },
  64.     { D_RETURN,   "return" },
  65.     { D_WHILE,    "while" },
  66.     { D_ENDWHILE, "endwhile" },
  67.     { D_BREAK,    "break" },
  68.     { D_FORCE,    "force" },
  69.     { D_ENDM,     "endm" }
  70.     };
  71. #endif
  72.  
  73. static int token_ended_line;  /* did the last token end at end of line? */
  74.  
  75. typedef struct {
  76.     int disabled;        /* nonzero to disable execution */
  77.     int level;        /* ~if ... ~endif nesting level */
  78.     int fired;        /* at top-level, ~if/... used */
  79.     } IFSTK;
  80.  
  81. static IFSTK ifstk;
  82.  
  83. /*----------------------------------------------------------------------------*/
  84.  
  85. /*ARGSUSED*/
  86. static int
  87. eol_range(const char * buffer, unsigned cpos, int c, int eolchar GCC_UNUSED)
  88. {
  89.     if (is_edit_char(c))
  90.         return FALSE;
  91.  
  92.     if (isspecial(c)    /* sorry, cannot scroll with arrow keys */
  93.      || isCntrl(c))
  94.         return TRUE;
  95.  
  96.     if (islinespecchar(c)
  97.      || (c == ':' && (cpos == 0 || buffer[cpos-1] == c))
  98.      || /* special test for 'a style mark references */
  99.         (cpos != 0
  100.         && buffer[cpos-1] == '\''
  101.         && (isLower(c) || (c == '\'') ) ) )
  102.         return FALSE;
  103.     return TRUE;
  104. }
  105.  
  106. /*
  107.  * Returns true iff the user ended the last prompt with a carriage-return.
  108.  */
  109. static int
  110. end_of_cmd(void)
  111. {
  112.     register int c = end_string();
  113.     return isreturn(c);
  114. }
  115.  
  116. /* returns true iff we are in a named-command and if the user ended the last
  117.  * prompt with a carriage-return.
  118.  */
  119. int
  120. end_named_cmd(void)
  121. {
  122.     if (isnamedcmd) {
  123.         if (clexec)
  124.             return token_ended_line;
  125.         else
  126.             return end_of_cmd();
  127.     }
  128.     return FALSE;
  129. }
  130.  
  131. /* returns true iff we are in a named-command and if the user did not end the
  132.  * last prompt with a carriage-return.
  133.  */
  134. int
  135. more_named_cmd(void)
  136. {
  137.     if (isnamedcmd) {
  138.         if (clexec)
  139.             return !token_ended_line;
  140.         else
  141.             return !end_of_cmd();
  142.     }
  143.     return FALSE;
  144. }
  145.  
  146. /* namedcmd:    execute a named command even if it is not bound */
  147. #if SMALLER
  148. #define execute_named_command namedcmd
  149. #else
  150. static    /* next procedure is local */
  151. #endif
  152.  
  153. int
  154. execute_named_command(int f, int n)
  155. {
  156.     register int    status;
  157.     register const CMDFUNC *cfp; /* function to execute */
  158.     register char *fnp;    /* ptr to the name of the cmd to exec */
  159.  
  160.     LINEPTR fromline;    /* first linespec */
  161.     LINEPTR toline;        /* second linespec */
  162.     MARK    save_DOT;
  163.     MARK    last_DOT;
  164.     static TBUFF *lspec;    /* text of line spec */
  165.     char cspec[NLINE];    /* text of command spec */
  166.     int cmode = 0;
  167.     int c;
  168.     char repeat_cmd = EOS;
  169.     int maybe_reg, maybe_num;
  170.     CMDFLAGS lflag, flags;
  171.  
  172.     tb_scopy(&lspec, "");
  173.     last_DOT = DOT;
  174.  
  175.     /* prompt the user to type a named command */
  176.     mlprompt(": ");
  177.  
  178.     /* and now get the function name to execute */
  179.     for_ever {
  180.         if (cmode == 0) {    /* looking for range-spec, if any */
  181.             status = kbd_reply(
  182.                 (char *)0,    /* no-prompt => splice */
  183.                 &lspec,        /* in/out buffer */
  184.                 eol_range,
  185.                 EOS,        /* may be a conflict */
  186.                 0,        /* no expansion, etc. */
  187.                 no_completion);
  188.             c = end_string();
  189.             if (status != TRUE && status != FALSE) {
  190.                 if (status == SORTOFTRUE) {
  191.                     (void)keystroke(); /* cancel ungetc */
  192.                     return FALSE;
  193.                 }
  194.                 return status;
  195.             } else if (isreturn(c) && (status == FALSE)) {
  196.                 return FALSE;
  197.             } else {
  198.                 unkeystroke(c);    /* ...so we can splice */
  199.             }
  200.             cmode = 1;
  201.             repeat_cmd = EOS;
  202.         } else {            /* looking for command-name */
  203.             fnp = NULL;
  204.             status = kbd_engl_stat((char *)0, cspec, KBD_STATED);
  205.             if (status == TRUE) {
  206.                 fnp = cspec;
  207. #if !SMALLER
  208.                 if (isRepeatable(*fnp) && clexec) {
  209.                     repeat_cmd = *fnp;
  210.                     cmode++;
  211.                     hst_glue(EOS);
  212.                 } else
  213. #endif
  214.                  if (isRepeatable(*fnp) && (*fnp == end_string())) {
  215.                     unkeystroke(*fnp);
  216.                     repeat_cmd = *fnp;
  217.                     cmode++;
  218.                     hst_glue(EOS);
  219.                 } else {
  220.                     c = fnp[strlen(fnp)-1];
  221.                     if (isPunct(c)) {
  222.                         c = end_string();
  223.                         if (c != NAMEC
  224.                          && c != ' '
  225.                          && !isreturn(c)) {
  226.                             unkeystroke(c);
  227.                             /* e.g., !-command */
  228.                          }
  229.                     } else {    /* e.g., ":e#" */
  230.                         c = end_string();
  231.                         if (isPunct(c)
  232.                          && strchr(global_g_val_ptr(GVAL_EXPAND_CHARS),c) != 0)
  233.                             unkeystroke(c);
  234.                     }
  235.                     break;
  236.                 }
  237.             } else if (status == SORTOFTRUE) {
  238.                 hst_remove((cmode > 1) ? "*" : "");
  239.                 if (cmode > 1)
  240.                     (void)keystroke(); /* eat the delete */
  241.                 if (--cmode <= 1) {
  242.                     cmode = 0;
  243.                     hst_remove(tb_values(lspec));
  244.                 }
  245.             } else if ((status == ABORT) || (lastkey == killc)) {
  246.                 return status;
  247.             } else {    /* status == FALSE (killc/wkillc) */
  248.                 if (cmode > 1) {
  249.                     fnp = cspec;
  250.                     cmode--;
  251.                     break;
  252.                 } else {
  253.                     if (tb_length(lspec) <= 1) {
  254.                         return status;
  255.                     } else {
  256.                         break;    /* range-only */
  257.                     }
  258.                 }
  259.             }
  260.         }
  261.     }
  262.  
  263.     /* reconstruct command if user edited it */
  264.     if (repeat_cmd != EOS && fnp != 0 && *fnp == EOS) {
  265.         fnp[0] = repeat_cmd;
  266.         fnp[1] = EOS;
  267.     }
  268.  
  269.     /* infer repeat count from repeated-command */
  270.     if (cmode > 1) {
  271.         if (!f) {
  272.             f = TRUE;
  273.             n = cmode;
  274.         } else {
  275.             mlforce("[Redundant repeat-count]");
  276.             return FALSE;
  277.         }
  278.     }
  279.  
  280.     /* parse the accumulated lspec */
  281.     if (rangespec(tb_values(lspec), &fromline, &toline, &lflag) != TRUE) {
  282.         mlforce("[Improper line range]");
  283.         return FALSE;
  284.     }
  285.  
  286.     /* if range given, and it wasn't "0" and the buffer's empty */
  287.     if (!(lflag & (DFLALL|ZERO)) && is_empty_buf(curbp)) {
  288.         mlforce("[No range possible in empty buffer]", fnp);
  289.         return FALSE;
  290.     }
  291.  
  292.     /* did we get a name? */
  293.     if (fnp == NULL) {
  294.         if ((lflag & DFLALL)) { /* no range, no function */
  295.             return FALSE;
  296.         } else { /* range, no function */
  297.             cfp = &f_gomark;
  298.             fnp = "";
  299.         }
  300.     } else if ((cfp = engl2fnc(fnp)) == NULL) { /* bad function */
  301.         return no_such_function(fnp);
  302.     }
  303.  
  304.     ev_end_of_cmd = end_of_cmd();
  305.     flags = cfp->c_flags;
  306.  
  307.     /* bad arguments? */
  308. #ifdef EXRC_FILES
  309. seems like we need one more check here -- is it from a .exrc file?
  310.         cmd not ok in .exrc         empty file
  311.     if (!(flags & EXRCOK) && is_empty_buf(curbp)) {
  312.         mlforce("[Can't use the \"%s\" command in a %s file.]",
  313.                     cmdnames[cmdidx].name, EXRC);
  314.         return FALSE;
  315.     }
  316. #endif
  317.  
  318.     /* was: if (!(flags & (ZERO | EXRCOK)) && fromline == NULL ) */
  319.     if ((lflag & ZERO)) {
  320.         if (!(flags & ZERO)) {
  321.             mlforce("[Can't use address 0 with \"%s\" command]", fnp);
  322.             return FALSE;
  323.         }
  324.         if (fromline == null_ptr)
  325.             fromline = buf_head(curbp); /* buffer is empty */
  326.  
  327.         /*  we're positioned at fromline == curbp->b_linep, so commands
  328.             must be willing to go _down_ from there.  Seems easiest
  329.             to special case the commands that prefer going up */
  330.         if (cfp == &f_insfile) {
  331.             /* EMPTY */ /* works okay, or acts down normally */ ;
  332.         } else if (cfp == &f_lineputafter) {
  333.             cfp = &f_lineputbefore;
  334.             fromline = lforw(fromline);
  335.         } else if (cfp == &f_opendown) {
  336.             cfp = &f_openup;
  337.             fromline = lforw(fromline);
  338.         } else if (cfp == &f_gomark) {
  339.             fromline = lforw(fromline);
  340.         } else {
  341.             mlforce("[Configuration error: ZERO]");
  342.             return FALSE;
  343.         }
  344.         flags = cfp->c_flags;
  345.         toline = fromline;
  346.     }
  347.  
  348.     /* if we're not supposed to have a line no., and the line no. isn't
  349.         the current line, and there's more than one line */
  350.     if (!(flags & FROM) && (fromline != DOT.l) &&
  351.             !is_empty_buf(curbp) &&
  352.           (lforw(lforw(buf_head(curbp))) != buf_head(curbp)) ) {
  353.         mlforce("[Can't use address with \"%s\" command.]", fnp);
  354.         return FALSE;
  355.     }
  356.     /* if we're not supposed to have a second line no., and the line no.
  357.         isn't the same as the first line no., and there's more than
  358.         one line */
  359.     if (!(flags & TO) && (toline != fromline) &&
  360.             !is_empty_buf(curbp) &&
  361.           (lforw(lforw(buf_head(curbp))) != buf_head(curbp)) ) {
  362.         mlforce("[Can't use a range with \"%s\" command.]", fnp);
  363.         return FALSE;
  364.     }
  365.  
  366.     /* some commands have special default ranges */
  367.     if (lflag & DFLALL) {
  368.         if (flags & DFLALL) {
  369.             if (cfp == &f_showlength) {
  370.                 fromline = lforw(buf_head(curbp));
  371.                 toline   = lback(buf_head(curbp));
  372.             } else if (cfp == &f_operwrite) {
  373.                 cfp = &f_filewrite;
  374.             } else if (cfp == &f_operglobals) {
  375.                 cfp = &f_globals;
  376.             } else if (cfp == &f_opervglobals) {
  377.                 cfp = &f_vglobals;
  378.             } else {
  379.                 mlforce("[Configuration error: DFLALL]");
  380.                 return FALSE;
  381.             }
  382.             lflag |= (FROM|TO); /* avoid prompt for line-count */
  383.         } else if (flags & DFLNONE) {
  384. #if OPT_SHELL
  385.             if (cfp == &f_operfilter) {
  386.                 cfp = &f_spawn;
  387.                 (void)setmark();  /* not that it matters */
  388.             } else
  389. #endif
  390. #if OPT_PERL
  391.             if (cfp == &f_perl) {
  392.                 perl_default_region();
  393.             } else
  394. #endif
  395.             {
  396.                 mlforce("[Configuration error: DFLNONE]");
  397.                 return FALSE;
  398.             }
  399.             fromline = toline = null_ptr;
  400.             lflag |= (FROM|TO); /* avoid prompt for line-count */
  401.         } else
  402.             lflag &= ~DFLALL;
  403.     }
  404.  
  405.     /* Process argument(s) for named-buffer & line-count, if present.  The
  406.      * line-count is expected only if no "to" address specification was
  407.      * given.
  408.      */
  409.     if ((*fnp != EOS) && !end_of_cmd() && !isPunct(end_string())) {
  410.         maybe_reg = ((flags & OPTREG) == OPTREG);
  411.         maybe_num = ((flags & TO) == TO)
  412.             && !((lflag & TO) == TO);
  413.         if (maybe_num && (lflag & FROM)) {
  414.             toline = fromline;
  415.             maybe_num = FALSE;
  416.         }
  417.     } else {
  418.         maybe_reg =
  419.         maybe_num = FALSE;
  420.     }
  421.  
  422.     if (maybe_reg || maybe_num) {
  423.         int    num,
  424.             that = (maybe_num && maybe_reg)
  425.                 ? 0
  426.                 : (maybe_num ? 1 : -1),
  427.             last = maybe_num ? 2 : 1;
  428.  
  429.         while (!end_of_cmd() && (that < last)) {
  430.             status = mlreply_reg_count(that, &num, &that);
  431.             if (status == ABORT)
  432.                 return status;
  433.             else if (status != TRUE)
  434.                 break;
  435.             if (that == 2) {
  436.                 if (is_empty_buf(curbp)) {
  437.                     mlforce(
  438.                 "[No line count possible in empty buffer]", fnp);
  439.                     return FALSE;
  440.                 }
  441.                 swapmark();
  442.                 DOT.l = fromline;
  443.                 (void)forwline(TRUE, num-1);
  444.                 toline = DOT.l;
  445.                 swapmark();
  446.             } else {
  447.                 ukb = (short)num;
  448.                 kregflag = (that == 1) ? KAPPEND : 0;
  449.                 that = 1;
  450.                 /* patch: need to handle recursion */
  451.             }
  452.         }
  453.     }
  454.  
  455.     if (flags & NOMOVE)
  456.         save_DOT = DOT;
  457.     else if ((toline != null_ptr) || (fromline != null_ptr)) {
  458.         /* assume it's an absolute motion */
  459.         /* we could probably do better */
  460.         curwp->w_lastdot = DOT;
  461.     }
  462.     if ((toline != null_ptr) && (flags & TO)) {
  463.         DOT.l = toline;
  464.         (void)firstnonwhite(FALSE,1);
  465.         (void)setmark();
  466.     }
  467.     if ((fromline != null_ptr) && (flags & FROM)) {
  468.         DOT.l = fromline;
  469.         (void)firstnonwhite(FALSE,1);
  470.         if (toline == null_ptr)
  471.             (void)setmark();
  472.     }
  473.     if (!sameline(last_DOT, DOT))
  474.         curwp->w_flag |= WFMOVE;
  475.  
  476.     /* and then execute the command */
  477.     isnamedcmd = TRUE;
  478.     havemotion = &f_gomark;
  479.     regionshape = FULLLINE;
  480.  
  481.     /* if the command ended with a bang, let the function know
  482.         that so it can take special action */
  483.     if ((flags & BANG) && (fnp[strlen(fnp)-1] == '!')) {
  484.         f = TRUE;
  485.         n = SPECIAL_BANG_ARG;
  486.     }
  487.  
  488.     /*
  489.      * Ensure that we've completed the command properly.  (We could make
  490.      * the same check after 'execute()', but that would tend to obscure
  491.      * informational/warning messages...).
  492.      */
  493.     if (flags == NONE
  494.      && end_string() != ' '
  495.      && !end_of_cmd()) {
  496.         status = FALSE;
  497.         if (!mapped_c_avail())
  498.             unkeystroke(end_string());
  499.         c = kbd_seq();
  500.         mlforce("[Extra characters after \"%s\" command]", fnp);
  501.     } else {
  502.         status = execute(cfp,f,n);
  503.     }
  504.  
  505.     havemotion = NULL;
  506.     isnamedcmd = FALSE;
  507.     regionshape = EXACT;
  508.  
  509.     /* don't use this if the command modifies! */
  510.     if (flags & NOMOVE) {
  511.         if (!samepoint(DOT, save_DOT)) {
  512.             DOT = save_DOT;
  513.             /* if an update() was called somewhere along
  514.                the way (as a result of mlyesno, for instance),
  515.                then we _have_ moved, so resetting DOT to
  516.                it's first value constitutes another one. */
  517.             /* i think at worst this means an extra call to
  518.                reframe...  */
  519.             curwp->w_flag |= WFMOVE;
  520.         }
  521.     }
  522.  
  523.     return status;
  524. }
  525.  
  526. #if !SMALLER
  527. /* intercept calls on 'namedcmd()' to allow logging of all commands, even
  528.  * those that have errors in them.
  529.  */
  530. int
  531. namedcmd(int f, int n)
  532. {
  533.     int status;
  534.     hst_init(EOS);
  535.     status = execute_named_command(f,n);
  536.     hst_flush();
  537.     return status;
  538. }
  539. #endif
  540.  
  541. /* parse an ex-style line spec -- code culled from elvis, file ex.c, by
  542.     Steve Kirkendall
  543. */
  544. static const char *
  545. parse_linespec(
  546. register const char *s,        /* start of the line specifier */
  547. LINEPTR        *markptr)    /* where to store the mark's value */
  548. {
  549.     int        num;
  550.     LINE        *lp;    /* where the linespec takes us */
  551.     register const char *t;
  552.     int        status;
  553.  
  554.     (void)setmark();
  555.     lp = NULL;
  556.  
  557.     /* parse each ;-delimited clause of this linespec */
  558.     do {
  559.         /* skip an initial ';', if any */
  560.         if (*s == ';')
  561.             s++;
  562.  
  563.         /* skip leading spaces */
  564.         while (isSPorTAB(*s))
  565.             s++;
  566.  
  567.         /* dot means current position */
  568.         if (*s == '.') {
  569.             s++;
  570.             lp = DOT.l;
  571.         } else if (*s == '$') { /* '$' means the last line */
  572.             s++;
  573.             status = gotoeob(TRUE,1);
  574.             if (status) lp = DOT.l;
  575.         } else if (isDigit(*s)) {
  576.             /* digit means an absolute line number */
  577.             for (num = 0; isDigit(*s); s++) {
  578.                 num = num * 10 + *s - '0';
  579.             }
  580.             status = gotoline(TRUE,num);
  581.             if (status) lp = DOT.l;
  582.         } else if (*s == '\'') {
  583.             /* apostrophe means go to a set mark */
  584.             s++;
  585.             status = gonmmark(*s);
  586.             if (status) lp = DOT.l;
  587.             s++;
  588.         } else if (*s == '+') {
  589.             s++;
  590.             for (num = 0; isDigit(*s); s++)
  591.                 num = num * 10 + *s - '0';
  592.             if (num == 0)
  593.                 num++;
  594.             while (*s == '+')
  595.                 s++, num++;
  596.             status = forwline(TRUE,num);
  597.             if (status)
  598.                 lp = DOT.l;
  599.         } else if (*s == '-') {
  600.             s++;
  601.             for (num = 0; isDigit(*s); s++)
  602.                     num = num * 10 + *s - '0';
  603.             if (num == 0)
  604.                 num++;
  605.             while (*s == '-')
  606.                 s++, num++;
  607.             status = forwline(TRUE,-num);
  608.             if (status)
  609.                 lp = DOT.l;
  610.         }
  611. #if PATTERNS
  612.         else if (*s == '/' || *s == '?') { /* slash means do a search */
  613.             /* put a null at the end of the search pattern */
  614.             t = parseptrn(s);
  615.  
  616.             /* search for the pattern */
  617.             lp &= ~(BLKSIZE - 1);
  618.             if (*s == '/') {
  619.                 pfetch(markline(lp));
  620.                 if (plen > 0)
  621.                     lp += plen - 1;
  622.                 lp = m_fsrch(lp, s);
  623.             } else {
  624.                 lp = m_bsrch(lp, s);
  625.             }
  626.  
  627.             /* adjust command string pointer */
  628.             s = t;
  629.         }
  630. #endif
  631.         else if (*s == EOS) {    /* empty string matches '.' */
  632.             lp = DOT.l;
  633.         }
  634.  
  635.         /* if linespec was faulty, quit now */
  636.         if (!lp) {
  637.             *markptr = lp;
  638.             swapmark();
  639.             return s;
  640.         }
  641.  
  642.         /* maybe add an offset */
  643.         t = s;
  644.         if (*t == '-' || *t == '+') {
  645.             s++;
  646.             for (num = 0; isDigit(*s); s++) {
  647.                 num = num * 10 + *s - '0';
  648.             }
  649.             if (num == 0)
  650.                 num = 1;
  651.             if (forwline(TRUE, (*t == '+') ? num : -num) == TRUE)
  652.                 lp = DOT.l;
  653.         }
  654.     } while (*s == ';' || *s == '+' || *s == '-');
  655.  
  656.     *markptr = lp;
  657.     swapmark();
  658.     return s;
  659. }
  660.  
  661. /* parse an ex-style line range -- code culled from elvis, file ex.c, by
  662.     Steve Kirkendall
  663. */
  664. static int
  665. rangespec(
  666. const char    *specp,        /* string containing a line range */
  667. LINEPTR        *fromlinep,    /* first linespec */
  668. LINEPTR        *tolinep,    /* second linespec */
  669. CMDFLAGS    *flagp)
  670. {
  671.     register const char *scan;    /* used to scan thru specp */
  672.     LINEPTR        fromline;    /* first linespec */
  673.     LINEPTR        toline;        /* second linespec */
  674.  
  675.     *flagp = 0;
  676.  
  677.     /* ignore command lines that start with a double-quote */
  678.     if (*specp == '"') {
  679.         *fromlinep = *tolinep = DOT.l;
  680.         return TRUE;
  681.     }
  682.  
  683.     /* permit extra colons at the start of the line */
  684.     while (isSPorTAB(*specp) || *specp == ':') {
  685.         specp++;
  686.     }
  687.  
  688.     /* parse the line specifier */
  689.     scan = specp;
  690.     if (*scan == '0') {
  691.         fromline = toline = buf_head(curbp); /* _very_ top of buffer */
  692.         *flagp |= (FROM|ZERO);
  693.         scan++;
  694.     } else if (*scan == '%') {
  695.         /* '%' means all lines */
  696.         fromline = lforw(buf_head(curbp));
  697.         toline = lback(buf_head(curbp));
  698.         scan++;
  699.         *flagp |= (FROM|TO);
  700.     } else {
  701.         scan = parse_linespec(scan, &toline);
  702.         *flagp |= FROM;
  703.         if (toline == null_ptr)
  704.             toline = DOT.l;
  705.         fromline = toline;
  706.         while (*scan == ',') {
  707.             fromline = toline;
  708.             scan++;
  709.             scan = parse_linespec(scan, &toline);
  710.             *flagp |= TO;
  711.             if (toline == null_ptr) {
  712.                 /* faulty line spec */
  713.                 return FALSE;
  714.             }
  715.         }
  716.     }
  717.  
  718.     if (is_empty_buf(curbp))
  719.         fromline = toline = null_ptr;
  720.  
  721.     if (scan == specp)
  722.         *flagp |= DFLALL;
  723.  
  724.     /* skip whitespace */
  725.     while (isSPorTAB(*scan))
  726.         scan++;
  727.  
  728.     if (*scan) {
  729.         /* dbgwrite("crud at end %s (%s)",specp, scan); */
  730.         return FALSE;
  731.     }
  732.  
  733.     *fromlinep = fromline;
  734.     *tolinep = toline;
  735.  
  736.     return TRUE;
  737. }
  738.  
  739. /*    docmd:    take a passed string as a command line and translate
  740.         it to be executed as a command. This function will be
  741.         used by execute-command-line and by all source and
  742.         startup files.
  743.  
  744.     format of the command line is:
  745.  
  746.         {# arg} <command-name> {<argument string(s)>}
  747.  
  748. */
  749.  
  750. int
  751. docmd(
  752. char *cline,    /* command line to execute */
  753. int execflag,
  754. int f, int n)
  755. {
  756.     int status;        /* return status of function */
  757.     int oldcle;        /* old contents of clexec flag */
  758.     const char *oldestr;    /* original exec string */
  759.     char tkn[NSTRING];    /* next token off of command line */
  760.     const CMDFUNC *cfp;
  761.  
  762.     set_end_string(EOS);
  763.     oldestr = execstr;    /* save last ptr to string to execute */
  764.     execstr = cline;    /* and set this one as current */
  765.  
  766.     do {
  767.         if ((status = macarg(tkn)) != TRUE) {    /* grab first token */
  768.             execstr = oldestr;
  769.             return status;
  770.         }
  771.         if (*tkn == ':') {    /* allow leading ':' on line */
  772.             register int j;
  773.             for (j = 0; (tkn[j] = tkn[j+1]) != EOS; j++)
  774.                 ;
  775.         }
  776.     } while (!*tkn);
  777.  
  778.     /* process leading argument */
  779.     if (toktyp(tkn) != TKCMD) {
  780.         f = TRUE;
  781.         n = atoi(strcpy(tkn, tokval(tkn)));
  782.  
  783.         /* and now get the command to execute */
  784.         if ((status = macarg(tkn)) != TRUE) {
  785.             execstr = oldestr;
  786.             return status;
  787.         }
  788.     }
  789.  
  790.     /* and match the token to see if it exists */
  791.     if ((cfp = engl2fnc(tkn)) == NULL) {
  792.         execstr = oldestr;
  793.         return no_such_function(tkn);
  794.     }
  795.  
  796.     /* save the arguments and go execute the command */
  797.     oldcle = clexec;        /* save old clexec flag */
  798.     clexec = execflag;        /* in cline execution */
  799.     /* flag the first time through for some commands -- e.g. subst
  800.         must know to not prompt for strings again, and pregion
  801.         must only restart the p-lines buffer once for each
  802.         command. */
  803.     calledbefore = FALSE;
  804.     status = execute(cfp,f,n);
  805.     cmdstatus = status;        /* save the status */
  806.     clexec = oldcle;        /* restore clexec flag */
  807.     execstr = oldestr;
  808.     return status;
  809. }
  810.  
  811. /*
  812.  *  Call the appropriate action for a given CMDFUNC
  813.  */
  814. int
  815. call_cmdfunc(const CMDFUNC *p, int f, int n)
  816. {
  817.     switch (p->c_flags & CMD_TYPE)
  818.     {
  819.     case CMD_FUNC: /* normal CmdFunc */
  820.     return (CMD_U_FUNC(p))(f, n);
  821.  
  822. #if OPT_NAMEBST
  823. #if OPT_PROCEDURES
  824.     case CMD_PROC: /* named procedure */
  825.     return dobuf(CMD_U_BUFF(p));
  826. #endif
  827.  
  828. #if OPT_PERL
  829.     case CMD_PERL: /* perl subroutine */
  830.     return perl_call_sub(CMD_U_PERL(p), p->c_flags & OPER, f, n);
  831. #endif
  832. #endif /* OPT_NAMEBST */
  833.     }
  834.  
  835.     mlforce("BUG: invalid CMDFUNC type");
  836.     return FALSE;
  837. }
  838.  
  839. /*
  840.  * This is the general command execution routine. It takes care of checking
  841.  * flags, globals, etc, to be sure we're not doing something dumb.
  842.  * Return the status of command.
  843.  */
  844.  
  845. int
  846. execute(
  847. const CMDFUNC *execfunc,    /* ptr to function to execute */
  848. int f, int n)
  849. {
  850.     register int status;
  851.     register CMDFLAGS flags;
  852.  
  853.     if (execfunc == NULL) {
  854. #if OPT_REBIND
  855.         mlwarn("[Key not bound]");    /* complain        */
  856. #else
  857.         mlwarn("[Not a command]");    /* complain        */
  858. #endif
  859.         return FALSE;
  860.     }
  861.  
  862.     flags = execfunc->c_flags;
  863.  
  864.     /* commands following operators can't be redone or undone */
  865.     if ( !doingopcmd && !doingsweep) {
  866.         /* don't record non-redoable cmds, */
  867.         /* but if we're in insertmode, it's okay, since we must
  868.             be executing a function key, like an arrow key,
  869.             that the user will want to have replayed later */
  870.         if ((curwp == NULL || !insertmode) && (flags & REDO) == 0)
  871.             dotcmdstop();
  872.         if (flags & UNDO) {
  873.             /* undoable command can't be permitted when read-only */
  874.             if (!(flags & VIEWOK)) {
  875.                 if (b_val(curbp,MDVIEW))
  876.                     return rdonly();
  877. #ifdef MDCHK_MODTIME
  878.                 if (!b_is_changed(curbp) &&
  879.                     !ask_shouldchange(curbp))
  880.                     return FALSE;
  881. #endif
  882.             }
  883.             if (!kbd_replaying(FALSE))
  884.                 mayneedundo();
  885.         }
  886.     }
  887.  
  888.     if (dotcmdmode != PLAY) {
  889.         if (execfunc != &f_dotcmdplay) {
  890.             /* reset dotcmdkreg on any command where ukb is
  891.              * unspecified.  usekreg() does it on the one's
  892.              * where it is specified.  */
  893.             if (ukb == 0)
  894.                 dotcmdkreg = 0;
  895.  
  896.             /* override the saved dot-cmd argument, if this
  897.                 is a new redoable command */
  898.             if (flags & REDO)
  899.                 dotcmdarg = FALSE;
  900.         }
  901.     } else {
  902.         /* if we _are_ playing, re-use the previously kreg */
  903.         if (dotcmdkreg != 0)
  904.             ukb = dotcmdkreg;
  905.     }
  906.  
  907.     if (curwp->w_tentative_lastdot.l == null_ptr)
  908.         curwp->w_tentative_lastdot = DOT;
  909.  
  910.     status = call_cmdfunc(execfunc, f, n);
  911.     if ((flags & GOAL) == 0) { /* goal should not be retained */
  912.         curgoal = -1;
  913.     }
  914.  
  915.     /* if motion was absolute, and it wasn't just on behalf of an
  916.         operator, and we moved, update the "last dot" mark */
  917.     if ((flags & ABSM) && !doingopcmd &&
  918.             !sameline(DOT, curwp->w_tentative_lastdot)) {
  919.         curwp->w_lastdot = curwp->w_tentative_lastdot;
  920.     }
  921.  
  922.     curwp->w_tentative_lastdot = DOT;
  923.  
  924.  
  925.     return status;
  926. }
  927.  
  928. /* token:    chop a token off a string
  929.         return a pointer past the token
  930. */
  931.  
  932. const char *
  933. token(
  934. const char *src,    /* source string */
  935. char *tok,        /* destination token string */
  936. int eolchar)
  937. {
  938.     register int quotef = EOS;    /* nonzero iff the current string quoted */
  939.     register int c, i, d;
  940.  
  941.     /* first scan past any whitespace in the source string */
  942.     while (isSPorTAB(*src))
  943.         ++src;
  944.  
  945.     /* scan through the source string */
  946.     while ((c = *src) != EOS) {
  947.         /* process special characters */
  948.         if (c == '\\') {
  949.             src++;
  950.             if (*src == EOS)
  951.                 break;
  952.             switch (c = *src++) {
  953.                 case 'r':    *tok++ = '\r'; break;
  954.                 case 'n':    *tok++ = '\n'; break;
  955.                 case 't':    *tok++ = '\t';  break;
  956.                 case 'b':    *tok++ = '\b';  break;
  957.                 case 'f':    *tok++ = '\f'; break;
  958.                 case 'a':    *tok++ = '\007'; break;
  959.                 case 's':    *tok++ = ' '; break;
  960.                 case 'e':    *tok++ = ESC; break;
  961.  
  962.                 case 'x':
  963.                 case 'X':
  964.                     i = 2; /* allow \xNN hex */
  965.                     c = 0;
  966.                     while (isAlnum(*src) && i--) {
  967.                         if (isDigit(*src)) {
  968.                             d = *src - '0';
  969.                         } else if (isLower(*src)) {
  970.                             d = *src - 'a' + 10;
  971.                         } else {
  972.                             d = *src - 'A' + 10;
  973.                         }
  974.                         if (d > 15)
  975.                             break;
  976.                         c = (c * 16) + d;
  977.                         src++;
  978.                     }
  979.                     *tok++ = (char)c;
  980.                     break;
  981.  
  982.                 default:
  983.                     if (c >= '0' && c <= '7') {
  984.                         i = 2; /* allow \NNN octal */
  985.                         c -= '0';
  986.                         while (isDigit(*src)
  987.                           && *src < '8'
  988.                           && i--) {
  989.                             c = (c * 8) + (*src++ - '0');
  990.                         }
  991.                     }
  992.                     *tok++ = (char)c;
  993.             }
  994.         } else {
  995.             /* check for the end of the token */
  996.             if (quotef != EOS) {
  997.                 if (c == quotef) {
  998.                     src++;
  999.                     break;
  1000.                 }
  1001.             } else {
  1002.                 if (c == eolchar) {
  1003.                     if (!isSPorTAB(c))
  1004.                         src++;
  1005.                     break;
  1006.                 } else if (c == '"') {
  1007.                     quotef = c;
  1008.                     /* note that leading quote
  1009.                         is included */
  1010.                 } else if (isSPorTAB(c)) {
  1011.                     break;
  1012.                 }
  1013.             }
  1014.  
  1015.             *tok++ = *src++;    /* record the character */
  1016.         }
  1017.     }
  1018.  
  1019.     /* scan past any whitespace remaining in the source string */
  1020.     while (isSPorTAB(*src))
  1021.         ++src;
  1022.     token_ended_line = isreturn(*src) || *src == EOS;
  1023.  
  1024.     *tok = EOS;
  1025.     return src;
  1026. }
  1027.  
  1028. /*
  1029.  * Convert the string 'src' into a string that we can read back with 'token()'.
  1030.  * If it is a shell-command, this will be a single-token.  Repeated shift
  1031.  * commands are multiple tokens.
  1032.  */
  1033. int
  1034. macroize(
  1035. TBUFF    **p,
  1036. TBUFF *    src,
  1037. int    skip)
  1038. {
  1039.     register int    c;
  1040.     char    *ref    = tb_values(src); /* FIXME */
  1041.     int    multi    = !isShellOrPipe(ref);    /* shift command? */
  1042.     int    count    = 0;
  1043.  
  1044.     if (tb_init(p, abortc) != 0) {
  1045.         ALLOC_T    n, len = tb_length(src);
  1046.         char    *txt = tb_values(src);
  1047.  
  1048.         TRACE(("macroizing %s\n", tb_visible(src)))
  1049.         (void)tb_append(p, '"');
  1050.         for (n = skip; n < len; n++) {
  1051.             c = txt[n];
  1052.             if (multi && count++)
  1053.                 (void)tb_sappend(p, "\" \"");
  1054.             if (c == '\\' || c == '"')
  1055.                 (void)tb_append(p, '\\');
  1056.             (void)tb_append(p, c);
  1057.         }
  1058.         (void)tb_append(p, '"');
  1059.         TRACE(("macroized %s\n", tb_visible(*p)))
  1060.         return (tb_append(p, EOS) != 0);
  1061.     }
  1062.     return FALSE;
  1063. }
  1064.  
  1065. int
  1066. macarg(        /* get a macro line argument */
  1067. char *tok)    /* buffer to place argument */
  1068. {
  1069.     int savcle;    /* buffer to store original clexec */
  1070.  
  1071.     savcle = clexec;    /* save execution mode */
  1072.     clexec = TRUE;        /* get the argument */
  1073.     /* grab token and advance past */
  1074.     execstr = token(execstr, tok, EOS);
  1075.     /* evaluate it */
  1076.     (void)strcpy(tok, tokval(tok));
  1077.     clexec = savcle;    /* restore execution mode */
  1078.     return TRUE;
  1079. }
  1080.  
  1081. int
  1082. macliteralarg(    /* get a macro line argument */
  1083. TBUFF **tok)    /* buffer to place argument */
  1084. {
  1085.     /* grab everything on this line, literally */
  1086.     (void)tb_scopy(tok, execstr);
  1087.     execstr += strlen(execstr);
  1088.     token_ended_line = TRUE;
  1089.     return TRUE;
  1090. }
  1091.  
  1092. /*    storemac:    Set up a macro buffer and flag to store all executed
  1093.             command lines there. 'n' is the macro number to use
  1094.  */
  1095.  
  1096. int
  1097. storemac(int f, int n)
  1098. {
  1099.     register struct BUFFER *bp;    /* pointer to macro buffer */
  1100.     char bname[NBUFN];        /* name of buffer to use */
  1101.  
  1102.     /* must have a numeric argument to this function */
  1103.     if (f == FALSE) {
  1104.         mlforce("[No macro specified]");
  1105.         return FALSE;
  1106.     }
  1107.  
  1108.     /* range check the macro number */
  1109.     if (n < 1 || n > 40) {
  1110.         mlforce("[Macro number out of range]");
  1111.         return FALSE;
  1112.     }
  1113.  
  1114.     /* construct the macro buffer name */
  1115.     (void)lsprintf(bname, MACRO_N_BufName, n);
  1116.  
  1117.     /* set up the new macro buffer */
  1118.     if ((bp = bfind(bname, BFINVS)) == NULL) {
  1119.         mlforce("[Cannot create macro]");
  1120.         return FALSE;
  1121.     }
  1122.  
  1123.     /* and make sure it is empty */
  1124.     if (!bclear(bp))
  1125.         return FALSE;
  1126.  
  1127.     set_rdonly(bp, bp->b_fname, MDVIEW);
  1128.  
  1129.     /* and set the macro store pointers to it */
  1130.     mstore = TRUE;
  1131.     bstore = bp;
  1132.     return TRUE;
  1133. }
  1134.  
  1135. #if    OPT_PROCEDURES
  1136. /*    storeproc:    Set up a procedure buffer and flag to store all
  1137.             executed command lines there.  'n' is the macro number
  1138.             to use.
  1139.  */
  1140.  
  1141. int
  1142. storeproc(int f, int n)
  1143. {
  1144.     static TBUFF *name;        /* procedure name */
  1145. #if OPT_ONLINEHELP
  1146.     static TBUFF *helpstring;    /* optional help string */
  1147. #endif
  1148.     register struct BUFFER *bp;    /* pointer to macro buffer */
  1149.     register int status;        /* return status */
  1150.     char bname[NBUFN];        /* name of buffer to use */
  1151.  
  1152.     /* a numeric argument means its a numbered macro */
  1153.     if (f == TRUE)
  1154.         return storemac(f, n);
  1155.  
  1156.     /* get the name of the procedure */
  1157.     tb_scopy(&name, "");
  1158.     if ((status = kbd_reply("Procedure name: ", &name,
  1159.         eol_history, ' ', KBD_NORMAL, no_completion)) != TRUE)
  1160.         return status;
  1161.  
  1162. #if OPT_ONLINEHELP
  1163.     /* get optional help string */
  1164.     if (more_named_cmd())
  1165.     {
  1166.         tb_scopy(&helpstring, "");
  1167.         if ((status = kbd_reply("help info: ", &helpstring,
  1168.         eol_history, '\n', KBD_NORMAL, no_completion)) != TRUE)
  1169.             return status;
  1170.     }
  1171.     else
  1172.         tb_scopy(&helpstring, "User-defined procedure");
  1173. #endif
  1174.  
  1175.     /* construct the macro buffer name */
  1176.     add_brackets(bname, tb_values(name));
  1177.  
  1178.     /* set up the new macro buffer */
  1179.     if ((bp = bfind(bname, BFINVS)) == NULL) {
  1180.         mlforce("[Cannot create procedure]");
  1181.         return FALSE;
  1182.     }
  1183.  
  1184.     /* and make sure it is empty */
  1185.     if (!bclear(bp))
  1186.         return FALSE;
  1187.  
  1188.     set_rdonly(bp, bp->b_fname, MDVIEW);
  1189.  
  1190.     /* save this into the list of : names */
  1191. #if OPT_NAMEBST
  1192.     {
  1193.         CMDFUNC *cf = typealloc(CMDFUNC);
  1194.  
  1195.         if (!cf)
  1196.         return no_memory("registering procedure name");
  1197.  
  1198. #if CC_CANNOT_INIT_UNIONS
  1199.         cf->c_union = (void *)bp;
  1200. #else
  1201.         cf->cu.c_buff = bp;
  1202. #endif
  1203.         cf->c_flags = UNDO|REDO|CMD_PROC|VIEWOK;
  1204. #if OPT_ONLINEHELP
  1205.         cf->c_help = strmalloc(tb_values(helpstring));
  1206. #endif
  1207.  
  1208.         if (insert_namebst(tb_values(name), cf, FALSE) != TRUE)
  1209.         return FALSE;
  1210.     }
  1211. #endif /* OPT_NAMEBST */
  1212.  
  1213.     /* and set the macro store pointers to it */
  1214.     mstore = TRUE;
  1215.     bstore = bp;
  1216.     return TRUE;
  1217. }
  1218.  
  1219. /*    execproc:    Execute a procedure                */
  1220.  
  1221. int
  1222. execproc(int f, int n)
  1223. {
  1224.     static char name[NBUFN];    /* name of buffer to execute */
  1225.     int status;
  1226.  
  1227.     /* find out what buffer the user wants to execute */
  1228.     if ((status = mlreply("Execute procedure: ",
  1229.                     name, sizeof(name))) != TRUE) {
  1230.         return status;
  1231.     }
  1232.  
  1233.     status = TRUE;
  1234.     if (!f)
  1235.         n = 1;
  1236.  
  1237.     while (status == TRUE && n--)
  1238.         status = run_procedure(name);
  1239.  
  1240.     return status;
  1241.  
  1242. }
  1243.  
  1244. int
  1245. run_procedure(const char *name)
  1246. {
  1247.     register BUFFER *bp;        /* ptr to buffer to execute */
  1248.     register int status;        /* status return */
  1249.     char bufn[NBUFN];        /* name of buffer to execute */
  1250.     register int odiscmd;
  1251.  
  1252.     if (!*name)
  1253.         return FALSE;
  1254.  
  1255.     /* construct the buffer name */
  1256.     add_brackets(bufn, name);
  1257.  
  1258.     /* find the pointer to that buffer */
  1259.     if ((bp = find_b_name(bufn)) == NULL) {
  1260.         return FALSE;
  1261.     }
  1262.  
  1263.     odiscmd = discmd;
  1264.     discmd = FALSE;
  1265.  
  1266.     status = dobuf(bp);
  1267.  
  1268.     discmd = odiscmd;
  1269.     return status;
  1270. }
  1271. #endif
  1272.  
  1273. #if ! SMALLER
  1274. /*    execbuf:    Execute the contents of a buffer of commands    */
  1275.  
  1276. int
  1277. execbuf(int f, int n)
  1278. {
  1279.     register BUFFER *bp;        /* ptr to buffer to execute */
  1280.     register int status;        /* status return */
  1281.     static char bufn[NBUFN];    /* name of buffer to execute */
  1282.     register int odiscmd;
  1283.  
  1284.     if (!f)
  1285.         n = 1;
  1286.  
  1287.     /* find out what buffer the user wants to execute */
  1288.     if ((status = mlreply("Execute buffer: ", bufn, sizeof(bufn))) != TRUE)
  1289.         return status;
  1290.  
  1291.     /* find the pointer to that buffer */
  1292.     if ((bp = find_b_name(bufn)) == NULL) {
  1293.         mlforce("[No such buffer \"%s\"]",bufn);
  1294.         return FALSE;
  1295.     }
  1296.  
  1297.     odiscmd = discmd;
  1298.     discmd = FALSE;
  1299.     status = TRUE;
  1300.     /* and now execute it as asked */
  1301.     while (n-- > 0 && status == TRUE)
  1302.         status = dobuf(bp);
  1303.  
  1304.     discmd = odiscmd;
  1305.  
  1306.     return status;
  1307. }
  1308. #endif
  1309.  
  1310. /*    dobuf:    execute the contents of the buffer pointed to
  1311.         by the passed BP
  1312.  
  1313.     Directives start with a "~" and include:
  1314.  
  1315.     ~endm        End a macro
  1316. #if !SMALLER
  1317.     ~if (cond)    conditional execution
  1318.     ~else
  1319.     ~endif
  1320.     ~return        Return (terminating current macro)
  1321.     ~goto <label>    Jump to a label in the current macro
  1322.     ~force        Force macro to continue...even if command fails
  1323.     ~while (cond)    Execute a loop if the condition is true
  1324.     ~endwhile
  1325.  
  1326.     Line Labels begin with a "*" as the first nonblank char, like:
  1327.  
  1328.     *LBL01
  1329. #endif
  1330.  
  1331. */
  1332.  
  1333. static void
  1334. freewhile(    /* free a list of while block pointers */
  1335. WHBLOCK *wp)    /* head of structure to free */
  1336. {
  1337.     if (wp == NULL)
  1338.         return;
  1339.     if (wp->w_next)
  1340.         freewhile(wp->w_next);
  1341.     free((char *)wp);
  1342. }
  1343.  
  1344. #define DIRECTIVE_CHAR '~'
  1345.  
  1346. #if ! SMALLER
  1347. #define DDIR_FAILED     -1
  1348. #define DDIR_COMPLETE    0
  1349. #define DDIR_INCOMPLETE  1
  1350. #define DDIR_FORCE       2
  1351.  
  1352. static DIRECTIVE
  1353. dname_to_dirnum(const char *eline, size_t length)
  1354. {
  1355.     DIRECTIVE dirnum = D_UNKNOWN;
  1356.     if (*eline++ == DIRECTIVE_CHAR) {
  1357.         size_t n, m;
  1358.         for (n = 0; n < TABLESIZE(dname); n++) {
  1359.             m = strlen(dname[n].name);
  1360.             if (length >= m
  1361.              && memcmp(eline, dname[n].name, m) == 0) {
  1362.                 dirnum = dname[n].type;
  1363.                 break;
  1364.             }
  1365.         }
  1366.     }
  1367.     return dirnum;
  1368. }
  1369.  
  1370. static const char *
  1371. dirnum_to_name(DIRECTIVE dirnum)
  1372. {
  1373.     size_t n;
  1374.     for (n = 0; n < TABLESIZE(dname); n++)
  1375.         if (dname[n].type == dirnum)
  1376.             return dname[n].name;
  1377.     return "?";
  1378. }
  1379.  
  1380. static int
  1381. unbalanced_directive(DIRECTIVE dirnum)
  1382. {
  1383.     mlforce("[Unexpected directive: %s]", dirnum_to_name(dirnum));
  1384.     return DDIR_FAILED;
  1385. }
  1386.  
  1387. static int
  1388. begin_directive(
  1389.     char **const eline,
  1390.     DIRECTIVE dirnum,
  1391.     WHBLOCK *whlist,
  1392.     BUFFER *bp,
  1393.     LINEPTR *lp)
  1394. {
  1395.     int status = DDIR_COMPLETE; /* assume directive is self-contained */
  1396.     WHBLOCK *wht;        /* temporary ptr to a WHBLOCK */
  1397.     char tkn[NSTRING];    /* buffer to evaluate an expression in */
  1398.     const char *old_execstr = execstr;
  1399.  
  1400.     execstr = *eline;
  1401.  
  1402.     switch (dirnum) {
  1403.     case D_IF:    /* IF directive */
  1404.         /* grab the value of the logical exp */
  1405.         ifstk.level++;
  1406.         if (!ifstk.disabled) {
  1407.             ifstk.fired = FALSE;
  1408.             ifstk.disabled = ifstk.level;
  1409.             if (macarg(tkn) != TRUE)
  1410.                 status = DDIR_INCOMPLETE;
  1411.             else if (stol(tkn) == TRUE) {
  1412.                 ifstk.disabled = 0;
  1413.                 ifstk.fired = TRUE;
  1414.             }
  1415.         }
  1416.         break;
  1417.  
  1418.     case D_WHILE:    /* WHILE directive */
  1419.         /* grab the value of the logical exp */
  1420.         if (!ifstk.disabled) {
  1421.             if (macarg(tkn) != TRUE) {
  1422.                 status = DDIR_INCOMPLETE;
  1423.                 break;
  1424.             } else if (stol(tkn) == TRUE) {
  1425.                 break;
  1426.             }
  1427.         }
  1428.         /* drop down and act just like BREAK */
  1429.  
  1430.         /* FALLTHRU */
  1431.     case D_BREAK:    /* BREAK directive */
  1432.         if (dirnum != D_BREAK || !ifstk.disabled) {
  1433.  
  1434.             /* Jump down to the endwhile, then find the right while
  1435.              * loop.
  1436.              */
  1437.             for (wht = whlist; wht != 0; wht = wht->w_next) {
  1438.                 if (wht->w_begin == *lp)
  1439.                     break;
  1440.             }
  1441.  
  1442.             if (wht == 0) {
  1443.                 status = unbalanced_directive(dirnum);
  1444.             } else { /* reset the line pointer back.. */
  1445.                 *lp = wht->w_end;
  1446.             }
  1447.         }
  1448.         break;
  1449.  
  1450.     case D_ELSEIF:    /* ELSEIF directive */
  1451.         if (ifstk.level == 0) {
  1452.             status = unbalanced_directive(dirnum);
  1453.         } else {
  1454.             if (ifstk.fired) {
  1455.                 if (!ifstk.disabled)
  1456.                     ifstk.disabled = ifstk.level;
  1457.             } else if (macarg(tkn) != TRUE) {
  1458.                 status = DDIR_INCOMPLETE;
  1459.             } else if (!ifstk.fired
  1460.               && ifstk.disabled == ifstk.level
  1461.               && (stol(tkn) == TRUE)) {
  1462.                 ifstk.disabled = 0;
  1463.                 ifstk.fired = TRUE;
  1464.             }
  1465.         }
  1466.         break;
  1467.  
  1468.     case D_ELSE:    /* ELSE directive */
  1469.         if (ifstk.level == 0) {
  1470.             status = unbalanced_directive(dirnum);
  1471.         } else {
  1472.             if (ifstk.fired) {
  1473.                 if (!ifstk.disabled)
  1474.                     ifstk.disabled = ifstk.level;
  1475.             } else if (ifstk.disabled == ifstk.level) {
  1476.                 ifstk.disabled = 0;
  1477.                 ifstk.fired = TRUE;
  1478.             }
  1479.         }
  1480.         break;
  1481.  
  1482.     case D_ENDIF:    /* ENDIF directive */
  1483.         if (ifstk.level == 0) {
  1484.             status = unbalanced_directive(dirnum);
  1485.         } else {
  1486.             ifstk.level--;
  1487.             if (ifstk.disabled > ifstk.level)
  1488.             {
  1489.                 ifstk.disabled = 0;
  1490.                 ifstk.fired = TRUE;
  1491.             }
  1492.         }
  1493.         break;
  1494.  
  1495.     case D_GOTO:    /* GOTO directive */
  1496.         /* .....only if we are currently executing */
  1497.         if (!ifstk.disabled) {
  1498.             register LINEPTR glp;    /* line to goto */
  1499.  
  1500.             /* grab label to jump to */
  1501.             *eline = (char *)token(*eline, golabel, EOS);
  1502.             glp = label2lp(bp, golabel);
  1503.             if (glp == 0) {
  1504.                 mlforce("[No such label \"%s\"]", golabel);
  1505.                 status = DDIR_FAILED;
  1506.             } else {
  1507.                 *lp = glp;
  1508.             }
  1509.         }
  1510.         break;
  1511.  
  1512.     case D_RETURN:    /* RETURN directive */
  1513.         if (!ifstk.disabled)
  1514.             status = DDIR_INCOMPLETE;
  1515.         break;
  1516.  
  1517.     case D_ENDWHILE: /* ENDWHILE directive */
  1518.         if (!ifstk.disabled) {
  1519.             /* find the right while loop */
  1520.             for (wht = whlist; wht != 0; wht = wht->w_next) {
  1521.                 if (wht->w_type == D_WHILE
  1522.                  && wht->w_end == *lp)
  1523.                     break;
  1524.             }
  1525.  
  1526.             if (wht == 0) {
  1527.                 status = unbalanced_directive(dirnum);
  1528.             } else { /* reset the line pointer back.. */
  1529.                 *lp = lback(wht->w_begin);
  1530.             }
  1531.         }
  1532.         break;
  1533.  
  1534.     case D_FORCE:    /* FORCE directive */
  1535.         status = DDIR_FORCE;
  1536.         break;
  1537.  
  1538.     case D_UNKNOWN:
  1539.     case D_ENDM:
  1540.         break;
  1541.     }
  1542.     execstr = old_execstr;
  1543.     return status;
  1544. }
  1545.  
  1546. static WHBLOCK *
  1547. alloc_WHBLOCK(WHBLOCK *scanpt, DIRECTIVE dirnum, LINEPTR lp)
  1548. {
  1549.     WHBLOCK *whtemp;    /* temporary ptr to a WHBLOCK */
  1550.  
  1551.     if ((whtemp = typealloc(WHBLOCK)) == 0) {
  1552.         mlforce("[Out of memory during '%s' scan]",
  1553.             dirnum_to_name(dirnum));
  1554.         freewhile(scanpt);
  1555.         return 0;
  1556.     }
  1557.     whtemp->w_begin = lp;
  1558.     whtemp->w_type = dirnum;
  1559.     whtemp->w_next = scanpt;
  1560.     scanpt = whtemp;
  1561.     return scanpt;
  1562. }
  1563.  
  1564. static int
  1565. setup_dobuf(BUFFER *bp, WHBLOCK **result)
  1566. {
  1567.     int status = TRUE;
  1568.     LINEPTR lp;        /* pointer to line to execute */
  1569.     char *eline;        /* text of line to execute */
  1570.     WHBLOCK *scanpt = 0;    /* ptr during scan */
  1571.     WHBLOCK *whtemp;    /* temporary ptr to a WHBLOCK */
  1572.  
  1573.     /* scan the buffer to execute, building WHILE header blocks */
  1574.     bp->b_dot.o = 0;
  1575.     *result = 0;
  1576.     for_each_line(lp, bp) {
  1577.         int i;            /* index */
  1578.  
  1579.         bp->b_dot.l = lp;
  1580.         /* scan the current line */
  1581.         eline = lp->l_text;
  1582.         i = lp->l_used;
  1583.  
  1584.         /* trim leading whitespace */
  1585.         while (i-- > 0 && isBlank(*eline))
  1586.             ++eline;
  1587.  
  1588.         /* if there's nothing here, don't bother */
  1589.         if (i <= 0)
  1590.             continue;
  1591.  
  1592.         switch (dname_to_dirnum(eline, (size_t)i)) {
  1593.         /* if is a while directive, make a block... */
  1594.         case D_WHILE:
  1595.             if ((scanpt = alloc_WHBLOCK(scanpt, D_WHILE, lp)) == 0) {
  1596.                 status = FALSE;
  1597.             }
  1598.             break;
  1599.  
  1600.         /* if it is a break directive, make a block... */
  1601.         case D_BREAK:
  1602.             if ((scanpt = alloc_WHBLOCK(scanpt, D_BREAK, lp)) == 0) {
  1603.                 status = FALSE;
  1604.             }
  1605.             break;
  1606.  
  1607.         /* if it is an endwhile directive, record the spot... */
  1608.         case D_ENDWHILE:
  1609.             if (scanpt == NULL) {
  1610.                 mlforce("[%s with no preceding %s in '%s']",
  1611.                     dirnum_to_name(D_ENDWHILE),
  1612.                     dirnum_to_name(D_WHILE),
  1613.                     bp->b_bname);
  1614.                 status = FALSE;
  1615.             }
  1616.             /* Move top records from the scanpt list to the result
  1617.              * until we have moved all BREAK records and one WHILE
  1618.              * record.
  1619.              */
  1620.             do {
  1621.                 scanpt->w_end = lp;
  1622.                 whtemp  = *result;
  1623.                 *result = scanpt;
  1624.                 scanpt  = scanpt->w_next;
  1625.                 (*result)->w_next = whtemp;
  1626.             } while ((*result)->w_type == D_BREAK);
  1627.             break;
  1628.  
  1629.         /* nothing else requires attention */
  1630.         default:
  1631.             break;
  1632.         }
  1633.         if (status != TRUE)
  1634.             break;
  1635.     }
  1636.  
  1637.     /* while and endwhile should match! */
  1638.     if (status == TRUE && scanpt != NULL) {
  1639.         mlforce("[%s with no matching %s in '%s']",
  1640.             dirnum_to_name(D_WHILE),
  1641.             dirnum_to_name(D_ENDWHILE),
  1642.             bp->b_bname);
  1643.         status = FALSE;
  1644.     }
  1645.     return status;    /* true iff we made it to the end w/o errors */
  1646. }
  1647. #else
  1648. #define dname_to_dirnum(eline,length) \
  1649.         (eline[0] == DIRECTIVE_CHAR && !strcmp(eline+1, "endm") \
  1650.         ? D_ENDM \
  1651.         : D_UNKNOWN)
  1652. #endif
  1653.  
  1654. #if OPT_TRACE && !SMALLER
  1655. static const char *TraceIndent(int level, const char *eline, size_t length)
  1656. {
  1657.     static    const char indent[] = ".  .  .  .  .  .  .  .  ";
  1658.     switch (dname_to_dirnum(eline, length)) {
  1659.     case D_ELSE:    /* FALLTHRU */
  1660.     case D_ELSEIF:    /* FALLTHRU */
  1661.     case D_ENDIF:
  1662.         if (level > 0)
  1663.             level--;
  1664.         break;
  1665.     default:
  1666.         break;
  1667.     }
  1668.     level = strlen(indent) - (3 * level);
  1669.     if (level < 0)
  1670.         level = 0;
  1671.     return &indent[level];
  1672. }
  1673. #define TRACE_INDENT(level, eline) TraceIndent(level, eline, linlen)
  1674. #else
  1675. #define TRACE_INDENT(level, eline) "" /* nothing */
  1676. #endif
  1677.  
  1678. static int
  1679. perform_dobuf(BUFFER *bp, WHBLOCK *whlist)
  1680. {
  1681.     int status = TRUE;
  1682.     int glue = 0;        /* nonzero to append lines */
  1683.     LINEPTR lp;        /* pointer to line to execute */
  1684.     DIRECTIVE dirnum;    /* directive index */
  1685.     size_t linlen;        /* length of line to execute */
  1686.     int force;        /* force TRUE result? */
  1687.     WINDOW *wp;        /* ptr to windows to scan */
  1688.     char *einit = 0;    /* initial value of eline */
  1689.     char *eline;        /* text of line to execute */
  1690.  
  1691.     static BUFFER *dobuferrbp;
  1692.  
  1693.     /* starting at the beginning of the buffer */
  1694.     for_each_line(lp, bp) {
  1695.         bp->b_dot.l = lp;
  1696.         /* allocate eline and copy macro line to it */
  1697.  
  1698.         if (lp->l_used <= 0)
  1699.             linlen = 0;
  1700.         else
  1701.             linlen = lp->l_used;
  1702.  
  1703.         if (glue) {
  1704.             if ((einit = castrealloc(char, einit, glue+linlen+1)) == 0) {
  1705.                 status = no_memory("during macro execution");
  1706.                 break;
  1707.             }
  1708.             eline = einit + glue;
  1709.             glue  = 0;
  1710.         } else {
  1711.             if (einit != 0)
  1712.                 free(einit);
  1713.  
  1714.             if ((einit = eline = castalloc(char, linlen+1)) == 0) {
  1715.                 status = no_memory("during macro execution");
  1716.                 break;
  1717.             }
  1718.         }
  1719.  
  1720.         if (linlen != 0)
  1721.             (void)strncpy(eline, lp->l_text, linlen);
  1722.         eline[linlen] = EOS;    /* make sure it ends */
  1723.  
  1724.         /* trim leading whitespace from each line */
  1725.         {
  1726.             char *src = eline;
  1727.             char *dst = eline;
  1728.             while (isBlank(*src))
  1729.                 src++;
  1730.             while ((*dst++ = *src++) != EOS)
  1731.                 ;
  1732.             linlen -= (size_t)(src - dst);
  1733.         }
  1734.  
  1735.         /*
  1736.          * If the last character on the line is a backslash, glue the
  1737.          * following line to the one we're processing.
  1738.          */
  1739.         if (lforw(lp) != buf_head(bp)
  1740.          && linlen != 0
  1741.          && eline[linlen-1] == '\\') {
  1742.             glue = linlen + (size_t)(eline - einit) - 1;
  1743.             continue;
  1744.         }
  1745.         eline = einit;
  1746.         while (isBlank(*eline))
  1747.             ++eline;
  1748.  
  1749.         /* Skip comments and blank lines.
  1750.          * ';' for uemacs backward compatibility, and
  1751.          * '"' for vi compatibility
  1752.          */
  1753.         if (*eline == ';'
  1754.          || *eline == '"'
  1755.          || *eline == EOS)
  1756.             continue;
  1757.  
  1758. #if    OPT_DEBUGMACROS
  1759.         /* if $debug == TRUE, every line to execute
  1760.            gets echoed and a key needs to be pressed to continue
  1761.            ^G will abort the command */
  1762.  
  1763.         if (macbug) {
  1764.             char    outline[NLINE];
  1765.             (void)strcpy(outline, "<<<");
  1766.  
  1767.             /* debug macro name */
  1768.             (void)strcat(outline, bp->b_bname);
  1769.             (void)strcat(outline, ":");
  1770.  
  1771.             /* debug if levels */
  1772.             (void)strcat(outline, l_itoa(ifstk.level));
  1773.             (void)strcat(outline, "/");
  1774.             (void)strcat(outline, l_itoa(ifstk.disabled));
  1775.             (void)strcat(outline, ":");
  1776.  
  1777.             /* and lastly the line */
  1778.             (void)strcat(outline, eline);
  1779.             (void)strcat(outline, ">>>");
  1780.  
  1781.             /* write out the debug line */
  1782.             mlforce("%s",outline);
  1783.             (void)update(TRUE);
  1784.  
  1785.             /* and get the keystroke */
  1786.             if (ABORTED(keystroke())) {
  1787.                 mlforce("[Macro aborted]");
  1788.                 status = FALSE;
  1789.                 break;
  1790.             }
  1791.         }
  1792. #endif
  1793.         TRACE(("<<<%s%s:%d/%d%c%s%s>>>\n",
  1794.             (bp == curbp) ? "*" : "",
  1795.             bp->b_bname, ifstk.level, ifstk.disabled,
  1796.             ifstk.fired ? '+' : ' ',
  1797.             TRACE_INDENT(ifstk.level, eline),
  1798.             eline))
  1799.  
  1800.         /* Parse directives here.... */
  1801.         dirnum = D_UNKNOWN;
  1802.         if (*eline == DIRECTIVE_CHAR) {
  1803.  
  1804.             /* Find out which directive this is */
  1805.             dirnum = dname_to_dirnum(eline, linlen);
  1806.  
  1807.             /* and bitch if it's illegal */
  1808.             if (dirnum == D_UNKNOWN) {
  1809.                 mlforce("[Unknown directive \"%s\"]", eline);
  1810.                 status = FALSE;
  1811.                 break;
  1812.             }
  1813.  
  1814.             /* service only the ENDM macro here */
  1815.             if (dirnum == D_ENDM && !ifstk.disabled) {
  1816.                 if (!mstore) {
  1817.                     mlforce(
  1818.                     "[No macro definition in progress]");
  1819.                     status = FALSE;
  1820.                     break;
  1821.                 }
  1822.                 bstore->b_dot.l = lforw(buf_head(bstore));
  1823.                 bstore->b_dot.o = 0;
  1824.                 bstore = NULL;
  1825.                 mstore = FALSE;
  1826.                 continue;
  1827.             }
  1828.         }
  1829.  
  1830.         /* if macro store is on, just salt this away */
  1831.         if (mstore) {
  1832.             /* allocate the space for the line */
  1833.             if (addline(bstore, eline, -1) == FALSE) {
  1834.                 mlforce("[Out of memory while storing macro]");
  1835.                 status = FALSE;
  1836.                 break;
  1837.             }
  1838.             continue;
  1839.         }
  1840.  
  1841.         if (*eline == '*')
  1842.             continue;
  1843.  
  1844.         force = FALSE;
  1845.  
  1846. #if ! SMALLER
  1847.         /* now, execute directives */
  1848.         if (dirnum != D_UNKNOWN) {
  1849.             int code;
  1850.  
  1851.             /* skip past the directive */
  1852.             while (*eline && !isBlank(*eline))
  1853.                 ++eline;
  1854.             code = begin_directive(&eline, dirnum, whlist, bp, &lp);
  1855.             if (code == DDIR_FAILED) {
  1856.                 status = FALSE;
  1857.                 break;
  1858.             } else if (code == DDIR_COMPLETE) {
  1859.                 continue;
  1860.             } else if (code == DDIR_INCOMPLETE) {
  1861.                 status = TRUE; /* not exactly an error */
  1862.                 break;
  1863.             } else if (code == DDIR_FORCE) {
  1864.                 force = TRUE;
  1865.             }
  1866.         }
  1867. #endif
  1868.  
  1869.         /* execute the statement */
  1870.         /* if we are scanning and not executing..go back here */
  1871.         if (ifstk.disabled)
  1872.             status = TRUE;
  1873.         else
  1874.             status = docmd(eline,TRUE,FALSE,1);
  1875.         if (force)        /* force the status */
  1876.             status = TRUE;
  1877.  
  1878.         /* check for a command error */
  1879.         if (status != TRUE) {
  1880.             /* look if buffer is showing */
  1881.             for_each_visible_window(wp) {
  1882.                 if (wp->w_bufp == bp) {
  1883.                     /* and point it */
  1884.                     wp->w_dot.l = lp;
  1885.                     wp->w_dot.o = 0;
  1886.                     wp->w_flag |= WFHARD;
  1887.                 }
  1888.             }
  1889.             /* in any case set the buffer's dot */
  1890.             bp->b_dot.l = lp;
  1891.             bp->b_dot.o = 0;
  1892.             bp->b_wline.l = lforw(buf_head(bp));
  1893.             if (dobuferrbp == NULL) {
  1894.                 dobuferrbp = bp;
  1895.                 (void)swbuffer(bp);
  1896.                 kbd_alarm();
  1897.             }
  1898.             break;
  1899.         }
  1900.     }
  1901.  
  1902.     if (einit != 0)
  1903.         free(einit);
  1904.  
  1905.     return status;
  1906. }
  1907.  
  1908. int
  1909. dobuf(BUFFER *bp)    /* buffer to execute */
  1910. {
  1911.     int status = FALSE;    /* status return */
  1912.     WHBLOCK *whlist;    /* ptr to WHILE list */
  1913.  
  1914.     static int dobufnesting; /* flag to prevent runaway recursion */
  1915.  
  1916.     if (++dobufnesting < 9) {
  1917.  
  1918. #if ! SMALLER
  1919.         if (setup_dobuf(bp, &whlist) != TRUE) {
  1920.             status = FALSE;
  1921.         } else
  1922. #else
  1923.         whlist = NULL;
  1924. #endif
  1925.         {
  1926.             static const IFSTK new_ifstk = {0,0,0}; /* all 0's */
  1927.             const CMDFUNC *save_havemotion  = havemotion;
  1928.             IFSTK save_ifstk;
  1929.             REGIONSHAPE save_regionshape = regionshape;
  1930.  
  1931.             save_ifstk  = ifstk;
  1932.             ifstk       = new_ifstk;
  1933.             havemotion  = NULL;
  1934.             regionshape = EXACT;
  1935.  
  1936.             status = perform_dobuf(bp, whlist);
  1937.  
  1938.             ifstk       = save_ifstk;
  1939.             havemotion  = save_havemotion;
  1940.             regionshape = save_regionshape;
  1941.         }
  1942.  
  1943.         mstore = FALSE;
  1944.         freewhile(whlist);
  1945.     }
  1946.     dobufnesting--;
  1947.  
  1948.     return status;
  1949. }
  1950.  
  1951.  
  1952. /*
  1953.  * Common function for startup-file, and for :so command.
  1954.  */
  1955. int
  1956. do_source(char *fname, int n, int optional)
  1957. {
  1958.     register int status;    /* return status of name query */
  1959.     char *fspec;        /* full file spec */
  1960.  
  1961.     /* look up the path for the file */
  1962.     fspec = flook(fname,
  1963. #if SYS_MSDOS || SYS_WIN31 || SYS_OS2 || SYS_WINNT
  1964.         FL_ANYWHERE | FL_READABLE
  1965. #else
  1966.         FL_HERE | FL_HOME | FL_TABLE | FL_READABLE
  1967. #endif
  1968.         );
  1969.  
  1970.     /* if it isn't around */
  1971.     if (fspec == NULL)
  1972.         return optional ? TRUE : no_such_file(fname);
  1973.  
  1974.     /* otherwise, execute it */
  1975.     while (n-- > 0)
  1976.         if ((status=dofile(fspec)) != TRUE)
  1977.             return status;
  1978.  
  1979.     return TRUE;
  1980. }
  1981.  
  1982. #if ! SMALLER
  1983. /* ARGSUSED */
  1984. int
  1985. execfile(    /* execute a series of commands in a file */
  1986. int f GCC_UNUSED, int n)    /* default flag and numeric arg to pass on to file */
  1987. {
  1988.     register int status;
  1989.     char fname[NFILEN];    /* name of file to execute */
  1990.     static    TBUFF    *last;
  1991.  
  1992.     if ((status = mlreply_file("File to execute: ", &last, FILEC_READ, fname)) != TRUE)
  1993.         return status;
  1994.  
  1995.     return do_source(fname, n, FALSE);
  1996. }
  1997. #endif
  1998.  
  1999. static L_NUM
  2000. get_b_lineno(BUFFER *bp)
  2001. {
  2002.     L_NUM result;
  2003.     WINDOW *wp;
  2004.  
  2005.     /* try to save the original location, so we can restore it */
  2006.     if (curwp->w_bufp == bp)
  2007.         result = line_no(bp, curwp->w_dot.l);
  2008.     else if ((wp = bp2any_wp(bp)) != 0)
  2009.         result = line_no(bp, wp->w_dot.l);
  2010.     else
  2011.         result = line_no(bp, bp->b_dot.l);
  2012.     return result;
  2013. }
  2014.  
  2015. static void
  2016. set_b_lineno(BUFFER *bp, L_NUM n)
  2017. {
  2018.     WINDOW *wp;
  2019.     LINE *lp;
  2020.  
  2021.     for_each_line(lp,bp) {
  2022.         if (--n <= 0) {
  2023.             bp->b_dot.l = lp;
  2024.             bp->b_dot.o = 0;
  2025.             for_each_visible_window(wp) {
  2026.                 if (wp->w_bufp == bp) {
  2027.                     wp->w_dot = bp->b_dot;
  2028.                     wp->w_flag |= WFMOVE;
  2029.                 }
  2030.             }
  2031.             break;
  2032.         }
  2033.     }
  2034. }
  2035.  
  2036. /*    dofile:    yank a file into a buffer and execute it
  2037.         if there are no errors, delete the buffer on exit */
  2038.  
  2039. int
  2040. dofile(
  2041. char *fname)        /* file name to execute */
  2042. {
  2043.     register BUFFER *bp;    /* buffer to place file to execute */
  2044.     register int status;    /* results of various calls */
  2045.     register int odiscmd;
  2046.     int clobber = FALSE;
  2047.     int original;
  2048.  
  2049.     /*
  2050.      * Check first for the name, assuming it's a filename.  If we don't
  2051.      * find an existing buffer with that filename, create a buffer.
  2052.      */
  2053.     if ((bp = find_b_file(fname)) == 0) {
  2054.         if ((bp = make_bp(fname, 0)) == 0)
  2055.             return FALSE;
  2056.         clobber = TRUE;
  2057.     }
  2058.     bp->b_flag = BFEXEC;
  2059.  
  2060.     /* try to save the original location, so we can restore it */
  2061.     original = get_b_lineno(bp);
  2062.  
  2063.     /* and try to read in the file to execute */
  2064.     if ((status = readin(fname, FALSE, bp, TRUE)) == TRUE) {
  2065.  
  2066.         /* go execute it! */
  2067.         odiscmd = discmd;
  2068.         discmd = FALSE;
  2069.         status = dobuf(bp);
  2070.         discmd = odiscmd;
  2071.  
  2072.         /*
  2073.          * If no errors occurred, and if the buffer isn't displayed,
  2074.          * remove it, unless it was loaded before we entered this
  2075.          * function.  In that case, (try to) jump back to the original
  2076.          * location.
  2077.          */
  2078.         if (status != TRUE)
  2079.             (void)swbuffer(bp);
  2080.         else if ((bp->b_nwnd == 0) && clobber)
  2081.             (void)zotbuf(bp);
  2082.         else
  2083.             set_b_lineno(bp,original);
  2084.     }
  2085.     return status;
  2086. }
  2087.  
  2088. /*    cbuf:    Execute the contents of a numbered buffer    */
  2089.  
  2090. static int
  2091. cbuf(
  2092. int f, int n,    /* default flag and numeric arg */
  2093. int bufnum)    /* number of buffer to execute */
  2094. {
  2095.     register BUFFER *bp;        /* ptr to buffer to execute */
  2096.     register int status;        /* status return */
  2097.     static char bufname[NBUFN];
  2098.     register int odiscmd;
  2099.  
  2100.     if (!f) n = 1;
  2101.  
  2102.     /* make the buffer name */
  2103.     (void)lsprintf(bufname, MACRO_N_BufName, bufnum);
  2104.  
  2105.     /* find the pointer to that buffer */
  2106.     if ((bp = find_b_name(bufname)) == NULL) {
  2107.         mlforce("[Macro %d not defined]", bufnum);
  2108.         return FALSE;
  2109.     }
  2110.  
  2111.     odiscmd = discmd;
  2112.     discmd = FALSE;
  2113.     status = TRUE;
  2114.     /* and now execute it as asked */
  2115.     while (n-- > 0 && status == TRUE)
  2116.         status = dobuf(bp);
  2117.  
  2118.     discmd = odiscmd;
  2119.     return status;
  2120.  
  2121. }
  2122.  
  2123. #include "neexec.h"
  2124.