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

/*	Expression evaluation functions
 *
 *	written 1986 by Daniel Lawrence
 *	modified by Petri Kutvonen
 */

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "basic.h"
#include "bind.h"
#include "buffer.h"
#include "defines.h"
#include "display.h"
#include "exec.h"
#include "execute.h"
#include "flook.h"
#include "input.h"
#include "line.h"
#include "random.h"
#include "search.h"
#include "terminal.h"
#include "termio.h"
#include "util.h"
#include "version.h"
#include "window.h"

/*	Macro argument token types					*/

#define	TKNUL	0		/* end-of-string                */
#define	TKARG	1		/* interactive argument         */
#define	TKBUF	2		/* buffer argument              */
#define	TKVAR	3		/* user variables               */
#define	TKENV	4		/* environment variables        */
#define	TKFUN	5		/* function....                 */
#define	TKDIR	6		/* directive                    */
#define	TKLBL	7		/* line label                   */
#define	TKLIT	8		/* numeric literal              */
#define	TKSTR	9		/* quoted string literal        */
#define	TKCMD	10		/* command name                 */


/* Emacs global flag bit definitions (for gflags). */
/* if GFREAD is set, current buffer will be set on first file (read in) */
#define	GFREAD	1

static int gflags = GFREAD ;	/* global control flag		*/

int readfirst_f( void) {
	return GFREAD == (gflags & GFREAD) ;
}

int macbug = FALSE ;		/* macro debuging flag          */
int cmdstatus = TRUE ;		/* last command status          */
static int flickcode = FALSE ;		/* do flicker supression?       */
int rval = 0 ;			/* return value of a subprocess */


static int saveflag = 0 ;	/* Flags, saved with the $target var */


unsigned envram = 0 ;		/* # of bytes current in use by malloc */


/* User variables ******************************************/
#define	MAXVARS	255
#define	NVSIZE	10	/* Max #chars in a var name. */

/* Structure to hold user variables and their definitions. */
static struct {
	char u_name[ NVSIZE + 1] ;	/* name of user variable */
	char *u_value ;				/* value (string) */
} uv[ MAXVARS + 1] ;


static char errorm[] = "ERROR" ;	/* error literal                */

static int seed = 0 ;			/* random number seed           */

static char *ltos( int val) ;
static char *mkupper( char *dst, char *src) ;

/* List of recognized environment variables. */

static const char *envars[] = {
	"fillcol",		/* current fill column */
	"pagelen",		/* number of lines used by editor */
	"curcol",		/* current column pos of cursor */
	"curline",		/* current line in file */
	"ram",			/* ram in use by malloc */
	"flicker",		/* flicker supression */
	"curwidth",		/* current screen width */
	"cbufname",		/* current buffer name */
	"cfname",		/* current file name */
	"sres",			/* current screen resolution */
	"debug",		/* macro debugging */
	"status",		/* returns the status of the last command */
	"palette",		/* current palette string */
	"asave",		/* # of chars between auto-saves */
	"acount",		/* # of chars until next auto-save */
	"lastkey",		/* last keyboard char struck */
	"curchar",		/* current character under the cursor */
	"discmd",		/* display commands on command line */
	"version",		/* current version number */
	"progname",		/* returns current prog name - "MicroEMACS" */
	"seed",			/* current random number seed */
	"disinp",		/* display command line input characters */
	"wline",		/* # of lines in current window */
	"cwline",		/* current screen line in window */
	"target",		/* target for line moves */
	"search",		/* search pattern */
	"replace",		/* replacement pattern */
	"match",		/* last matched magic pattern */
	"kill",			/* kill buffer (read only) */
	"cmode",		/* mode of current buffer */
	"gmode",		/* global modes */
	"tpause",		/* length to pause for paren matching */
	"pending",		/* type ahead pending flag */
	"lwidth",		/* width of current line */
	"line",			/* text of current line */
	"gflags",		/* global internal emacs flags */
	"rval",			/* child process return value */
	"tab",			/* tab width, 1... */
	"hardtab",		/* TRUE for hard coded tab, FALSE for soft ones */
	"viewtab",		/* TRUE to visualize hard coded tabs */
	"overlap",
	"jump",
#if SCROLLCODE
	"scroll",		/* scroll enabled */
#endif
};

/* And its preprocessor definitions. */

#define	EVFILLCOL	0
#define	EVPAGELEN	1
#define	EVCURCOL	2
#define	EVCURLINE	3
#define	EVRAM		4
#define	EVFLICKER	5
#define	EVCURWIDTH	6
#define	EVCBUFNAME	7
#define	EVCFNAME	8
#define	EVSRES		9
#define	EVDEBUG		10
#define	EVSTATUS	11
#define	EVPALETTE	12
#define	EVASAVE		13
#define	EVACOUNT	14
#define	EVLASTKEY	15
#define	EVCURCHAR	16
#define	EVDISCMD	17
#define	EVVERSION	18
#define	EVPROGNAME	19
#define	EVSEED		20
#define	EVDISINP	21
#define	EVWLINE		22
#define EVCWLINE	23
#define	EVTARGET	24
#define	EVSEARCH	25
#define	EVREPLACE	26
#define	EVMATCH		27
#define	EVKILL		28
#define	EVCMODE		29
#define	EVGMODE		30
#define	EVTPAUSE	31
#define	EVPENDING	32
#define	EVLWIDTH	33
#define	EVLINE		34
#define	EVGFLAGS	35
#define	EVRVAL		36
#define EVTAB		37
#define EVHARDTAB	38
#define EVVIEWTAB	39
#define EVOVERLAP	40
#define EVSCROLLCOUNT	41
#define EVSCROLL	42

