/* exec.c -- implements exec.h */
#include "exec.h"

/*  exec.c
 *
 *  This file is for functions dealing with execution of
 *  commands, command lines, buffers, files and startup files.
 *
 *  written 1986 by Daniel Lawrence
 *  modified by Petri Kutvonen
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "buffer.h"
#include "bind.h"
#include "display.h"
#include "estruct.h"
#include "eval.h"
#include "file.h"
#include "flook.h"
#include "input.h"
#include "line.h"
#include "random.h"
#include "util.h"
#include "window.h"


static char *execstr = NULL ;	/* pointer to string to execute */
boolean clexec = FALSE ;    /* command line execution flag  */



/*  Directive definitions   */

#define DIF     0
#define DELSE       1
#define DENDIF      2
#define DGOTO       3
#define DRETURN     4
#define DENDM       5
#define DWHILE      6
#define DENDWHILE   7
#define DBREAK      8
#define DFORCE      9

#define NUMDIRS     10

/* The !WHILE directive in the execution language needs to
 * stack references to pending whiles. These are stored linked
 * to each currently open procedure via a linked list of
 * the following structure.
*/
struct while_block {
    struct line *w_begin;        /* ptr to !while statement */
    struct line *w_end;          /* ptr to the !endwhile statement */
    int w_type;          /* block type */
    struct while_block *w_next;  /* next while */
};

#define BTWHILE     1
#define BTBREAK     2

/* directive name table:
    This holds the names of all the directives....  */

static const char *dname[] = {
    "if", "else", "endif",
    "goto", "return", "endm",
    "while", "endwhile", "break",
    "force"
};

static char golabel[ NSTRING] = "" ;    /* current line to go to        */
static int execlevel = 0 ;          /* execution IF level           */
static struct buffer *bstore = NULL ;   /* buffer to store macro text to */
static int mstore = FALSE ;         /* storing text to macro flag   */

static int dobuf( struct buffer *bp) ;
static void freewhile( struct while_block *wp) ;
static int macarg( char *tok, int toksz) ;

/*
 * Execute a named command even if it is not bound.
 */
int namedcmd( int f, int n) {
    /* prompt the user to type a named command */
    mlwrite("execute-named-cmd: ");

    /* and now get the function name to execute */
	nbind_p nbp = getname() ;
	if( nbp == NULL)	/* abort */
		return FALSE ;

    fnp_t kfunc = nbp->n_func ;
    if (kfunc == NULL) {
        mlwrite("(No such function)");
        return FALSE;
    }

	if( (bind_tag( nbp) & 1) && (curbp->b_mode & MDVIEW))
		return rdonly() ;

    /* and then execute the command */
    return kfunc(f, n) ;
}

static int docmd( char *cline) ;

/*
 * execcmd:
 *  Execute a command line command to be typed in
 *  by the user
 *
 * int f, n;        default Flag and Numeric argument
 */
int execcmd( int f, int n) {
    int status ;	/* status return */
    char *cmdstr ;	/* string holding command to execute */

    /* get the line wanted */
    status = newmlarg( &cmdstr, "execute-command-line: ", 0) ;
    if( status != TRUE)
        return status ;

    execlevel = 0 ;
    while( status == TRUE && n-- > 0)
        status = docmd( cmdstr) ;

    free( cmdstr) ;
    return status ;
}

/*
 * docmd:
 *  take a passed string as a command line and translate
 *  it to be executed as a command. This function will be
 *  used by execute-command-line and by all source and
 *  startup files. Lastflag/thisflag is also updated.
 *
 *  format of the command line is:
 *
 *      {# arg} <command-name> {<argument string(s)>}
 *
 * char *cline;     command line to execute
 */
