v07i074: A BASIC Interpreter, Part02/06

sources-request at mirror.UUCP sources-request at mirror.UUCP
Fri Dec 5 01:26:53 AEST 1986


Submitted by: phil at Cs.Ucl.AC.UK
Mod.sources: Volume 7, Issue 74
Archive-name: basic/Part02

# Shar file shar02 (of 6)
#
# This is a shell archive containing the following files :-
#	bas2.c
#	bas3.c
#	bas4.c
#	bas5.c
#	bas6.c
# ------------------------------
# This is a shell archive, shar, format file.
# To unarchive, feed this text into /bin/sh in the directory
# you wish the files to be in.

echo x - bas2.c 1>&2
sed 's/^X//' > bas2.c << 'End of bas2.c'
X/*
X * BASIC by Phil Cockcroft
X */
X#include        "bas.h"
X
X/*
X *  This file contains the routines to get a variable from its name
X *  To dimension arrays and assignment to a variable.
X *
X *      A variable name consists of a letter followed by an optional
X *    letter or digit followed by the type specifier.
X *      A type specifier is a '%' for an integer a '$' for a string
X *    or is absent if the variable is a real ( Default ).
X *      An integer variable also has the top bit of its second letter
X *    set this is used to distinguish between real and integer variables.
X *      A variable name can be optionally followed by a subscript
X *    turning the variable into a subscripted variable.
X *    A subscript is specified by a list of indexes in square brackets
X *    e.g.  [1,2,3] , a maximum of three subscripts may be used.
X *    All arrays must be specified before use.
X *
X *      The variable to be accessed has its name in the array nm[],
X *    and its type in the variable 'vartype'.
X *
X *      'vartype' is very important as it is used all over the place
X *
X *      The value in 'vartype' can have the following values:-
X *              0:      real variable (Default ).
X *              1:      integer variable.
X *              2:      string variable.
X *
X */
X
X#ifdef  V6
X#define LBRACK  '['
X#define RBRACK  ']'
X#else
X#define LBRACK  '('
X#define RBRACK  ')'
X#endif
X
X/*
X * getnm will return with nm[] and vartype set appropriately but without
X * any regard for subscript parameters. Called by dimensio() only.
X */
X
Xgetnm()
X{
X#ifdef  LNAMES
X	register char   *p,*q;
X	register struct entry   *ep,*np;
X	register int    c;
X	register int    l;
X	nam[0]=c=getch();
X	if(!isletter(c))
X		error(VARREQD);
X	p = &nam[1];
X	for(l =c,c = *point;isletter(c) || isnumber(c) ; c = *++point)
X		if(p < &nam[MAXNAME-1] ){
X			l +=c;
X			*p++ = c;
X		}
X	*p = 0;
X	for(np = 0,ep=hshtab[l%HSHTABSIZ]; ep ; np = ep,ep=ep->link)
X		if(l == ep->ln_hash)
X			for(p = ep->_name,q = nam ; *q == *p++ ; )
X				if(!*q++)
X					goto got;
X	ep = (struct entry *)xpand(&enames,sizeof(struct entry));
X	if(!np)
X		hshtab[l%HSHTABSIZ] = ep;
X	else
X		np->link = ep;
X	for(p = ep->_name , q = nam ; *p++ = *q++ ; );
X	ep->ln_hash = l;
Xgot:
X	nm = (char *)ep - estring;
X#else
X	register int    c;
X	nm=c=getch();
X	if(!isletter(c))
X		error(VARREQD);
X	c= *point;
X	if(isletter(c) ||isnumber(c)){
X		nm |= c<<8;
X		do
X			c= *++point;
X		while(isletter(c) || isnumber(c));
X	}
X#endif
X	vartype=0;
X	if(c=='$'){
X		point++;
X		vartype=02;
X	}
X	else if(c=='%'){
X		point++;
X		vartype++;
X		nm |=0200<<8;
X	}
X}
X
X/*
X *      getname() will return a pointer to a variable with vartype
X *    set to the correct type. If the variable is subscripted getarray
X *    is called and the subscripts are evaluated and depending upon
X *    the type of variable the index into that array is returned.
X *      Any simple variable that is not already declared is defined
X *    and has a value of 0 or null (for strings) assigned to it.
X *      In all instances a valid pointer is returned.
X */
Xmemp getname()
X{
X	memp    getstring();
X#ifdef  LNAMES
X	register char   *p,*q;
X	register struct entry   *ep;
X	register int    c;
X	register struct vardata *pt;
X	struct  entry   *np;
X	register int    l;
X	nam[0]=c=getch();
X	if(!isletter(c))
X		error(VARREQD);
X	p = &nam[1];
X	for(l =c,c = *point;isletter(c) || isnumber(c) ; c = *++point)
X		if(p < &nam[MAXNAME-1] ){
X			l +=c;
X			*p++ = c;
X		}
X	*p = 0;
X	for(np = 0,ep=hshtab[l%HSHTABSIZ]; ep ; np = ep,ep=ep->link)
X		if(l == ep->ln_hash)
X			for(p = ep->_name,q = nam ; *q == *p++ ; )
X				if(!*q++)
X					goto got;
X	ep = (struct entry *)xpand(&enames,sizeof(struct entry));
X	if(!np)
X		hshtab[l%HSHTABSIZ] = ep;
X	else
X		np->link = ep;
X	for(p = ep->_name ,q = nam ; *p++ = *q++ ; );
X	ep->ln_hash = l;
Xgot:
X	nm = (char *)ep - estring;
X#else
X	register int    c;
X	register struct vardata *pt;
X
X	nm=c=getch();
X	if(!isletter(c))
X		error(VARREQD);
X	c= *point;
X	if(isletter(c) ||isnumber(c)){
X		nm |=c<<8;
X		do{ c= *++point; }while(isletter(c) || isnumber(c));
X	}
X#endif
X	vartype=0;
X	if(c=='$'){
X		vartype=02;
X		if(*++point==LBRACK)
X			getarray();
X		return(getstring());
X	}
X	else if(c=='%'){
X		point++;
X		vartype++;
X		nm |= 0200<<8;
X	}
X	if(*point==LBRACK)
X		return( (memp) getarray());
X#ifdef  LNAMES
X	/*
X	 * now do hashing of the variables
X	 */
X	if( (c = varshash[l % HSHTABSIZ]) >= 0){
X		pt = (vardp)earray;
X		for(pt += c; pt < (vardp) vend;pt++)
X			if(pt->nam ==nm )
X				return( (memp) &pt->dt);
X		/*
X		 * not found ****
X		 */
X	}
X	/*
X	 * really look for it - will force varshash to be the lowest
X	 * value. The hassle of chaining.
X	 */
X	if(chained)
X		for(pt = (vardp)earray; pt < (vardp) vend;pt++)
X			if(pt->nam ==nm ){
X				varshash[l % HSHTABSIZ] = pt - (vardp)earray;
X				return((memp) &pt->dt);
X			}
X		/*
X		 * not found ****
X		 */
X	pt= (vardp) xpand(&vend,sizeof(struct vardata));
X	if(c < 0)
X		varshash[l % HSHTABSIZ] = pt - (vardp)earray;
X#else
X	for(pt = (vardp)earray;  pt < (vardp) vend;pt++)
X		if(pt->nam ==nm )
X			return( (memp) &pt->dt);
X	pt= (vardp) xpand(&vend,sizeof(struct vardata));
X#endif
X	pt->nam=nm;
X	return( (memp) &pt->dt);
X}
X
X/*
X *      getstring() returns a pointer to a string structure if the string
X *    is not declared then it is defined.
X */
X
Xmemp
Xgetstring()
X{
X	register struct stdata  *p;
X	vartype=02;
X	for(p= (stdatap)estdt ; p < (stdatap)estring ; p++)
X		if(p->snam == nm )
X			return( (memp) p);
X	if( estdt - sizeof(struct stdata) < eostring){
X		garbage();
X		if(estdt - sizeof(struct stdata) <eostring)
X			error(OUTOFSTRINGSPACE);
X	}
X	p = (stdatap)estdt;
X	--p; estdt = (memp)p;
X	p->snam = nm;
X	p->stpt=0;
X	return( (memp) p);
X}
X
X/*
X *      getarray() evaluates the subscripts of an array and the tries
X *    to access it. getarray() returns different things dependent
X *    on the type of variable. For an integer or real then the pointer to
X *    the element of the array is returned.
X *      For a string array element then the nm[] array is filled out
X *    with a unique number and then getstring() is called to access it.
X *      The variable hash (in the strarr structure ) is used as the
X *    offset to the next array if the array is real or integer, but
X *    is the base for the unique number to access the string structure.
X *
X *      This is a piece of 'hairy' codeing.
X */
X
Xgetarray()
X{
X	register struct strarr  *p;
X	register int     l;
X	short   *m;
X	int     c;
X	int     i=1;
X	register int     j=0;
X	char    vty;
X#ifdef  LNAMES
X	memp    savee;
X#endif
X
X	point++;
X	vty=vartype;
X	if(vty==02){
X		for(p= (strarrp) edefns ; p < (strarrp) estarr ; p++)
X			if(p->snm ==nm )
X				goto got;
X	}
X	else {
X		for( p = (strarrp) estarr ; p < (strarrp)earray ;
X					p = (strarrp)((memp)p + p->hash) )
X			if(p->snm ==nm )
X				goto got;
X	}
X	error(19);
Xgot:    m = p->dim;
X	i=1;
X	do{
X#ifdef  LNAMES
X		savee = edefns;
X#endif
X		l=evalint()-baseval;
X#ifdef  LNAMES
X		p = (strarrp)((memp)p + (edefns - savee));
X#endif
X		if(l >= *m || l <0)
X			error(17);
X		j= l + j * *m;
X		if((c=getch())!=',')
X			break;
X		m++,i++;
X	} while(i <= p->dimens);
X	if(i!=p->dimens || c!=RBRACK)
X		error(16);
X	vartype=vty;
X	if(vty==02){
X		j += p->hash;
X		j |= 0100000;
X		nm = j;
X	}
X	else {
X		j <<= (vty ? 1 : 3 );
X		p++;
X		return( (int) ((char *)p+j) );
X	}
X}
X
X/*
X *      dimensio() executes the dim command. It sets up the strarr structure
X *    as needed. If the array is a string array then only the structure
X *    is filled in. This means that elements of a string array do not have
X *    storage allocated until assigned to. If the array is real or integer
X *    then the array is allocated space as well as the strarr array.
X *      This is why the hash element is needed so as to be able to access
X *    the next array.
X */
X
X
Xdimensio()
X{
X	int     dims[3];
X	int     nmm;
X	long    j;
X	int     c;
X	char    vty;
X	register int     i;
X	register int    *r;
X	register struct strarr *p;
Xfor(;;){
X	r=dims;
X	i=0;
X	j=1;
X	getnm();
X	nmm = nm;
X	vty=vartype;            /* save copy of type of array */
X	if(*point++!=LBRACK)
X		error(SYNTAX);
X	do{
X		*r=evalint() + 1 - baseval;
X#ifndef pdp11
X		if( (j *= *r) <= 0 || j > 32767)
X#else
X		if( (j=dimmul( (int)j , *r)) <= 0)
X#endif
X			error(17);
X		if((c=getch())!=',')
X			break;
X		r++;i++;
X	}while(i<3);
X	if(i ==3 || c!=RBRACK)
X		error(16);
X	i++;
X	if(vty== 02){
X		for(p= (strarrp) edefns ;p < (strarrp) estarr;p++)
X			if(p->snm == nmm )
X				error(20);
X		if(j+shash > 32767)
X			error(17);
X		p = (strarrp) xpand(&estarr,sizeof(struct strarr));
X		p->hash= shash;
X		shash+=j;
X	}
X	else   {
X		for(p = (strarrp)estarr ; p < (strarrp)earray ;
X					p = (strarrp)((memp)p + p->hash) )
X			if(p->snm == nmm )
X				error(20);
X		j<<= (vty ? 1 : 3);
X		j += sizeof(struct strarr);
X#ifdef  ALIGN4
X		j = (j + 3) & ~03;
X#endif
X		if(nospace(j))
X			error(17);
X		p = (strarrp) xpand(&earray,(int)j);
X		p->hash = j;    /* offset to next array */
X	}
X	p->snm = nmm;       /* fill in common stuff */
X	p->dimens=i;
X	p->dim[0]=dims[0];
X	p->dim[1]=dims[1];
X	p->dim[2]=dims[2];
X	if(getch()!=',')        /* any more arrays */
X		break;
X	}
X	point--;
X	normret;
X}
X
X/*
X *      Assign() is called if there is no keyword at the start of a
X *    statement ( Default assignment statement ) and by let.
X *    it just calls the relevent evaluation routine and leaves all the
X *    hard work to stringassign() and putin() to actualy assign the variables.
X */
X
Xassign()
X{
X	register memp   p;
X	register char   vty;
X	register int    c;
X	int     i;
X	value   t1;
X	extern  int     (*mbin[])();
X#ifdef  LNAMES
X	memp    savee;
X#endif
X
X	p= getname();
X	vty=vartype;
X	if(vty==02){
X		if(getch()!='=')
X			error(4);
X		stringeval(gblock);
X		stringassign( (stdatap)p );
X		return;
X	}
X#ifdef  LNAMES
X	savee = edefns;
X#endif
X	if((c = getch()) != '='){
X		i = 6;
X		switch(c){
X		default:
X			error(4);
X		case '*':
X		case '/':
X			i += 2;
X			break;
X		case '+':
X		case '-':
X			break;
X		}
X		if(*point++ != '=')
X			error(4);
X#ifndef V6C
X		t1 = *((value *)p);
X#else
X		movein(p,&t1);
X#endif
X		eval();
X		if(vty != vartype){
X			if(vty)
X				cvt(&t1);
X			else
X				cvt(&res);
X			vartype = 0;
X		}
X		(*mbin[i+vartype])(&t1,&res,c);
X	}
X	else
X		eval();
X#ifdef  LNAMES
X	/*
X	 * cope with adding new names - pushes space up
X	 */
X	p += edefns - savee;
X#endif
X	putin(p,vty);
X}
End of bas2.c
chmod u=rw-,g=r,o=r bas2.c
echo x - bas3.c 1>&2
sed 's/^X//' > bas3.c << 'End of bas3.c'
X/*
X * BASIC by Phil Cockcroft
X */
X#include        "bas.h"
X
X/*
X *      This file contains the numeric evaluation routines and some
X *    of the numeric functions.
X */
X
X/*
X *      evalint() is called by a routine that requires an integer value
X *    e.g. string functions. It will always return an integer. If
X *    the result will not overflow an integer -1 is returned.
X *      N.B. most ( all ) routines assume that a negative return is an
X *    error.
X */
X
X
Xevalint()
X{
X	eval();
X	if(vartype)
X		return(res.i);
X	if(conv(&res))
X		return(-1);
X	return(res.i);
X}
X
X/*
X *      This structure is only ever used by eval() and so is not declared
X *    in 'bas.h' with the others.
X */
X
X
Xstruct  m {
X	value   r1;
X	int     lastop;
X	char    value;
X	char    vty;
X	};
X
X/*
X *      eval() will evaluate any numeric expression and return the result
X *    in the UNION 'res'.
X *      A valid expression can be any numeric expression or a string
X *    comparison expression e.g. "as" <> "gh" . String expressions can
X *    themselves be used in relational tests and also be used with the
X *    logical operators. e.g. "a" <> "b" and "1" <> a$ is a valid
X *    expression.
X */
X
Xeval()
X{
X	extern   (*mbin[])();
X	register int    i;
X	register int    c;
X	register struct    m    *j;
X	value   *pp;
X	char    firsttime=1;
X	char    minus=0,noting=0;
X	struct   m      restab[6];
X
X	checksp();
X	j=restab;
X	j->value=0;
X
Xfor(;;){
X	c=getch();
X	if(c=='-' && firsttime){
X		if(minus)
X			error(SYNTAX);
X		minus++;
X		continue;
X	}
X	else if(c==NOTT){
X		if(noting)
X			error(SYNTAX);
X		noting++;
X		firsttime++;
X		continue;
X	}
X	else if(c&0200){
X		if(c<MINFUNC || c>MAXFUNC)      /* we have a function */
X			goto err1;      /* possibly a string function */
X		if(c>= RND )                    /* functions that don't */
X			(*functs[c-RND])();     /* require arguments */
X		else  {
X			if(*point++ !='(')
X				error(SYNTAX);  /* functions that do */
X			(*functb[c-MINFUNC])();
X			if(getch()!=')')
X				error(SYNTAX);
X		}
X	}
X	else if(isletter(c)){
X		char    *sp = --point;
X
X		pp= (value *)getname();         /* we have a variable */
X		if(vartype== 02){       /* a string !!!!!! */
X			if(firsttime){  /* no need for checktype() since */
X				point = sp;     /* we know it's a string */
X				stringcompare();
X				goto ex;
X			}
X			else error(2);          /* variable required */
X		}
X#ifdef  V6C
X		getv(pp);
X#else
X		res = *pp;
X#endif
X	}
X	else if(isnumber(c) || c=='.'){
X		point--;
X		if(!getop())            /* we have a number */
X			error(36);      /* bad number */
X	}
X	else if(c=='('){                /* bracketed expression */
X		eval();                 /* recursive call of eval() */
X		if(getch()!=')')
X			error(SYNTAX);
X	}
X	else  {
Xerr1:           /* get here if the function we tried to access was not   */
X		/* a legal maths func. or a string variable */
X		/* stringcompare() will give a syntax error if not a valid */
X		/* string. therefore this works ok */
X		point--;
X		if(!firsttime)
X			error(SYNTAX);
X		stringcompare();
X	}
Xex:
X	if(minus){                      /* do the unary minus */
X		minus=0;
X		negate();
X	}
X	if(noting){                     /* do the not */
X		noting=0;
X		notit();
X	}
X	i=0;
X	switch(c=getch()){              /* get the precedence of the */
X		case    '^':    i++;    /* operator */
X		case    '*':
X		case    '/':
X		case    MODD:   i++;
X		case    '+':
X		case    '-':    i++;
X		case    EQL:            /* comparison operators */
X		case    LTEQ:
X		case    NEQE:
X		case    LTTH:
X		case    GTEQ:
X		case    GRTH:   i++;    /* logical operators */
X		case    ANDD:
X		case    ORR:
X		case    XORR:   i++;
X	}
X	if(i>2)
X		firsttime = 0;
Xame:    if(j->value< (char)i){          /* current operator has higher */
X		(++j)->lastop=c;                        /* precedence */
X#ifndef V6C
X		j->r1 = res;
X#else
X		push(&j->r1);  /* block moving */
X#endif
X		j->value=i;
X		j->vty=vartype;
X		continue;
X	}
X	if(! j->value ){                /* end of expression */
X		point--;
X		return;
X	}
X	if(j->vty!=vartype){            /* make both parameters */
X		if(vartype)             /* the same type */
X			cvt(&res);
X		else
X			cvt(&j->r1);    /* if changed then they must be */
X		vartype=0;              /* changed to reals */
X	}
X	(*mbin[(j->value<<1)+vartype])(&j->r1,&res,j->lastop);
X	j--;                    /* execute it then pop the stack and */
X	goto ame;               /* deal with the next operator */
X	}
X}
X
X/*
X *      The rest of the routines in this file evaluate functions and are
X *    relatively straight forward.
X */
X
Xtim()
X{
X	time(&overfl);
X
X#ifndef SOFTFP
X	res.f = overfl;
X	vartype = 0;
X#else
X	over(0,&res);           /* convert from long to real */
X#endif
X}
X
Xrnd()
X{
X	static  double  recip32 = 32767.0;
X	value   temp;
X	register int    rn;
X
X	rn = rand() & 077777;
X	if(*point!='('){
X		res.i=rn;
X		vartype=01;
X		return;
X	}
X	point++;
X	eval();
X	if(getch()!=')')
X		error(SYNTAX);
X#ifdef  PORTABLE
X	if(vartype ? res.i : res.f){
X#else
X	if(res.i){
X#endif
X		if(!vartype && conv(&res))
X			error(FUNCT);
X		res.i= rn % res.i + 1;
X		vartype=01;
X		return;
X	}
X#ifndef SOFTFP
X	res.f = (double)rn / recip32;
X#else
X	temp.i=rn;
X	cvt(&temp);
X#ifndef V6C
X	res = *( (value *)( &recip32 ) );
X#else
X	movein(&recip32,&res);
X#endif
X	fdiv(&temp,&res);            /* horrible */
X#endif
X	vartype =0;
X}
X
X/*
X *      This routine is the command 'random' and is placed here for some
X *    unknown reason it just sets the seed to rnd to the value from
X *    the time system call ( is a random number ).
X */
X
Xrandom()
X{
X	long    m;
X	time(&m);
X	srand((int)m);
X	normret;
X}
X
Xerlin()
X{
X	res.i = elinnumb;
X	vartype=01;
X	if(res.i < 0 ){                      /* make large linenumbers */
X#ifndef SOFTFP
X		res.f = (unsigned)elinnumb;
X		vartype = 0;
X#else
X		overfl=(unsigned)elinnumb;      /* into reals as they */
X		over(0,&res);                   /* overflow integers */
X#endif
X	}
X}
X
Xerval()
X{
X	res.i =ecode;
X	vartype=01;
X}
X
Xsgn()
X{
X	eval();
X#ifdef  PORTABLE
X	if(!vartype){
X		if(res.f < 0)
X			res.i = -1;
X		else if(res.f > 0)
X			res.i = 1;
X		else res.i = 0;
X		vartype = 1;
X		return;
X	}
X#endif
X	if(res.i<0)             /* bit twiddling */
X		res.i = -1;     /* real numbers have the top bit set if */
X	else if(res.i>0)        /* negative and the top word is non-zero */
X		res.i= 1;       /* for all non-zero numbers */
X	vartype=01;
X}
X
Xabs()
X{
X	eval();
X#ifdef  PORTABLE
X	if(!vartype){
X		if(res.f < 0)
X			negate();
X		return;
X	}
X#endif
X	if(res.i<0)
X		negate();
X}
X
Xlen()
X{
X	stringeval(gblock);
X	res.i =gcursiz;
X	vartype=01;
X}
X
Xascval()
X{
X	stringeval(gblock);
X	if(!gcursiz)
X		error(FUNCT);
X	res.i = *gblock & 0377;
X	vartype=01;
X}
X
Xsqrtf()
X{
X#ifndef SOFTFP
X	double  sqrt();
X#endif
X	eval();
X	if(vartype)
X		cvt(&res);
X	vartype=0;
X#ifdef  PORTABLE
X	if(res.f < 0)
X#else
X	if(res.i < 0)
X#endif
X		error(37);      /* negative square root */
X#ifndef SOFTFP
X	res.f = sqrt(res.f);
X#else
X	sqrt(&res);
X#endif
X}
X
Xlogf()
X{
X#ifndef SOFTFP
X	double  log();
X#endif
X	eval();
X	if(vartype)
X		cvt(&res);
X	vartype=0;
X#ifdef  PORTABLE
X	if(res.f <= 0)
X#else
X	if(res.i <= 0)
X#endif
X		error(38);      /* bad log value */
X#ifndef SOFTFP
X	res.f = log(res.f);
X#else
X	log(&res);
X#endif
X}
X
Xexpf()
X{
X#ifndef SOFTFP
X	double  exp();
X#endif
X	eval();
X	if(vartype)
X		cvt(&res);
X	vartype=0;
X#ifndef SOFTFP
X	if(res.f > 88.02969)
X		error(39);
X	res.f = exp(res.f);
X#else
X	if(!exp(&res))
X		error(39);      /* overflow in exp */
X#endif
X}
X
Xpii()
X{
X#ifndef SOFTFP
X	res.f = pivalue;
X#else
X	movein(&pivalue,&res);
X#endif
X	vartype=0;
X}
X
X/*
X *      This routine will deal with the eval() function. It has to do
X *    a lot of moving of data. to enable it to 'compile' an expression
X *    so that it can be evaluated.
X */
X
X
Xevalu()
X{
X	register char   *tmp;
X	char    chblck1[256];
X	char    chblck2[256];
X
X	checksp();
X	if(evallock>5)
X		error(43);      /* mutually recursive eval */
X	evallock++;
X	stringeval(gblock);
X	gblock[gcursiz]=0;
X	strcpy(nline,chblck2);          /* save nline */
X	line[0]='\01';                  /* stop a line number being created */
X	strcpy(gblock,&line[1]);
X	compile(0);
X	strcpy(&nline[1],chblck1);    /* restore nline ( eval in immeadiate */
X	strcpy(chblck2,nline);        /* mode ). */
X	tmp=point;
X	point=chblck1;
X	eval();
X	if(getch())
X		error(SYNTAX);
X	point=tmp;
X	evallock--;
X}
X
Xffn()
X{
X	register struct  deffn   *p;
X	value   ovrs[3];
X	value   nvrs[3];
X	char    vttys[3];
X	char    *spoint;
X	register int    i;
X	if(!isletter(*point))
X		error(SYNTAX);
X	getnm();
X#ifdef  LNAMES
X	for(p = (deffnp)enames ; p < (deffnp)edefns ;
X					p = (deffnp)((memp)p + p->offs) )
X#else
X	for( p = (deffnp)estring ; p < (deffnp)edefns ;
X					p = (deffnp)((memp)p + p->offs) )
X#endif
X		if(p->dnm ==nm )
X			goto got;
X	error(UNDEFFN);
Xgot:
X	for(i=0;i<p->narg;i++)  /* save values */
X#ifndef V6C
X		ovrs[i] = *((value *) (p->vargs[i] + earray) );
X#else
X		movein( (double *)(p->vargs[i] + earray) ,&ovrs[i]);
X#endif
X	if(p->narg){
X		if(*point++!='(')
X			error(SYNTAX);
X		for(i=0;;){
X			eval();
X#ifndef V6C
X			nvrs[i] = res;
X#else
X			movein(&res,&nvrs[i]);
X#endif
X			vttys[i] = vartype;
X			if(++i >= p->narg )
X				break;
X			if( getch() != ',' )
X				error(SYNTAX);
X		}
X		if( getch() != ')' )
X			error(SYNTAX);
X	}                               /* got arguments in nvrs[] */
X
X	for(i=0;i<p->narg;i++){         /* put in new values */
X#ifndef V6C
X		res = nvrs[i];
X#else
X		movein(&nvrs[i],&res);
X#endif
X		vartype=vttys[i];
X		putin((value *)(p->vargs[i] + earray),((p->vtys>>i)&01));
X	}
X	spoint=point;
X	point=p->exp;
X	eval();
X	for(i=0;i<p->narg;i++)
X#ifndef V6C
X		*( (value *)(p->vargs[i] + earray)) = ovrs[i];
X#else
X		movein(&ovrs[i], (double *) (p->vargs[i] + earray) );
X#endif
X	if(getch())
X		error(SYNTAX);
X	point= spoint;
X	i= p->vtys>>4;
X	if(vartype != (char)i){
X		if(vartype)
X			cvt(&res);
X		else if(conv(&res))
X			error(INTOVER);
X		vartype=i;
X	}
X}
X
X/* int() - return the greatest integer less than x */
X
Xintf()
X{
X#ifndef SOFTFP
X	double  floor();
X	eval();
X	if(!vartype)
X		res.f = floor(res.f);
X	if(!conv(&res))
X		vartype=01;
X#else
X	value   temp;
X	static  double  ONE = 1.0;
X
X	eval();
X	if(vartype)             /* conv and integ truncate not round */
X		return;
X#ifdef  PORTABLE
X	if(res.f>=0){
X#else
X	if(res.i>=0){                   /* positive easy */
X#endif
X		if(!conv(&res))
X			vartype=01;
X		else integ(&res);
X		return;
X	}
X#ifndef V6C
X	temp = res;
X#else
X	movein(&res,&temp);
X#endif
X	integ(&res);
X	if(cmp(&res,&temp)){            /* not got an integer subtract one */
X#ifndef V6C
X		res = *((value *)&ONE);
X#else
X		movein(&ONE,&res);
X#endif
X		fsub(&temp,&res);
X		integ(&res);
X	}
X	if(!conv(&res))
X		vartype=01;
X#endif                                  /* not floating point */
X}
X
Xpeekf(sp)
X{
X	register char   *p;
X#ifndef pdp11
X	register long   l;
X	eval();
X	if(vartype)
X		cvt(&res);
X	l = res.f;
X	if(res.f > 0x7fff000 || res.f < 0)      /* check this */
X		error(FUNCT);
X	p = (char *)l;
X#else
X	eval();
X	if(!vartype && conv(&res))
X		error(FUNCT);
X	p= (char *)res.i;               /* horrible - fix for a Vax */
X#endif
X	vartype=01;
X	if(p>vvend && p < (char *)&sp )
X		res.i=0;
X	else res.i = *p & 0377;
X}
X
Xpoke(sp)                /* sp = approx position of stack */
X{                                       /* can give bus errors */
X#ifndef pdp11                           /* why are you poking any way ??? */
X	register long   l;
X#endif
X	register char   *p;
X	register int    i;
X	eval();
X	if(getch()!=',')
X		error(SYNTAX);
X#ifndef pdp11
X	if(vartype)
X		cvt(&res);
X	l = res.f;
X	if(res.f > 0x7fff000 || res.f < 0)      /* check this */
X		error(FUNCT);
X	p = (char *)l;
X#else
X	if(!vartype && conv(&res))
X		error(FUNCT);
X	p= (char *)res.i;
X#endif
X	i= evalint();
X	check();
X	if(i<0)
X		error(FUNCT);
X	if(p< vvend || p > (char *)&sp)
X		*p = i;
X	normret;
X}
X
Xsinf()
X{
X#ifndef SOFTFP
X	double  sin();
X#endif
X	eval();
X	if(vartype)
X		cvt(&res);
X	vartype=0;
X#ifndef SOFTFP
X	res.f = sin(res.f);
X#else
X	sin(&res);
X#endif
X}
X
Xcosf()
X{
X#ifndef SOFTFP
X	double  cos();
X#endif
X	eval();
X	if(vartype)
X		cvt(&res);
X	vartype=0;
X#ifndef SOFTFP
X	res.f = cos(res.f);
X#else
X	cos(&res);
X#endif
X}
X
Xatanf()
X{
X#ifndef SOFTFP
X	double  atan();
X#endif
X	eval();
X	if(vartype)
X		cvt(&res);
X	vartype=0;
X#ifndef SOFTFP
X	res.f = atan(res.f);
X#else
X	atan(&res);
X#endif
X}
X
X/*
X * the "system" function, returns the status of the command it executes
X */
X
X
Xssystem()
X{
X	register int    i;
X	register int    (*q)() , (*p)();
X	int     (*signal())();
X	char    *s;
X	int     status;
X#ifdef  SIGTSTP
X	int     (*t)();
X#endif
X
X	stringeval(gblock);             /* get the command */
X	gblock[gcursiz] = 0;
X
X	flushall();
X#ifdef  SIGTSTP
X	t = signal(SIGTSTP, SIG_DFL);
X#endif
X#ifdef  VFORK
X	i = vfork();
X#else
X	i=fork();
X#endif
X	if(i==0){
X		rset_term(1);
X		setuid(getuid());               /* stop user getting clever */
X#ifdef  V7
X		s = getenv("SHELL");
X		if(!s || !*s)
X			s = "/bin/sh";
X#else
X		s = "/bin/sh";
X#endif
X		execl(s, "sh (from basic)", "-c", gblock, 0);
X		exit(-1);                       /* problem */
X	}
X	if(i != -1){
X		p=signal(SIGINT,SIG_IGN);       /* ignore some signals */
X		q=signal(SIGQUIT, SIG_IGN);
X		while(i != wait(&status) );     /* wait on the 'child' */
X		signal(SIGINT,p);               /* resignal to what they */
X		signal(SIGQUIT,q);              /* were before */
X						/* in a mode fit for basic */
X		set_term();                     /* reset terminal modes */
X		rset_term(0);
X		i = status;
X	}
X#ifdef  SIGTSTP
X	signal(SIGTSTP, t);
X#endif
X	vartype = 1;
X	res.i = i;
X}
End of bas3.c
chmod u=rw-,g=r,o=r bas3.c
echo x - bas4.c 1>&2
sed 's/^X//' > bas4.c << 'End of bas4.c'
X/*
X * BASIC by Phil Cockcroft
X */
X#include        "bas.h"
X
X/*
X *      Stringeval() will evaluate a string expression of any
X *    form. '+' is used as the concatenation operator
X *
X *      gblock and gcursiz are used as global variables by the
X *    string routines. Gblock contains the resultant string while
X *    gcursiz holds the length of the resultant string ( even if not
X *    put in gblock ).
X *      For routines that need more than one result e.g. mid$ instr$
X *    then one result at least is put on the stack while the other
X *    ( possibly ) is put in gblock.
X */
X
X/*
X *      The parameter to stringeval() is a pointer to where the
X *    result will be put.
X */
X
X
Xstringeval(gblck)
Xchar    *gblck;
X{
X	int     cursiz=0;
X	memp    l;
X	int     c;
X	char    charac;
X	register char   *p,*q;
X	register int    i;
X	int     m[2];
X	char    chblock[256];
X	char    *ctime();
X	checksp();
X	q=chblock;
Xfor(;;){
X	gcursiz=0;
X	c=getch();
X	if(c&0200){             /* a string function */
X		if(c==DATE){            /* date does not want a parameter */
X			time(m);
X			p=ctime(m);
X			gcursiz=24;
X		}
X		else {
X			if(c<MINSTRING || c>MAXSTRING)
X				error(11);
X			if(*point++!='(')
X				error(1);
X			(*strngcommand[c-MINSTRING])();
X			if(getch()!=')')
X				error(1);
X			p=gblock;       /* string functions return with */
X		}                       /* result in gblock */
X	}
X	else if(c=='"' || c=='`'){      /* a quoted string */
X		charac=c;
X		p=point;
X		while(*point && *point!= charac){
X			gcursiz++;
X			point++;
X		}
X		if(*point)
X			point++;
X	}
X	else if(isletter(c)){           /* a string variable */
X		point--;
X		l=getname();
X		if(vartype!=02)
X			error(SYNTAX);
X		if(p= ((stdatap)l)->stpt)           /* newstring routines */
X			gcursiz= *p++ &0377;
X	}
X	else
X		error(SYNTAX);
X   /* all routines return to here with the string pointed to by p */
X	if(cursiz+gcursiz>255)
X		error(9);
X	i=gcursiz;
X	if(getch()!='+')
X		break;
X	cursiz += i;
X	if(i)  do
X		 *q++ = *p++;
X	       while(--i);
X	}
X	point--;                        /* the following code is */
X	if(!cursiz){                    /* horrible but it speeds */
X		if(p==gblck)            /* execution by reducing the amount */
X			return;         /* of movement of strings */
X		cursiz=gcursiz;
X	}
X	else {
X		cursiz+=gcursiz;
X		if(i) do
X			*q++ = *p++;
X		      while(--i);
X		p=chblock;
X	}
X	q=gblck;
X	gcursiz=cursiz;
X	if(i=cursiz)
X	      do
X		*q++ = *p++;
X	      while(--i);
X}
X
X/*
X *      stringassign() will put the sting in gblock into the string
X *    pointed to by p.
X *      It will call the garbage collection routine as neccasary.
X */
X
Xstringassign(p)
Xstruct  stdata *p;
X{
X	register char   *q,*r;
X	register int    i;
X
X	p->stpt=0;
X	if(!gcursiz)
X		return;
X	if(estdt-eostring <gcursiz+1){
X		garbage();
X		if(estdt-eostring <gcursiz+1)
X			error(3);       /* out of string space */
X	}
X	p->stpt=eostring;
X	q=eostring;
X	i=gcursiz;
X	*q++ = i;
X	r= gblock;
X	do
X		*q++ = *r++;
X	while(--i);
X	eostring=q;
X}
X
X/*
X *      This will collect all unused strings and free the space
X *    It works that is about all tha can be said for it.
X */
X
Xgarbage()               /* new string routine */
X{
X	register char   *p,*q;
X	register struct stdata  *r;
X	register int     j;
X
X	p=ecore;
X	q=ecore;
X	while(p<eostring){
X		j= (*p&0377)+1;
X		for(r = (stdatap)estdt ; r < (stdatap)estring ; r++)
X			if(r->stpt==p)
X				if(q==p){
X					p+=j;
X					q=p;
X					goto more;
X				}
X				else  {
X					r->stpt=q;
X					do{
X						*q++ = *p++;
X					  }while(--j);
X					goto more;
X				}
X		p+=j;
Xmore:           ;
X	}
X	eostring=q;
X}
X
X/*
X *      The following routines implement string functions they are all quite
X *    straight forward in operation.
X */
X
Xstrng()
X{
X	int     m;
X	register char   *q,*p;
X	int    cursiz=0;
X	int     siz;
X	register int     i;
X	char    chblock[256];
X
X	checksp();
X	stringeval(chblock);
X	cursiz=gcursiz;
X	if(getch()!=',')
X		error(1);
X	m=evalint();
X	if(m>255 || m <0)
X		error(10);
X	if(!cursiz){
X		gcursiz=0;
X		return;
X	}
X	siz=m;
X	if((unsigned)(cursiz * siz) >255)
X		error(9);
X	gcursiz= cursiz *siz;
X	p=gblock;
X	while(siz--)
X		for(q=chblock,i=cursiz;i--;)
X			*p++ = *q++;
X}
X
X/*      left$ string function */
X
Xleftst()
X{
X	int     l1;
X	register int    i;
X	register char   *p,*q;
X	int     cursiz;
X	char    chblock[256];
X
X	checksp();
X	stringeval(chblock);
X	cursiz=gcursiz;
X	if(getch()!=',')
X		error(SYNTAX);
X	l1=evalint();
X	if(l1<0 || l1 >255)
X		error(10);
X	i=l1;
X	if(l1>cursiz)
X		i=cursiz;
X	p=chblock;
X	q=gblock;
X	if(gcursiz=i) do
X		   *q++ = *p++;
X	      while(--i);
X}
X
X/*      right$ string function */
X
Xrightst()
X{
X	int     l1,l2;
X	register int    i;
X	register char   *p,*q;
X	int     cursiz;
X	char    chblock[256];
X
X	checksp();
X	stringeval(chblock);
X	cursiz=gcursiz;
X	if(getch()!=',')
X		error(SYNTAX);
X	l1=evalint();
X	if(l1<0 || l1 >255)
X		error(10);
X	l2= cursiz-l1;
X	i=l1;
X	if(i>cursiz){
X		i=cursiz;
X		l2=0;
X	}
X	p= &chblock[l2];
X	q= gblock;
X	if(gcursiz=i) do
X		*q++ = *p++;
X	      while(--i);
X}
X
X/*
X *      midst$ string function:-
X *              can have two or three parameters , if third
X *              parameter is missing then a value of cursiz
X *              is used.
X */
X
Xmidst()
X{
X	int     l1,l2;
X	int    cursiz;
X	register int     i;
X	register char   *q,*p;
X	char    chblock[256];
X
X	checksp();
X	stringeval(chblock);
X	cursiz=gcursiz;
X	if(getch()!=',')
X		error(1);
X	l1=evalint()-1;
X	if(getch()!=','){
X		point--;
X		l2=255;
X	}
X	else
X		l2=evalint();
X	if(l1<0 || l2<0 || l1 >255 || l2 >255)
X		error(10);
X	l2+=l1;
X	if(l2>cursiz)
X		l2=cursiz;
X	if(l1>cursiz)
X		l1=cursiz;
X	i= l2-l1;
X	p=gblock;
X	q= &chblock[l1];
X	if(gcursiz=i) do
X		  *p++ = *q++;
X	      while(--i);
X}
X
X/*      ermsg$ string routine , returns the specified error message */
X
Xestrng()
X{
X	register char   *p,*q,*r;
X	int     l;
X
X	l=evalint();
X	if(l<1 || l> MAXERR)
X		error(22);
X	p=ermesg[l-1];
X	q=gblock;
X	r=p;
X	while(*q++ = *p++);
X	gcursiz= p-r-1;
X}
X
X/*      chr$ string function , returns character from the ascii value */
X
Xchrstr()
X{
X	register int    i;
X
X	i=evalint();
X	if(i<0 || i>255)
X		error(FUNCT);
X	*gblock= i;
X	gcursiz=1;
X}
X
X/*      str$ string routine , returns a string representation
X *      of the number given. There is NO leading space on positive
X *      numbers.
X */
X
Xnstrng()
X{
X	register char   *p,*q;
X
X	eval();
X	gcvt();
X	if(*gblock!=' ')
X		return;
X	q=gblock;
X	p= gblock+1;
X	while(*q++ = *p++);
X	gcursiz= --q -gblock;
X}
X
X/*      val() maths function , returns the value of a string. If
X *    no numeric value is used then a value of zero is returned.
X */
X
Xval()
X{
X	register char   *tmp,*p;
X	register minus=0;
X
X	stringeval(gblock);
X	gblock[gcursiz]=0;
X	p=gblock;
X	while(*p++ == ' ');
X	if(*--p=='-'){
X		p++;
X		minus++;
X	}
X	if(!isnumber(*p) && *p!='.'){
X		res.i=0;
X		vartype=01;
X		return;
X	}
X	tmp=point;
X	point=p;
X	if(!getop()){
X		point=tmp;
X		error(36);
X	}
X	point=tmp;
X	if(minus)
X		negate();
X}
X
X/*      instr() maths function , returns the index of the first string
X *    in the second. Starting either from the first character or from
X *    the optional third parameter position.
X */
X
Xinstr()
X{
X	int     cursiz1;
X	int     cursiz2;
X	register char   *p,*q,*r;
X	int     i=0;
X	char    chbl1ck[256];
X	char    chbl2ck[256];
X
X	checksp();
X	stringeval(chbl1ck);
X	cursiz1=gcursiz;
X	if(getch()!=',')
X		error(SYNTAX);
X	stringeval(chbl2ck);
X	cursiz2=gcursiz;
X	if(getch()==','){
X		i=evalint()-1;
X		if(i<0 || i>255)
X			error(10);
X	}
X	else
X		point--;
X	cursiz2-=cursiz1;
X	vartype=01;
X	r= &chbl2ck[cursiz1+i];
X	for(;i<=cursiz2;i++,r++){
X		p= chbl1ck;
X		q= &chbl2ck[i];
X		while(q < r && *p== *q)
X			p++,q++;
X		if( q == r ){
X			res.i = i+1;
X			return;
X		}
X	}
X	res.i = 0;
X}
X
X/*      space$ string function returns a string of spaces the number
X *    of which is the argument to the function
X */
X
Xspace()
X{
X	register int    i;
X	register char   *q;
X
X	i=evalint();
X	if(i<0 || i>255)
X		error(10);
X	if(gcursiz=i){
X		q= gblock;
X		do{
X			*q++ =' ';
X		}while(--i);
X	}
X}
X
X/* get$() read a single character from a file */
X
Xgetstf()
X{
X	register struct filebuf *p;
X	register i;
X
X	i=evalint();
X	if(!i){
X		if(noedit)        /* illegal function with silly terminals */
X			error(11);
X		if(!trapped){
X			set_term();
X			*gblock=readc();
X			rset_term(0);
X		}
X		if(!trapped)
X			gcursiz=1;
X		else
X			gcursiz =0;
X	}
X	else {
X		p=getf(i,_READ);
X		if(!(i = filein(p,gblock,1)) )
X			error(30);
X		gcursiz=i;
X	}
X}
X
X
X/*      mid$() when on the left of an assignment */
X/* can have optional third argument */
X
X/*      a$ = "this is me"
X * mid$(a$,2) = "hello"         ->   a$ = "thello"
X * mid$(a$,2,5) = "hello"       ->   a$ = "thellos me"
X */
X
Xlhmidst()
X{
X	char    chbl1ck[256];
X	char    chbl2ck[256];
X	int     cursiz,rhside,i1,i2;
X	memp    pt;
X	register char   *p,*q;
X	register int    i;
X
X	if(*point++ !='(')
X		error(SYNTAX);
X	pt=getname();
X	if(vartype!=02)
X		error(VARREQD);
X	if(getch()!=',')
X		error(SYNTAX);
X	i1=evalint()-1;
X	if(getch()!=','){
X		i2=255;
X		point--;
X	}
X	else
X		i2= evalint();
X	if(i2<0 || i2>255 || i1<0 || i1>255)
X		error(10);
X	if(getch()!=')' )
X		error(SYNTAX);
X	if(getch()!='=')
X		error(4);
X	cursiz=0;
X	if(p= ((stdatap)pt)->stpt){
X		cursiz=i= *p++ & 0377;
X		q=chbl1ck;
X		do{
X			*q++ = *p++;
X		}while(--i);
X	}
X	if(i1>cursiz)
X		i1=cursiz;
X	i2+=i1;
X	if(i2>cursiz)
X		i2=cursiz;
X	rhside= cursiz -i2;
X	if(i=rhside){
X		p=chbl2ck;
X		q= &chbl1ck[i2];
X		do{
X			*p++ = *q++;
X		}while(--i);
X	}
X	stringeval(gblock);
X	check();
X	if(gcursiz+rhside+i1>255)
X		error(9);
X	p= &chbl1ck[i1];
X	q= gblock;
X	if(i=gcursiz)
X		do{             /* what a lot of data movement */
X			*p++ = *q++;
X		}while(--i);
X	gcursiz+=i1;
X	q=chbl2ck;
X	if(i=rhside)
X		do{
X			*p++ = *q++;
X		}while(--i);
X	gcursiz+=rhside;
X	p=gblock;
X	q=chbl1ck;
X	if(i=gcursiz)
X		do{
X			*p++ = *q++;
X		}while(--i);
X	stringassign( (stdatap)pt );    /* done it !! */
X	normret;
X}
X
X#ifdef  _BLOCKED
X
X/* mkint(a$)
X * routine to make the first 2 bytes of string into a integer
X * for use with formatted files.
X */
X
Xmkint()
X{
X      register short  *p = (short *)gblock;
X      stringeval(gblock);
X      if(gcursiz < sizeof(short) )
X	      error(10);
X      res.i = *p;
X      vartype = 01;
X}
X
X/* ditto for string to double */
X
Xmkdouble()
X{
X      stringeval(gblock);
X      if(gcursiz < sizeof(double) )
X	      error(10);
X#ifndef V6C
X      res = *( (value *)gblock);
X#else
X      movein(gblock,&res);
X#endif
X      vartype = 0;
X}
X
X/*
X * mkistr$(x%)
X * convert an integer into a string for use with disk files
X */
X
Xmkistr()
X{
X      register short  *p = (short *)gblock;
X      eval();
X      if(!vartype && conv(&res))
X	      error(FUNCT);
X      *p = res.i;
X      gcursiz = sizeof(short);
X}
X
X/* mkdstr$(x)
X * ditto for doubles.
X */
X
Xmkdstr()
X{
X      eval();
X      if(vartype)
X	      cvt(&res);
X#ifndef V6C
X      *((value *)gblock) = res;
X#else
X      movein(&res,gblock);
X#endif
X      gcursiz = sizeof(double);
X}
X#else
Xmkdstr(){}
Xmkistr(){}
Xmkint(){}
Xmkdouble(){}
X#endif
End of bas4.c
chmod u=rw-,g=r,o=r bas4.c
echo x - bas5.c 1>&2
sed 's/^X//' > bas5.c << 'End of bas5.c'
X/*
X * BASIC by Phil Cockcroft
X */
X#include        "bas.h"
X
X/*
X *      This file contains the routines for input and read since they
X *    do almost the same they can use a lot of common code.
X */
X
X/*
X *      input can have a text string, which it outputs as a prompt
X *    instead of the usual '?'. If input is from a file this
X *    facility is not permitted ( what use anyway ? ).
X *
X *      added 28-oct-81
X */
X
Xinput()
X{
X	register char   *p;
X	register int    i;
X	memp    l;
X	register filebufp infile=0;
X	char    lblock[512];
X	int     firsttime=0;
X	int     c;
X	char    vty;
X	char    *getstrdt(),*getdata();
X
X	c=getch();
X	if(c=='"'){
X		i=0;
X		p=line;
X		while(*point && *point != '"'){
X			*p++ = *point++;
X			i++;
X		}
X		if(*point)
X			point++;
X		if(getch()!=';')
X			error(SYNTAX);
X		*p=0;
X		firsttime++;
X	}
X	else if(c=='#'){
X		i=evalint();
X		if(getch()!=',')
X			error(SYNTAX);
X		infile=getf(i,_READ);
X	}
X	else
X		point--;
X	l=getname();
X	vty=vartype;
Xfor(;;){
X	if(!infile){
X		if(!firsttime){
X			*line='?';
X			i=1;
X		}
X		firsttime=0;
X		edit(i,i,i);
X		if(trapped){
X			point=savepoint; /* restore point to start of in. */
X			return(-1);     /* will trap at start of this in. */
X		}
X		strcpy(&line[i],lblock);
X	}
X	else if(! filein(infile,lblock,512) )
X		error(30);
X	p= lblock;
Xex3:    while(*p++ ==' ');      /* ignore leading spaces */
X	if(!*--p && vty!=02)
X		continue;
X	p= ((vty==02)?(getstrdt(p)) :( getdata(p)));
X	if(p){
X		while(*p++ == ' ');
X		p--;
X	}
X	if(!p || (*p!=',' && *p)){
X		if(infile)
X			error(26);
X		prints("Bad data redo\n");
X		continue;
X	}
X	if(vartype == 02)
X		stringassign( (stdatap)l );
X	else
X		putin(l,vty);
X	if(getch()!=',')
X		break;
X	l=getname();
X	vty=vartype;
X	if(*p==','){
X		p++;
X		goto ex3;
X	}
X	}
X	point--;
X	normret;
X}
X
X/* valid types for string input :-
X * open quote followed by any character until another quote or the end of line
X * no quote followed by a sequence of characters except a quote
X * terminated by a comma (or end of line).
X */
X
X/*      the next two routines return zero on error and a pointer to
X *    rest of string on success.
X */
X
X/*      read string data routine */
X
Xchar    *
Xgetstrdt(p)
Xregister char   *p;
X{
X	register char *q;
X	register int    cursiz=0;
X	char    charac;
X
X	q=gblock;
X	if(*p=='"' || *p=='`' ){
X		charac= *p++;
X		while(*p!= charac && *p ){
X			*q++ = *p++;
X			if(++cursiz>255)
X				return(0);
X		}
X		if(*p)
X			p++;
X		gcursiz=cursiz;
X		return(p);
X	}
X	while( *p && *p!=',' && *p!='"' && *p!='`'){
X		*q++ = *p++;
X		if(++cursiz>255)
X			return(0);
X	}
X	gcursiz=cursiz;
X	return(p);
X}
X
X/*      read number routine */
X
Xchar    *
Xgetdata(p)
Xregister char   *p;
X{
X	register char    *tmp;
X	register int     minus=0;
X	if(*p=='-'){
X		p++;
X		minus++;
X	}
X	if(!isnumber(*p) && *p!='.')
X		return(0);
X	tmp=point;
X	point=p;
X	if(!getop()){
X		point=tmp;
X		return(0);
X	}
X	p=point;
X	point=tmp;
X	if(minus)
X		negate();
X	return(p);
X}
X
X/* input a whole line of text (into a string ) */
X
Xlinput()
X{
X
X	register char   *p;
X	register int    i;
X	memp    l;
X	register filebufp infile;
X	char    lblock[512];
X	int     c;
X
X	c=getch();
X	if(c=='#'){
X		i=evalint();
X		if(getch()!=',')
X			error(SYNTAX);
X		infile=getf(i,_READ);
X		l=getname();
X		if(vartype!=02)
X			error(VARREQD);
X		check();
X		if(!(i= filein(infile,lblock,512)) )
X			error(30);
X		if(i>255)
X			error(9);
X		p=strcpy(lblock,gblock);
X	}
X	else {
X		if(c=='"'){
X			i=0;
X			p=line;
X			while(*point && *point != '"'){
X				*p++ = *point++;
X				i++;
X			}
X			if(*point)
X				point++;
X			if(getch()!=';')
X				error(SYNTAX);
X			*p=0;
X		}
X		else {
X			point--;
X			*line='?';
X			i=1;
X		}
X		l=getname();
X		if(vartype!=02)
X			error(VARREQD);
X		check();
X		edit(i,i,i);
X		if(trapped){
X			point=savepoint; /* restore point to start of in. */
X			return(-1);     /* will trap at start of this in. */
X		}
X		p=strcpy(&line[i],gblock);
X	}
X	gcursiz= p-gblock;
X	stringassign( (stdatap)l );
X	normret;
X}
X
X/* read added 3-12-81 */
X
X/*
X * Read routine this should :-
X *      get variable then search for data then assign it
X *      repeating until end of command
X *              ( The easy bit. )
X */
X
X/*
X * Getting data :-
X *      if the data pointer points to anywhere then it points to a line
X *      to a point where getch would get an end of line or the next data item
X *      at the end of a line a null string must be implemented as
X *      a pair of quotes i.e. "" , on inputing data '"'`s are significant
X *      this is no problem normally .
X *      If the read routine finds an end of line then there is bad data
X *
X */
X
Xreadd()
X{
X	register memp   l;
X	register char   *p;
X	register char    vty;
X	if(!datapoint)
X		getmore();
X	for(;;){
X		l=getname();
X		vty=vartype;
X		p= datapoint;
X		while(*p++ == ' ');
X		datapoint= --p;
X		if(!*p){
X			getmore();
X			p=datapoint;
X			while(*p++ ==' ');
X			p--;
X		}
X	/* get here the next thing should be a data item or an error */
X		datapoint=p;
X		if(!*p)
X			error(BADDATA);
X		p= ((vty==02)?(getstrdt(p)) :( getdata(p)));
X		if(!p)
X			error(BADDATA);
X		while(*p++ == ' ');
X		p--;
X		if(*p!=',' && *p)
X			error(BADDATA);
X		if(vty == 02)
X			stringassign( (stdatap)l );
X		else  putin(l,vty);
X		if(*p)
X			p++;
X		datapoint=p;
X		if(getch()!=',')
X			break;
X	}
X	point--;
X	normret;
X}
X
X/*
X * This is only called when datapoint is at the end of the line
X * it is also called if datapoint is zero e.g. when this is the first call
X * to read.
X */
X
Xgetmore()
X{
X	register lpoint p;
X	register char   *q;
X	if(!datapoint)
X		p = (lpoint)fendcore;
X	else {
X		p=datastolin;
X		if(p->linnumb)
X			p = (lpoint)((memp)p + lenv(p));
X	}
X	for(;p->linnumb; p = (lpoint)((memp)p + lenv(p)) ){
X		q=p->lin;
X		while(*q++ == ' ');
X		if(*--q == (char)DATA){
X			datapoint= ++q;
X			datastolin=p;
X			return;
X		}
X	}
X	datastolin=p;
X	error(OUTOFDATA);
X}
X
X/*      the 'data' command it just checks things and sets up pointers
X *    as neccasary.
X */
X
Xdodata()
X{
X	register char    *p;
X	if(runmode){
X		p=stocurlin->lin;
X		while(*p++ ==' ');
X		if(*--p != (char) DATA)
X			error(BADDATA);
X		if(!datapoint){
X			datastolin= stocurlin;
X			datapoint= ++p;
X		}
X	}
X	return(GTO);    /* ignore rest of line */
X}
X
X/*      the 'restore' command , will reset the data pointer to
X *     the first bit of data it finds or to the start of the program
X *     if it doesn't find any. It will start searching from a line if
X *     tthat line is given as an optional parameter
X */
X
Xrestore()
X{
X	register unsigned i;
X	register lpoint p;
X	register char   *q;
X
X	i=getlin();
X	check();
X	p= (lpoint)fendcore;
X	if(i!= (unsigned)(-1) ){
X		for(;p->linnumb; p = (lpoint)( (memp)p + lenv(p)) )
X			if(p->linnumb== i)
X				goto got;
X		error(6);
X	}
Xgot:    datapoint=0;
X	for(;p->linnumb; p = (lpoint)((memp)p + lenv(p)) ){
X		q= p->lin;
X		while(*q++ ==' ');
X		if(*--q == (char)DATA){
X			datapoint= ++q;
X			break;
X		}
X	}
X	datastolin= p;
X	normret;
X}
End of bas5.c
chmod u=rw-,g=r,o=r bas5.c
echo x - bas6.c 1>&2
sed 's/^X//' > bas6.c << 'End of bas6.c'
X/*
X * BASIC by Phil Cockcroft
X */
X#include        "bas.h"
X#ifdef  V7
X#include <sys/ioctl.h>
X#endif
X
X/*
X *      This file contains all the routines to implement terminal
X *    like files.
X */
X
X/*
X *      setupfiles is called only once, it finds out how many files are
X *    required and allocates buffers for them. It will also execute
X *    'silly' programs that are given as parameters.
X */
X
Xsetupfiles(argc,argv)
Xchar    **argv;
X{
X	register int    fp;
X	register int     nfiles=2;
X	register struct filebuf *p;
X	char    *q;
X	extern  memp    sbrk();
X
X#ifdef  NOEDIT
X	noedit=1;
X#endif
X	while(argc > 1 ){
X		q = *++argv;
X		if(*q++ !='-')
X			break;
X		if(isnumber(*q)){
X			nfiles= atoi(q);
X			if(nfiles<0 || nfiles > MAXFILES)
X				nfiles=2;
X		}
X		else if(*q=='x')
X			noedit=1;
X		else if(*q=='e')
X			noedit=0;
X		argc--;
X	}
X	filestart= sbrk(0);
X	fendcore= filestart+(sizeof(struct filebuf) * nfiles);
X	brk(fendcore+sizeof(xlinnumb) );        /* allocate enough core */
X	for(p = (filebufp)filestart ; p < (filebufp)fendcore ; p++){
X		p->filedes=0;
X		p->userfiledes=0;
X		p->use=0;
X		p->nleft=0;
X	}
X		/* code added to execute silly programs */
X	if(argc <= 1)
X		return;
X	if((fp=open(*argv,0))!=-1)
X		runfile(fp);
X	prints("file not found\n");
X	_exit(1);
X}
X
X/*
X *      This routine executes silly programs. It has to load up
X *    the program and then simulate the environment as is usually seen
X *    in main. It works....
X */
X
Xrunfile(fp)
X{
X	int    firsttime;
X	register lpoint p;
X
X	setupterm();            /* set up terminal - now done after files */
X	ecore= fendcore+sizeof(xlinnumb);
X	( (lpoint) fendcore )->linnumb=0;
X	firsttime=1;           /* flag to say that we are just loading */
X	setexit();              /* the file at the moment */
X	if(ertrap)              /* setexit is the return for error */
X		goto execut;    /* and execute */
X	if(!firsttime)          /* an error or cntrl-c */
X		quit();
X	firsttime=0;
X	readfi(fp);
X	clear(DEFAULTSTRING);
X	p= (lpoint)fendcore;
X	stocurlin=p;
X	if(!(curline=p->linnumb))       /* is this needed - yes */
X		quit();
X	point= p->lin;
X	elsecount=0;
X	runmode=1;                      /* go and run it */
Xexecut:
X	execute();
X}
X
X/* commands implemented are :-
X	open / creat
X	close
X	input
X	print
X*/
X
X/* syntax of commands :-
X	open "filename" for input as <filedesc>
X	open "filename" [for output] as <filedesc>
X	close <filedesc> ,[<filedesc>]
X	input #<filedesc> , v1 , v2 , v3 ....
X	print #<filedesc> , v1 , v2 , v3 ....
X	*/
X
X/* format of file buffers    added 17-12-81
X	struct  {
X		int     filedes;        / * Unix file descriptor
X		int     userfiledes;    / * name by which it is used
X		int     posn;           / * position of cursor in file
X		int     dev;            / * dev and inode are used to
X		int     inode;          / * stop r/w to same file
X		int     use;            / * r/w etc. + other info
X		int     nleft;          / * number of characters in buffer
X		char    buf[BLOCKSIZ];  / * the actual buffer
X		} file_buffer ;
X
X	The file_buffers are stored between the end of initialised data
X      and fendcore. uses sbrk() at start up.
X
X	At start up there are two buffer spaces allocated.
X*/
X
X/*
X *      The 'open' command it allocates file descriptors and buffer
X *    space then sets about opening the file and checking weather the
X *    the file is opened already and then checks to see if that file
X *    was opened for reading or writing.  It stops files being read and
X *    written at the same time
X */
X
Xfopen()
X{
X	char    chblock[256];
X	register struct filebuf *p;
X	register struct filebuf *q;
X	register int     c;
X	int     i;
X	int     append=0;
X	int     bl = 0;
X	int     mode= _READ;
X	struct  stat    inod;
X
X	stringeval(chblock);
X	chblock[gcursiz]=0;
X	c=getch();
X	if(c== FOR){
X		c=getch();
X		if(c== OUTPUT)
X			mode = _WRITE;
X		else if(c== APPEND){
X			append++;
X			mode = _WRITE;
X		}
X		else if(c== TERMINAL)
X			mode = _TERMINAL;
X		else if(c != INPUT)
X			error(SYNTAX);
X		c=getch();
X	}
X	if(c!= AS)
X		error(SYNTAX);
X	i=evalint();
X#ifdef  _BLOCKED
X	if(getch() == ','){
X		bl = evalint();
X		if(bl <= 0 || bl > 255)
X			error(10);
X	}
X	else
X		point--;
X#endif
X	check();
X
X/* here we have mode set. i is the file descriptor 1-9
X   now check to see if already allocated then allocate the descriptor
X   and open file etc. */
X
X	if(i<1 || i>MAXFILES)
X		error(29);
X	for(q=0,p = (filebufp)filestart ; p < (filebufp)fendcore ; p++){
X		if(i== p->userfiledes)
X			error(29);
X		else if(!p->userfiledes && !q)
X			q=p;
X	}
X	if(!(p=q))              /* out of file descriptors */
X		error(31);
X
X/*   code to check to see if file is open twice */
X
X	if(stat(chblock,&inod)!= -1){
X		if( (inod.st_mode & S_IFMT) == S_IFDIR)
X			if(mode== _READ )  /* cannot deal with directories */
X				error(15);
X			else
X				error(14);
X		for(q = (filebufp)filestart ; q < (filebufp)fendcore ; q++)
X			if(q->userfiledes && q->inodnumber== inod.st_ino &&
X						q->device== inod.st_dev){
X				if(mode== _READ ){
X					if( q->use & mode )
X						break;
X					error(15);
X				}
X				else
X					error(14);
X			}
X	}
X	else if(mode == _TERMINAL)              /* terminals */
X		error(15);
X	if(mode == _READ){
X		if( (p->filedes=open(chblock,0))== -1)
X			error(15);
X	}
X	else  if(mode == _TERMINAL){
X#ifdef  _BLOCKED                        /* can't block terminals */
X		if(bl)
X			error(15);
X#endif
X		if((p->filedes = open(chblock,2)) == -1)
X			error(15);
X		mode |= _READ | _WRITE;
X	}
X	else  {
X		if(append){
X			p->filedes=open(chblock,1);
X#ifndef V6C
X			lseek(p->filedes, 0L, 2);
X#else
X			seek(p->filedes,0,2);
X#endif
X		}
X		if(!append || p->filedes== -1)
X			if((p->filedes=creat(chblock,0644))== -1)
X				error(14);
X	}
X	p->posn = 0;
X	fstat(p->filedes,&inod);
X#ifdef  V7
X	ioctl(p->filedes,FIOCLEX,0);    /* close on exec */
X#endif
X	p->device= inod.st_dev;         /* fill in all relevent details */
X	p->inodnumber= inod.st_ino;
X	p->userfiledes= i;
X#ifdef  _BLOCKED
X	if(bl){
X		p->blocksiz = bl;
X		mode |= _BLOCKED;
X	}
X#endif
X	p->nleft=0;
X	p->use=mode;
X	normret;
X}
X
X/*      the 'close' command it runs through the list of file descriptors
X *    and flushes all buffers and closes the file and clears all
X *    relevent entry in the structure
X */
X
Xfclosef()
X{
X	register struct filebuf *p;
X	for(;;){
X		p=getf(evalint(),(_READ | _WRITE) );
X		if(p->use & _WRITE )
X			f_flush(p);
X		close(p->filedes);
X		p->filedes=0;
X		p->userfiledes=0;
X		p->nleft=0;
X		p->use=0;
X		if(getch()!=',')
X			break;
X	}
X	point--;
X	normret;
X}
X
X/* the 'seek' command thought to be neccasary
X */
X
Xfseek()
X{
X	register struct filebuf *p;
X	register int    j;
X	register long    l;
X
X	if(getch() != '#')
X		error(SYNTAX);
X	p = getf(evalint(),(_READ | _WRITE));   /* get file */
X	if(getch() != ',')
X		error(SYNTAX);
X	eval();
X	if(getch() != ',')
X		error(SYNTAX);
X	if(!vartype && conv(&res))
X		error(FUNCT);
X#ifdef  _BLOCKED
X	if(p->use & _BLOCKED)
X#ifndef pdp11
X		l = res.i * p->blocksiz;
X#else
X		{ register k = 0;                 /* fast multiply for non */
X		for(l = 0 ; k < 8 ; k++)             /* vax systems. this */
X			if(p->blocksiz & (1<<k) )    /* won't bring in the */
X				l += (long)res.i << k;  /* library */
X		}
X#endif
X	else                    /* watch this. note the indents */
X#endif                          /* it is right */
X	l = res.i;
X	j = evalint();
X	check();
X	if(j < 0 || j > 5)      /* out of range */
X		error(FUNCT);
X	if(p->use & _WRITE)     /* flush out all buffered output */
X		f_flush(p);
X	if(j >=3){
X		j -= 3;
X		l <<= 10;       /* blocks are 1024 */
X	}
X#ifndef V6C
X	lseek(p->filedes, l ,j);
X#else
X	if(l > 512)
X		seek(p->filedes, (int)(l >> 9) , j + 3);
X	seek(p->filedes,(int)l & 0777 ,j);
X#endif
X	p->posn = 0;
X	p->nleft = 0;
X	p->use &= ~_EOF;
X	normret;
X}
X
X
X/*      the 'eof' maths function eof is true if writting to the file
X *    or if the _EOF flag is set.
X */
X
Xeofl()
X{
X	register struct filebuf *p;
X
X	p=getf(evalint(),(_READ | _WRITE) );
X	vartype=01;
X	if( p->use & ( _EOF | _WRITE) ){
X		res.i = -1;
X		return;
X	}
X	if(!p->nleft){
X		p->posn = 0;
X		if( (p->nleft= read(p->filedes,p->buf,BLOCKSIZ)) <= 0){
X			p->nleft=0;
X			p->use |= _EOF;
X			res.i = -1;
X			return;
X		}
X	}
X	res.i =0;
X}
X
X/*      the 'posn' maths function returns the current 'virtual' cursor
X *    in the file. If the file descriptor is zero then the screen
X *    cursor is accessed.
X */
X
Xfposn()
X{
X	register struct filebuf *p;
X	register i;
X
X	i=evalint();
X	vartype=01;
X	if(!i){
X		res.i =cursor;
X		return;
X	}
X	p=getf(i,(_READ | _WRITE) );
X	if(p->use & _WRITE)
X		res.i = p->posn;
X	else
X		res.i = 0;
X}
X
X/*      getf() returns a pointer to a file buffer structure. with the
X *    relevent file descriptor and with the relevent access permissions
X */
X
Xstruct  filebuf *
Xgetf(i,j)
Xregister i;     /* file descriptor */
Xregister j;     /* access permission */
X{
X	register struct filebuf *p;
X
X	if(i == 0)
X		error(29);
X	j &= ( _READ | _WRITE ) ;
X	for(p= (filebufp)filestart ; p < (filebufp)fendcore ; p++)
X		if(p->userfiledes==i && ( p->use & j) )
X			return(p);
X	error(29);      /* unknown file descriptor */
X}
X
X/*      flushes the file pointed to by p */
X
Xf_flush(p)
Xregister struct filebuf *p;
X{
X	if(p->nleft ){
X		write(p->filedes,p->buf,p->nleft);
X		p->nleft=0;
X	}
X}
X
X/*      will flush all files , for use in 'shell' and in quit */
X
Xflushall()
X{
X	register struct filebuf *p;
X	for(p = (filebufp)filestart ; p < (filebufp)fendcore ; p++)
X		if(p->nleft && ( p->use & _WRITE ) ){
X			write(p->filedes,p->buf,p->nleft);
X			p->nleft=0;
X		}
X}
X
X/*      closes all files and clears the relevent bits of info
X *    used in clear and new.
X */
X
Xcloseall()
X{
X	register struct filebuf *p;
X	flushall();
X	for(p= (filebufp)filestart ; p < (filebufp)fendcore ; p++)
X		if(p->userfiledes){
X			close(p->filedes);
X			p->filedes=0;
X			p->userfiledes=0;
X			p->nleft=0;
X			p->use=0;
X		}
X}
X
X/*      write to a file , same as write in parameters (see print )
X */
X
Xputfile(p,q,i)
Xregister struct filebuf *p;
Xregister char   *q;
Xint     i;
X{
X	register char   *r;
X	if(!i)
X		return;
X	r= &p->buf[p->nleft];
X	do{
X		if(p->nleft >= BLOCKSIZ ){
X			f_flush(p);
X			r= p->buf;
X		}
X		*r++ = *q++;
X		p->nleft++;
X	}while(--i);
X	if(p->use & _TERMINAL)
X		f_flush(p);
X}
X
X/* gets a line into q (MAX 512 or j) from file p terminating with '\n'
X * or _EOF returns number of characters read.
X */
X
Xfilein(p,q,j)
Xregister struct filebuf *p;
Xregister char *q;
X{
X	register char   *r;
X	register int     i=0;
X
X	if(p->use & _TERMINAL)          /* kludge for terminal files */
X		p->use &= ~_EOF;
X	else if(p->use & _EOF)
X		return(0);              /* end of file */
X#ifdef  _BLOCKED
X	if(p->use & _BLOCKED)
X		j = p->blocksiz;
X#endif
X	r= &p->buf[p->posn];
X	for(;;){
X		if(!p->nleft){
X			r=p->buf;
X			if( (p->nleft= read(p->filedes,p->buf,BLOCKSIZ)) <=0){
X				p->nleft=0;     /* a read error */
X				p->use |= _EOF; /* or end of file */
X				break;
X			}
X		}
X		*q= *r++;
X		p->nleft--;
X		if(++i == j){
X			q++;
X			break;
X		}
X#ifdef  _BLOCKED
X		if(*q++ == '\n' && !(p->use & _BLOCKED) ){
X#else
X		if(*q++ =='\n'){
X#endif
X			q--;
X			break;
X		}
X		if(i>=512){             /* problems */
X			p->posn= r - p->buf;
X			error(32);
X		}
X	}                               /* end of for loop */
X	*q=0;
X	if(p->use & _TERMINAL){
X		p->nleft = 0;
X		p->posn = 0;
X	}
X	else
X		p->posn = r - p->buf;
X#ifdef  _BLOCKED
X	if( (p->use & _BLOCKED) && j != i){
X		p->use |= _EOF;
X		p->nleft = 0;
X		return(0);
X	}
X#endif
X	return(i);
X}
End of bas6.c
chmod u=rw-,g=r,o=r bas6.c



More information about the Mod.sources mailing list