enum function_type {
	NILNAMIC	= 0,
	MONAMIC		= (1 << 6),
	DYNAMIC		= (2 << 6),
	TRINAMIC	= (3 << 6)
} ;

#define ARGCOUNT( ft) (ft >> 6)
#define FUNID( ft) (ft & ((1 << 6) - 1))

enum function_code {
	UFADD = 0,	UFSUB,		UFTIMES,	UFDIV,		UFMOD,		UFNEG,
	UFCAT,		UFLEFT,		UFRIGHT,	UFMID,		UFNOT,		UFEQUAL,
	UFLESS,		UFGREATER,	UFSEQUAL,	UFSLESS,	UFSGREAT,	UFIND,
	UFAND,		UFOR,		UFLENGTH,	UFUPPER,	UFLOWER,	UFTRUTH,
	UFASCII,	UFCHR,		UFGTKEY,	UFRND,		UFABS,		UFSINDEX,
	UFENV,		UFBIND,		UFEXIST,	UFFIND,		UFBAND,		UFBOR,
	UFBXOR,		UFBNOT,		UFXLATE
} ;

/* List of recognized user functions. */
static struct {
	const char f_name[ 4] ;
	const int  f_type ;
} const funcs[] = {
	{ "abs", UFABS		| MONAMIC },	/* absolute value of a number */
	{ "add", UFADD		| DYNAMIC },	/* add two numbers together */
	{ "and", UFAND		| DYNAMIC },	/* logical and */
	{ "asc", UFASCII	| MONAMIC },	/* char to integer conversion */
	{ "ban", UFBAND		| DYNAMIC },	/* bitwise and   9-10-87  jwm */
	{ "bin", UFBIND		| MONAMIC },	/* look up function name bound to key */
	{ "bno", UFBNOT		| MONAMIC },	/* bitwise not */
	{ "bor", UFBOR		| DYNAMIC },	/* bitwise or    9-10-87  jwm */
	{ "bxo", UFBXOR		| DYNAMIC },	/* bitwise xor   9-10-87  jwm */
	{ "cat", UFCAT		| DYNAMIC },	/* concatenate string */
	{ "chr", UFCHR		| MONAMIC },	/* integer to char conversion */
	{ "div", UFDIV		| DYNAMIC },	/* division */
	{ "env", UFENV		| MONAMIC },	/* retrieve a system environment var */
	{ "equ", UFEQUAL	| DYNAMIC },	/* logical equality check */
	{ "exi", UFEXIST	| MONAMIC },	/* check if a file exists */
	{ "fin", UFFIND		| MONAMIC },	/* look for a file on the path... */
	{ "gre", UFGREATER	| DYNAMIC },	/* logical greater than */
	{ "gtk", UFGTKEY	| NILNAMIC },	/* get 1 charater */
	{ "ind", UFIND		| MONAMIC },	/* evaluate indirect value */
	{ "lef", UFLEFT		| DYNAMIC },	/* left string(string, len) */
	{ "len", UFLENGTH	| MONAMIC },	/* string length */
	{ "les", UFLESS		| DYNAMIC },	/* logical less than */
	{ "low", UFLOWER	| MONAMIC },	/* lower case string */
	{ "mid", UFMID		| TRINAMIC },	/* mid string(string, pos, len) */
	{ "mod", UFMOD		| DYNAMIC },	/* mod */
	{ "neg", UFNEG		| MONAMIC },	/* negate */
	{ "not", UFNOT		| MONAMIC },	/* logical not */
	{ "or",  UFOR		| DYNAMIC },	/* logical or */
	{ "rig", UFRIGHT	| DYNAMIC },	/* right string(string, pos) */
	{ "rnd", UFRND		| MONAMIC },	/* get a random number */
	{ "seq", UFSEQUAL	| DYNAMIC },	/* string logical equality check */
	{ "sgr", UFSGREAT	| DYNAMIC },	/* string logical greater than */
	{ "sin", UFSINDEX	| DYNAMIC },	/* find the index of one string in another */
	{ "sle", UFSLESS	| DYNAMIC },	/* string logical less than */
	{ "sub", UFSUB		| DYNAMIC },	/* substraction */
	{ "tim", UFTIMES	| DYNAMIC },	/* multiplication */
	{ "tru", UFTRUTH	| MONAMIC },	/* Truth of the universe logical test */
	{ "upp", UFUPPER	| MONAMIC },	/* uppercase string */
	{ "xla", UFXLATE	| TRINAMIC },	/* XLATE character string translation */
} ;


/* When emacs' command interpetor needs to get a variable's name,
 * rather than it's value, it is passed back as a variable description
 * structure. The v_num field is a index into the appropriate variable table.
 */
typedef struct {
	int v_type ;	/* Type of variable. */
	int v_num ;		/* Ordinal pointer to variable in list. */
} variable_description ;

static void findvar( char *var, variable_description *vd, int size) ;
static int svar( variable_description *var, char *value) ;
static const char *i_to_a( int i) ;

/*
 * putctext:
 *	replace the current line with the passed in text
 *
 * char *iline;			contents of new line
 */