static int docmd( char *cline) {
    int f;      /* default argument flag */
    int n;      /* numeric repeat value */
    int status;     /* return status of function */
    boolean oldcle ;    /* old contents of clexec flag */
    char *oldestr;      /* original exec string */
    char tkn[NSTRING];  /* next token off of command line */

    /* if we are scanning and not executing..go back here */
    if (execlevel)
        return TRUE;

    oldestr = execstr;  /* save last ptr to string to execute */
    execstr = cline;    /* and set this one as current */

    /* first set up the default command values */
    f = FALSE;
    n = 1;
    lastflag = thisflag;
    thisflag = 0;

    status = macarg( tkn, sizeof tkn) ;
    if( status != TRUE) {   /* and grab the first token */
        execstr = oldestr;
        return status;
    }

    /* process leadin argument */
    if( !is_it_cmd( tkn)) {
        f = TRUE;
/* macarg already includes a getval, skip for now
		mystrscpy( tkn, getval( tkn), sizeof tkn) ;
*/
        n = atoi(tkn);

        /* and now get the command to execute */
        status = macarg( tkn, sizeof tkn) ;
        if( status != TRUE) {
            execstr = oldestr;
            return status;
        }
    }

    /* and match the token to see if it exists */
	nbind_p nbp = fncmatch( tkn) ;
    fnp_t fnc = nbp->n_func ;
    if( fnc == NULL) {
        mlwrite("(No such Function)");
        execstr = oldestr;
        return FALSE;
    }

	if( (bind_tag( nbp) & 1) && (curbp->b_mode & MDVIEW))
		status = rdonly() ;
	else {
	    /* save the arguments and go execute the command */
    	oldcle = clexec;    /* save old clexec flag */
    	clexec = TRUE;      /* in cline execution */
	    status = fnc( f, n) ;	/* call the function */
	    clexec = oldcle;    /* restore clexec flag */
    	execstr = oldestr;
	}

    cmdstatus = status; /* save the status */
    return status;
}

/*
 * new token:
 *  chop a token off a string
 *  return a pointer past the token
 *
 * char *src		in, source string
 * char **tokref	out, destination of newly allocated token string
 */
static char *newtoken( char *src, char **tokref) {
    boolean quotef ;	/* is the current string quoted? */
    char	*tok ;		/* allocated string */
    int		size ;		/* allocated size */
    int		idx = 0 ;	/* insertion point into token string */

	tok = malloc( NSTRING) ;
	size = (tok == NULL) ? 0 : NSTRING ;

    /* first scan past any whitespace in the source string */
    while (*src == ' ' || *src == '\t')
        ++src;

    /* scan through the source string */
    quotef = FALSE;
    while (*src) {
	    char c ;	/* temporary character */

        /* process special characters */
        if (*src == '~') {
            ++src;
            if (*src == 0)
                break;
            switch (*src++) {
            case 'r':
                c = 13;
                break;
            case 'n':
                c = 10;
                break;
            case 't':
                c = 9;
                break;
            case 'b':
                c = 8;
                break;
            case 'f':
                c = 12;
                break;
            default:
                c = *(src - 1);
            }
        } else {
            /* check for the end of the token */
            if (quotef) {
                if (*src == '"')
                    break;
            } else {
                if (*src == ' ' || *src == '\t')
                    break;
            }

            /* set quote mode if quote found */
            if (*src == '"')
                quotef = TRUE;

            /* record the character */
            c = *src++;
        }

		if( idx < size - 1)
			tok[ idx++] = c ;
		else if( size > 1) {
			char *tmptok ;
			
			tmptok = malloc( size + 32) ;
			if( tmptok == NULL)
				size = 0 ;
			else {
				memcpy( tmptok, tok, idx) ;
				free( tok) ;
				tok = tmptok ;
				size += 32 ;
				tok[ idx++] = c ;
			}
		}
    }

    /* terminate the token and exit */
    if (*src)
        ++src;

    if( tok != NULL)
        tok[ idx] = 0 ;

    *tokref = tok ;
    return src;
}

static char *token( char *srcstr, char *tok, int maxtoksize) {
	char *newtok ;

	srcstr = newtoken( srcstr, &newtok) ;
	if( newtok == NULL)
		tok[ 0] = 0 ;
	else {
		mystrscpy( tok, newtok, maxtoksize) ;
		free( newtok) ;
	}

	return srcstr ;
}

void gettoken( char *tok, int maxtoksize) {
	execstr = token( execstr, tok, maxtoksize) ;
}

static char *getnewtoken( void) {
	char *tok ;

	execstr = newtoken( execstr, &tok) ;
	return tok ;
}

