home *** CD-ROM | disk | FTP | other *** search
- Subject: v07i075: A BASIC Interpreter, Part03/06
- Newsgroups: mod.sources
- Approved: mirror!rs
-
- Submitted by: phil@Cs.Ucl.AC.UK
- Mod.sources: Volume 7, Issue 75
- Archive-name: basic/Part03
-
- # Shar file shar03 (of 6)
- #
- # This is a shell archive containing the following files :-
- # bas7.c
- # bas8.c
- # bas9.c
- # gen
- # ------------------------------
- # 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 - bas7.c 1>&2
- sed 's/^X//' > bas7.c << 'End of bas7.c'
- X/*
- X * BASIC by Phil Cockcroft
- X */
- X#include "bas.h"
- X
- X#define COMPILE
- X#include "cursor.c"
- X#undef COMPILE
- X
- X/*
- X * this file conatins the user interface e.g. the line editor
- X */
- X
- X#define PADC 0400 /* the character output for padding */
- X /* more than 0377 but can still be passed to putc */
- X
- X/* read a single character */
- X
- Xreadc()
- X{
- X char c=RETURN;
- X
- X#ifdef BSD42
- X if(!setjmp(ecall)){
- X ecalling = 1;
- X if(!read(0,&c,1)){
- X ecalling = 0;
- X quit();
- X }
- X ecalling = 0;
- X }
- X#else
- X if(!read(0,&c,1)) /* reading from a pipe exit on eof */
- X quit();
- X#endif
- X return(c&0177);
- X}
- X
- X/* sets up the terminal structures so that the editor is in rare
- X * with no paging or line boundries and no echo
- X * Also sets up the user modes so that they are sensible when
- X * we exit. ( friendly ).
- X */
- X
- Xsetupterm()
- X{
- X set_cap();
- X setu_term();
- X}
- X
- X
- X/* the actual editor pretty straight forward but.. */
- X
- Xedit(fl,fi,fc)
- X{
- X register int cursr;
- X register char *q;
- X register char *p;
- X int c;
- X int quitf=0; /* say we have finished the edit */
- X int special;
- X int lastc;
- X int inschar =1;
- X
- X set_term();
- X for(p= &line[fi]; p<= &line[MAXLIN] ;)
- X *p++ = ' ';
- X *p=0;
- X write(1,line,fi);
- X cursr=fi;
- X if(noedit){
- X for(p= &line[cursr];p< &line[MAXLIN] ; ){
- X c=readc();
- X if(c=='\n' || trapped)
- X break;
- X else if(c >=' ' )
- X *p++ =c;
- X else if(c == ESCAPE)
- X break;
- X }
- X while(c != '\n' && c != ESCAPE && !trapped)
- X c=readc();
- X }
- X else
- X do{
- X putch(0); /* flush the buffers */
- X lastc = lastch(fl);
- X c=readc();
- X if(c >= ' ' && c < '\177'){
- X if( cursr < MAXLIN && ( inschar && lastc < MAXLIN || !inschar) ){
- X if(cursr < lastc && inschar){
- X p= &line[MAXLIN];
- X q= p-1;
- X while(p> &line[cursr])
- X *--p= *--q;
- X if(*o_INSCHAR)
- X puts(o_INSCHAR);
- X else
- X inchar(cursr,lastc,c);
- X }
- X putch(c);
- X line[cursr++]=c;
- X continue;
- X }
- X }
- X else switch( (c <' ') ? _in_char[c] : _in_char[32] ){
- Xcase i_LEFT:
- X if(cursr==fl)
- X break;
- X cursr--;
- X puts(o_LEFT);
- X continue;
- Xcase i_CLEAR: /* control l - redraw */
- X puts(o_RETURN);
- X cursr=lastc;
- X for(p= line; p< &line[cursr];)
- X putch(*p++);
- X deol(cursr);
- X continue;
- Xcase i_DELLINE: /* control b - zap line */
- X if(cursr==fl && lastc == fl)
- X break;
- X puts(o_RETURN);
- X p=line;
- X while(p<&line[fl])
- X putch(*p++);
- X deol(cursr);
- X p= &line[fl];
- X while(p<&line[MAXLIN])
- X *p++ = ' ';
- X cursr=fl;
- X continue;
- Xcase i_DELCHAR:
- X if(cursr >= lastc )
- X break;
- X goto rubit;
- Xcase i_RUBOUT:
- X if(cursr==fl)
- X break;
- X puts(o_LEFT);
- X cursr--;
- X if(!inschar)
- X continue;
- X rubit:
- X if(cursr <= lastc ){
- X if(*o_DELCHAR)
- X puts(o_DELCHAR);
- X p= &line[cursr];
- X q= p+1;
- X while(q < &line[MAXLIN] )
- X *p++ = *q++;
- X *p= ' ';
- X }
- X if(!*o_DELCHAR)
- X delchar(cursr,lastc);
- X continue;
- Xcase i_UP:
- X if(cursr-ter_width< fl)
- X break;
- X if(*o_UP)
- X puts(o_UP);
- X else for(special = 0; special < ter_width ; special++)
- X puts(o_LEFT);
- X cursr -= ter_width;
- X continue;
- Xcase i_DOWN1:
- X if(cursr+ter_width > MAXLIN )
- X break;
- X puts(o_DOWN2);
- X cursr+=ter_width;
- X continue;
- Xcase i_CNTRLD:
- X if( (c = readc()) >= ' ' || _in_char[c] != i_CNTRLD)
- X break;
- X putch(0);
- X cursor= (cursor+cursr)%ter_width;
- X quit();
- Xcase i_INSCHAR:
- X inschar = !inschar;
- X continue;
- Xcase i_RIGHT:
- X if(cursr>= MAXLIN)
- X break;
- X putch(line[cursr++]);
- X continue;
- Xcase i_LLEFT:
- X if(cursr <= fl)
- X break;
- X do{
- X puts(o_LEFT);
- X }while(((--cursr) &07) && cursr > fl);
- X continue;
- Xcase i_RRIGHT:
- X if(cursr>= MAXLIN)
- X break;
- X do{
- X putch(line[cursr++]);
- X }while((cursr&07) && cursr < MAXLIN);
- X continue;
- Xcase i_DELSOL: /* delete to start of line */
- X if(cursr==fl)
- X break;
- X special = cursr;
- X cursr = fl;
- X goto delit; /* same code as del word almost */
- Xcase i_DELWORD: /* control w - del word */
- X if(cursr==fl)
- X break;
- X special=cursr;
- X do{
- X cursr--;
- X }while(cursr>fl &&(line[cursr-1]!=' ' || line[cursr]==' '));
- X delit:
- X q= &line[special];
- X p= &line[cursr];
- X while(q < &line[MAXLIN] )
- X *p++ = *q++;
- X while(p < &line[MAXLIN]){
- X puts(o_LEFT);
- X *p++ = ' ';
- X if(*o_DELCHAR && --special <= lastc )
- X puts(o_DELCHAR);
- X }
- X if(!*o_DELCHAR)
- X delchar(cursr,lastc);
- X continue;
- Xcase i_BACKWORD: /* back word */
- X if(cursr==fl)
- X break;
- X do{
- X puts(o_LEFT);
- X cursr--;
- X }while(cursr>fl && (line[cursr-1]!=' ' || line[cursr]==' ' ));
- X continue;
- Xcase i_NEXTWORD: /* next word */
- X if(cursr >= MAXLIN || cursr > lastc || lastc == fl)
- X break;
- X do{
- X putch(line[cursr++]);
- X }while(cursr < MAXLIN && cursr <= lastc &&
- X (line[cursr]==' '|| line[cursr-1]!=' ' ) );
- X continue;
- Xcase i_DEOL:
- X if(cursr >= lastc )
- X break;
- X for(p= &line[cursr];p < &line[MAXLIN];)
- X *p++ = ' ';
- X deol(cursr);
- X continue;
- Xcase i_ESCAPE:
- Xcase i_RETURN:
- Xcase i_DOWN2:
- X while(cursr< lastc)
- X putch(line[cursr++]);
- X puts(o_RETURN);
- X puts(o_DOWN2);
- X quitf++;
- X continue;
- Xdefault:
- X break;
- X }
- X puts(o_PING);
- X }while(!quitf && !trapped);
- X putch(0);
- X line[lastch(fl)]=0;
- X/* special characters are dealt with here- null is never returned */
- X for(p=line,q=line,special=0;*p;p++){
- X if(special){
- X special=0;
- X if(*p>='a' && *p<='~')
- X *q++ = *p -('a'-1);
- X else *q++ = *p;
- X }
- X else if(*p=='\\')
- X special++;
- X else *q++ = *p;
- X }
- X *q=0;
- X cursor=0;
- X rset_term(0);
- X return(c);
- X}
- X
- X/*
- X * put a string out ( using putch )
- X */
- X
- Xputs(s)
- Xregister char *s;
- X{
- X /*
- X * now cope with padding
- X */
- X if(*s >='0' && *s <= '9'){
- X register i = 0;
- X do{
- X i = i * 10 + *s++ -'0';
- X }while(*s >= '0' && *s <= '9');
- X if(*s == '.')
- X s++, i++;
- X if(*s == '*') /* should only affect 1 line */
- X s++;
- X while(i-- > 0)
- X putch(PADC);
- X }
- X while(*s)
- X putch(*s++);
- X}
- X
- X/* put out a character uses buffere output of up to 256 characters
- X * It used to use a static buffer but this is a waste of space so
- X * it now uses gblock as this is never used during an edit.
- X * A value of zero for the parameter will flush the buffer.
- X */
- X
- Xputch(c)
- X{
- X static nleft=0;
- X
- X if(!c || nleft>=256){
- X if(nleft)
- X write(1,gblock,nleft);
- X nleft=0;
- X }
- X if(!c)
- X return;
- X gblock[nleft++]= c;
- X}
- X
- X/* lastch() returns the last character on the line used in the
- X * editor to see if any more characters can be placed on the line and
- X * by the redraw key.
- X */
- X
- Xlastch(f)
- X{
- X register char *p;
- X register char *q;
- X p= &line[f];
- X q= &line[MAXLIN];
- X while(*q==' ' && q>=p)
- X q--;
- X return(q-line+1);
- X}
- X
- X/* delete from current cursor position to end of line. */
- X
- Xdeol(cursr)
- X{
- X register cc,i;
- X if(*o_DEOL){
- X puts(o_DEOL);
- X return;
- X }
- X i = ter_width - (cursr % ter_width);
- X for(cc = i ; cc ; cc--)
- X putch(' ');
- X for(; i ; i--)
- X puts(o_LEFT);
- X}
- X
- X/* delete nchar characters from cursr */
- X
- Xdelchar(cursr,lc)
- X{
- X register char *p;
- X register char *q;
- X p = &line[cursr];
- X q = &line[lc];
- X while(p < q )
- X putch(*p++);
- X q = &line[cursr];
- X while(p > q ){
- X if( *o_UP && p - q > ter_width ){
- X puts(o_UP);
- X p -= ter_width;
- X }
- X else {
- X p--;
- X puts(o_LEFT);
- X }
- X }
- X}
- X
- X/* display a new character */
- X
- Xinchar(cursr,lastc,c)
- X{
- X register char *p,*q;
- X p = &line[cursr+1];
- X q = &line[lastc+1];
- X putch(c);
- X while(p < q)
- X putch(*p++);
- X q = &line[cursr];
- X while(p > q ){
- X if( *o_UP && p - q > ter_width ){
- X puts(o_UP);
- X p -= ter_width;
- X }
- X else {
- X p--;
- X puts(o_LEFT);
- X }
- X }
- X}
- End of bas7.c
- chmod u=rw-,g=r,o=r bas7.c
- echo x - bas8.c 1>&2
- sed 's/^X//' > bas8.c << 'End of bas8.c'
- X/*
- X * BASIC by Phil Cockcroft
- X */
- X#include "bas.h"
- X
- X/*
- X * This file contains all the standard commands that are not placed
- X * anywhere else for any reason.
- X */
- X
- X/*
- X * The 'for' command , this is fairly straight forward , but
- X * the way that the variable is not allowed to be indexed is
- X * dependent on the layout of variables in core.
- X * Most of the fiddly bits of code are so that all the variables
- X * are of the right type (real / integer ). The code for putting
- X * a '1' in the step for default cases is not very good and could be
- X * improved.
- X * A variable is accessed by its displacement from 'earray'
- X * it is this index that speeds execution ( no need to search through
- X * the variables for a name ) and that enables the next routine to be
- X * so efficient.
- X */
- X
- Xforr()
- X{
- X register struct forst *p;
- X register memp l;
- X register char *r;
- X char vty;
- X value start;
- X value end;
- X value step;
- X
- X l=getname();
- X vty=vartype;
- X if(l<earray) /* string or array element */
- X error(2); /* variable required */
- X if(getch()!='=')
- X error(SYNTAX);
- X r= (char *)(l - earray); /* index */
- X eval(); /* get the from part */
- X putin(&start,vty); /* convert and move the right type */
- X if(getch()!=TO)
- X error(SYNTAX);
- X eval(); /* the to part */
- X putin(&end,vty);
- X if(getch()==STEP)
- X eval(); /* the step part */
- X else {
- X point--; /* default case */
- X res.i=1;
- X vartype = 01;
- X }
- X putin(&step,vty);
- X check(); /* syntax check */
- X for(p=(forstp)vvend,p--;p>=(forstp)bstk;p--) /* have we had it */
- X if(p->fr && p->fnnm == r) /* in a for loop before */
- X goto got; /* if so then reset its limits */
- X p= (forstp)vvend;
- X vvend += sizeof(struct forst); /* no then allocate a */
- X mtest(vvend); /* new structure on the stack */
- X p->fnnm=r;
- X p->fr= 01+vty;
- Xgot: p->elses=elsecount; /* set up all information for the */
- X p->stolin=stocurlin; /* next routine */
- X p->pt=point;
- X vartype=vty;
- X#ifndef V6C
- X p->final = end;
- X p->step = step;
- X res = start;
- X#else
- X movein(&end,&p->final); /* move the variables to the correct */
- X movein(&step,&p->step); /* positions */
- X movein(&start,&res);
- X#endif
- X#ifdef LNAMES
- X l = (int)r + earray; /* force it back */
- X#endif
- X putin(l,vty);
- X normret;
- X}
- X
- X/*
- X * the 'next' command , this does not need an argument , if there is
- X * none then the most deeply nested 'next' is accessed. If there is
- X * a list of arguments then the variable name is accessed and a search
- X * is made for it. ( next_without_for error ). Then the step is added
- X * to the varable and the result is compared to the final. If the loop
- X * is not ended then the stack is set to the end of this 'for' structure
- X * and a return is executed. Otherwise the stack is popped and a return
- X * to the required line is performed.
- X */
- X
- X
- Xnext()
- X{
- X register struct forst *p;
- X register value *l;
- X register char *r;
- X register int c;
- X
- X c=getch();
- X point--;
- X if(istermin(c)){ /* no argument */
- X for( p = (forstp)vvend , p-- ; p >= (forstp)bstk ; p--)
- X if(p->fr){
- X l = (value *)(p->fnnm + (int) earray);
- X goto got;
- X }
- X error(18); /* no next */
- X }
- Xfor(;;){
- X l= (value *)getname();
- X r= (memp)((memp)l - earray);
- X for(p= (forstp)vvend , p-- ; p >= (forstp)bstk ; p--)
- X if(p->fr &&p->fnnm == r)
- X goto got;
- X error(18); /* next without for */
- Xgot: vartype=p->fr-1;
- X if(vartype){
- X#ifndef pdp11
- X#ifdef VAX_ASSEM /* if want to use assembler */
- X l->i += p->step.i;
- X asm(" bvc nov"); /* it is a lot faster.... */
- X error(35);
- X asm("nov:");
- X#else
- X register long m = p->step.i;
- X if( (m += l->i) > 32767 || m < -32768 )
- X error(35);
- X else l->i = m;
- X#endif
- X#else
- X foreadd(p->step.i,l);
- X#endif
- X if(p->step.i < 0){
- X if( l->i >= p->final.i)
- X goto nort;
- X else goto rt;
- X }
- X else if( l->i <= p->final.i)
- X goto nort;
- X }
- X else {
- X fadd(&p->step, l );
- X if(p->step.i <0){ /* bit twiddling */
- X#ifndef SOFTFP
- X if( l->f >= p->final.f)
- X goto nort;
- X else goto rt;
- X }
- X else if( l->f <= p->final.f)
- X goto nort;
- X#else
- X if(cmp(l,&p->final)>=0 )
- X goto nort;
- X goto rt;
- X }
- X else if(cmp(l,&p->final)<= 0)
- X goto nort;
- X#endif
- X }
- Xrt: vvend=(memp)p; /* don't loop - pop the stack */
- X if(getch()==',')
- X continue;
- X else point--;
- X break;
- Xnort:
- X if(stocurlin=p->stolin) /* go back to the 'for' */
- X curline=stocurlin->linnumb; /* need this for very */
- X else runmode=0; /* obscure reasons */
- X point = p->pt;
- X elsecount=p->elses;
- X vvend = (memp) (p+1);
- X break;
- X }
- X normret;
- X}
- X
- X/*
- X * The 'gosub' command , This uses the same structure as 'for' for
- X * the storage of data. A gosub is identified by the flag 'fr' in
- X * the 'for' structure being zero. This just gets the line on which
- X * we are on and sets up th structure. Gosubs from immeadiate mode
- X * are dealt with and this is one of the obscure reasons for the
- X * the comment and code in 'return' and 'next'.
- X */
- X
- Xgosub()
- X{
- X register struct forst *p;
- X register lpoint l;
- X
- X l=getline();
- X check();
- X p = (forstp) vvend;
- X vvend += sizeof(struct forst);
- X mtest(vvend);
- X runmode=1;
- X p->fr=0;
- X p->fnnm=0;
- X p->elses=elsecount;
- X p->pt=point;
- X p->stolin=stocurlin;
- X stocurlin=l;
- X curline=l->linnumb;
- X point= l->lin;
- X elsecount=0;
- X return(-1); /* return to execute the next instruction */
- X}
- X
- X/*
- X * The 'return' command this just searches the stack for the
- X * first gosub/return it can find, pops the stack to that level
- X * and returns to the correct point. Deals with returns to
- X * immeadiate mode, as well.
- X */
- X
- Xretn()
- X{
- X register struct forst *p;
- X
- X check();
- X for(p= (forstp)vvend , p-- ; p >= (forstp)bstk ; p--)
- X if(!p->fr && !p->fnnm)
- X goto got;
- X error(21); /* return without gosub */
- Xgot:
- X elsecount=p->elses;
- X point=p->pt;
- X if(stocurlin=p->stolin)
- X curline=stocurlin->linnumb;
- X else runmode=0; /* return to immeadiate mode */
- X vvend= (memp)p;
- X normret;
- X}
- X
- X/*
- X * The 'run' command , run will execute a program by putting it in
- X * runmode and setting the start address to the start of the program
- X * or to the optional line number. It clears all the variables and
- X * closes all files.
- X */
- X
- Xrunn()
- X{
- X register lpoint p;
- X register unsigned l;
- X
- X l=getlin();
- X check();
- X p = (lpoint)fendcore;
- X if(l== (unsigned)(-1) )
- X goto got;
- X else for(;p->linnumb; p = (lpoint)((memp) p + lenv(p)) )
- X if(l== p->linnumb)
- X goto got;
- X error(6); /* undefined line */
- Xgot:
- X clear(DEFAULTSTRING); /* zap the variables */
- X closeall();
- X if(!p->linnumb) /* no program so return */
- X reset();
- X curline=p->linnumb; /* set up all the standard pointers */
- X stocurlin=p;
- X point=p->lin;
- X elsecount=0;
- X runmode=1;
- X return(-1); /* return to execute the next instruction */
- X}
- X
- X/*
- X * The 'end' command , checks its syntax ( no parameters ) then
- X * gets out of what we were doing.
- X */
- X
- Xendd()
- X{
- X check();
- X reset();
- X}
- X
- X/*
- X * The 'goto' command , simply gets the required line number
- X * and sets the pointers to it. If in immeadiate mode , go into
- X * runmode and zap the stack .
- X */
- X
- Xgotos()
- X{
- X register lpoint p;
- X p=getline();
- X check();
- X curline=p->linnumb;
- X point=p->lin;
- X stocurlin=p;
- X elsecount=0;
- X if(!runmode){
- X runmode++;
- X vvend=bstk; /* zap the stack */
- X }
- X return(-1);
- X}
- X
- X/*
- X * The 'print' command , The code for this routine is rather weird.
- X * It works ( well ) for all types of printing ( including files ),
- X * but it is a bit 'kludgy' and could be done better ( I don't know
- X * how ). Every expression must be followed by a comma a semicolon
- X * or the end of a statement. To get it all to work was tricky but it
- X * now does and that is all that can be said for it.
- X * The use of filedes assumes that an integer has the same size as
- X * a structure pointer. If this is not the case. This system will not
- X * work ( nor will most of the rest of the interpreter ).
- X */
- X
- Xprint()
- X{
- X int i;
- X register int c;
- X extern write(),putfile();
- X static char spaces[]=" "; /* 16 spaces */
- X register int (*outfunc)(); /* pointer to the output function */
- X register int *curcursor; /* pointer to the current cursor */
- X /* 'posn' if a file, or 'cursor' */
- X int Twidth; /* width of the screen or of the */
- X filebufp filedes; /* file. BLOCKSIZ if a file */
- X
- X c=getch();
- X if(c=='#'){
- X i=evalint();
- X if(getch()!=',')
- X error(SYNTAX);
- X filedes=getf(i,_WRITE);
- X outfunc= putfile; /* see bas6.c */
- X curcursor= &filedes->posn;
- X Twidth = BLOCKSIZ;
- X c=getch();
- X }
- X else {
- X outfunc= write;
- X curcursor= &cursor;
- X filedes = (filebufp)1;
- X Twidth = ter_width;
- X }
- X point--;
- X
- Xfor(;;){
- X if(istermin(c))
- X break;
- X else if(c==TABB){ /* tabing */
- X point++;
- X if(*point++!='(')
- X error(SYNTAX);
- X i=evalint();
- X if(getch()!=')')
- X error(SYNTAX);
- X while(i > *curcursor+16 && !trapped){
- X (*outfunc)(filedes,spaces,16);
- X *curcursor+=16;
- X }
- X if(i> *curcursor && !trapped){
- X (*outfunc)(filedes,spaces,i- *curcursor);
- X *curcursor = i;
- X }
- X *curcursor %= Twidth;
- X c=getch();
- X goto outtab;
- X }
- X else if(c==',' || c==';'){
- X point++;
- X goto outtab;
- X }
- X else if(checktype())
- X stringeval(gblock);
- X else {
- X eval();
- X gcvt();
- X }
- X (*outfunc)(filedes,gblock,gcursiz);
- X *curcursor = (*curcursor + gcursiz) % Twidth;
- X c=getch();
- Xouttab: if(c==',' ||c==';'){
- X if(c==','){
- X (*outfunc)(filedes,spaces,16-(*curcursor%16));
- X *curcursor=(*curcursor+(16- *curcursor%16)) % Twidth;
- X }
- X c=getch();
- X point--;
- X if(istermin(c))
- X normret;
- X }
- X else if(istermin(c)){
- X point--;
- X break;
- X }
- X else error(SYNTAX);
- X }
- X
- X (*outfunc)(filedes,nl,1);
- X *curcursor=0;
- X normret;
- X}
- X
- X/*
- X * The 'if' command , no real problems here but the 'else' part
- X * could do with a bit more checking of what it's going over.
- X */
- X
- Xiff()
- X{
- X register int elsees;
- X register int c;
- X register char *p;
- X
- X eval();
- X if(getch()!=THEN)
- X error(SYNTAX);
- X#ifdef PORTABLE
- X if(vartype ? res.i : res.f){
- X#else
- X if(res.i ){ /* naughty bit twiddleing */
- X#endif
- X c=getch(); /* true */
- X point--;
- X elsecount++; /* say `else`s are allowed */
- X if(isnumber(c)) /* if it's a number then */
- X gotos(); /* execute a goto */
- X return(-1); /* return to execute another ins. */
- X }
- X for(elsees = 0, p= point; *p ; p++) /* skip all nested 'if'-'else' */
- X if(*p==(char)ELSE){ /* pairs */
- X if(--elsees < 0){
- X p++;
- X break;
- X }
- X }
- X else if(*p==(char)IF)
- X elsees++;
- X point = p; /* we are after the else or at */
- X if(!*p)
- X normret;
- X while(*p++ == ' '); /* end of line */
- X p--; /* ignore the space after else */
- X if(isnumber(*p)) /* if number then do a goto */
- X gotos();
- X return(-1);
- X}
- X
- X/*
- X * The 'on' command , this deals with everything , it has to do
- X * its own searching so that undefined lines are not accessed until
- X * a 'goto' to that line is actually required.
- X * Deals with on_gosubs from immeadiate mode.
- X */
- X
- Xonn()
- X{
- X unsigned lnm[128];
- X register unsigned *l;
- X register lpoint p;
- X register forstp pt;
- X int m;
- X int i;
- X int c;
- X int k;
- X
- X if(getch()==ERROR){
- X if(getch()!=GOTO)
- X error(SYNTAX);
- X errtrap(); /* do the trapping of errors */
- X normret;
- X }
- X else point--;
- X m=evalint();
- X if((k=getch())!= GOTO && k != GOSUB)
- X error(SYNTAX);
- X for(l=lnm,i=1;;l++,i++){ /* get the line numbers */
- X if( (*l = getlin()) == (unsigned)(-1) )
- X error(5); /* line number required */
- X if(getch()!=',')
- X break;
- X }
- X point--;
- X check();
- X if(m<1 || m> i) /* index is out of bounds */
- X normret; /* so return */
- X c= lnm[m-1];
- X for(p = (lpoint)fendcore ; p->linnumb ;
- X p = (lpoint)((memp)p + lenv(p)) )
- X if(p->linnumb==c)
- X goto got;
- X error(6); /* undefined line */
- Xgot: if(k== GOSUB) {
- X pt=(forstp)vvend; /* fix the gosub stack */
- X vvend += sizeof(struct forst);
- X mtest(vvend);
- X pt->fnnm=0;
- X pt->fr=0;
- X pt->elses=elsecount;
- X pt->pt=point;
- X pt->stolin=stocurlin;
- X }
- X if(!runmode){
- X runmode++;
- X if(k==GOTO) /* gotos in immeadiate mode */
- X vvend=bstk;
- X }
- X stocurlin=p;
- X curline=p->linnumb;
- X point= p->lin;
- X elsecount=0;
- X return(-1);
- X}
- X
- X/*
- X * The 'cls' command , neads to set the terminal into 'rare' mode
- X * so that there is no waiting on the page clearing ( form feed ).
- X */
- X
- Xcls()
- X{
- X extern char o_CLEARSCR[];
- X
- X set_term();
- X puts(o_CLEARSCR);
- X putch(0); /* flush it out */
- X rset_term(0);
- X cursor = 0;
- X normret;
- X}
- X
- X/*
- X * The 'base' command , sets the start index for arrays to either
- X * '0' or '1' , simple.
- X */
- X
- Xbase()
- X{
- X register int i;
- X i=evalint();
- X check();
- X if(i && i!=1)
- X error(28); /* bad base value */
- X baseval=i;
- X normret;
- X}
- X
- X/*
- X * The 'rem' and '\'' command , ignore the rest of the line
- X */
- X
- Xrem() { return(GTO); }
- X
- X/*
- X * The 'let' command , all the work is done in assign , the first
- X * getch() is to get the pointer in the right place for assign().
- X */
- X
- Xlets()
- X{
- X assign();
- X normret;
- X}
- X
- X/*
- X * The 'clear' command , clears all variables , closes all files
- X * and allocates the required amount of storage for strings,
- X * maximum is 32K.
- X */
- X
- Xclearl()
- X{
- X register int i;
- X
- X i=evalint();
- X check();
- X if(i < 0 || i + ecore > MAXMEM)
- X error(12); /* bad core size */
- X clear(i);
- X closeall();
- X normret;
- X}
- X
- X/*
- X * The 'list' command , can have an optional two arguments and
- X * a dash is also used.
- X * Most of this routine is the getting of the arguments. All the
- X * actual listing is done in listl() , This routine should call write()
- X * and not clr(), but then the world is not perfect.
- X */
- X
- Xlist()
- X{
- X register unsigned l1,l2;
- X register lpoint p;
- X l1=getlin();
- X if(l1== (unsigned)(-1) ){
- X l1=0;
- X l2= -1;
- X if(getch()=='-'){
- X if( (l2 = getlin()) == (unsigned)(-1) )
- X error(SYNTAX);
- X }
- X else point--;
- X }
- X else {
- X if(getch()!='-'){
- X l2= l1;
- X point--;
- X }
- X else
- X l2 = getlin();
- X }
- X check();
- X for(p= (lpoint)fendcore ; p->linnumb < l1 ;
- X p = (lpoint)((memp)p + lenv(p)) )
- X if(!p->linnumb)
- X reset();
- X if(l1== l2 && l1 != p->linnumb )
- X reset();
- X while(p->linnumb && p->linnumb <=l2 && !trapped){
- X l1=listl(p);
- X line[l1++] = '\n';
- X write(1,line,(int)l1);
- X p = (lpoint)((memp)p + lenv(p));
- X }
- X reset();
- X}
- X
- X/*
- X * The routine that does the listing of a line , it searches through
- X * the table of reserved words if it find a byte with the top bit set,
- X * It should ( ha ha ) find it.
- X * This routine could run off the end of line[] since line is followed
- X * by nline[] this should not cause any problems.
- X * The result is in line[].
- X */
- X
- Xlistl(p)
- Xlpoint p;
- X{
- X register char *q;
- X register struct tabl *l;
- X register char *r;
- X
- X r=strcpy(printlin(p->linnumb) ,line); /* do the linenumber */
- X for(q= p->lin; *q && r < &line[MAXLIN]; q++){
- X if(*q &(char)0200) /* reserved words */
- X for(l=table;l->chval;l++){
- X if((char)(l->chval) == *q){
- X r=strcpy(l->string,r);
- X break;
- X }
- X }
- X else if(*q<' '){ /* do special characters */
- X *r++ ='\\';
- X *r++ = *q+ ('a'-1);
- X }
- X else {
- X if(*q == '\\') /* the special character */
- X *r++ = *q;
- X *r++ = *q; /* non special characters */
- X }
- X }
- X if(r >= &line[MAXLIN]) /* get it back a bit */
- X r = &line[MAXLIN-1];
- X *r=0;
- X return(r-line); /* length of line */
- X}
- X
- X/*
- X * The 'stop' command , prints the message that it has stopped
- X * and then exits the 'user' program.
- X */
- X
- Xstop()
- X{
- X check();
- X dostop(0);
- X}
- X
- X/*
- X * Called if trapped is set (by control-c ) and just calls dostop
- X * with a different parameter to print a slightly different message
- X */
- X
- Xdobreak()
- X{
- X dostop(1);
- X}
- X
- X/*
- X * prints out the 'stopped' or 'breaking' message then exits.
- X * These two functions were lumped together so that it might be
- X * possible to add a 'cont'inue command at a latter date ( not
- X * implemented yet ) - ( it is now ).
- X */
- X
- Xdostop(i)
- X{
- X if(cursor){
- X cursor=0;
- X prints(nl);
- X }
- X prints( (i) ? "breaking" : "stopped" );
- X if(runmode){
- X prints(" at line ");
- X prints(printlin(curline));
- X if(!intrap){ /* save environment */
- X cancont=i+1;
- X conpoint=point;
- X constolin=stocurlin;
- X concurlin=curline;
- X contelse=elsecount;
- X conerp=errortrap;
- X }
- X }
- X prints(nl);
- X reset();
- X}
- X
- X/* the 'cont' command - it seems to work ?? */
- X
- Xcont()
- X{
- X check();
- X if( contpos && !runmode){
- X point = conpoint; /* restore environment */
- X stocurlin =constolin;
- X curline = concurlin;
- X elsecount = contelse;
- X errortrap = conerp;
- X vvend= bstk;
- X bstk = vend;
- X mtest(vvend); /* yeuch */
- X runmode =1;
- X if(contpos==1){
- X contpos=0;
- X normret; /* stopped */
- X }
- X contpos=0; /* ctrl-c ed */
- X return(-1);
- X }
- X contpos=0;
- X error(CANTCONT);
- X}
- X
- X/*
- X * The 'delete' command , will only delete the required lines if it
- X * can find the two end lines. stops ' delete 1' etc. as a slip up.
- X * very slow algorithm. But who cares ??
- X */
- X
- Xdelete()
- X{
- X register lpoint p1,p2;
- X register unsigned i2;
- X
- X p1=getline();
- X if(getch()!='-')
- X error(SYNTAX);
- X p2=getline();
- X check();
- X if(p1>p2)
- X reset();
- X i2 = p2->linnumb;
- X do{
- X linenumber = p1->linnumb;
- X insert(0);
- X }while(p1->linnumb && p1->linnumb <= i2 );
- X reset();
- X}
- X
- X/*
- X * The 'shell' command , calls the v7 shell as an entry into unix
- X * without going out of basic. Has to set the terminal in a decent
- X * mode , else 'ded' doesn't like it.
- X * Clears out all buffered file output , so that you can see what
- X * you have done so far, and sets your userid to your real-id
- X * this stops people becoming unauthorised users if basic is made
- X * setuid ( for games via runfile of the command file ).
- X */
- X
- Xshell()
- X{
- X register int i;
- X register int (*q)() , (*p)();
- X int (*signal())();
- X char *s;
- X#ifdef SIGTSTP
- X int (*t)();
- X#endif
- X
- X check();
- 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)",0);
- X exit(-1); /* problem */
- X }
- X else if(i== -1)
- X prints("cannot shell out\n");
- X else { /* daddy */
- X p=signal(SIGINT,SIG_IGN); /* ignore some signals */
- X q=signal(SIGQUIT, SIG_IGN);
- X while(i != wait(0) && i != -1); /* 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#ifdef SIGTSTP
- X signal(SIGTSTP, t);
- X#endif
- X normret;
- X}
- X
- X/*
- X * The 'edit' command , can only edit in immeadiate mode , and with the
- X * specified line ( maybe could be more friendly here , no real need to
- X * since the editor is the same as on line input.
- X */
- X
- Xeditl()
- X{
- X register lpoint p;
- X register int i;
- X
- X p= getline();
- X check();
- X if(runmode || noedit)
- X error(13); /* illegal edit */
- X i=listl(p);
- X edit(0,i,0); /* do the edit */
- X if(trapped) /* ignore it if exited via cntrl-c */
- X reset();
- X i=compile(0);
- X if(linenumber) /* ignore it if there is no line number */
- X insert(i);
- X reset(); /* return to 'ready' */
- X}
- X
- X/*
- X * The 'auto' command , allows input of lines with automatic line
- X * numbering. Most of the code is to do with getting the arguments
- X * otherwise the loop is fairly simple. There are three ways of getting
- X * out of this routine. cntrl-c will exit the routine immeadiately
- X * If there is no linenumber then it also exits. If the line typed in is
- X * terminated by an ESCAPE character the line is inserted and the routine
- X * is terminated.
- X */
- X
- Xdauto()
- X{
- X register unsigned start , end , i1;
- X unsigned int i2;
- X long l;
- X int c;
- X i2=autoincr;
- X i1=getlin();
- X if( i1 != (unsigned)(-1) ){
- X if(getch()!= ','){
- X point--;
- X i2=autoincr;
- X }
- X else {
- X i2=getlin();
- X if(i2 == (unsigned)(-1) )
- X error(SYNTAX);
- X }
- X }
- X else
- X i1=autostart;
- X check();
- X start=i1;
- X autoincr=i2;
- X end=i2;
- X for(;;){
- X i1= strcpy(printlin(start),line) - line;
- X line[i1++]=' ';
- X c=edit(0,i1,i1);
- X if(trapped)
- X break;
- X i1=compile(0);
- X if(!linenumber)
- X break;
- X insert(i1);
- X if( (l= (long)start+end) >=65530){
- X autostart=10;
- X autoincr=10;
- X error(6); /* undefined line number */
- X }
- X start+=end;
- X autostart=l;
- X if(c == ESCAPE )
- X break;
- X }
- X reset();
- X}
- X
- X/*
- X * The 'save' command , saves a basic program on a file.
- X * It just lists the lines adds a newline then writes them out
- X */
- X
- Xsave()
- X{
- X register lpoint p;
- X register int fp;
- X register int i;
- X
- X stringeval(gblock); /* get the name */
- X gblock[gcursiz]=0;
- X check();
- X if((fp=creat(gblock,0644))== -1)
- X error(14); /* cannot creat file */
- X for(p= (lpoint)fendcore ; p->linnumb ;
- X p = (lpoint)((memp) p + lenv(p)) ){
- X i=listl(p);
- X line[i++]='\n';
- X write(fp,line,i); /* could be buffered ???? */
- X }
- X close(fp);
- X normret;
- X}
- X
- X/*
- X * The 'old' command , loads a program from a file. The old
- X * program (if any ) is wiped.
- X * Most of the work is done in readfi, ( see also error ).
- X */
- X
- Xold()
- X{
- X register int fp;
- X
- X stringeval(gblock);
- X gblock[gcursiz]=0; /* get the file name */
- X check();
- X if((fp=open(gblock,0))== -1)
- X error(15); /* can't open file */
- X ecore= fendcore+sizeof(xlinnumb); /* zap old program */
- X ( (lpoint) fendcore)->linnumb=0;
- X readfi(fp); /* read the new file */
- X reset();
- X}
- X
- X/*
- X * The 'merge' command , similar to 'old' but does not zap the old
- X * program so the two files are 'merged' .
- X */
- X
- Xmerge()
- X{
- X register int fp;
- X
- X stringeval(gblock);
- X gblock[gcursiz]=0;
- X check();
- X if((fp=open(gblock,0))== -1)
- X error(15);
- X readfi(fp);
- X reset();
- X}
- X
- X/*
- X * The routine that actually reads in a file. It sets up readfile
- X * so that if there is an error ( linenumber overflow ) , then error
- X * can pick up the pieces , else the number of file descriptors are
- X * reduced and can ( unlikely ), run out of them so stopping any file
- X * being saved or restored , ( This is the reason that all files are
- X * closed so meticulacly ( see 'chain' and its pipes ).
- X */
- X
- Xreadfi(fp)
- X{
- X register char *p;
- X int i;
- X char chblock[BLOCKSIZ];
- X int nleft=0;
- X register int special=0;
- X register char *q;
- X
- X readfile=fp;
- X inserted=1; /* make certain variables are cleared */
- X p=line; /* input into line[] */
- X for(;;){
- X if(!nleft){
- X q=chblock;
- X if( (nleft=read(fp,q,BLOCKSIZ)) <= 0)
- X break;
- X }
- X *p= *q++;
- X nleft--;
- X if(special){
- X special=0;
- X if(*p>='a' && *p<='~'){
- X *p -= ('a'-1);
- X continue;
- X }
- X }
- X if(*p =='\n'){
- X *p=0;
- X i=compile(0);
- X if(!linenumber)
- X goto bad;
- X insert(i);
- X p=line;
- X continue;
- X }
- X else if(*p<' ')
- X goto bad;
- X else if(*p=='\\')
- X special++;
- X if(++p > &line[MAXLIN])
- X goto bad;
- X }
- X if(p!=line)
- X goto bad;
- X close(fp);
- X readfile=0;
- X return;
- X
- Xbad: close(fp); /* come here if there is an error */
- X readfile=0; /* that readfi() has detected */
- X error(23); /* stops error() having to tidy up */
- X}
- X
- X/*
- X * The 'new' command , This deletes any program and clears all
- X * variables , can take an extra parameter to say how many files are
- X * needed. If so then clears the number of buffers ( default 2 ).
- X */
- X
- Xneww()
- X{
- X register int i,c;
- X register struct filebuf *p;
- X register memp size;
- X
- X c=getch();
- X point--;
- X if(!istermin(c)){
- X i=evalint();
- X check();
- X closeall(); /* flush the buffers */
- X if(i<0 || i> MAXFILES)
- X i=2;
- X fendcore= filestart + (sizeof(struct filebuf) * i );
- X size = fendcore + sizeof(xlinnumb);
- X size = (char *) ( ((int)size + MEMINC) & ~MEMINC);
- X brk(size);
- 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 }
- X else
- X check();
- X autostart=10;
- X autoincr=10;
- X baseval=1;
- X ecore= fendcore + sizeof(xlinnumb);
- X ( (lpoint)fendcore )->linnumb=0;
- X clear(DEFAULTSTRING);
- X closeall();
- X reset();
- X}
- X
- X/*
- X * The 'chain' command , This routine chains the program.
- X * all simple numeric variables are kept. ( max of 4 k ).
- X * all other variables are cleared.
- X * runs the loaded file
- X * files are kept open
- X *
- X * error need only check pipe[0] to see if it is to be closed.
- X */
- X
- Xchain()
- X{
- X register int fp;
- X register int size;
- X register char *p;
- X int ssize,nsize;
- X#ifdef LNAMES
- X register struct entry *ep,*np;
- X register int *xp;
- X#endif
- X
- X stringeval(gblock);
- X check();
- X gblock[gcursiz]=0;
- X size= vend- earray;
- X#ifdef LNAMES
- X nsize = enames - estring; /* can only save offsets */
- X if(nsize + size >= 4096) /* cos ecore/estring might */
- X#else /* change */
- X if(size >= 4096 )
- X#endif
- X error(42); /* out of space for varibles */
- X if((fp=open(gblock,0))== -1)
- X error(15);
- X ssize= estring- ecore; /* amount of string space */
- X pipe(pipes);
- X write(pipes[1],earray,size); /* check this */
- X#ifdef LNAMES
- X write(pipes[1],estring,nsize);
- X#endif
- X close(pipes[1]);
- X pipes[1]=0;
- X ecore= fendcore + sizeof(xlinnumb); /* bye bye old file */
- X ( (lpoint)fendcore )->linnumb=0; /* commited to new file now */
- X readfi(fp);
- X clear(ssize);
- X errortrap=0;
- X inserted=0; /* say we don't actually want to */
- X p= xpand(&vend,size); /* clear variables on return */
- X read(pipes[0],p,size);
- X#ifdef LNAMES
- X p = xpand(&enames,nsize);
- X read(pipes[0],p,nsize);
- X /*
- X * now rehash the symbol table
- X * cos it gets munged when it moves
- X */
- X for(ep = (struct entry *)estring; ep < (struct entry *)enames; ep++){
- X ep->link = 0;
- X for(p = ep->_name,size = 0; *p ; size += *p++);
- X ep->ln_hash = size;
- X if(np = hshtab[size %= HSHTABSIZ]){
- X for(;np->link ;np = np->link);
- X np->link = ep;
- X }
- X else
- X hshtab[size] = ep;
- X }
- X /*
- X * must zap varshash - because of above
- X */
- X for( xp = varshash ; xp < &varshash[HSHTABSIZ] ; *xp++ = -1);
- X chained = 1;
- X#endif
- X close(pipes[0]); /* now have data back from pipe */
- X pipes[0]=0;
- X stocurlin= (lpoint)fendcore;
- X if(!(curline=stocurlin->linnumb))
- X reset();
- X point= stocurlin->lin;
- X elsecount=0;
- X runmode=1;
- X return(-1); /* now run the file */
- X}
- X
- X/* define a function def fna() - can have up to 3 parameters */
- X
- Xdeffunc()
- X{
- X struct deffn fn; /* temporary place for evaluation */
- X register struct deffn *p;
- X register int i=0;
- X int c;
- X char *j;
- X register char *l;
- X
- X if(getch() != FN)
- X error(SYNTAX);
- X if(!isletter(*point))
- X error(SYNTAX);
- X getnm();
- X if(vartype == 02)
- X error(VARREQD);
- X fn.dnm = nm;
- X#ifdef LNAMES
- X for(p = (deffnp)enames ; p < (deffnp)edefns ;
- X#else
- X for(p = (deffnp)estring ; p < (deffnp)edefns ;
- X#endif
- X p = (deffnp)( (memp)p + p->offs) )
- X if(p->dnm == nm )
- X error(REDEFFN); /* redefined functions */
- X fn.vtys=vartype<<4; /* save return type of function */
- X if(*point=='('){ /* get arguments */
- X point++;
- X for(;i<3;i++){
- X l=getname();
- X if( l < earray)
- X error(VARREQD);
- X fn.vargs[i]= l - earray;
- X fn.vtys |= vartype <<i; /* save type of arguments */
- X if((c=getch())!=',')
- X break;
- X }
- X if(c!= ')')
- X error(SYNTAX);
- X i++;
- X }
- X if(getch()!='=')
- X error(SYNTAX);
- X fn.narg=i;
- X l = point;
- X while(*l++ == ' ');
- X point = --l;
- X while(!istermin(*l)) /* get rest of expression */
- X l++;
- X if(l==point)
- X error(SYNTAX);
- X i= l - point + sizeof(struct deffn);
- X#ifdef ALIGN4
- X i = (i + 03) & ~03;
- X#else
- X if(i&01) /* even up space requirement */
- X i++;
- X#endif
- X p= (deffnp) xpand(&edefns,i ); /* get the space */
- X#ifndef V6C
- X *p = fn;
- X p->offs = i;
- X#else
- X p->dnm = fn.dnm; /* put all values in */
- X p->offs=i;
- X p->narg=fn.narg;
- X p->vtys= fn.vtys;
- X p->vargs[0]=fn.vargs[0];
- X p->vargs[1]=fn.vargs[1];
- X p->vargs[2]=fn.vargs[2];
- X#endif
- X j= p->exp;
- X while( point<l) /* store away line */
- X *j++ = *point++;
- X *j=0;
- X normret;
- X}
- X
- X/* the repeat part of the repeat - until loop */
- X/* now can have a construct like 'repeat until eof(1)'. */
- X/* It might be of use ?? it's a special case */
- X
- X
- Xrept()
- X{
- X register struct forst *p;
- X register int c;
- X register char *tp;
- X
- X if(getch() == UNTIL){
- X tp = point; /* save point */
- X eval(); /* calculate the value */
- X check(); /* check syntax */
- X#ifdef PORTABLE
- X while((vartype ? (!res.i) :(res.f == 0)) && !trapped){
- X#else
- X while(!res.i && !trapped){ /* now repeat the loop until <>0 */
- X#endif
- X point = tp;
- X eval();
- X }
- X normret;
- X }
- X point--;
- X check();
- X p= (forstp)vvend;
- X vvend += sizeof(struct forst);
- X mtest(vvend);
- X p->pt = point;
- X p->stolin = stocurlin;
- X p->elses = elsecount;
- X p->fr = 0; /* make it look like a gosub like */
- X p->fnnm = (char *)01; /* distinguish from gosub's */
- X normret;
- X}
- X
- X/* the until bit of the command */
- X
- Xuntilf()
- X{
- X register struct forst *p;
- X eval();
- X check();
- X for(p= (forstp)vvend , p-- ; p >= (forstp)bstk ; p--)
- X if(!p->fr)
- X goto got;
- X error(48);
- Xgot:
- X if(p->fnnm != (char *)01 )
- X error(51);
- X#ifdef PORTABLE
- X if(vartype ? (!res.i) : (res.f == 0)){
- X#else
- X if(!res.i){ /* not true so repeat loop */
- X#endif
- X elsecount = p->elses;
- X point = p->pt;
- X if(stocurlin = p->stolin)
- X curline = stocurlin->linnumb;
- X else runmode =0;
- X vvend = (memp)(p+1); /* pop all off stack up until here */
- X }
- X else
- X vvend = (memp)p; /* pop stack if finished here. */
- X normret;
- X}
- X
- X/* while part of while - wend construct. This is like repeat until unless
- X * loop fails on the first time. (Yeuch - next we need syntax checking on
- X * input ).
- X */
- X
- Xwhilef()
- X{
- X register char *spoint = point;
- X register lpoint lp;
- X register struct forst *p;
- X lpoint get_end();
- X eval();
- X check();
- X#ifdef PORTABLE
- X if(vartype ? res.i : res.f){
- X#else
- X if(res.i){ /* got to go through it once so make it look like a */
- X /* repeat - until */
- X#endif
- X p= (forstp)vvend;
- X vvend += sizeof(struct forst);
- X mtest(vvend);
- X p->pt = spoint;
- X p->stolin = stocurlin;
- X p->elses = elsecount;
- X p->fr = 0; /* make it look like a gosub like */
- X p->fnnm = (char *)02; /* distinguish from gosub's */
- X normret;
- X }
- X lp=get_end(); /* otherwise find a wend */
- X check();
- X if(runmode){
- X stocurlin =lp;
- X curline = lp->linnumb;
- X }
- X normret;
- X}
- X
- X/* the end part of a while loop - wend */
- X
- Xwendf()
- X{
- X register struct forst *p;
- X char *spoint =point;
- X check();
- X for(p= (forstp)vvend , p-- ; p >= (forstp)bstk ; p--)
- X if(!p->fr)
- X goto got;
- X error(49);
- Xgot:
- X if( p->fnnm != (char *)02 )
- X error(51);
- X point = p->pt;
- X eval();
- X#ifdef PORTABLE
- X if(vartype ? (!res.i) : (res.f == 0)){
- X#else
- X if(!res.i){ /* failure of the loop */
- X#endif
- X vvend= (memp)p;
- X point = spoint;
- X normret;
- X }
- X vvend = (memp)(p+1); /* pop stack after an iteration */
- X elsecount = p->elses;
- X if(stocurlin = p->stolin)
- X curline = stocurlin->linnumb;
- X else runmode=0;
- X normret;
- X}
- X
- X/* get_end - search from current position until found a wend statement - of
- X * the correct nesting. Keeping track of elses + if's(Yeuch ).
- X */
- X
- Xlpoint
- Xget_end()
- X{
- X register lpoint lp;
- X register char *p;
- X register int c;
- X int wcount=0;
- X int rcount=0;
- X int flag=0;
- X
- X p= point;
- X lp= stocurlin;
- X if(getch()!=':'){
- X if(!runmode)
- X error(50);
- X lp = (lpoint)((memp)lp +lenv(lp));
- X if(!lp->linnumb)
- X error(50);
- X point = lp->lin;
- X elsecount=0;
- X }
- X for(;;){
- X c=getch();
- X if(c==WHILE)
- X wcount++;
- X else if(c==WEND){
- X if(--wcount <0)
- X break; /* only get out point in loop */
- X }
- X else if(c==REPEAT)
- X rcount++;
- X else if(c==UNTIL){
- X if(--rcount<0)
- X error(51); /* bad nesting */
- X }
- X else if(c==IF){
- X flag++;
- X elsecount++;
- X }
- X else if(c==ELSE){
- X flag++;
- X if(elsecount)
- X elsecount--;
- X }
- X else if(c==REM || c==DATA || c==QUOTE){
- X if(!runmode)
- X error(50); /* no wend */
- X lp = (lpoint)((memp)lp +lenv(lp));
- X if(!lp->linnumb)
- X error(50); /* no wend */
- X point =lp->lin;
- X elsecount=0;
- X flag=0;
- X continue;
- X }
- X else for(p=point;!istermin(*p);p++)
- X if(*p=='"' || *p=='`'){
- X c= *p++;
- X while(*p && *p != (char) c)
- X p++;
- X if(!*p)
- X break;
- X }
- X if(!*p++){
- X if(!runmode)
- X error(50);
- X lp = (lpoint)((memp)lp +lenv(lp));
- X if(!lp->linnumb)
- X error(50);
- X point =lp->lin;
- X elsecount=0;
- X flag=0;
- X }
- X else
- X point = p;
- X }
- X /* we have found it at this point - end of loop */
- X if(rcount || (lp!=stocurlin && flag) )
- X error(51); /* bad nesting or wend after an if */
- X return(lp); /* not on same line */
- X}
- X
- X#ifdef RENUMB
- X
- X/*
- X * the renumber routine. It is a three pass algorithm.
- X * 1) Find all line numbers that are in text.
- X * Save in table.
- X * 2) Renumber all lines.
- X * Fill in table with lines that are found
- X * 3) Find all line numbers and update to new values.
- X *
- X * This routine eats stack space and also some code space
- X * If you don't want it don't define RENUMB.
- X * Could run out of stack if on V7 PDP-11's
- X * ( On vax's it does not matter. Also can increase MAXRLINES.)
- X * MAXRLINES can be reduced if not got split i-d. If this is
- X * the case then probarbly do not want this code anyway.
- X */
- X
- X#define MAXRLINES 500 /* the maximum number of lines that */
- X /* can be changed. Change if neccasary */
- X
- Xrenumb()
- X{
- X struct ta {
- X unsigned linn;
- X unsigned toli;
- X } ta[MAXRLINES];
- X
- X struct ta *eta = ta;
- X register struct ta *tp;
- X register char *q;
- X register lpoint p;
- X
- X unsigned l1,start,inc;
- X int size,sl,pl;
- X char onfl,chg,*r,*s;
- X long numb;
- X
- X start = 10;
- X inc = 10;
- X l1 = getlin();
- X if(l1 != (unsigned)(-1) ){ /* get start line number */
- X start = l1;
- X if(getch() != ',')
- X point--;
- X else {
- X l1 = getlin(); /* get increment */
- X if(l1 == (unsigned)(-1))
- X error(5);
- X inc = l1;
- X }
- X }
- X check(); /* check rest of line */
- X numb = start; /* set start counter */
- X for(p=(lpoint)fendcore; p->linnumb ;p=(lpoint)((char *)p+lenv(p))){
- X numb += inc;
- X if(numb >= 65530 ) /* check line numbers */
- X error(7); /* line number overflow */
- X onfl = 0; /* flag to deal with on_goto */
- X for(q = p->lin; *q ; q++){ /* now find keywords */
- X if( !(*q & (char)0200 )) /* not one */
- X continue; /* ignore */
- X if(*q == (char) ON){ /* the on keyword */
- X onfl++; /* set flag */
- X continue;
- X } /* check items with optional numbers*/
- X if(*q == (char)ELSE || *q == (char)THEN ||
- X *q == (char)RESUME || *q == (char)RESTORE
- X || *q == (char) RUNN ){
- X q++;
- X while(*q++ == ' ');
- X q--;
- X if(isnumber(*q)) /* got one ok */
- X goto ok1;
- X }
- X if(*q != (char) GOTO && *q != (char)GOSUB)
- X continue; /* can't be anything else */
- X q++;
- X ok1: /* have a label */
- X do{
- X while(*q++ == ' ');
- X q--; /* look for number */
- X if( !isnumber(*q) ){
- X prints("Line number required on line ");
- X prints(printlin(p->linnumb));
- X prints(nl); /* missing */
- X goto out1;
- X }
- X for(l1 = 0; isnumber(*q) ; q++) /* get it */
- X if(l1 >= 6553)
- X error(7);
- X else l1 = l1 * 10 + *q - '0';
- X for(tp = ta ; tp < eta ; tp++) /* already */
- X if(tp->linn == l1) /* got it ? */
- X break;
- X if(tp >= eta ){ /* add another entry */
- X tp->linn = l1;
- X tp->toli = -1;
- X if(++eta >= &ta[MAXRLINES])
- X error(24); /* out of core */
- X }
- X if(!onfl) /* check flag */
- X break; /* get next item */
- X while(*q++== ' '); /* if ON and comma */
- X }while( *(q-1) ==',');
- X if(onfl)
- X q--;
- X onfl =0;
- X q--;
- X }
- X out1: ;
- X }
- X numb = start; /* reset counter */
- X for(p= (lpoint)fendcore ; p->linnumb ;p=(lpoint)((char *)p+lenv(p)) ){
- X for(tp = ta ; tp < eta ; tp++) /* change numbers */
- X if(tp->linn == p->linnumb){
- X tp->toli = numb; /* inform of new number */
- X break;
- X }
- X p->linnumb = numb;
- X numb += inc;
- X }
- X for(p= (lpoint)fendcore ; p->linnumb ;p=(lpoint)((char *)p+lenv(p)) ){
- X onfl = 0;
- X chg = 0; /* set if line changed */
- X for(r = nline, q = p->lin ; *q ; *r++ = *q++){
- X if( r >= &nline[MAXLIN]) /* overflow of line */
- X break;
- X if( !(*q & (char) 0200 )) /* repeat search for */
- X continue; /* keywords */
- X if(*q == (char) ON){
- X onfl++;
- X continue;
- X }
- X if(*q == (char)ELSE || *q == (char)THEN ||
- X *q == (char)RESUME || *q == (char)RESTORE
- X || *q == (char) RUNN ){
- X *r++ = *q++;
- X while(*q == ' ' && r < &nline[MAXLIN] )
- X *r++ = *q++;
- X if(isnumber(*q)) /* got optional line number*/
- X goto ok2;
- X }
- X if(*q != (char) GOTO && *q != (char)GOSUB)
- X continue;
- X *r++ = *q++;
- X for(;;){
- X while(*q == ' ' && r < &nline[MAXLIN] )
- X *r++ = *q++;
- X ok2: ;
- X if(r>= &nline[MAXLIN] )
- X break;
- X for(l1 = 0 ; isnumber(*q) ; q++) /* get numb*/
- X l1 = l1 * 10 + *q - '0';
- X if(l1 == 0) /* skip if not found */
- X goto out; /* never happen ?? */
- X for(tp = ta ; tp < eta ; tp++)
- X if(tp->linn == l1)
- X break;
- X if(tp->linn != tp->toli)
- X chg++; /* number has changed */
- X if(tp >= eta || tp->toli == (unsigned)(-1) ){
- X prints("undefined line: ");
- X prints(printlin(l1));
- X prints(" on line ");
- X prints(printlin(p->linnumb));
- X prints(nl); /* can't find it */
- X goto out;
- X }
- X s = printlin(tp->toli); /* get new number */
- X while( *s && r < &nline[MAXLIN])
- X *r++ = *s++;
- X if(r >= &nline[MAXLIN] )
- X break;
- X if(onfl){ /* repeat if ON statement */
- X while(*q == ' ' && r < &nline[MAXLIN])
- X *r++ = *q++;
- X if(*q == ','){
- X *r++ = *q++;
- X continue;
- X }
- X }
- X break;
- X }
- X onfl = 0;
- X if(r >= &nline[MAXLIN])
- X error(32); /* line length overflow */
- X }
- X if(!chg) /* not changed so don't put back */
- X continue;
- X inserted =1; /* say we have changed it */
- X for(*r = 0, r = nline; *r++ ;);
- X r--;
- X size = (r - nline) + sizeof(struct olin); /* get size */
- X#ifdef ALIGN4
- X size = (size + 03) & ~03;
- X#else
- X if(size & 01) /* even it up */
- X size++;
- X#endif
- X if(size != lenv(p) ){ /* size changed. insert */
- X pl = p->linnumb; /* save line number */
- X sl = lenv(p); /* save length */
- X bmov((short *)p,sl); /* compress core */
- X ecore -= sl; /* shrink it */
- X mtest(ecore+size); /* get more core */
- X ecore += size; /* add it */
- X bmovu((short *)p,size); /* expand core */
- X p->linnumb = pl; /* restore line number */
- X lenv(p) = size; /* set size */
- X }
- X strcpy(nline,p->lin); /* copy back new line */
- X out: ;
- X }
- X reset();
- X}
- X#else
- Xrenumb(){}
- X#endif /* RENUMB */
- X
- X/* the load command. Load a dump image. Works fastwer than save/old */
- X
- X#define MAGIC1 013121
- X#define MAGIC2 027212
- X
- Xloadd()
- X{
- X register int nsize;
- X register fp;
- X int header[3];
- X
- X stringeval(gblock);
- X check();
- X gblock[gcursiz] = 0;
- X if( (fp = open(gblock,0))< 0)
- X error(14);
- X if(read(fp,(char *)header,sizeof(int)*3) != sizeof(int)*3){
- X close(fp);
- X error(23); /* bad load / format file */
- X }
- X if(header[0] != MAGIC1 && header[1] != MAGIC2){
- X close(fp);
- X error(23);
- X }
- X ecore = fendcore + sizeof(xlinnumb);
- X mtest(ecore); /* good bye old image */
- X ((lpoint)fendcore)->linnumb = 0;
- X inserted = 1;
- X readfile = fp;
- X mtest(ecore+header[2]);
- X readfile = 0;
- X ecore += header[2];
- X nsize = read(fp,fendcore,header[2]);
- X close(fp);
- X if(nsize != header[2]){
- X ecore = fendcore + sizeof(xlinnumb);
- X mtest(ecore);
- X ((lpoint)fendcore)->linnumb = 0;
- X error(23);
- X }
- X reset();
- X}
- X
- X/* write out the core to the file */
- X
- Xdump()
- X{
- X register int nsize;
- X register fp;
- X int header[3];
- X
- X stringeval(gblock);
- X check();
- X gblock[gcursiz] = 0;
- X if( (fp = creat(gblock,0644))< 0)
- X error(15);
- X header[0] = MAGIC1;
- X header[1] = MAGIC2;
- X nsize = ecore - fendcore;
- X header[2] = nsize;
- X write(fp,(char *)header,sizeof(int)*3);
- X write(fp,fendcore,nsize);
- X close(fp);
- X normret;
- X}
- End of bas8.c
- chmod u=rw-,g=r,o=r bas8.c
- echo x - bas9.c 1>&2
- sed 's/^X//' > bas9.c << 'End of bas9.c'
- X/*
- X * BASIC by Phil Cockcroft
- X */
- X#include "bas.h"
- X
- X/*
- X * This file contains subroutines used by many commands
- X */
- X
- X/* stringcompare will compare two strings and return a valid
- X * logical value
- X */
- X
- Xstringcompare()
- X{
- X char chblock[256];
- X register int i;
- X register char *p,*q;
- X int cursiz;
- X int reslt=0;
- X int c;
- X
- X checksp();
- X stringeval(chblock);
- X cursiz=gcursiz;
- X if(! (c=getch()) )
- X error(SYNTAX);
- X stringeval(gblock);
- X if(i = ((cursiz > gcursiz) ? gcursiz : cursiz) ){
- X /*
- X * make i the minimum of gcursiz and cursiz
- X */
- X gcursiz -= i; cursiz -= i;
- X p=chblock; q=gblock; /* set pointers */
- X do{
- X if(*p++ != *q++){ /* do the compare */
- X if( (*(p-1) & 0377) > (*(q-1) & 0377) )
- X reslt++;
- X else
- X reslt--;
- X compare(c,reslt);
- X return;
- X }
- X }while(--i);
- X }
- X if(cursiz)
- X reslt++;
- X else if(gcursiz)
- X reslt--;
- X compare(c,reslt);
- X}
- X
- X/* given the comparison operator 'c' then returns a value
- X * given that 'reslt' has a value of:-
- X * 0: equal
- X * 1: greater than
- X * -1: less than
- X */
- X
- Xcompare(c,reslt)
- Xregister int c;
- Xregister int reslt;
- X{
- X vartype=01;
- X if(c==EQL){
- X if(!reslt)
- X goto true;
- X }
- X else if(c==LTEQ){
- X if( reslt<=0)
- X goto true;
- X }
- X else if(c==NEQE){
- X if( reslt)
- X goto true;
- X }
- X else if(c==LTTH){
- X if( reslt<0)
- X goto true;
- X }
- X else if(c==GTEQ){
- X if( reslt>=0)
- X goto true;
- X }
- X else if(c==GRTH){
- X if( reslt>0)
- X goto true;
- X }
- X else
- X error(SYNTAX);
- X res.i=0; /* false */
- X return;
- Xtrue:
- X res.i = -1;
- X}
- X
- X/* converts a number in 'res' to a string in gblock
- X * the string will have a space at the start if it is positive
- X */
- X
- Xgcvt()
- X{
- X int sign, decpt;
- X int ndigit=9;
- X register char *p1, *p2;
- X register int i;
- X#ifndef SOFTFP
- X char *ecvt();
- X#else
- X char *necvt();
- X#endif
- X
- X#ifdef PORTABLE
- X if(vartype==01 || !res.f){
- X#else
- X if(vartype==01 || !res.i){ /* integer deal with them separately */
- X#endif
- X lgcvt();
- X return;
- X }
- X#ifndef SOFTFP
- X p1 = ecvt(res.f, ndigit+2, &decpt, &sign);
- X#else
- X p1 = necvt(&res, ndigit+2, &decpt, &sign);
- X#endif
- X if (sign)
- X *gblock = '-';
- X else
- X *gblock = ' ';
- X if(ndigit > 1){
- X p2 = p1 + ndigit-1;
- X do {
- X if(*p2 != '0')
- X break;
- X ndigit--;
- X }while(--p2 > p1);
- X }
- X p2 = &gblock[1];
- X/*
- X for (i=ndigit-1; i>0 && *(p1+i) =='0'; i--)
- X ndigit--;
- X*/
- X if (decpt < 0 || decpt > 9){
- X decpt--;
- X *p2++ = *p1++;
- X if(ndigit != 1){
- X *p2++ = '.';
- X for (i=1; i<ndigit; i++)
- X *p2++ = *p1++;
- X }
- X *p2++ = 'e';
- X if (decpt<0) {
- X decpt = -decpt;
- X *p2++ = '-';
- X }
- X if(decpt >= 10){
- X *p2++ = decpt/10 + '0';
- X decpt %= 10;
- X }
- X *p2++ = decpt + '0';
- X }
- X else {
- X if (!decpt) {
- X *p2++ = '0';
- X *p2++ = '.';
- X }
- X for (i=1; i<=ndigit; i++) {
- X *p2++ = *p1++;
- X if (i==decpt && i != ndigit)
- X *p2++ = '.';
- X }
- X while (ndigit++<decpt)
- X *p2++ = '0';
- X }
- X *p2 =0;
- X gcursiz= p2 -gblock;
- X}
- X
- X/* integer version of above - a very simple algorithm */
- X
- Xlgcvt()
- X{
- X static char s[7];
- X register char *p,*q;
- X int fl=0;
- X register unsigned l;
- X
- X l= res.i;
- X p= &s[6];
- X if((int)l <0){
- X fl++;
- X l= -l;
- X }
- X do{
- X *p-- = l%10 +'0';
- X }while(l/=10 );
- X if(fl)
- X *p ='-';
- X else
- X *p =' ';
- X q=gblock;
- X while(*q++ = *p++);
- X gcursiz= --q - gblock;
- X}
- X
- X/* get a linenumber or if no linenumber return a -1
- X * used by all routines with optional linenumbers
- X */
- X
- Xgetlin()
- X{
- X register unsigned l=0;
- X register int c;
- X
- X c=getch();
- X if(!isnumber(c)){
- X point--;
- X return(-1);
- X }
- X do{
- X if(l>=6553 )
- X error(7);
- X l= l*10 + (c-'0');
- X c= *point++;
- X }while(isnumber(c));
- X point--;
- X return(l);
- X}
- X
- X/* getline() gets a line number and returns a valid pointer
- X * to it, if there is no linenumber or the line is not there
- X * then there is an error. Used by 'goto' etc.
- X */
- X
- Xlpoint
- Xgetline()
- X{
- X register unsigned l=0;
- X register lpoint p;
- X register int c;
- X
- X c=getch();
- X if(!isnumber(c))
- X error(5);
- X do{
- X if(l>=6553)
- X error(7);
- X l= l*10+(c-'0');
- X c= *point++;
- X }while(isnumber(c));
- X point--;
- X if(runmode && l >= curline) /* speed it up a bit */
- X p = stocurlin; /* no need to search the whole lot */
- X else
- X p = (lpoint)fendcore;
- X for(; p->linnumb ;p = (lpoint)((memp)p + lenv(p)))
- X if(p->linnumb == l)
- X return(p);
- X error(6);
- X}
- X
- X/* printlin() returns a pointer to a string representing the
- X * the numeric value of the linenumber. linenumbers are unsigned
- X * quantities.
- X */
- X
- Xchar *
- Xprintlin(l)
- Xregister unsigned l;
- X{
- X static char ln[7];
- X register char *p;
- X
- X p = &ln[5];
- X do{
- X *p-- = l %10 + '0';
- X }while(l/=10);
- X p++;
- X return(p);
- X}
- X
- X/* routine used to check the type of expression being evaluated
- X * used by print and eval.
- X * A string expression returns a value of '1'
- X * A numeric expression returns a value of '0'
- X */
- X
- Xchecktype()
- X{
- X register char *tpoint;
- X register int c;
- X
- X if( (c= *point) & 0200){
- X if( (c&0377) >= MINFUNC)
- X goto data;
- X else goto string;
- X }
- X if(isnumber(c) || c=='.' || c== '-' || c=='(')
- X goto data;
- X if(c=='"' || c=='`')
- X goto string;
- X if(!isletter(c))
- X error(SYNTAX);
- X tpoint= point;
- X do{
- X c= *++tpoint;
- X }while(isletter(c) || isnumber(c));
- X if(c!='$')
- Xdata: return(0);
- Xstring: return(1);
- X}
- X
- X/* print out a message , used for all types of 'basic' messages
- X */
- X
- Xprints(s)
- Xchar *s;
- X{
- X register char *i;
- X
- X i=s;
- X while(*i++);
- X write(1,s,--i-s);
- X}
- X
- X/* copy a string from a to b returning the last address used in b
- X */
- X
- Xchar *
- Xstrcpy(a,b)
- Xregister char *a,*b;
- X{
- X while(*b++ = *a++);
- X return(--b);
- X}
- X
- X
- X#ifndef SOFTFP
- X
- X/* convert an ascii string into a number. If it is possibly an integer
- X * return an integer.
- X * Otherwise return a double ( in res )
- X * should never overflow. One day I may fix the non floating point one.
- X */
- X
- X
- X#define BIG 1.701411835e37
- X
- Xgetop()
- X{
- X register double x = 0;
- X register int exponent = 0;
- X register int ndigits = 0;
- X register int c;
- X register int exp;
- X char decp = 0;
- X char lzeros = 0;
- X int minus;
- X short xx;
- X
- Xdot: for(c = *point ; isnumber(c) ; c = *++point){
- X if(!lzeros){
- X if(c == '0'){ /* ignore leading zeros */
- X if(decp)
- X exponent--;
- X continue;
- X }
- X lzeros++;
- X }
- X if(ndigits >= 15){ /* ignore insignificant digits */
- X if(!decp)
- X exponent++;
- X continue;
- X }
- X if(decp)
- X exponent--;
- X ndigits++;
- X x = x * 10 + c - '0';
- X }
- X if(c == '.'){
- X point++;
- X if(decp)
- X return(0);
- X decp++;
- X goto dot;
- X }
- X if(c == 'e' || c == 'E'){
- X minus = 0;
- X if( (c = *++point) == '+')
- X point++;
- X else if(c =='-'){
- X minus++;
- X point++;
- X }
- X else if(c < '0' || c > '9')
- X return(0);
- X for(exp = 0, c = *point; c >= '0' && c <= '9' ; c = *++point){
- X if(exp < 1000)
- X exp = exp * 10 + c - '0';
- X }
- X if(minus)
- X exponent -= exp;
- X else
- X exponent += exp;
- X }
- X while(exponent < 0){
- X exponent++;
- X x /= 10;
- X }
- X while(exponent > 0){
- X exponent--;
- X if(x > BIG)
- X return(0);
- X x *= 10;
- X }
- X xx = x; /* see if x is == an integer */
- X /*
- X * shouldn't need a cast below but there is a bug in the 68000
- X * compiler which does the comparison wrong without it.
- X */
- X if( (double) xx == x){
- X vartype= 01;
- X res.i = xx;
- X } else {
- X vartype = 0;
- X res.f = x;
- X }
- X return(1);
- X}
- X#endif
- End of bas9.c
- chmod u=rw-,g=r,o=r bas9.c
- echo x - gen 1>&2
- sed 's/^X//' > gen << 'End of gen'
- Xcase $1 in
- X vax)
- X make -f vax/Makefile ;;
- X pdp11)
- X echo "Please specify pdp11fp or pdp11nofp" ;;
- X
- X pdp11fp)
- X make -f pdp11/Makefile.fp ;;
- X
- X pdp11nofp)
- X make -f pdp11/Makefile.nofp ;;
- X
- X m68000)
- X make -f m68000/Makefile ;;
- X
- X pyramid)
- X make -f pyramid/Makefile ;;
- X
- X clean)
- X rm -f *.o cursor.c term.c core basic ;;
- X
- X *)
- X echo "please specify one of vax pdp11fp pdp11nofp m68000 pyramid" ;;
- Xesac
- End of gen
- chmod u=rwx,g=xr,o=xr gen
-
-