static int putctext( char *iline)
{
	int status;

	/* delete the current line */
	curwp->w_doto = 0;	/* starting at the beginning of the line */
	if ((status = killtext(TRUE, 1)) != TRUE)
		return status;

	/* insert the new line */
	if ((status = linstr(iline)) != TRUE)
		return status;
	status = lnewline();
	backline(TRUE, 1);
	return status;
}


static char *result ;		/* string result */
static int ressize = 0 ;	/* mark result as uninitialized */

static int ernd( int i) ;
static int sindex( char *source, char *pattern) ;
static char *xlat( char *source, char *lookup, char *trans) ;

/* Initialize the user variable list. */
void varinit(void)
{
	int i;
	for (i = 0; i < MAXVARS; i++)
		uv[i].u_name[0] = 0;

	if( ressize == 0) {
		result = malloc( NSTRING) ;
		ressize = NSTRING ;
	}

	seed = time( NULL) ;
}


/* Evaluate a function.
 *
 * @fname: name of function to evaluate.
 */
static const char *gtfun( char *fname) {
	char *argv[ 3] ;
	const char *retstr ;		/* return value */
	int i ;

/* look the function up in the function table */
	fname[ 3] = 0 ;		/* only first 3 chars significant */
	mklower( fname) ;	/* and let it be upper or lower case */
	unsigned fnum = ARRAY_SIZE( funcs) ;	/* index to function to eval */
	int low = 0 ;				/* binary search low bound */
	int high = fnum - 1 ;		/* binary search high bound */
	do {
		int s, cur ;

		cur = (high + low) / 2 ;
		s = strcmp( fname, funcs[ cur].f_name) ;
		if( s == 0) {
			fnum = cur ;
			break ;
		} else if( s < 0)
			high = cur - 1 ;
		else
			low = cur + 1 ;
	} while( low <= high) ;

/* return errorm on a bad reference */
	if( fnum == ARRAY_SIZE( funcs))
		return errorm ;

/* fetch arguments */
	assert( clexec == TRUE) ; /* means macarg can be replaced by gettokval */
	int argc = ARGCOUNT( funcs[ fnum].f_type) ;
	for( int i = 0 ; i < argc ; i++) {
		argv[ i] = getnewtokval() ;
		if( argv[ i] == NULL) {
			while( i > 0)
				free( argv[ --i]) ;

			return errorm ;
		}
	}

/* and now evaluate it! */
	switch( FUNID( funcs[ fnum].f_type)) {
		int sz, sz1 ;
		unicode_t c ;

	case UFADD:
		retstr = i_to_a( atoi( argv[ 0]) + atoi( argv[ 1])) ;
		break ;
	case UFSUB:
		retstr = i_to_a( atoi( argv[ 0]) - atoi( argv[ 1])) ;
		break ;
	case UFTIMES:
		retstr = i_to_a( atoi( argv[ 0]) * atoi( argv[ 1])) ;
		break ;
	case UFDIV:
		sz = atoi( argv[ 1]) ;
		retstr = (sz == 0) ? errorm : i_to_a( atoi( argv[ 0]) / sz) ;
		break ;
	case UFMOD:
		sz = atoi( argv[ 1]) ;
		retstr = (sz == 0) ? errorm : i_to_a( atoi( argv[ 0]) % sz) ;
		break ;
	case UFNEG:
		retstr = i_to_a( -atoi( argv[ 0])) ;
		break ;
	case UFCAT:
		sz1 = strlen( argv[ 0]) ;
		sz = sz1 + strlen( argv[ 1]) + 1 ;
		if( sz > ressize) {
			free( result) ;
			result = malloc( sz) ;
			ressize = sz ;
		}

		strcpy( result, argv[ 0]) ;
		strcpy( &result[ sz1], argv[ 1]) ;
		retstr = result ;
		break ;
	case UFLEFT:
		sz1 = strlen( argv[ 0]) ;
		sz = 0 ;
		for( int i = atoi( argv[ 1]) ; i > 0 ; i -= 1) {
			unicode_t c ;

			sz += utf8_to_unicode( argv[ 0], sz, sz1, &c) ;
			if( sz == sz1)
				break ;
		}

		if( sz >= ressize) {
			free( result) ;
			result = malloc( sz + 1) ;
			ressize = sz + 1 ;
		}

		mystrscpy( result, argv[ 0], sz + 1) ;
		retstr = result ;
		break ;
	case UFRIGHT:
		sz = strlen( argv[ 0]) ;
		for( sz1 = atoi( argv[ 1]) ; sz1 > 0 && sz > 0 ; sz1--)
			if( --sz > 0)
				sz -= utf8_revdelta( (unsigned char *) &( argv[ 0])[ sz], sz) ;

		retstr = &( argv[ 0])[ sz] ;
		sz = strlen( retstr) ;
		if( sz >= ressize) {
			free( result) ;
			ressize = sz + 1 ;
			result = malloc( ressize) ;
		}

		retstr = strcpy( result, retstr) ;
		break ;
	case UFMID:
		sz1 = strlen( argv[ 0]) ;
		int start = 0 ;
		for( i = atoi( argv[ 1]) - 1 ; i > 0 ; i -= 1) {
			start +=  utf8_to_unicode( argv[ 0], start, sz1, &c) ;
			if( start == sz1)
				break ;
		}

		sz = start ;
		if( sz < sz1)
		for( i = atoi( argv[ 2]) ; i > 0 ; i -= 1) {
			sz += utf8_to_unicode( argv[ 0], sz, sz1, &c) ;
			if( sz == sz1)
				break ;
		}

		sz -= start ;
		if( sz >= ressize) {
			free( result) ;
			result = malloc( sz + 1) ;
			ressize = sz + 1 ;
		}

		mystrscpy( result, &(argv[ 0][ start]), sz + 1) ;
		retstr = result ;
		break ;
	case UFNOT:
		retstr = ltos( stol( argv[ 0]) == FALSE) ;
		break ;
	case UFEQUAL:
		retstr = ltos( atoi( argv[ 0]) == atoi( argv[ 1])) ;
		break ;
	case UFLESS:
		retstr = ltos( atoi( argv[ 0]) < atoi( argv[ 1])) ;
		break ;
	case UFGREATER:
		retstr = ltos( atoi( argv[ 0]) > atoi( argv[ 1])) ;
		break ;
	case UFSEQUAL:
		retstr = ltos( strcmp( argv[ 0], argv[ 1]) == 0) ;
		break ;
	case UFSLESS:
		retstr = ltos( strcmp( argv[ 0], argv[ 1]) < 0) ;
		break ;
	case UFSGREAT:
		retstr = ltos( strcmp( argv[ 0], argv[ 1]) > 0) ;
		break ;
	case UFIND:
		retstr = getval( argv[ 0]) ;
		sz = strlen( retstr) + 1 ;
		if( sz > ressize) {
			free( result) ;
			result = malloc( sz) ;
			ressize = sz ;
		}

		retstr = strcpy( result, retstr) ;
		break ;
	case UFAND:
		retstr = ltos( stol( argv[ 0]) && stol( argv[ 1])) ;
		break ;
	case UFOR:
		retstr = ltos( stol( argv[ 0]) || stol( argv[ 1])) ;
		break ;
	case UFLENGTH:
		retstr = i_to_a( strlen( argv[ 0])) ;
		break ;
	case UFUPPER:
		sz = strlen( argv[ 0]) ;
		if( sz >= ressize) {
			free( result) ;
			result = malloc( sz + 1) ;
			ressize = sz + 1 ;
		}

		retstr = mkupper( result, argv[ 0]) ;
		break ;
	case UFLOWER:
		sz = strlen( argv[ 0]) ;
		if( sz >= ressize) {
			free( result) ;
			result = malloc( sz + 1) ;
			ressize = sz + 1 ;
		}

		strcpy( result, argv[ 0]) ;	/* result is at least as long as argv[ 0] */
		retstr = mklower( result) ;
		break ;
	case UFTRUTH:
		retstr = ltos( atoi( argv[ 0]) == 42) ;
		break ;
	case UFASCII:
		utf8_to_unicode( argv[ 0], 0, 4, &c) ;
		retstr = i_to_a( c) ;
		break ;
	case UFCHR:
		c = atoi( argv[ 0]) ;
		if( c > 0x10FFFF)
			retstr = errorm ;
		else {
			sz = unicode_to_utf8( c, result) ;
			result[ sz] = 0 ;
			retstr = result ;
		}

		break ;
	case UFGTKEY:
		result[0] = tgetc();
		result[1] = 0;
		retstr = result ;
		break ;
	case UFRND:
		retstr = i_to_a( ernd( atoi( argv[ 0]))) ;
		break ;
	case UFABS:
		retstr = i_to_a( abs( atoi( argv[ 0]))) ;
		break ;
	case UFSINDEX:
		retstr = i_to_a( sindex( argv[ 0], argv[ 1])) ;
		break ;
	case UFENV:
#if	ENVFUNC
		retstr = getenv( argv[ 0]) ;
		if( retstr == NULL)
#endif
			retstr = "" ;

		break ;
	case UFBIND:
		retstr = transbind( argv[ 0]) ;
		break ;
	case UFEXIST:
		retstr = ltos( fexist( argv[ 0])) ;
		break ;
	case UFFIND:
		retstr = flook( argv[ 0], TRUE) ;
		if( retstr == NULL)
			retstr = "" ;
		break ;
	case UFBAND:
		retstr = i_to_a( atoi( argv[ 0]) & atoi( argv[ 1])) ;
		break ;
	case UFBOR:
		retstr = i_to_a( atoi( argv[ 0]) | atoi( argv[ 1])) ;
		break ;
	case UFBXOR:
		retstr = i_to_a( atoi( argv[ 0]) ^ atoi( argv[ 1])) ;
		break ;
	case UFBNOT:
		retstr = i_to_a( ~atoi( argv[ 0])) ;
		break ;
	case UFXLATE:
		retstr = xlat( argv[ 0], argv[ 1], argv[ 2]) ;
		break ;
	default:
		assert( FALSE) ;	/* never should get here */
		retstr = errorm ;
	}

	while( argc > 0)
		free( argv[ --argc]) ;

	return retstr ;
}

