|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T b
Length: 40982 (0xa016) Types: TextFile Names: »bas8.c«
└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki └─⟦this⟧ »EUUGD11/euug-87hel/sec1/basic/bas8.c«
/* * 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; }