boolean gettokval( char *tok, int size) {
	char *tmpbuf ;

    /* grab token and advance past */
	tmpbuf = getnewtoken() ;
	if( tmpbuf == NULL)
		return FALSE ;

    /* evaluate it */
    mystrscpy( tok, getval( tmpbuf), size) ;
    free( tmpbuf) ;
    return TRUE ;
}

char *getnewtokval( void) {
	char *tmpbuf ;
	char *valbuf ;

    /* grab token and advance past */
	tmpbuf = getnewtoken() ;
	if( tmpbuf == NULL)
		return NULL ;

    /* evaluate it */
    const char *tmpval = getval( tmpbuf) ;
    valbuf = malloc( strlen( tmpval) + 1 ) ;
    if( valbuf != NULL)
		strcpy( valbuf, tmpval) ;

    free( tmpbuf) ;
    return valbuf ;
}

/*
 * get a macro line argument
 *
 * char *tok;       buffer to place argument
 */
static int macarg( char *tok, int toksz) {
	int status ;
	boolean savcle ;	/* buffer to store original clexec */

	savcle = clexec ;	/* save execution mode */
	clexec = TRUE ;		/* get the argument */
	status = gettokval( tok, toksz) ;
	clexec = savcle ;	/* restore execution mode */
	return status ;
}

/*
 * storemac:
 *  Set up a macro buffer and flag to store all
 *  executed command lines there
 *
 * int f;       default flag
 * int n;       macro number to use
 */
static char macbufname[] = "*Macro xx*" ;
#define MACDIGITPOS 7

int storemac(int f, int n)
{
    struct buffer *bp;  /* pointer to macro buffer */

    /* must have a numeric argument to this function */
    if (f == FALSE) {
        mlwrite("No macro specified");
        return FALSE;
    }

    /* range check the macro number */
    if (n < 1 || n > 40) {
        mlwrite("Macro number out of range");
        return FALSE;
    }

    /* construct the macro buffer name */
    macbufname[ MACDIGITPOS]     = '0' + (n / 10) ;
    macbufname[ MACDIGITPOS + 1] = '0' + (n % 10) ;

    /* set up the new macro buffer */
    bp = bfind( macbufname, TRUE, BFINVS) ;
    if( bp == NULL) {
        mlwrite("Can not create macro");
        return FALSE;
    }

    /* and make sure it is empty */
    bclear(bp);

    /* and set the macro store pointers to it */
    mstore = TRUE;
    bstore = bp;
    return TRUE;
}

/*
**  exec -- execute a buffer
**	common to execute buffer, procedure and macro
*/
static int exec( int n, char *bufname, char *errstr) {
    struct buffer *bp ;		/* ptr to buffer to execute */

    /* find the pointer to that buffer */
    bp = bfind( bufname, FALSE, 0) ;
    if( bp == NULL) {
        mlwrite( "No such %s", errstr) ;
        return FALSE ;
    }

    /* and now execute it as asked */
    int status = TRUE ;
    while( status == TRUE && n-- > 0)
        status = dobuf( bp) ;

    return status ;
}

#if PROC
/*
 * storeproc:
 *  Set up a procedure buffer and flag to store all
 *  executed command lines there
 *
 * int f;       default flag
 * int n;       macro number to use
 */
int storeproc( int f, int n) {
    struct buffer *bp ;	/* pointer to macro buffer */
    int status ;		/* return status */
    bname_t bname ;		/* name of buffer to use */
    char *name ;

    /* a numeric argument means its a numbered macro */
    if( f == TRUE)
        return storemac( f, n) ;

    /* get the name of the procedure */
    status = newmlarg( &name, "Procedure name: ", sizeof bname - 2) ;
    if( status != TRUE)
        return status ;

    /* construct the macro buffer name */
    bname[ 0] = '*';
    mystrscpy( &bname[ 1], name, sizeof bname - 2) ;
    strcat( bname, "*") ;
	free( name) ;

    /* set up the new macro buffer */
    bp = bfind( bname, TRUE, BFINVS) ;
    if( bp == NULL) {
        mlwrite( "Can not create macro") ;
        return FALSE ;
    }

    /* and make sure it is empty */
    bclear( bp) ;

    /* and set the macro store pointers to it */
    mstore = TRUE ;
    bstore = bp ;
    return TRUE ;
}