/*
 * look up a user var's value
 *
 * char *vname;			name of user variable to fetch
 */
static char *gtusr( char *vname) {
	int vnum;	/* ordinal number of user var */

	/* scan the list looking for the user var name */
	for (vnum = 0; vnum < MAXVARS; vnum++) {
		if (uv[vnum].u_name[0] == 0)
			break ;

		if( strncmp( vname, uv[ vnum].u_name, NVSIZE) == 0)
			return uv[vnum].u_value;
	}

	/* return errorm if we run off the end */
	return errorm;
}


/* getctext:    grab and return a string with the text of the current line */
static const char *getctext( void) {
    static int  rsize = 0 ;
    static char *rline = NULL ;	/* line to return */

/* find the contents of the current line and its length */
    line_p lp = curwp->w_dotp ;	/* line to copy */
    int size = lp->l_used ;		/* length of line to return */
    if( size >= rsize) {
	/* extend storage */
        int newsize = size + 1 ;
		char *newstr = realloc( rline, newsize) ;
		if( newstr == NULL)
			return "" ;

		rline = newstr ;
		rsize = newsize ;
    }

    /* copy it across */
    memcpy( rline, lp->l_text, size) ;
    rline[ size] = 0 ;
    return rline ;
}


/* gtenv()
 *
 * char *vname;			name of environment variable to retrieve
 */
