DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download
Index: ┃ T b

⟦b083d5898⟧ TextFile

    Length: 40982 (0xa016)
    Types: TextFile
    Names: »bas8.c«

Derivation

└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
    └─ ⟦this⟧ »EUUGD11/euug-87hel/sec1/basic/bas8.c« 

TextFile

/*
 * BASIC by Phil Cockcroft
 */
#include        "bas.h"

/*
 *      This file contains all the standard commands that are not placed
 *    anywhere else for any reason.
 */

/*
 *      The 'for' command , this is fairly straight forward , but
 *    the way that the variable is not allowed to be indexed is
 *    dependent on the layout of variables in core.
 *      Most of the fiddly bits of code are so that all the variables
 *    are of the right type (real / integer ). The code for putting
 *    a '1' in the step for default cases is not very good and could be
 *    improved.
 *      A variable is accessed by its displacement from 'earray'
 *    it is this index that speeds execution ( no need to search through
 *    the variables for a name ) and that enables the next routine to be
 *    so efficient.
 */

forr()
{
	register struct forst *p;
	register memp   l;
	register char   *r;
	char    vty;
	value   start;
	value   end;
	value   step;

	l=getname();
	vty=vartype;
	if(l<earray)                    /* string or array element */
		error(2);               /* variable required */
	if(getch()!='=')
		error(SYNTAX);
	r= (char *)(l - earray);        /* index */
	eval();                         /* get the from part */
	putin(&start,vty);              /* convert and move the right type */
	if(getch()!=TO)
		error(SYNTAX);
	eval();                         /* the to part */
	putin(&end,vty);
	if(getch()==STEP)
		eval();                 /* the step part */
	else {
		point--;                /* default case */
		res.i=1;
		vartype = 01;
	}
	putin(&step,vty);
	check();                                /* syntax check */
	for(p=(forstp)vvend,p--;p>=(forstp)bstk;p--) /* have we had it */
		if(p->fr && p->fnnm == r)       /* in a for loop before */
			goto got;          /* if so then reset its limits */
	p= (forstp)vvend;
	vvend += sizeof(struct forst);  /* no then allocate a */
	mtest(vvend);                   /* new structure on the stack */
	p->fnnm=r;
	p->fr= 01+vty;
got:    p->elses=elsecount;             /* set up all information for the */
	p->stolin=stocurlin;            /* next routine */
	p->pt=point;
	vartype=vty;
#ifndef V6C
	p->final = end;
	p->step = step;
	res = start;
#else
	movein(&end,&p->final);        /* move the variables to the correct */
	movein(&step,&p->step);         /* positions */
	movein(&start,&res);
#endif
#ifdef  LNAMES
	l = (int)r + earray;                    /* force it back */
#endif
	putin(l,vty);
	normret;
}

/*
 *      the 'next' command , this does not need an argument , if there is
 *    none then the most deeply nested 'next' is accessed. If there is
 *    a list of arguments then the variable name is accessed and a search
 *    is made for it. ( next_without_for error ). Then the step is added
 *    to the varable and the result is compared to the final. If the loop
 *    is not ended then the stack is set to the end of this 'for' structure
 *    and a return is executed. Otherwise the stack is popped and a return
 *    to the required line is performed.
 */


next()
{
	register struct forst *p;
	register value  *l;
	register char   *r;
	register int    c;

	c=getch();
	point--;
	if(istermin(c)){                /* no argument */
		for( p = (forstp)vvend , p-- ; p >= (forstp)bstk ; p--)
			if(p->fr){
				l =  (value *)(p->fnnm + (int) earray);
				goto got;
			}
		error(18);      /* no next */
	}
for(;;){
	l= (value *)getname();
	r= (memp)((memp)l - earray);
	for(p= (forstp)vvend , p-- ; p >= (forstp)bstk ; p--)
		if(p->fr &&p->fnnm == r)
			goto got;
	error(18);                      /* next without for */
got:    vartype=p->fr-1;
	if(vartype){
#ifndef pdp11
#ifdef  VAX_ASSEM                       /* if want to use assembler */
		l->i += p->step.i;
		asm("        bvc nov");         /* it is a lot faster.... */
		    error(35);
		asm("nov:");
#else
		register long   m = p->step.i;
		if( (m += l->i) > 32767 || m < -32768 )
			error(35);
		else l->i = m;
#endif
#else
		foreadd(p->step.i,l);
#endif
		if(p->step.i < 0){
			if( l->i >= p->final.i)
				goto nort;
			else goto rt;
		}
		else if( l->i <= p->final.i)
			goto nort;
	}
	else {
		fadd(&p->step, l );
		if(p->step.i <0){               /* bit twiddling */
#ifndef SOFTFP
			if( l->f >= p->final.f)
				goto nort;
			else goto rt;
		}
		else if( l->f <= p->final.f)
			goto nort;
#else
			if(cmp(l,&p->final)>=0 )
				goto nort;
			goto rt;
		}
		else  if(cmp(l,&p->final)<= 0)
			goto nort;
#endif
	}
rt:     vvend=(memp)p;                  /* don't loop - pop the stack */
	if(getch()==',')
		continue;
	else point--;
	break;
nort:
	if(stocurlin=p->stolin)                 /* go back to the 'for' */
		curline=stocurlin->linnumb;     /* need this for very */
	else runmode=0;                         /* obscure reasons */
	point = p->pt;
	elsecount=p->elses;
	vvend = (memp) (p+1);
	break;
	}
	normret;
}

/*
 *      The 'gosub' command , This uses the same structure as 'for' for
 *    the storage of data. A gosub is identified by the flag 'fr' in
 *    the 'for' structure being zero. This just gets the line on which
 *    we are on and sets up th structure. Gosubs from immeadiate mode
 *    are dealt with and this is one of the obscure reasons for the
 *    the comment and code in 'return' and 'next'.
 */