/*
 * execproc:
 *  Execute a procedure
 *
 * int f, n;        default flag and numeric arg
 */
int execproc( int f, int n) {
    int status ;			/* status return */
    bname_t bufn ;		/* name of buffer to execute */
    char *name ;

    /* find out what buffer the user wants to execute */
    status = newmlarg( &name, "execute-procedure: ", sizeof bufn - 2) ;
    if( status != TRUE)
        return status ;

    /* construct the buffer name */
    bufn[ 0] = '*' ;
    mystrscpy( &bufn[ 1], name, sizeof bufn - 2) ;
    strcat( bufn, "*") ;
    free( name) ;

	return exec( n, bufn, "procedure") ;
}
#endif

/*
 * execbuf:
 *  Execute the contents of a buffer of commands
 *
 * int f, n;        default flag and numeric arg
 */
int execbuf( int f, int n) {
    int status ;		/* status return */
    char *bufn ;		/* name of buffer to execute */

    /* find out what buffer the user wants to execute */
    status = newmlarg( &bufn, "Execute buffer: ", sizeof( bname_t)) ;
    if( status != TRUE)
        return status ;

    status = exec( n, bufn, "buffer") ;
    free( bufn) ;
    return status ;
}

/*
 * dobuf:
 *  execute the contents of the buffer pointed to
 *  by the passed BP
 *
 *  Directives start with a "!" and include:
 *
 *  !endm       End a macro
 *  !if (cond)  conditional execution
 *  !else
 *  !endif
 *  !return     Return (terminating current macro)
 *  !goto <label>   Jump to a label in the current macro
 *  !force      Force macro to continue...even if command fails
 *  !while (cond)   Execute a loop if the condition is true
 *  !endwhile
 *
 *  Line Labels begin with a "*" as the first nonblank char, like:
 *
 *  *LBL01
 *
 * struct buffer *bp;       buffer to execute
 */