static const char *gtenv( char *vname) {
	unsigned vnum ;	/* ordinal number of var referenced */

	/* scan the list, looking for the referenced name */
	for( vnum = 0 ; vnum < ARRAY_SIZE( envars) ; vnum++)
		if( strcmp( vname, envars[ vnum]) == 0)
			break ;

	/* return errorm on a bad reference */
	if( vnum == ARRAY_SIZE( envars)) {
#if	ENVFUNC
		char *ename = getenv(vname);

		if( ename != NULL)
			return ename ;
#endif
		return errorm ;
	}

	/* otherwise, fetch the appropriate value */
	switch (vnum) {
	case EVFILLCOL:
		return i_to_a(fillcol);
	case EVPAGELEN:
		return i_to_a(term.t_nrow + 1);
	case EVCURCOL:
		return i_to_a(getccol(FALSE));
	case EVCURLINE:
		return i_to_a(getcline());
	case EVRAM:
		return i_to_a((int) (envram / 1024l));
	case EVFLICKER:
		return ltos(flickcode);
	case EVCURWIDTH:
		return i_to_a(term.t_ncol);
	case EVCBUFNAME:
		return curbp->b_bname;
	case EVCFNAME:
		return curbp->b_fname;
	case EVSRES:
		return sres;
	case EVDEBUG:
		return ltos(macbug);
	case EVSTATUS:
		return ltos(cmdstatus);
	case EVPALETTE: {
		static char palstr[ 49] = "" ; /* palette string */
		
		return palstr;
	    }

	case EVASAVE:
		return i_to_a(gasave);
	case EVACOUNT:
		return i_to_a(gacount);
	case EVLASTKEY:
		return i_to_a(lastkey);
	case EVCURCHAR: {
			unicode_t c ;

			lgetchar( &c) ;
			return i_to_a( c) ;
		}
	
	case EVDISCMD:
		return ltos(discmd);
	case EVVERSION:
		return VERSION;
	case EVPROGNAME:
		return PROGRAM_NAME_UTF8 ;
	case EVSEED:
		return i_to_a(seed);
	case EVDISINP:
		return ltos(disinp);
	case EVWLINE:
		return i_to_a(curwp->w_ntrows);
	case EVCWLINE:
		return i_to_a(getwpos());
	case EVTARGET:
		saveflag = lastflag;
		return i_to_a(curgoal);
	case EVSEARCH:
		return pat;
	case EVREPLACE:
		return rpat;
	case EVMATCH:
		return (patmatch == NULL) ? "" : patmatch;
	case EVKILL:
		return getkill();
	case EVCMODE:
		return i_to_a(curbp->b_mode);
	case EVGMODE:
		return i_to_a(gmode);
	case EVTPAUSE:
		return i_to_a(term.t_pause);
	case EVPENDING:
#if	TYPEAH
		return ltos(typahead());
#else
		return falsem;
#endif
	case EVLWIDTH:
		return i_to_a(llength(curwp->w_dotp));
	case EVLINE:
		return getctext();
	case EVGFLAGS:
		return i_to_a(gflags);
	case EVRVAL:
		return i_to_a(rval);
	case EVTAB:
		return i_to_a( tabwidth) ;
	case EVHARDTAB:
		return ltos( hardtab) ;
	case EVVIEWTAB:
		return ltos( viewtab) ;
	case EVOVERLAP:
		return i_to_a(overlap);
	case EVSCROLLCOUNT:
		return i_to_a(scrollcount);
#if SCROLLCODE
	case EVSCROLL:
		return ltos(term.t_scroll != NULL);
#else
	case EVSCROLL:
		return ltos(0);
#endif
	}

	assert( FALSE) ;	/* again, we should never get here */
	return errorm ;
}


/* set a variable
 *
 * int f;		default flag
 * int n;		numeric arg (can overide prompted value)
 */
BINDABLE( setvar) {
	int status;	/* status return */
	variable_description vd ;		/* variable num/type */
	char var[NVSIZE + 2];	/* name of variable to fetch %1234567890\0 */
	char *value ;			/* value to set variable to */

	/* first get the variable to set.. */
	if (clexec == FALSE) {
		status = getstring( "Variable to set: ", var, sizeof var, nlc) ;
		if (status != TRUE)
			return status;
	} else {		/* macro line argument */
		/* grab token and skip it */
		gettoken( var, sizeof var) ;
	}

	/* check the legality and find the var */
	findvar( var, &vd, sizeof var) ;

	/* if its not legal....bitch */
	if (vd.v_type == -1) {
		mlwrite("%%No such variable as '%s'", var);
		return FALSE;
	}

	/* get the value for that variable */
	if( f == TRUE) {
		value = malloc( NSTRING) ;
		if( value == NULL)
			return FALSE ;

		/* a bit overcautious here */
		mystrscpy( value, i_to_a( n), NSTRING) ;
	} else {
		status = newmlarg( &value, "Value: ", 0) ;
		if (status != TRUE)
			return status;
	}

	/* and set the appropriate value */
	status = svar(&vd, value);

#if	DEBUGM
	/* if $debug == TRUE, every assignment will echo a statment to
	   that effect here. */

	if( macbug)
		if( abortc == mdbugout( "(((%s:%s:%s)))", ltos( status), var, value))
			status = FALSE ;
#endif

	/* and return it */
	free( value) ;
	return status;
}