gosub()
{
	register struct forst   *p;
	register lpoint l;

	l=getline();
	check();
	p = (forstp) vvend;
	vvend += sizeof(struct forst);
	mtest(vvend);
	runmode=1;
	p->fr=0;
	p->fnnm=0;
	p->elses=elsecount;
	p->pt=point;
	p->stolin=stocurlin;
	stocurlin=l;
	curline=l->linnumb;
	point= l->lin;
	elsecount=0;
	return(-1);     /* return to execute the next instruction */
}

/*
 *      The 'return' command this just searches the stack for the
 *    first gosub/return it can find, pops the stack to that level
 *    and returns to the correct point. Deals with returns to
 *    immeadiate mode, as well.
 */

retn()
{
	register struct forst   *p;

	check();
	for(p= (forstp)vvend , p-- ; p >= (forstp)bstk ; p--)
		if(!p->fr && !p->fnnm)
			goto got;
	error(21);              /* return without gosub */
got:
	elsecount=p->elses;
	point=p->pt;
	if(stocurlin=p->stolin)
		curline=stocurlin->linnumb;
	else runmode=0;                 /* return to immeadiate mode */
	vvend= (memp)p;
	normret;
}

/*
 *      The 'run' command , run will execute a program by putting it in
 *    runmode and setting the start address to the start of the program
 *    or to the optional line number. It clears all the variables and
 *    closes all files.
 */

runn()
{
	register lpoint p;
	register unsigned l;

	l=getlin();
	check();
	p = (lpoint)fendcore;
	if(l== (unsigned)(-1) )
		goto got;
	else for(;p->linnumb; p = (lpoint)((memp) p + lenv(p)) )
		if(l== p->linnumb)
			goto got;
	error(6);               /* undefined line */
got:
	clear(DEFAULTSTRING);   /* zap the variables */
	closeall();
	if(!p->linnumb)                 /* no program so return */
		reset();
	curline=p->linnumb;     /* set up all the standard pointers */
	stocurlin=p;
	point=p->lin;
	elsecount=0;
	runmode=1;
	return(-1);             /* return to execute the next instruction */
}

/*
 *      The 'end' command , checks its syntax ( no parameters ) then
 *    gets out of what we were doing.
 */

endd()
{
	check();
	reset();
}

/*
 *      The 'goto' command , simply gets the required line number
 *    and sets the pointers to it. If in immeadiate mode , go into
 *    runmode and zap the stack .
 */

gotos()
{
	register lpoint p;
	p=getline();
	check();
	curline=p->linnumb;
	point=p->lin;
	stocurlin=p;
	elsecount=0;
	if(!runmode){
		runmode++;
		vvend=bstk;     /* zap the stack */
	}
	return(-1);
}

/*
 *      The 'print' command , The code for this routine is rather weird.
 *    It works ( well ) for all types of printing ( including files ),
 *    but it is a bit 'kludgy' and could be done better ( I don't know
 *    how ). Every expression must be followed by a comma a semicolon
 *    or the end of a statement. To get it all to work was tricky but it
 *    now does and that is all that can be said for it.
 *      The use of filedes assumes that an integer has the same size as
 *      a structure pointer. If this is not the case. This system will not
 *      work ( nor will most of the rest of the interpreter ).
 */

print()
{
	int     i;
	register int     c;
	extern  write(),putfile();
	static  char    spaces[]="                ";    /* 16 spaces */
	register int    (*outfunc)();   /* pointer to the output function */
	register int    *curcursor;     /* pointer to the current cursor */
					/* 'posn' if a file, or 'cursor' */
	int     Twidth;                 /* width of the screen or of the */
	filebufp filedes;               /* file. BLOCKSIZ if a file */

	c=getch();
	if(c=='#'){
		i=evalint();
		if(getch()!=',')
			error(SYNTAX);
		filedes=getf(i,_WRITE);
		outfunc= putfile;               /* see bas6.c */
		curcursor= &filedes->posn;
		Twidth = BLOCKSIZ;
		c=getch();
	}
	else {
		outfunc= write;
		curcursor= &cursor;
		filedes = (filebufp)1;
		Twidth = ter_width;
	}
	point--;

for(;;){
	if(istermin(c))
		break;
	else if(c==TABB){                       /* tabing */
		point++;
		if(*point++!='(')
			error(SYNTAX);
		i=evalint();
		if(getch()!=')')
			error(SYNTAX);
		while(i > *curcursor+16 && !trapped){
			(*outfunc)(filedes,spaces,16);
			*curcursor+=16;
		}
		if(i> *curcursor && !trapped){
			(*outfunc)(filedes,spaces,i- *curcursor);
			*curcursor = i;
		}
		*curcursor %= Twidth;
		c=getch();
		goto outtab;
	}
	else if(c==',' || c==';'){
		point++;
		goto outtab;
	}
	else if(checktype())
		stringeval(gblock);
	else {
		eval();
		gcvt();
	}
	(*outfunc)(filedes,gblock,gcursiz);
	*curcursor = (*curcursor + gcursiz) % Twidth;
	c=getch();
outtab: if(c==',' ||c==';'){
		if(c==','){
			(*outfunc)(filedes,spaces,16-(*curcursor%16));
			*curcursor=(*curcursor+(16- *curcursor%16)) % Twidth;
		}
		c=getch();
		point--;
		if(istermin(c))
			normret;
	}
	else if(istermin(c)){
		point--;
		break;
	}
	else error(SYNTAX);
	}

	(*outfunc)(filedes,nl,1);
	*curcursor=0;
	normret;
}

/*
 *      The 'if' command , no real problems here but the 'else' part
 *    could do with a bit more checking of what it's going over.
 */