static int dobuf(struct buffer *bp)
{
    int status; /* status return */
    struct line *lp;    /* pointer to line to execute */
    struct line *hlp;   /* pointer to line header */
    struct line *glp;   /* line to goto */
    struct line *mp;        /* Macro line storage temp */
    int dirnum;     /* directive index */
    int linlen;     /* length of line to execute */
    int i;          /* index */
    int force;      /* force TRUE result? */
    struct window *wp;      /* ptr to windows to scan */
    struct while_block *whlist; /* ptr to !WHILE list */
    struct while_block *scanner;    /* ptr during scan */
    struct while_block *whtemp; /* temporary ptr to a struct while_block */
    char *einit;        /* initial value of eline */
    char *eline;        /* text of line to execute */
    char tkn[NSTRING];  /* buffer to evaluate an expresion in */

    /* clear IF level flags/while ptr */
    execlevel = 0;
    whlist = NULL;
    scanner = NULL;

    /* scan the buffer to execute, building WHILE header blocks */
    hlp = bp->b_linep;
    lp = hlp->l_fp;
    while (lp != hlp) {
        /* scan the current line */
        eline = lp->l_text;
        i = lp->l_used;

        /* trim leading whitespace */
        while (i-- > 0 && (*eline == ' ' || *eline == '\t'))
            ++eline;

        /* if theres nothing here, don't bother */
        if (i <= 0)
            goto nxtscan;

        /* if is a while directive, make a block... */
        if (eline[0] == '!' && eline[1] == 'w' && eline[2] == 'h') {
            whtemp = (struct while_block *)malloc(sizeof(struct while_block));
            if (whtemp == NULL) {
                  noram:mlwrite
                    ("%%Out of memory during while scan");
                  failexit:freewhile
                    (scanner);
                freewhile(whlist);
                return FALSE;
            }
            whtemp->w_begin = lp;
            whtemp->w_type = BTWHILE;
            whtemp->w_next = scanner;
            scanner = whtemp;
        }

        /* if is a BREAK directive, make a block... */
        if (eline[0] == '!' && eline[1] == 'b' && eline[2] == 'r') {
            if (scanner == NULL) {
                mlwrite
                    ("%%!BREAK outside of any !WHILE loop");
                goto failexit;
            }
            whtemp = (struct while_block *)malloc(sizeof(struct while_block));
            if (whtemp == NULL)
                goto noram;
            whtemp->w_begin = lp;
            whtemp->w_type = BTBREAK;
            whtemp->w_next = scanner;
            scanner = whtemp;
        }

        /* if it is an endwhile directive, record the spot... */
        if (eline[0] == '!' && strncmp(&eline[1], "endw", 4) == 0) {
            if (scanner == NULL) {
                mlwrite
                    ("%%!ENDWHILE with no preceding !WHILE in '%s'",
                     bp->b_bname);
                goto failexit;
            }
            /* move top records from the scanner list to the
               whlist until we have moved all BREAK records
               and one WHILE record */
            do {
                scanner->w_end = lp;
                whtemp = whlist;
                whlist = scanner;
                scanner = scanner->w_next;
                whlist->w_next = whtemp;
            } while (whlist->w_type == BTBREAK);
        }

          nxtscan:      /* on to the next line */
        lp = lp->l_fp;
    }

    /* while and endwhile should match! */
    if (scanner != NULL) {
        mlwrite("%%!WHILE with no matching !ENDWHILE in '%s'",
            bp->b_bname);
        goto failexit;
    }

    /* let the first command inherit the flags from the last one.. */
    thisflag = lastflag;

    /* starting at the beginning of the buffer */
    hlp = bp->b_linep;
    lp = hlp->l_fp;
    while (lp != hlp) {
        /* allocate eline and copy macro line to it */
        linlen = lp->l_used;
        if ((einit = eline = malloc(linlen + 1)) == NULL) {
            mlwrite("%%Out of Memory during macro execution");
            freewhile(whlist);
            return FALSE;
        }

        mystrscpy( eline, lp->l_text, linlen + 1) ;

        /* trim leading whitespace */
        while (*eline == ' ' || *eline == '\t')
            ++eline;

        /* dump comments and blank lines */
        if (*eline == ';' || *eline == '#' || *eline == 0)
            goto onward;

#if DEBUGM
        /* if $debug == TRUE, every line to execute
           gets echoed and a key needs to be pressed to continue
           ^G will abort the command */

		if( macbug) {
			int c ;
        	
            /* debug macro name, if levels and lastly the line */
			c = mdbugout( "<<<%s:%d:%s>>>", bp->b_bname, execlevel, eline) ;
        	if( c == abortc) {
                freewhile( whlist) ;
				free( einit) ;
                return FALSE ;
        	} else if( c == metac) {
        		macbug = FALSE ;
        	}
        }
#endif

        /* Parse directives here.... */
        dirnum = -1;
        if (*eline == '!') {
            /* Find out which directive this is */
            ++eline;
            for (dirnum = 0; dirnum < NUMDIRS; dirnum++)
                if (strncmp(eline, dname[dirnum],
                        strlen(dname[dirnum])) == 0)
                    break;

            /* and bitch if it's illegal */
            if (dirnum == NUMDIRS) {
                mlwrite("%%Unknown Directive");
                freewhile(whlist);
				free( einit) ;
                return FALSE;
            }

            /* service only the !ENDM macro here */
            if (dirnum == DENDM) {
                mstore = FALSE;
                bstore = NULL;
                goto onward;
            }

            /* restore the original eline.... */
            --eline;
        }

        /* if macro store is on, just salt this away */
        if (mstore) {
            /* allocate the space for the line */
            linlen = strlen(eline);
            if ((mp = lalloc(linlen)) == NULL) {
				free( einit) ;
                mlwrite( "Out of memory while storing macro") ;
                return FALSE ;
            }

            /* copy the text into the new line */
            for (i = 0; i < linlen; ++i)
                lputc(mp, i, eline[i]);

            /* attach the line to the end of the buffer */
            bstore->b_linep->l_bp->l_fp = mp;
            mp->l_bp = bstore->b_linep->l_bp;
            bstore->b_linep->l_bp = mp;
            mp->l_fp = bstore->b_linep;
            goto onward;
        }


        force = FALSE;

        /* dump comments */
        if (*eline == '*')
            goto onward;

        /* now, execute directives */
        if (dirnum != -1) {
            /* skip past the directive */
            while (*eline && *eline != ' ' && *eline != '\t')
                ++eline;
            execstr = eline;

            switch (dirnum) {
            case DIF:   /* IF directive */
                /* grab the value of the logical exp */
                if (execlevel == 0) {
                    if( macarg( tkn, sizeof tkn) != TRUE)
                        goto eexec;
                    if (stol(tkn) == FALSE)
                        ++execlevel;
                } else
                    ++execlevel;
                goto onward;

            case DWHILE:    /* WHILE directive */
                /* grab the value of the logical exp */
                if (execlevel == 0) {
                    if( macarg( tkn, sizeof tkn) != TRUE)
                        goto eexec;
                    if (stol(tkn) == TRUE)
                        goto onward;
                }
                /* drop down and act just like !BREAK */

				/* fallthrough */
            case DBREAK:    /* BREAK directive */
                if (dirnum == DBREAK && execlevel)
                    goto onward;

                /* jump down to the endwhile */
                /* find the right while loop */
                whtemp = whlist;
                while (whtemp) {
                    if (whtemp->w_begin == lp)
                        break;
                    whtemp = whtemp->w_next;
                }

                if (whtemp == NULL) {
                    mlwrite
                        ("%%Internal While loop error");
                    freewhile(whlist);
                    return FALSE;
                }

                /* reset the line pointer back.. */
                lp = whtemp->w_end;
                goto onward;

            case DELSE: /* ELSE directive */
                if (execlevel == 1)
                    --execlevel;
                else if (execlevel == 0)
                    ++execlevel;
                goto onward;

            case DENDIF:    /* ENDIF directive */
                if (execlevel)
                    --execlevel;
                goto onward;

            case DGOTO: /* GOTO directive */
                /* .....only if we are currently executing */
                if (execlevel == 0) {
                    /* grab label to jump to */
                    eline = token( eline, golabel, sizeof golabel) ;
                    linlen = strlen(golabel);
                    glp = hlp->l_fp;
                    while (glp != hlp) {
                        if (*glp->l_text == '*' &&
                            (strncmp
                             (&glp->l_text[1],
                              golabel,
                              linlen) == 0)) {
                            lp = glp;
                            goto onward;
                        }
                        glp = glp->l_fp;
                    }
                    mlwrite("%%No such label");
                    freewhile(whlist);
                    return FALSE;
                }
                goto onward;

            case DRETURN:   /* RETURN directive */
                if (execlevel == 0)
                    goto eexec;
                goto onward;

            case DENDWHILE: /* ENDWHILE directive */
                if (execlevel) {
                    --execlevel;
                    goto onward;
                } else {
                    /* find the right while loop */
                    whtemp = whlist;
                    while (whtemp) {
                        if (whtemp->w_type ==
                            BTWHILE
                            && whtemp->w_end == lp)
                            break;
                        whtemp = whtemp->w_next;
                    }

                    if (whtemp == NULL) {
                        mlwrite
                            ("%%Internal While loop error");
                        freewhile(whlist);
                        return FALSE;
                    }

                    /* reset the line pointer back.. */
                    lp = whtemp->w_begin->l_bp;
                    goto onward;
                }

            case DFORCE:    /* FORCE directive */
                force = TRUE;

            }
        }

        /* execute the statement */
        status = docmd(eline);
        if (force)  /* force the status */
            status = TRUE;

        /* check for a command error */
        if (status != TRUE) {
            /* look if buffer is showing */
            wp = wheadp;
            while (wp != NULL) {
                if (wp->w_bufp == bp) {
                    /* and point it */
                    wp->w_dotp = lp;
                    wp->w_doto = 0;
                    wp->w_flag |= WFHARD;
                }
                wp = wp->w_wndp;
            }
            /* in any case set the buffer . */
            bp->b_dotp = lp;
            bp->b_doto = 0;
            free(einit);
            execlevel = 0;
            freewhile(whlist);
            return status;
        }

          onward:       /* on to the next line */
        free(einit);
        lp = lp->l_fp;
    }

      eexec:            /* exit the current function */
    execlevel = 0;
    freewhile(whlist);
    return TRUE;
}

