/* exec.c -- implements exec.h */ #include "exec.h" /* This file is for bindable functions dealing with execution of commands, command lines, buffers, files and startup files. written 1986 by Daniel Lawrence modified by Petri Kutvonen */ #include #include #include #include "buffer.h" #include "bind.h" #include "defines.h" /* malloc/allocate, free/release */ #include "eval.h" #include "file.h" #include "flook.h" #include "input.h" #include "line.h" #include "list.h" #include "mlout.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 DBREAK 0 #define DELSE 1 #define DENDIF 2 #define DENDM 3 #define DENDWHILE 4 #define DFORCE 5 #define DGOTO 6 #define DGOSUB 7 #define DIF 8 #define DRETURN 9 #define DWHILE 10 /* directive name table: holds the names of all the directives.... */ static const char *dname[] = { "break", "else", "endif", "endm", "endwhile", "force", "goto", "gosub", "if", "return", "while" } ; #define NUMDIRS ARRAY_SIZE( dname) typedef struct caller { struct caller *next ; line_p caller ; } *caller_p ; /* 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. */ typedef struct while_block { struct while_block *w_next ; /* next while */ line_p w_begin ; /* ptr to !while statement */ line_p w_end ; /* ptr to the !endwhile statement */ int w_type ; /* block type */ } *while_p ; #define BTWHILE 1 #define BTBREAK 2 static char golabel[ NSTRING] = "" ; /* current line to go to */ static buffer_p bstore = NULL ; /* buffer to store macro text to */ static int storing_f = FALSE ; /* storing text to macro flag */ static int dobuf( buffer_p bp) ; static int macarg( char *tok, int toksz) ; /* Execute a named command even if it is not bound. */ BINDABLE( namedcmd) { /* prompt the user to type a named command */ mloutstr( "execute-named-cmd: "); /* and now get the function name to execute */ nbind_p nbp = getname() ; if( nbp == NULL) /* abort */ return ABORT ; fnp_t kfunc = nbp->n_func ; if( kfunc == NULL) return mloutfail( "(No such function)") ; if( (bind_tag( nbp) & 1) && (curbp->b_mode & MDVIEW)) return rdonly() ; /* and then execute the command */ return kfunc( f, n) ; } /* 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} {} * * char *cline; command line to execute */ static int docmd( char *cline) { char tkn[ NSTRING] ; /* next token off of command line */ int status ; char *oldestr = execstr ; /* save last ptr to string to execute */ execstr = cline ; /* and set this one as current */ /* first set up the default command values */ int f = FALSE ; int n = 1 ; lastflag = thisflag ; thisflag = 0 ; for( ;;) { /* evaluate next token */ status = macarg( tkn, sizeof tkn) ; if( status != TRUE || !*tkn) { execstr = oldestr ; return status ; } /* proceed if it is a command */ if( is_it_cmd( tkn)) break ; /* otherwise set as argument of coming command */ f = TRUE ; n = atoi( tkn) ; } /* and match the token to see if it exists */ nbind_p nbp = fncmatch( tkn) ; fnp_t fnc = nbp->n_func ; if( fnc == NULL) { execstr = oldestr ; return mloutfail( "(No such Function)") ; } if( (bind_tag( nbp) & 1) && (curbp->b_mode & MDVIEW)) status = rdonly() ; else { /* save the arguments and go execute the command */ boolean 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 ; } /* execcmd: * Execute a command line command to be typed in by the user * * int f, n; default Flag and Numeric argument */ BINDABLE( execcmd) { char *cmdstr ; /* string holding command to execute */ /* get the line wanted */ int status = newmlarg( &cmdstr, "execute-command-line: ", 0) ; if( status != TRUE) return status ; while( status == TRUE && n-- > 0) status = docmd( cmdstr) ; free( cmdstr) ; 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) { char *tok = malloc( NSTRING) ; int size = (tok == NULL) ? 0 : NSTRING ; int idx = 0 ; /* insertion point into token string */ /* first scan past any whitespace in the source string */ while( *src == ' ' || *src == '\t') ++src ; /* scan through the source string */ boolean quote_f = FALSE ; /* is the current string quoted? */ char c = *src ; for( ; c ; c = *++src) { /* process special characters */ if( c == '~') { c = *++src ; if( c == 0) break ; switch( c) { 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 ; } } else { /* check for the end of the token: EOS, space or comment */ if( quote_f) { if( c == '"') break ; } else if( c == ' ' || c == '\t') break ; else if( c == '#' || c == ';') { /* comments act like EOS */ c = 0 ; break ; } /* set quote mode if quote found */ if( c == '"') quote_f = TRUE ; } /* record the character */ if( idx < size - 1) tok[ idx++] = c ; else if( size > 1) { char *tmptok ; tmptok = malloc( size + 32) ; if( tmptok == NULL) size = 0 ; /* can't store more */ else { memcpy( tmptok, tok, idx) ; free( tok) ; tok = tmptok ; size += 32 ; tok[ idx++] = c ; } } } if( tok != NULL) tok[ idx] = 0 ; *tokref = tok ; return src + (c != 0) ; } 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 start recording all command lines until !endm * * int f; default flag * int n; macro number to use */ static char macbufname[] = "*Macro xx*" ; #define MACDIGITPOS 7 static boolean setstore( char *bufname) { /* set up the new macro buffer */ bstore = bfind( bufname, TRUE, BFINVS) ; if( bstore == NULL) { storing_f = FALSE ; /* should be already the case as we are executing */ return mloutfail( "Can not create macro") ; } /* and make sure it is empty */ bclear( bstore) ; /* start recording */ storing_f = TRUE ; return TRUE ; } BBINDABLE( storemac) { /* must have a numeric argument to this function */ if( f == FALSE) return mloutfail( "No macro number specified"); /* range check the macro number */ if( n < 1 || n > 40) return mloutfail( "Macro number out of range") ; /* construct the macro buffer name */ macbufname[ MACDIGITPOS] = '0' + (n / 10) ; macbufname[ MACDIGITPOS + 1] = '0' + (n % 10) ; return setstore( macbufname) ; } /* exec -- execute a buffer ** common to execute buffer, procedure and macro */ static int exec( int n, char *bufname, char *errstr) { /* find the pointer to that buffer */ buffer_p bp = bfind( bufname, FALSE, 0) ; if( bp == NULL) { mloutfmt( "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 ; } /* storeproc: * Set up a procedure buffer and start recording all command lines until !endm * * int f; default flag * int n; macro number to use */ BINDABLE( storeproc) { bname_t bname ; /* name of buffer to use */ char *name ; /* a numeric argument means it is a numbered macro */ if( f == TRUE) return storemac( f, n) ; /* get the name of the procedure */ int 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) ; return setstore( bname) ; } /* execproc: * Execute a procedure * * int f, n; default flag and numeric arg */ BINDABLE( execproc) { bname_t bufn ; /* name of buffer to execute */ char *name ; /* find out what buffer the user wants to execute */ int 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") ; } /* execbuf: * Execute the contents of a buffer of commands * * int f, n; default flag and numeric arg */ BINDABLE( execbuf) { char *bufn ; /* name of buffer to execute */ /* find out what buffer the user wants to execute */ int status = newmlarg( &bufn, "execute-buffer: ", sizeof( bname_t)) ; if( status != TRUE) return status ; status = exec( n, bufn, "buffer") ; free( bufn) ; return status ; } static boolean storeline( char *eline, int linlen) { /* allocate the space for the line */ line_p mp = lalloc( linlen) ; if( mp == NULL) return mloutfail( "Out of memory while storing macro") ; /* copy the text into the new line */ memcpy( mp->l_text, eline, linlen) ; /* lalloc has set lp->l_used */ /* 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 ; return TRUE ; } /* 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