iff()
{
	register int    elsees;
	register int    c;
	register char   *p;

	eval();
	if(getch()!=THEN)
		error(SYNTAX);
#ifdef  PORTABLE
	if(vartype ? res.i : res.f){
#else
	if(res.i ){                     /* naughty bit twiddleing */
#endif
		c=getch();              /* true */
		point--;
		elsecount++;            /* say `else`s are allowed */
		if(isnumber(c))         /* if it's a number then */
			gotos();        /* execute a goto */
		return(-1);             /* return to execute another ins. */
	}
	for(elsees = 0, p= point; *p ; p++) /* skip all nested 'if'-'else' */
		if(*p==(char)ELSE){         /* pairs */
			if(--elsees < 0){
				p++;
				break;
			}
		}
		else if(*p==(char)IF)
			elsees++;
	point = p;                      /* we are after the else or at */
	if(!*p)
		normret;
	while(*p++ == ' ');             /* end of line */
	p--;                            /* ignore the space after else */
	if(isnumber(*p))                /* if number then do a goto */
		gotos();
	return(-1);
}

/*
 *      The 'on' command , this deals with everything , it has to do
 *    its own searching so that undefined lines are not accessed until
 *    a 'goto' to that line is actually required.
 *    Deals with on_gosubs from immeadiate mode.
 */

onn()
{
	unsigned lnm[128];
	register unsigned *l;
	register lpoint p;
	register forstp pt;
	int     m;
	int     i;
	int     c;
	int     k;

	if(getch()==ERROR){
		if(getch()!=GOTO)
			error(SYNTAX);
		errtrap();      /* do the trapping of errors */
		normret;
	}
	else point--;
	m=evalint();
	if((k=getch())!= GOTO && k != GOSUB)
		error(SYNTAX);
	for(l=lnm,i=1;;l++,i++){        /* get the line numbers */
		if( (*l = getlin()) == (unsigned)(-1) )
			error(5);       /* line number required */
		if(getch()!=',')
			break;
	}
	point--;
	check();
	if(m<1 || m> i)                 /* index is out of bounds */
		normret;                /* so return */
	c= lnm[m-1];
	for(p = (lpoint)fendcore ; p->linnumb ;
					p = (lpoint)((memp)p + lenv(p)) )
		if(p->linnumb==c)
			goto got;
	error(6);                       /* undefined line */
got:    if(k== GOSUB) {
		pt=(forstp)vvend;               /* fix the gosub stack */
		vvend += sizeof(struct forst);
		mtest(vvend);
		pt->fnnm=0;
		pt->fr=0;
		pt->elses=elsecount;
		pt->pt=point;
		pt->stolin=stocurlin;
	}
	if(!runmode){
		runmode++;
		if(k==GOTO)             /* gotos in immeadiate mode */
			vvend=bstk;
	}
	stocurlin=p;
	curline=p->linnumb;
	point= p->lin;
	elsecount=0;
	return(-1);
}

/*
 *      The 'cls' command , neads to set the terminal into 'rare' mode
 *    so that there is no waiting on the page clearing ( form feed ).
 */

cls()
{
	extern  char    o_CLEARSCR[];

	set_term();
	puts(o_CLEARSCR);
	putch(0);       /* flush it out */
	rset_term(0);
	cursor = 0;
	normret;
}

/*
 *      The 'base' command , sets the start index for arrays to either
 *      '0' or '1' , simple.
 */

base()
{
	register int    i;
	i=evalint();
	check();
	if(i && i!=1)
		error(28);      /* bad base value */
	baseval=i;
	normret;
}

/*
 *      The 'rem' and '\'' command , ignore the rest of the line
 */

rem() {  return(GTO); }

/*
 *      The 'let' command , all the work is done in assign , the first
 *    getch() is to get the pointer in the right place for assign().
 */

lets()
{
	assign();
	normret;
}

/*
 *      The 'clear' command , clears all variables , closes all files
 *    and allocates the required amount of storage for strings,
 *    maximum is 32K.
 */

clearl()
{
	register int    i;

	i=evalint();
	check();
	if(i < 0 || i + ecore > MAXMEM)
		error(12);      /* bad core size */
	clear(i);
	closeall();
	normret;
}

/*
 *      The 'list' command , can have an optional two arguments and
 *    a dash is also used.
 *      Most of this routine is the getting of the arguments. All the
 *    actual listing is done in listl() , This routine should call write()
 *    and not clr(), but then the world is not perfect.
 */

list()
{
	register unsigned l1,l2;
	register lpoint p;
	l1=getlin();
	if(l1== (unsigned)(-1) ){
		l1=0;
		l2= -1;
		if(getch()=='-'){
			if( (l2 = getlin()) == (unsigned)(-1) )
				error(SYNTAX);
		}
		else point--;
	}
	else  {
		if(getch()!='-'){
			l2= l1;
			point--;
		}
		else
			l2 = getlin();
	}
	check();
	for(p= (lpoint)fendcore ; p->linnumb < l1 ;
					p = (lpoint)((memp)p + lenv(p)) )
		if(!p->linnumb)
			reset();
	if(l1== l2 && l1 != p->linnumb )
			reset();
	while(p->linnumb && p->linnumb <=l2 && !trapped){
		l1=listl(p);
		line[l1++] = '\n';
		write(1,line,(int)l1);
		p = (lpoint)((memp)p + lenv(p));
	}
	reset();
}

/*
 *      The routine that does the listing of a line , it searches through
 *    the table of reserved words if it find a byte with the top bit set,
 *    It should ( ha ha ) find it.
 *      This routine could run off the end of line[] since line is followed
 *    by nline[] this should not cause any problems.
 *      The result is in line[].
 */

listl(p)
lpoint p;
{
	register char   *q;
	register struct tabl *l;
	register char    *r;

	r=strcpy(printlin(p->linnumb) ,line);  /* do the linenumber */
	for(q= p->lin; *q && r < &line[MAXLIN]; q++){
		if(*q &(char)0200)              /* reserved words */
			for(l=table;l->chval;l++){
				if((char)(l->chval) == *q){
					r=strcpy(l->string,r);
					break;
				}
			}
		else if(*q<' '){                /* do special characters */
			*r++ ='\\';
			*r++ = *q+ ('a'-1);
		}
		else {
			if(*q == '\\')          /* the special character */
				*r++ = *q;
			*r++ = *q;              /* non special characters */
		}
	}
	if(r >= &line[MAXLIN])                  /* get it back a bit */
		r = &line[MAXLIN-1];
	*r=0;
	return(r-line);                 /* length of line */
}

/*
 *      The 'stop' command , prints the message that it has stopped
 *    and then exits the 'user' program.
 */

stop()
{
	check();
	dostop(0);
}

/*
 *      Called if trapped is set (by control-c ) and just calls dostop
 *    with a different parameter to print a slightly different message
 */

dobreak()
{
	dostop(1);
}

/*
 *      prints out the 'stopped' or 'breaking' message then exits.
 *    These two functions were lumped together so that it might be
 *    possible to add a 'cont'inue command at a latter date ( not
 *    implemented yet ) - ( it is now ).
 */

dostop(i)
{
	if(cursor){
		cursor=0;
		prints(nl);
	}
	prints( (i) ? "breaking" : "stopped" );
	if(runmode){
		prints(" at line ");
		prints(printlin(curline));
		if(!intrap){            /* save environment */
			cancont=i+1;
			conpoint=point;
			constolin=stocurlin;
			concurlin=curline;
			contelse=elsecount;
			conerp=errortrap;
		}
	}
	prints(nl);
	reset();
}

/*      the 'cont' command - it seems to work ?? */

cont()
{
	check();
	if( contpos && !runmode){
		point = conpoint;       /* restore environment */
		stocurlin =constolin;
		curline = concurlin;
		elsecount = contelse;
		errortrap = conerp;
		vvend= bstk;
		bstk = vend;
		mtest(vvend);           /* yeuch */
		runmode =1;
		if(contpos==1){
			contpos=0;
			normret;        /* stopped */
		}
		contpos=0;              /* ctrl-c ed */
		return(-1);
	}
	contpos=0;
	error(CANTCONT);
}

/*
 *      The 'delete' command , will only delete the required lines if it
 *    can find the two end lines. stops ' delete 1' etc. as a slip up.
 *      very slow algorithm. But who cares ??
 */

delete()
{
	register lpoint p1,p2;
	register unsigned i2;

	p1=getline();
	if(getch()!='-')
		error(SYNTAX);
	p2=getline();
	check();
	if(p1>p2)
		reset();
	i2 = p2->linnumb;
	do{
		linenumber = p1->linnumb;
		insert(0);
	}while(p1->linnumb && p1->linnumb <= i2 );
	reset();
}

/*
 *      The 'shell' command , calls the v7 shell as an entry into unix
 *    without going out of basic. Has to set the terminal in a decent
 *    mode , else 'ded' doesn't like it.
 *      Clears out all buffered file output , so that you can see what
 *    you have done so far, and sets your userid to your real-id
 *    this stops people becoming unauthorised users if basic is made
 *    setuid ( for games via runfile of the command file ).
 */

shell()
{
	register int    i;
	register int    (*q)() , (*p)();
	int     (*signal())();
	char    *s;
#ifdef  SIGTSTP
	int     (*t)();
#endif

	check();
	flushall();
#ifdef  SIGTSTP
	t = signal(SIGTSTP, SIG_DFL);
#endif
#ifdef  VFORK
	i = vfork();
#else
	i=fork();
#endif
	if(i==0){
		rset_term(1);
		setuid(getuid());               /* stop user getting clever */
#ifdef  V7
		s = getenv("SHELL");
		if(!s || !*s)
			s = "/bin/sh";
#else
		s = "/bin/sh";
#endif
		execl(s,"sh (from basic)",0);
		exit(-1);                       /* problem */
	}
	else if(i== -1)
		prints("cannot shell out\n");
	else {                                  /* daddy */
		p=signal(SIGINT,SIG_IGN);       /* ignore some signals */
		q=signal(SIGQUIT, SIG_IGN);
		while(i != wait(0) && i != -1); /* wait on the 'child' */
		signal(SIGINT,p);               /* resignal to what they */
		signal(SIGQUIT,q);              /* were before */
	}                                       /* in a mode fit for basic */
#ifdef  SIGTSTP
	signal(SIGTSTP, t);
#endif
	normret;
}

/*
 *      The 'edit' command , can only edit in immeadiate mode , and with the
 *    specified line ( maybe could be more friendly here , no real need to
 *    since the editor is the same as on line input.
 */

editl()
{
	register lpoint p;
	register int    i;

	p= getline();
	check();
	if(runmode || noedit)
		error(13);      /* illegal edit */
	i=listl(p);
	edit(0,i,0);            /* do the edit */
	if(trapped)             /* ignore it if exited via cntrl-c */
		reset();
	i=compile(0);
	if(linenumber)          /* ignore it if there is no line number */
		insert(i);
	reset();                /* return to 'ready' */
}

/*
 *      The 'auto' command , allows input of lines with automatic line
 *    numbering. Most of the code is to do with getting the arguments
 *    otherwise the loop is fairly simple. There are three ways of getting
 *    out of this routine. cntrl-c will exit the routine immeadiately
 *    If there is no linenumber then it also exits. If the line typed in is
 *    terminated by an ESCAPE character the line is inserted and the routine
 *    is terminated.
 */

dauto()
{
	register unsigned start , end , i1;
	unsigned int      i2;
	long    l;
	int     c;
	i2=autoincr;
	i1=getlin();
	if( i1 != (unsigned)(-1) ){
		if(getch()!= ','){
			point--;
			i2=autoincr;
		}
		else {
			i2=getlin();
			if(i2 == (unsigned)(-1) )
				error(SYNTAX);
		}
	}
	else
		i1=autostart;
	check();
	start=i1;
	autoincr=i2;
	end=i2;
	for(;;){
		i1= strcpy(printlin(start),line) - line;
		line[i1++]=' ';
		c=edit(0,i1,i1);
		if(trapped)
			break;
		i1=compile(0);
		if(!linenumber)
			break;
		insert(i1);
		if( (l= (long)start+end) >=65530){
			autostart=10;
			autoincr=10;
			error(6);       /* undefined line number */
		}
		start+=end;
		autostart=l;
		if(c == ESCAPE )
			break;
	}
	reset();
}

/*
 *      The 'save' command , saves a basic program on a file.
 *    It just lists the lines adds a newline then writes them out
 */

save()
{
	register lpoint p;
	register int    fp;
	register int    i;

	stringeval(gblock);     /* get the name */
	gblock[gcursiz]=0;
	check();
	if((fp=creat(gblock,0644))== -1)
		error(14);              /* cannot creat file */
	for(p= (lpoint)fendcore ; p->linnumb ;
					p = (lpoint)((memp) p + lenv(p)) ){
		i=listl(p);
		line[i++]='\n';
		write(fp,line,i);       /* could be buffered ???? */
	}
	close(fp);
	normret;
}

/*
 *      The 'old' command , loads a program from a file. The old
 *    program (if any ) is wiped.
 *      Most of the work is done in readfi, ( see also error ).
 */

old()
{
	register int    fp;

	stringeval(gblock);
	gblock[gcursiz]=0;              /* get the file name */
	check();
	if((fp=open(gblock,0))== -1)
		error(15);              /* can't open file */
	ecore= fendcore+sizeof(xlinnumb);       /* zap old program */
	( (lpoint) fendcore)->linnumb=0;
	readfi(fp);                     /* read the new file */
	reset();
}

/*
 *      The 'merge' command , similar to 'old' but does not zap the old
 *    program so the two files are 'merged' .
 */

merge()
{
	register int    fp;

	stringeval(gblock);
	gblock[gcursiz]=0;
	check();
	if((fp=open(gblock,0))== -1)
		error(15);
	readfi(fp);
	reset();
}

/*
 *      The routine that actually reads in a file. It sets up readfile
 *    so that if there is an error ( linenumber overflow ) , then error
 *    can pick up the pieces , else the number of file descriptors are
 *    reduced and can ( unlikely ), run out of them so stopping any file
 *    being saved or restored , ( This is the reason that all files are
 *    closed so meticulacly ( see 'chain' and its  pipes ).
 */

readfi(fp)
{
	register char   *p;
	int     i;
	char    chblock[BLOCKSIZ];
	int     nleft=0;
	register int    special=0;
	register char   *q;

	readfile=fp;
	inserted=1;     /* make certain variables are cleared */
	p=line;         /* input into line[] */
	for(;;){
		if(!nleft){
			q=chblock;
			if( (nleft=read(fp,q,BLOCKSIZ)) <= 0)
				break;
		}
		*p= *q++;
		nleft--;
		if(special){
			special=0;
			if(*p>='a' && *p<='~'){
				*p -= ('a'-1);
				continue;
			}
		}
		if(*p =='\n'){
			*p=0;
			i=compile(0);
			if(!linenumber)
				goto bad;
			insert(i);
			p=line;
			continue;
		}
		else if(*p<' ')
			goto bad;
		else if(*p=='\\')
			special++;
		if(++p > &line[MAXLIN])
			goto bad;
	}
	if(p!=line)
		goto bad;
	close(fp);
	readfile=0;
	return;

bad:    close(fp);              /* come here if there is an error */
	readfile=0;             /* that readfi() has detected */
	error(23);              /* stops error() having to tidy up */
}

/*
 *      The 'new' command , This deletes any program and clears all
 *    variables , can take an extra parameter to say how many files are
 *    needed. If so then clears the number of buffers ( default 2 ).
 */

neww()
{
	register int    i,c;
	register struct filebuf *p;
	register memp   size;

	c=getch();
	point--;
	if(!istermin(c)){
		i=evalint();
		check();
		closeall();             /* flush the buffers */
		if(i<0 || i> MAXFILES)
			i=2;
		fendcore= filestart + (sizeof(struct filebuf) * i );
		size = fendcore + sizeof(xlinnumb);
		size = (char *) ( ((int)size + MEMINC) & ~MEMINC);
		brk(size);
		for(p = (filebufp)filestart ; p < (filebufp)fendcore ; p++){
			p->filedes=0;
			p->userfiledes=0;
			p->use=0;
			p->nleft=0;
		}
	}
	else
		check();
	autostart=10;
	autoincr=10;
	baseval=1;
	ecore= fendcore + sizeof(xlinnumb);
	( (lpoint)fendcore )->linnumb=0;
	clear(DEFAULTSTRING);
	closeall();
	reset();
}

/*
 *      The 'chain' command , This routine chains the program.
 *      all simple numeric variables are kept. ( max of 4 k ).
 *      all other variables are cleared.
 *      runs the loaded file
 *      files are kept open
 *
 *      error need only check pipe[0] to see if it is to be closed.
 */

chain()
{
	register int     fp;
	register int     size;
	register char    *p;
	int     ssize,nsize;
#ifdef  LNAMES
	register struct  entry  *ep,*np;
	register int    *xp;
#endif

	stringeval(gblock);
	check();
	gblock[gcursiz]=0;
	size= vend- earray;
#ifdef  LNAMES
	nsize = enames - estring;               /* can only save offsets */
	if(nsize + size >= 4096)                /* cos ecore/estring might */
#else                                           /* change */
	if(size >= 4096 )
#endif
		error(42);              /* out of space for varibles */
	if((fp=open(gblock,0))== -1)
		error(15);
	ssize= estring- ecore;          /* amount of string space */
	pipe(pipes);
	write(pipes[1],earray,size);    /* check this */
#ifdef  LNAMES
	write(pipes[1],estring,nsize);
#endif
	close(pipes[1]);
	pipes[1]=0;
	ecore= fendcore + sizeof(xlinnumb);     /* bye bye old file */
	( (lpoint)fendcore )->linnumb=0; /* commited to new file now */
	readfi(fp);
	clear(ssize);
	errortrap=0;
	inserted=0;                     /* say we don't actually want to */
	p= xpand(&vend,size);           /* clear variables on return */
	read(pipes[0],p,size);
#ifdef  LNAMES
	p = xpand(&enames,nsize);
	read(pipes[0],p,nsize);
	/*
	 * now rehash the symbol table
	 * cos it gets munged when it moves
	 */
	for(ep = (struct entry *)estring; ep < (struct entry *)enames; ep++){
		ep->link = 0;
		for(p = ep->_name,size = 0; *p ; size += *p++);
		ep->ln_hash = size;
		if(np = hshtab[size %= HSHTABSIZ]){
			for(;np->link ;np = np->link);
			np->link = ep;
		}
		else
			hshtab[size] = ep;
	}
	/*
	 * must zap varshash - because of above
	 */
	for( xp = varshash ; xp < &varshash[HSHTABSIZ] ; *xp++ = -1);
	chained = 1;
#endif
	close(pipes[0]);                /* now have data back from pipe */
	pipes[0]=0;
	stocurlin= (lpoint)fendcore;
	if(!(curline=stocurlin->linnumb))
		reset();
	point= stocurlin->lin;
	elsecount=0;
	runmode=1;
	return(-1);                     /* now run the file */
}

/* define a function def fna() - can have up to 3 parameters */

deffunc()
{
	struct  deffn   fn;     /* temporary place for evaluation */
	register struct deffn *p;
	register int     i=0;
	int     c;
	char    *j;
	register char   *l;

	if(getch() != FN)
		error(SYNTAX);
	if(!isletter(*point))
		error(SYNTAX);
	getnm();
	if(vartype == 02)
		error(VARREQD);
	fn.dnm = nm;
#ifdef  LNAMES
	for(p = (deffnp)enames ; p < (deffnp)edefns ;
#else
	for(p = (deffnp)estring ; p < (deffnp)edefns ;
#endif
					p = (deffnp)( (memp)p + p->offs) )
		if(p->dnm == nm )
			error(REDEFFN); /* redefined functions */
	fn.vtys=vartype<<4;     /* save return type of function */
	if(*point=='('){        /* get arguments */
		point++;
		for(;i<3;i++){
			l=getname();
			if( l < earray)
				error(VARREQD);
			fn.vargs[i]= l - earray;
			fn.vtys |= vartype <<i;  /* save type of arguments */
			if((c=getch())!=',')
				break;
		}
		if(c!= ')')
			error(SYNTAX);
		i++;
	}
	if(getch()!='=')
		error(SYNTAX);
	fn.narg=i;
	l = point;
	while(*l++ == ' ');
	point = --l;
	while(!istermin(*l))    /* get rest of expression */
		l++;
	if(l==point)
		error(SYNTAX);
	i= l - point + sizeof(struct deffn);
#ifdef  ALIGN4
	i = (i + 03) & ~03;
#else
	if(i&01)                /* even up space requirement */
		i++;
#endif
	p= (deffnp) xpand(&edefns,i );          /* get the space */
#ifndef V6C
	*p = fn;
	p->offs = i;
#else
	p->dnm = fn.dnm;                    /* put all values in */
	p->offs=i;
	p->narg=fn.narg;
	p->vtys= fn.vtys;
	p->vargs[0]=fn.vargs[0];
	p->vargs[1]=fn.vargs[1];
	p->vargs[2]=fn.vargs[2];
#endif
	j= p->exp;
	while( point<l)         /* store away line */
		*j++ = *point++;
	*j=0;
	normret;
}

/* the repeat part of the repeat - until loop */
/* now can have a construct like  'repeat until eof(1)'. */
/* It might be of use ?? it's a special case */


rept()
{
	register struct forst   *p;
	register int    c;
	register char   *tp;

	if(getch() == UNTIL){
		tp = point;     /* save point */
		eval();         /* calculate the value */
		check();        /* check syntax */
#ifdef  PORTABLE
		while((vartype ? (!res.i) :(res.f == 0)) && !trapped){
#else
		while(!res.i && !trapped){ /* now repeat the loop until <>0 */
#endif
			point = tp;
			eval();
		}
		normret;
	}
	point--;
	check();
	p= (forstp)vvend;
	vvend += sizeof(struct forst);
	mtest(vvend);
	p->pt = point;
	p->stolin = stocurlin;
	p->elses = elsecount;
	p->fr = 0;              /* make it look like a gosub like */
	p->fnnm = (char *)01;   /* distinguish from gosub's */
	normret;
}

/* the until bit of the command */

untilf()
{
	register struct forst   *p;
	eval();
	check();
	for(p= (forstp)vvend , p-- ; p >= (forstp)bstk ; p--)
		if(!p->fr)
			goto got;
	error(48);
got:
	if(p->fnnm != (char *)01 )
		error(51);
#ifdef  PORTABLE
	if(vartype ? (!res.i) : (res.f == 0)){
#else
	if(!res.i){             /* not true so repeat loop */
#endif
		elsecount = p->elses;
		point = p->pt;
		if(stocurlin = p->stolin)
			curline = stocurlin->linnumb;
		else runmode =0;
		vvend = (memp)(p+1);    /* pop all off stack up until here */
	}
	else
		vvend = (memp)p;        /* pop stack if finished here. */
	normret;
}

/* while part of while - wend construct. This is like repeat until unless
 * loop fails on the first time. (Yeuch - next we need syntax checking on
 * input ).
 */

whilef()
{
	register char    *spoint = point;
	register lpoint lp;
	register struct forst   *p;
	lpoint  get_end();
	eval();
	check();
#ifdef  PORTABLE
	if(vartype ? res.i : res.f){
#else
	if(res.i){  /* got to go through it once so make it look like a */
		    /* repeat - until */
#endif
		p= (forstp)vvend;
		vvend += sizeof(struct forst);
		mtest(vvend);
		p->pt = spoint;
		p->stolin = stocurlin;
		p->elses = elsecount;
		p->fr = 0;              /* make it look like a gosub like */
		p->fnnm = (char *)02;   /* distinguish from gosub's */
		normret;
	}
	lp=get_end();                   /* otherwise find a wend */
	check();
	if(runmode){
		stocurlin =lp;
		curline = lp->linnumb;
	}
	normret;
}

/* the end part of a while loop - wend */

wendf()
{
	register struct forst   *p;
	char    *spoint =point;
	check();
	for(p= (forstp)vvend , p-- ; p >= (forstp)bstk ; p--)
		if(!p->fr)
			goto got;
	error(49);
got:
	if( p->fnnm != (char *)02 )
		error(51);
	point = p->pt;
	eval();
#ifdef  PORTABLE
	if(vartype ? (!res.i) : (res.f == 0)){
#else
	if(!res.i){                     /* failure of the loop */
#endif
		vvend= (memp)p;
		point = spoint;
		normret;
	}
	vvend = (memp)(p+1);            /* pop stack after an iteration */
	elsecount = p->elses;
	if(stocurlin = p->stolin)
		curline = stocurlin->linnumb;
	else runmode=0;
	normret;
}

/* get_end - search from current position until found a wend statement - of
 * the correct nesting. Keeping track of elses + if's(Yeuch ).
 */

lpoint
get_end()
{
	register lpoint lp;
	register char   *p;
	register int    c;
	int     wcount=0;
	int     rcount=0;
	int     flag=0;

	p= point;
	lp= stocurlin;
	if(getch()!=':'){
		if(!runmode)
			error(50);
		lp = (lpoint)((memp)lp +lenv(lp));
		if(!lp->linnumb)
			error(50);
		point = lp->lin;
		elsecount=0;
	}
	for(;;){
		c=getch();
		if(c==WHILE)
			wcount++;
		else if(c==WEND){
			if(--wcount <0)
				break;  /* only get out point in loop */
		}
		else if(c==REPEAT)
			rcount++;
		else if(c==UNTIL){
			if(--rcount<0)
				error(51);      /* bad nesting */
		}
		else if(c==IF){
			flag++;
			elsecount++;
		}
		else if(c==ELSE){
			flag++;
			if(elsecount)
				elsecount--;
		}
		else if(c==REM || c==DATA || c==QUOTE){
			if(!runmode)
				error(50);      /* no wend */
			lp = (lpoint)((memp)lp +lenv(lp));
			if(!lp->linnumb)
				error(50);      /* no wend */
			point =lp->lin;
			elsecount=0;
			flag=0;
			continue;
		}
		else for(p=point;!istermin(*p);p++)
			if(*p=='"' || *p=='`'){
				c= *p++;
				while(*p && *p != (char) c)
					p++;
				if(!*p)
					break;
			}
		if(!*p++){
			if(!runmode)
				error(50);
			lp = (lpoint)((memp)lp +lenv(lp));
			if(!lp->linnumb)
				error(50);
			point =lp->lin;
			elsecount=0;
			flag=0;
		}
		else
			point = p;
	}
	/* we have found it at this point - end of loop */
	if(rcount || (lp!=stocurlin && flag) )
		error(51);      /* bad nesting or wend after an if */
	return(lp);             /* not on same line */
}

#ifdef  RENUMB

/*
 * the renumber routine. It is a three pass algorithm.
 *      1) Find all line numbers that are in text.
 *         Save in table.
 *      2) Renumber all lines.
 *         Fill in table with lines that are found
 *      3) Find all line numbers and update to new values.
 *
 *      This routine eats stack space and also some code space
 *      If you don't want it don't define RENUMB.
 *      Could run out of stack if on V7 PDP-11's
 *      ( On vax's it does not matter. Also can increase MAXRLINES.)
 *      MAXRLINES can be reduced if not got split i-d. If this is
 *      the case then probarbly do not want this code anyway.
 */

#define MAXRLINES       500     /* the maximum number of lines that */
				/* can be changed. Change if neccasary */

renumb()
{
	struct  ta {
		unsigned linn;
		unsigned toli;
		} ta[MAXRLINES];

	struct  ta      *eta = ta;
	register struct ta *tp;
	register char   *q;
	register lpoint p;

	unsigned l1,start,inc;
	int     size,sl,pl;
	char    onfl,chg,*r,*s;
	long    numb;

	start = 10;
	inc = 10;
	l1 = getlin();
	if(l1 != (unsigned)(-1) ){              /* get start line number */
		start = l1;
		if(getch() != ',')
			point--;
		else {
			l1 = getlin();          /* get increment */
			if(l1 == (unsigned)(-1))
				error(5);
			inc = l1;
		}
	}
	check();                /* check rest of line */
	numb = start;           /* set start counter */
	for(p=(lpoint)fendcore; p->linnumb ;p=(lpoint)((char *)p+lenv(p))){
		numb += inc;
		if(numb >= 65530 )      /* check line numbers */
			error(7);       /* line number overflow */
		onfl = 0;               /* flag to deal with on_goto */
		for(q = p->lin; *q ; q++){      /* now find keywords */
			if( !(*q & (char)0200 ))        /* not one */
				continue;               /* ignore */
			if(*q == (char) ON){            /* the on keyword */
				onfl++;                 /* set flag */
				continue;
			}               /* check items with optional numbers*/
			if(*q == (char)ELSE || *q == (char)THEN ||
				*q == (char)RESUME || *q == (char)RESTORE
					|| *q == (char) RUNN ){
				q++;
				while(*q++ == ' ');
				q--;
				if(isnumber(*q))        /* got one ok */
					goto ok1;
			}
			if(*q != (char) GOTO && *q != (char)GOSUB)
				continue;       /* can't be anything else */
			q++;
		ok1:                            /* have a label */
			do{
				while(*q++ == ' ');
				q--;                    /* look for number */
				if( !isnumber(*q) ){
				      prints("Line number required on line ");
					prints(printlin(p->linnumb));
					prints(nl);             /* missing */
					goto out1;
				}
				for(l1 = 0; isnumber(*q) ; q++) /* get it */
					if(l1 >= 6553)
						error(7);
					else l1 = l1 * 10 + *q - '0';
				for(tp  = ta ; tp < eta ; tp++) /* already */
					if(tp->linn == l1)      /* got it ? */
						break;
				if(tp >= eta ){        /* add another entry */
					tp->linn = l1;
					tp->toli = -1;
					if(++eta >= &ta[MAXRLINES])
						error(24);   /* out of core */
				}
				if(!onfl)               /* check flag */
					break;          /* get next item */
				while(*q++== ' ');      /* if ON and comma */
			}while( *(q-1) ==',');
			if(onfl)
				q--;
			onfl =0;
			q--;
		}
	out1:   ;
	}
	numb = start;           /* reset counter */
	for(p= (lpoint)fendcore ; p->linnumb ;p=(lpoint)((char *)p+lenv(p)) ){
		for(tp = ta ; tp < eta ; tp++)          /* change numbers */
			if(tp->linn == p->linnumb){
				tp->toli = numb;  /* inform of new number */
				break;
			}
		p->linnumb = numb;
		numb += inc;
	}
	for(p= (lpoint)fendcore ; p->linnumb ;p=(lpoint)((char *)p+lenv(p)) ){
		onfl = 0;
		chg = 0;                        /* set if line changed */
		for(r = nline, q = p->lin ; *q ; *r++ = *q++){
			if(  r >= &nline[MAXLIN])  /* overflow of line */
				break;
			if( !(*q & (char) 0200 )) /* repeat search for */
				continue;         /* keywords */
			if(*q == (char) ON){
				onfl++;
				continue;
			}
			if(*q == (char)ELSE || *q == (char)THEN ||
				*q == (char)RESUME || *q == (char)RESTORE
					|| *q == (char) RUNN ){
				*r++ = *q++;
				while(*q == ' ' && r < &nline[MAXLIN] )
					*r++ = *q++;
				if(isnumber(*q)) /* got optional line number*/
					goto ok2;
			}
			if(*q != (char) GOTO && *q != (char)GOSUB)
				continue;
			*r++ = *q++;
			for(;;){
				while(*q == ' ' && r < &nline[MAXLIN] )
					*r++ = *q++;
			ok2: ;
				if(r>= &nline[MAXLIN] )
					break;
				for(l1 = 0 ; isnumber(*q) ; q++) /* get numb*/
					l1 = l1 * 10 + *q - '0';
				if(l1 == 0)         /* skip if not found */
					goto out;   /* never happen ?? */
				for(tp = ta ; tp < eta ; tp++)
					if(tp->linn == l1)
						break;
				if(tp->linn != tp->toli)
					chg++;       /* number has changed */
				if(tp >= eta || tp->toli == (unsigned)(-1) ){
					prints("undefined line: ");
					prints(printlin(l1));
					prints(" on line ");
					prints(printlin(p->linnumb));
					prints(nl);     /* can't find it */
					goto out;
				}
				s = printlin(tp->toli); /* get new number */
				while( *s && r < &nline[MAXLIN])
					*r++ = *s++;
				if(r >= &nline[MAXLIN] )
					break;
				if(onfl){       /* repeat if ON statement */
					while(*q == ' ' && r < &nline[MAXLIN])
						*r++ = *q++;
					if(*q == ','){
						*r++ = *q++;
						continue;
					}
				}
				break;
			}
			onfl = 0;
			if(r >= &nline[MAXLIN])
				error(32);      /* line length overflow */
		}
		if(!chg)                /* not changed so don't put back */
			continue;
		inserted =1;            /* say we have changed it */
		for(*r = 0, r = nline; *r++ ;);
		r--;
		size = (r - nline) + sizeof(struct olin); /* get size */
#ifdef  ALIGN4
		size = (size + 03) & ~03;
#else
		if(size & 01)                   /* even it up */
			size++;
#endif
		if(size != lenv(p) ){           /* size changed. insert */
			pl = p->linnumb;        /* save line number */
			sl = lenv(p);           /* save length */
			bmov((short *)p,sl);    /* compress core */
			ecore -= sl;            /* shrink it */
			mtest(ecore+size);      /* get more core */
			ecore += size;          /* add it */
			bmovu((short *)p,size);   /* expand core */
			p->linnumb = pl;        /* restore line number */
			lenv(p) = size;         /* set size */
		}
		strcpy(nline,p->lin);   /* copy back new line */
	out:    ;
	}
	reset();
}
#else
renumb(){}
#endif  /* RENUMB */

/* the load command. Load a dump image. Works fastwer than save/old */

#define MAGIC1          013121
#define MAGIC2          027212

loadd()
{
	register int     nsize;
	register fp;
	int     header[3];

	stringeval(gblock);
	check();
	gblock[gcursiz] = 0;
	if( (fp = open(gblock,0))< 0)
		error(14);
	if(read(fp,(char *)header,sizeof(int)*3) != sizeof(int)*3){
		close(fp);
		error(23);      /* bad load / format file */
	}
	if(header[0] != MAGIC1 && header[1] != MAGIC2){
		close(fp);
		error(23);
	}
	ecore = fendcore + sizeof(xlinnumb);
	mtest(ecore);           /* good bye old image */
	((lpoint)fendcore)->linnumb = 0;
	inserted = 1;
	readfile = fp;
	mtest(ecore+header[2]);
	readfile = 0;
	ecore += header[2];
	nsize = read(fp,fendcore,header[2]);
	close(fp);
	if(nsize != header[2]){
		ecore = fendcore + sizeof(xlinnumb);
		mtest(ecore);
		((lpoint)fendcore)->linnumb = 0;
		error(23);
	}
	reset();
}

/* write out the core to the file */

dump()
{
	register int     nsize;
	register fp;
	int     header[3];

	stringeval(gblock);
	check();
	gblock[gcursiz] = 0;
	if( (fp = creat(gblock,0644))< 0)
		error(15);
	header[0] = MAGIC1;
	header[1] = MAGIC2;
	nsize = ecore - fendcore;
	header[2] = nsize;
	write(fp,(char *)header,sizeof(int)*3);
	write(fp,fendcore,nsize);
	close(fp);
	normret;
}