/*
 * free a list of while block pointers
 *
 * struct while_block *wp;      head of structure to free
 */
static void freewhile(struct while_block *wp)
{
    if (wp == NULL)
        return;
    if (wp->w_next)
        freewhile(wp->w_next);
    free(wp);
}

/*
 * execute a series of commands in a file
 *
 * int f, n;        default flag and numeric arg to pass on to file
 */
int execfile( int f, int n) {
    int status ;	/* return status of name query */
    char *fname ;	/* name of file to execute */
    char *fspec ;	/* full file spec */

    status = newmlarg( &fname, "Execute file: ", 0) ;
    if( status != TRUE)
        return status ;

/* look up the path for the file */
    fspec = flook( fname, FALSE) ;    /* used to be TRUE, P.K. */
    free( fname) ;

/* if it isn't around */
    if( fspec == NULL)
        return FALSE ;

/* otherwise, execute it */
    while( status == TRUE && n-- > 0)
        status = dofile( fspec) ;

    return status ;
}

/*
 * dofile:
 *  yank a file into a buffer and execute it
 *  if there are no errors, delete the buffer on exit
 *
 * char *fname;     file name to execute
 */
int dofile( const char *fname) {
    struct buffer *bp;  /* buffer to place file to exeute */
    struct buffer *cb;  /* temp to hold current buf while we read */
    int status; /* results of various calls */
    bname_t bname ; /* name of buffer */

    makename(bname, fname); /* derive the name of the buffer */
    unqname(bname);     /* make sure we don't stomp things */
    if ((bp = bfind(bname, TRUE, 0)) == NULL)   /* get the needed buffer */
        return FALSE;

    bp->b_mode = MDVIEW;    /* mark the buffer as read only */
    cb = curbp;     /* save the old buffer */
    curbp = bp;     /* make this one current */
    /* and try to read in the file to execute */
    if ((status = readin(fname, FALSE)) != TRUE) {
        curbp = cb; /* restore the current buffer */
        return status;
    }

    /* go execute it! */
    curbp = cb;     /* restore the current buffer */
    if ((status = dobuf(bp)) != TRUE)
        return status;

    /* if not displayed, remove the now unneeded buffer and exit */
    if (bp->b_nwnd == 0)
        zotbuf(bp);
    return TRUE;
}