static void mlforce( char *s) ;

#if DEBUGM
int mdbugout( char *fmt, ...) {
	int	c ;	/* input from kbd, output to terminal */
	int savediscmd ;
	va_list	ap ;

	/* assignment status ; variable name ; value we tried to assign  */
	/* write out the debug line */
	savediscmd = discmd ;
	discmd = TRUE ;
	va_start( ap, fmt) ;
	vmlwrite( fmt, ap) ;
	va_end( ap) ;
	discmd = savediscmd ;
	update( TRUE) ;

	/* and get the keystroke to hold the output */
	c = get1key() ;
	if( c == abortc)
		mlforce( "(Macro aborted)") ;

	return c ;
}
#endif

/*
 * Find a variables type and name.
 *
 * @var: name of variable to get.
 * @vd: structure to hold type and pointer.
 * @size: size of variable array.
 */
static void findvar(char *var, variable_description *vd, int size)
{
	unsigned vnum = 0 ;	/* subscript in variable arrays */
	int vtype;	/* type to return */

fvar:
	vtype = -1;
	switch (var[0]) {

	case '$':		/* check for legal enviromnent var */
		for (vnum = 0; vnum < ARRAY_SIZE(envars); vnum++)
			if (strcmp(&var[1], envars[vnum]) == 0) {
				vtype = TKENV;
				break;
			}
		break;

	case '%':		/* check for existing legal user variable */
		for (vnum = 0; vnum < MAXVARS; vnum++)
			if (strcmp(&var[1], uv[vnum].u_name) == 0) {
				vtype = TKVAR;
				break;
			}
		if (vnum < MAXVARS)
			break;

		/* create a new one??? */
		for (vnum = 0; vnum < MAXVARS; vnum++)
			if (uv[vnum].u_name[0] == 0) {
				vtype = TKVAR;
				mystrscpy( uv[ vnum].u_name, &var[ 1], NVSIZE + 1) ;
				break;
			}
		break;

	case '&':		/* indirect operator? */
		var[4] = 0;
		if (strcmp(&var[1], "ind") == 0) {
			/* grab token, and eval it */
			if( TRUE == gettokval( var, size))
				goto fvar ;
		}
	}

	/* return the results */
	vd->v_num = vnum;
	vd->v_type = vtype;
	return;
}

/*
 * Set a variable.
 *
 * @var: variable to set.
 * @value: value to set to.
 */
static int svar( variable_description *var, char *value)
{
	int vnum;	/* ordinal number of var refrenced */
	int vtype;	/* type of variable to set */
	int status;	/* status return */
	int c;		/* translated character */
	char *sp;	/* scratch string pointer */

	/* simplify the vd structure (we are gonna look at it a lot) */
	vnum = var->v_num;
	vtype = var->v_type;

	/* and set the appropriate value */
	status = TRUE;
	switch (vtype) {
	case TKVAR:		/* set a user variable */
		if (uv[vnum].u_value != NULL)
			free(uv[vnum].u_value);
		sp = malloc(strlen(value) + 1);
		if (sp == NULL)
			return FALSE;
		strcpy(sp, value);
		uv[vnum].u_value = sp;
		break;

	case TKENV:		/* set an environment variable */
		status = TRUE;	/* by default */
		switch (vnum) {
		case EVFILLCOL:
			fillcol = atoi(value);
			break;
		case EVPAGELEN:
			status = newsize(TRUE, atoi(value));
			break;
		case EVCURCOL:
			status = setccol(atoi(value));
			break;
		case EVCURLINE:
			status = gotoline(TRUE, atoi(value));
			break;
		case EVRAM:
			break;
		case EVFLICKER:
			flickcode = stol(value);
			break;
		case EVCURWIDTH:
			status = newwidth(TRUE, atoi(value));
			break;
		case EVCBUFNAME:
			strcpy(curbp->b_bname, value);
			curwp->w_flag |= WFMODE;
			break;
		case EVCFNAME:
			strcpy(curbp->b_fname, value);
			curwp->w_flag |= WFMODE;
			break;
		case EVSRES:
			status = TTrez(value);
			break;
		case EVDEBUG:
			macbug = stol(value);
			break;
		case EVSTATUS:
			cmdstatus = stol(value);
			break;
		case EVASAVE:
			gasave = atoi(value);
			break;
		case EVACOUNT:
			gacount = atoi(value);
			break;
		case EVLASTKEY:
			lastkey = atoi(value);
			break;
		case EVCURCHAR:
			ldelchar(1, FALSE);	/* delete 1 char */
			c = atoi(value);
			if (c == '\n')
				lnewline();
			else
				linsert(1, c);
			backchar(FALSE, 1);
			break;
		case EVDISCMD:
			discmd = stol(value);
			break;
		case EVVERSION:
			break;
		case EVPROGNAME:
			break;
		case EVSEED:
			seed = atoi(value);
			break;
		case EVDISINP:
			disinp = stol(value);
			break;
		case EVWLINE:
			status = resize(TRUE, atoi(value));
			break;
		case EVCWLINE:
			status = forwline(TRUE, atoi(value) - getwpos());
			break;
		case EVTARGET:
			curgoal = atoi(value);
			thisflag = saveflag;
			break;
		case EVSEARCH:
			strcpy(pat, value);
			rvstrcpy(tap, pat);
#if	MAGIC
			mcclear();
#endif
			break;
		case EVREPLACE:
			strcpy(rpat, value);
			break;
		case EVMATCH:
			break;
		case EVKILL:
			break;
		case EVCMODE:
			curbp->b_mode = atoi(value);
			curwp->w_flag |= WFMODE;
			break;
		case EVGMODE:
			gmode = atoi(value);
			break;
		case EVTPAUSE:
			term.t_pause = atoi(value);
			break;
		case EVPENDING:
			break;
		case EVLWIDTH:
			break;
		case EVLINE:
			return putctext( value) ;
		case EVGFLAGS:
			gflags = atoi(value);
			break;
		case EVRVAL:
			break;
		case EVTAB:
			c = atoi( value) ;
			if( c > 0) {
				tabwidth = c ;
				curwp->w_flag |= WFHARD;
			} else
				status = FALSE ;

			break;
		case EVHARDTAB:
			hardtab = stol( value) ;
			break ;
		case EVVIEWTAB:
			viewtab = stol( value) ;
			break ;
		case EVOVERLAP:
			overlap = atoi(value);
			break;
		case EVSCROLLCOUNT:
			scrollcount = atoi(value);
			break;
		case EVSCROLL:
#if SCROLLCODE
			if (!stol(value))
				term.t_scroll = NULL;
#endif
			break;
		}
		break;
	}
	return status;
}

/*
 * i_to_a:
 *	integer to ascii string.......... This is too
 *	inconsistant to use the system's
 *
 * int i;		integer to translate to a string
 */
static const char *i_to_a( int i) {
	unsigned u ;
	int sign ;	/* sign of resulting number */
	/* returns result string: sign digits null */
	static char result[ 1 + (sizeof i * 5 + 1) / 2 + 1] ;
	char *sp = &result[ sizeof result - 1] ; /* points on result's last byte */

	*sp = 0 ;

	/* record the sign... */
	sign = i < 0 ;
	u = sign ? -i : i ;

	/* and build the string (backwards!) */
	do {
		*(--sp) = '0' + u % 10 ;	/* install the new digit */
		u = u / 10 ;
	} while( u) ;

	/* and fix the sign */
	if( sign)
		*(--sp) = '-';	/* and install the minus sign */

	return sp ;
}


/* find the type of a token based on first character
 *
 * char c ; 	first character of analyzed token
 */
static int gettyp( char c) {
 	switch( c) {
	case '*':
	case ':':
		return TKLBL ;
	case 0:	/* no blanks!!! */
		return TKNUL ;
	case '"':
		return TKSTR ;
	case '!':
		return TKDIR ;
	case '@':
		return TKARG ;
	case '=':
		return TKBUF ;
	case '$':
		return TKENV ;
	case '%':
		return TKVAR ;
	case '&':
		return TKFUN ;

	default:
		/* a numeric literal? */
		if( (c >= '0' && c <= '9') || c == '-')
			return TKLIT ;
		else
			return TKCMD ;
	}
}

int is_it_cmd( char *token) {
	return TKCMD == gettyp( *token) ;
}


/* find the value of a token
 *
 * char *token;		token to evaluate
 */
const char *getval( char *token) {
	static char buf[ NSTRING] ;	/* string buffer for some returns */

	switch( gettyp( *token)) {
	case TKARG:		/* interactive argument */
		strcpy( token, getval( &token[ 1])) ;
		int distmp = discmd ;	/* echo it always! */
		discmd = TRUE ;
		int status = getstring( token, buf, NSTRING, nlc) ;
		discmd = distmp ;
		if (status == ABORT)
			return errorm ;

		return buf ;

	case TKBUF:		/* buffer contents fetch */
	/* grab the right buffer */
		strcpy( token, getval( &token[ 1])) ;
		buffer_p bp = bfind( token, FALSE, 0) ;
		if (bp == NULL)
			return errorm ;

	/* if the buffer is displayed,
	   get the window vars instead of the buffer vars */
		if (bp->b_nwnd > 0) {
			curbp->b_dotp = curwp->w_dotp;
			curbp->b_doto = curwp->w_doto;
		}

	/* make sure we are not at the end */
		if (bp->b_linep == bp->b_dotp)
			return errorm;

	/* grab the line as an argument */
		unsigned blen = bp->b_dotp->l_used - bp->b_doto;
		if( blen >= sizeof buf)
			blen = sizeof buf - 1 ;

		mystrscpy( buf, bp->b_dotp->l_text + bp->b_doto, blen + 1) ;

	/* and step the buffer's line ptr ahead a line */
		bp->b_dotp = bp->b_dotp->l_fp;
		bp->b_doto = 0;

	/* if displayed buffer, reset window ptr vars */
		if (bp->b_nwnd > 0) {
			curwp->w_dotp = curbp->b_dotp;
			curwp->w_doto = 0;
			curwp->w_flag |= WFMOVE;
		}

	/* and return the spoils */
		return buf;

	case TKVAR:
		return gtusr(token + 1);
	case TKENV:
		return gtenv(token + 1);
	case TKFUN:
		return gtfun(token + 1);
	case TKLIT:
		return token;
	case TKSTR:
		return token + 1;
	case TKCMD:
		return token;
	case TKDIR:
	case TKLBL:
	case TKNUL:
		return "" ;
	}

	return errorm ;
}