/*
 * cbuf:
 *  Execute the contents of a numbered buffer
 *
 * int f, n;        default flag and numeric arg
 * int bufnum;      number of buffer to execute
 */
static int cbuf( int f, int n, int bufnum) {
    /* make the buffer name */
    macbufname[ MACDIGITPOS]     = '0' + (bufnum / 10) ;
    macbufname[ MACDIGITPOS + 1] = '0' + (bufnum % 10) ;

	return exec( n, macbufname, "macro") ;
}

/* execute buffer of numbered macro [1..40] */
#define cbufnn( nn) \
int cbuf##nn( int f, int n) { \
    return cbuf( f, n, nn) ; \
}

cbufnn( 1)
cbufnn( 2)
cbufnn( 3)
cbufnn( 4)
cbufnn( 5)
cbufnn( 6)
cbufnn( 7)
cbufnn( 8)
cbufnn( 9)
cbufnn( 10)
cbufnn( 11)
cbufnn( 12)
cbufnn( 13)
cbufnn( 14)
cbufnn( 15)
cbufnn( 16)
cbufnn( 17)
cbufnn( 18)
cbufnn( 19)
cbufnn( 20)
cbufnn( 21)
cbufnn( 22)
cbufnn( 23)
cbufnn( 24)
cbufnn( 25)
cbufnn( 26)
cbufnn( 27)
cbufnn( 28)
cbufnn( 29)
cbufnn( 30)
cbufnn( 31)
cbufnn( 32)
cbufnn( 33)
cbufnn( 34)
cbufnn( 35)
cbufnn( 36)
cbufnn( 37)
cbufnn( 38)
cbufnn( 39)
cbufnn( 40)

/* end of exec.c */