/*
 * convert a string to a numeric logical
 *
 * char *val;		value to check for stol
 */
int stol(char *val)
{
	/* check for logical values */
	if (val[0] == 'F')
		return FALSE;
	if (val[0] == 'T')
		return TRUE;

	/* check for numeric truth (!= 0) */
	return (atoi(val) != 0);
}

/*
 * numeric logical to string logical
 *
 * int val;		value to translate
 */
static char *ltos( int val) {
	static char *boolm[] = { "TRUE", "FALSE" } ;
	
	return boolm[ !val] ;
}

/*
 * make a string upper case
 *
 * char *src ;		string to upper case
 * char *dst ;		where to store
 * dst must be at least as long as src.
 */
static char *mkupper( char *dst, char *src) {
	char c, *sp ;

	sp = dst ;
	while( (c = *src++)) {
		if( 'a' <= c && c <= 'z')
			c += 'A' - 'a' ;
			
		*sp++ = c ;
	}
	
	*sp = 0 ;
	return dst ;
}

/*
 * make a string lower case
 *
 * char *str;		string to lower case
 */
char *mklower(char *str)
{
	char *sp;

	sp = str;
	while (*sp) {
		if ('A' <= *sp && *sp <= 'Z')
			*sp += 'a' - 'A';
		++sp;
	}
	return str;
}

/*
 * returns a random integer
 *	ernd( 0)			[ 0 .. 2147483647]
 *	ernd( -2147483648)	[ 0 .. 2147483647]
 *	ernd( 1) 			[ 1]
 *  ernd( i)			[ 1 .. abs( i)]
 */
static int ernd( int i) {
	int s ;

	seed = seed * 1721 + 10007 ;
	s = ((seed >> 16) & 0x0000FFFF) | (seed << 16) ;
	s &= ~(1 << 31) ; /* avoid abs() */
	i = i < 0 ? -i : i ;	/* abs( i) */
	return (i <= 0) ? s : s % i + 1 ;
}


/* find pattern within source
 *
 * char *source;	source string to search
 * char *pattern;	string to look for
 */
static int sindex( char *source, char *pattern) {
/* scanning through the source string */
	char *sp = source ;		/* ptr to current position to scan */
	int idx = 1 ;
	int pos = 0 ;
	int len = strlen( source) ;

	while( *sp) {
		char c ;
		unicode_t uc ;
		
		/* scan through the pattern */
		char *cp = pattern ;		/* ptr to place to check for equality */
		char *csp = sp ;			/* ptr to source string during comparison */

		while( (c = *cp++) && eq( c, *csp))
			csp++ ;

		/* was it a match? */
		if( c == 0)
			return idx ;

		idx += 1 ;
		pos += utf8_to_unicode( source, pos, len, &uc) ;
		sp = &source[ pos] ;
	}

	/* no match at all.. */
	return 0 ;
}

/*
 * Filter a string through a translation table
 *
 * char *source;	string to filter
 * char *lookup;	characters to translate
 * char *trans;		resulting translated characters
 */
static char *xlat( char *source, char *lookup, char *trans) {
	char *sp;	/* pointer into source table */
	char *lp;	/* pointer into lookup table */
	char *rp;	/* pointer into result */
	static char result[NSTRING];	/* temporary result */

	/* scan source string */
	sp = source;
	rp = result;
	while (*sp) {
		/* scan lookup table for a match */
		lp = lookup;
		while (*lp) {
			if (*sp == *lp) {
				*rp++ = trans[lp - lookup];
				goto xnext;
			}
			++lp;
		}

		/* no match, copy in the source char untranslated */
		*rp++ = *sp;

	      xnext:++sp;
	}

	/* terminate and return the result */
	*rp = 0;
	return result;
}

/*
 * Force a string out to the message line regardless of the
 * current $discmd setting. This is needed when $debug is TRUE
 * and for the write-message and clear-message-line commands
 *
 * char *s;		string to force out
 */
static void mlforce( char *s) {
	int oldcmd;	/* original command display flag */

	oldcmd = discmd;	/* save the discmd value */
	discmd = TRUE;		/* and turn display on */
	mlwrite( (*s) ? "%s" : "", s) ;	/* write the string out or erase line */
	discmd = oldcmd;	/* and restore the original setting */
}


/* This function simply clears the message line, mainly for macro usage
 *
 * int f, n;		arguments ignored
 */
TBINDABLE( clrmes) {
	mlforce( "") ;
	return TRUE ;
}


/* This function writes a string on the message line mainly for macro usage
 *
 * int f, n; arguments ignored
 */
BINDABLE( writemsg) {
	char *buf ; /* buffer to receive message into */

	int status = newmlarg( &buf, "write-message: ", 0) ;
	if( status == TRUE) {
	/* write the message out */
		mlforce( buf) ;
		free( buf) ;
	}

	return status ;
}


/* end of eval.c */