diff options
Diffstat (limited to 'steer.c')
-rw-r--r-- | steer.c | 2241 |
1 files changed, 2241 insertions, 0 deletions
@@ -0,0 +1,2241 @@ +/* MIRANDA STEER */ +/* initialisation routines and assorted routines for I/O etc */ + +/************************************************************************** + * Copyright (C) Research Software Limited 1985-90. All rights reserved. * + * The Miranda system is distributed as free software under the terms in * + * the file "COPYING" which is included in the distribution. * + * * + * Revised to C11 standard and made 64bit compatible, January 2020 * + *------------------------------------------------------------------------*/ + +/* this stuff is to get the time-last-modified of files */ +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> /* creat() */ +/* #include <sys/wait.h> /* seems not needed, oct 05 */ +struct stat buf; /* see man(2) stat - gets file status */ + +#include "data.h" +#include "big.h" +#include "lex.h" +#include <float.h> +word nill,Void; +word main_id; /* change to magic scripts 19.11.2013 */ +word message,standardout; +word diagonalise,concat,indent_fn,outdent_fn,listdiff_fn; +word shownum1,showbool,showchar,showlist,showstring,showparen,showpair, + showvoid,showfunction,showabstract,showwhat; + +char PRELUDE[pnlim+10],STDENV[pnlim+9]; + /* if anyone complains, elasticate these buffers! */ + +#define DFLTSPACE 2500000l +#define DFLTDICSPACE 100000l +/* default values for size of heap, dictionary */ +word SPACELIMIT=DFLTSPACE,DICSPACE=DFLTDICSPACE; + +#ifdef CYGWIN +#define EDITOR "joe +!" +#else +#define EDITOR "vi +!" +#endif +/* The name of whatever is locally considered to be the default editor - the + user will be able to override this using the `/editor' command. + It is also overriden by shell/environment variable EDITOR if present */ + +extern FILE *s_out; +int UTF8=0, UTF8OUT=0; +extern char *vdate, *host; +extern word version, ND; +extern word *dstack,*stackp; + +static void allnamescom(void); +static void announce(void); +static int badeditor(void); +static int checkversion(char*); +static void command(void); +static void commandloop(char*); +static void diagnose(char*); +static void editfile(char*,int); +static void ed_warn(void); +static void filecopy(char*); +static void filecp(char*,char*); +static void finger(char*); +static void fixeditor(void); +static void fixexports(void); +static int getln(FILE*,word,char*); +static word isfreeid(word); +static void libfails(void); +static void loadfile(char*); +static void makedump(void); +static void manaction(void); +static void mira_setup(void); +static void missparam(char*); +static char *mkabsolute(char*); +static word mkincludes(word); +static word mktiny(void); +static void namescom(word); +static void primlib(void); +static word privatise(word); +static void privlib(void); +static word publicise(word); +static word rc_read(char*); +static void rc_write(void); +static int src_update(void); +static void stdlib(void); +static char *strvers(int); +static int twidth(void); +static void undump(char*); +static int utf8test(void); +static void unfixexports(void); +static void unlinkx(char*); +static void unload(void); +static void v_info(int); +static void xschars(void); + +char *editor=NULL; +word okprel=0; /* set to 1 when prelude loaded */ +word nostdenv=0; /* if set to 1 mira does not load stdenv at startup */ +/* to allow a NOSTDENV directive _in_the_script_ we would need to + (i) replace isltmess() test in rules by eg is this a list of thing, + where thing is algebraic type originally defined in STDENV + (ii) arrange to pick up <stdenv> when current script not loaded + not implemented */ +word baded=0; /* see fixeditor() */ +char *miralib=NULL; +char *mirahdr,*lmirahdr; +char *promptstr="Miranda "; +char *obsuffix="x"; +FILE *s_in=NULL; +word commandmode=0; /* true only when reading command-level expressions */ +int atobject=0,atgc=0,atcount=0,debug=0; +word magic=0; /* set to 1 means script will start with UNIX magic string */ +word making=0; /* set only for mira -make */ +word mkexports=0; /* set only for mira -exports */ +word mksources=0; /* set only for mira -sources */ +word make_status=0; /* exit status of -make */ +int compiling=1; +/* there are two types of MIRANDA process - compiling (the main process) and +subsidiary processes launched for each evaluation - the above flag tells +us which kind of process we are in */ +int ideep=0; /* depth of %include we are at, see mkincludes() */ +word SYNERR=0; +word initialising=1; +word primenv=NIL; +char *current_script; +word lastexp=UNDEF; /* value of `$$' */ +word echoing=0,listing=0,verbosity; +word strictif=1,rechecking=0; +word errline=0; /* records position of last error, for editor */ +word errs=0; /* secondary error location, in inserted script, if relevant */ +word *cstack; +extern word c; +extern char *dicp,*dicq; +char linebuf[BUFSIZE]; /* used for assorted purposes */ + /* NB cannot share with linebuf in lex.c, or !! goes wrong */ +static char ebuf[pnlim]; +word col; +char home_rc[pnlim+8]; +char lib_rc[pnlim+8]; +char *rc_error=NULL; +#define badval(x) (x<1||x>478000000) + +#include <setjmp.h> /* for longjmp() - see man (3) setjmp */ +jmp_buf env; + +#ifdef sparc8 +#include <ieeefp.h> +fp_except commonmask = FP_X_INV|FP_X_OFL|FP_X_DZ; /* invalid|ovflo|divzero */ +#endif + +int main(argc,argv) /* system initialisation, followed by call to YACC */ +int argc; +char *argv[]; +{ word manonly=0; + char *home, *prs; + int okhome_rc; /* flags valid HOME/.mirarc file present */ + char *argv0=argv[0]; + char *initscript; + int badlib=0; + extern int ARGC; extern char **ARGV; + extern word newtyps,algshfns; + char *progname=rindex(argv[0],'/'); + cstack= &manonly; +/* used to indicate the base of the C stack for garbage collection purposes */ + verbosity=isatty(0); +/*if(isatty(1))*/ setbuf(stdout,NULL); /* for unbuffered tty output */ + if(home=getenv("HOME")) + { strcpy(home_rc,home); + if(strcmp(home_rc,"/")==0)home_rc[0]=0; /* root is special case */ + strcat(home_rc,"/.mirarc"); + okhome_rc=rc_read(home_rc); } +/*setup policy: + if valid HOME/.mirarc found look no further, otherwise try + <miralib>/.mirarc + Complaints - if any .mirarc contained bad data, `announce' complains about + the last such looked at. */ + UTF8OUT=UTF8=utf8test(); + while(argc>1&&argv[1][0]=='-') /* strip off flags */ + { if(strcmp(argv[1],"-stdenv")==0)nostdenv=1; else + if(strcmp(argv[1],"-count")==0)atcount=1; else + if(strcmp(argv[1],"-list")==0)listing=1; else + if(strcmp(argv[1],"-nolist")==0)listing=0; else + if(strcmp(argv[1],"-nostrictif")==0)strictif=0; else + if(strcmp(argv[1],"-gc")==0)atgc=1; else + if(strcmp(argv[1],"-object")==0)atobject=1; else + if(strcmp(argv[1],"-lib")==0) + { argc--,argv++; + if(argc==1)missparam("lib"); else miralib=argv[1]; + } else + if(strcmp(argv[1],"-dic")==0) + { argc--,argv++; + if(argc==1)missparam("dic"); else + if(sscanf(argv[1],"%ld",&DICSPACE)!=1||badval(DICSPACE)) + fprintf(stderr,"mira: bad value after flag \"-dic\"\n"),exit(1); + } else + if(strcmp(argv[1],"-heap")==0) + { argc--,argv++; + if(argc==1)missparam("heap"); else + if(sscanf(argv[1],"%ld",&SPACELIMIT)!=1||badval(SPACELIMIT)) + fprintf(stderr,"mira: bad value after flag \"-heap\"\n"),exit(1); + } else + if(strcmp(argv[1],"-editor")==0) + { argc--,argv++; + if(argc==1)missparam("editor"); + else editor=argv[1],fixeditor(); + } else + if(strcmp(argv[1],"-hush")==0)verbosity=0; else + if(strcmp(argv[1],"-nohush")==0)verbosity=1; else + if(strcmp(argv[1],"-exp")==0||strcmp(argv[1],"-log")==0) + fprintf(stderr,"mira: obsolete flag \"%s\"\n" + "use \"-exec\" or \"-exec2\", see manual\n", + argv[1]),exit(1); else + if(strcmp(argv[1],"-exec")==0) /* replaces -exp 26.11.2019 */ + ARGC=argc-2,ARGV=argv+2,magic=1,verbosity=0; else + if(strcmp(argv[1],"-exec2")==0) /* version of -exec for debugging CGI scripts */ + { if(argc<=2)fprintf(stderr,"incorrect use of -exec2 flag, missing filename\n"),exit(1); + char *logfilname, *p=strrchr(argv[2],'/'); + FILE *fil=NULL; + if(!p)p=argv[2]; /* p now holds last component of prog name */ + if(logfilname=malloc((strlen(p)+9))) + sprintf(logfilname,"miralog/%s",p), + fil=fopen(logfilname,"a"); + else mallocfail("logfile name"); + /* process requires write permission on local directory "miralog" */ + if(fil)dup2(fileno(fil),2); /* redirect stderr to log file */ + else fprintf(stderr,"could not open %s\n",logfilname); + ARGC=argc-2,ARGV=argv+2,magic=1,verbosity=0; } else + if(strcmp(argv[1],"-man")==0){ manonly=1; break; } else + if(strcmp(argv[1],"-version")==0)v_info(0),exit(0); else + if(strcmp(argv[1],"-V")==0)v_info(1),exit(0); else + if(strcmp(argv[1],"-make")==0) making=1,verbosity=0; else + if(strcmp(argv[1],"-exports")==0) making=mkexports=1,verbosity=0; else + if(strcmp(argv[1],"-sources")==0) making=mksources=1,verbosity=0; else + if(strcmp(argv[1],"-UTF-8")==0) UTF8=1; else + if(strcmp(argv[1],"-noUTF-8")==0) UTF8=0; else + fprintf(stderr,"mira: unknown flag \"%s\"\n",argv[1]),exit(1); + argc--,argv++; } + if(argc>2&&!magic&&!making)fprintf(stderr,"mira: too many args\n"),exit(1); + if(!miralib) /* no -lib flag */ + { char *m; + /* note search order */ + if((m=getenv("MIRALIB")))miralib=m; else + if(checkversion(m="/usr/lib/miralib"))miralib=m; else + if(checkversion(m="/usr/local/lib/miralib"))miralib=m; else + if(checkversion(m="miralib"))miralib=m; else + badlib=1; + } + if(badlib) + { fprintf(stderr,"fatal error: miralib version %s not found\n", + strvers(version)); + libfails(); + exit(1); + } + if(!okhome_rc) + { if(rc_error==lib_rc)rc_error=NULL; + (void)strcpy(lib_rc,miralib); + (void)strcat(lib_rc,"/.mirarc"); + rc_read(lib_rc); } + if(editor==NULL) /* .mirarc was absent or unreadable */ + { editor=getenv("EDITOR"); + if(editor==NULL)editor=EDITOR; + else strcpy(ebuf,editor),editor=ebuf,fixeditor(); } + if(prs=getenv("MIRAPROMPT"))promptstr=prs; + if(getenv("RECHECKMIRA")&&!rechecking)rechecking=1; + if(getenv("NOSTRICTIF"))strictif=0; + setupdic(); /* used by mkabsolute */ + s_in=stdin; + s_out=stdout; + miralib=mkabsolute(miralib); /* protection against "/cd" */ + if(manonly)manaction(),exit(0); + (void)strcpy(PRELUDE,miralib); (void)strcat(PRELUDE,"/prelude"); + /* convention - change spelling of "prelude" at each release */ + (void)strcpy(STDENV,miralib); + (void)strcat(STDENV,"/stdenv.m"); + mira_setup(); + if(verbosity)announce(); + files=NIL; + undump(PRELUDE),okprel=1; + mkprivate(fil_defs(hd[files])); + files=NIL; /* don't wish unload() to unsetids on prelude */ + if(!nostdenv) + { undump(STDENV); + while(files!=NIL) /* stdenv may have %include structure */ + primenv=alfasort(append1(primenv,fil_defs(hd[files]))), + files=tl[files]; + primenv=alfasort(primenv); + newtyps=files=NIL; /* don't wish unload() to unsetids */ } + if(!magic)rc_write(); + echoing = verbosity&listing; + initialising=0; + if(mkexports) + { /* making=1, to say if recompiling, also to undump as for %include */ + word f,argcount=argc-1; + extern word exports,freeids; + char *s; + setjmp(env); /* will return here on blankerr (via reset) */ + while(--argc) /* where do error messages go?? */ + { word x=NIL; + s=addextn(1,*++argv); + if(s==dicp)keep(dicp); + undump(s); /* bug, recompile messages goto stdout - FIX LATER */ + if(files==NIL||ND!=NIL)continue; + if(argcount!=1)printf("%s\n",s); + if(exports!=NIL)x=exports; + /* true (if ever) only if just recompiled */ + else for(f=files;f!=NIL;f=tl[f])x=append1(fil_defs(hd[f]),x); + /* method very clumsy, because exports not saved in dump */ + if(freeids!=NIL) + { word f=freeids; + while(f!=NIL) + { word n=findid((char *)hd[hd[tl[hd[f]]]]); + id_type(n)=tl[tl[hd[f]]]; + id_val(n)=the_val(hd[hd[f]]); + hd[f]=n; + f=tl[f]; } + f=freeids=typesfirst(freeids); + printf("\t%%free {\n"); + while(f!=NIL) + putchar('\t'), + report_type(hd[f]), + putchar('\n'), + f=tl[f]; + printf("\t}\n"); } + for(x=typesfirst(alfasort(x));x!=NIL;x=tl[x]) + { putchar('\t'); + report_type(hd[x]); + putchar('\n'); } } + exit(0); } + if(mksources){ extern word oldfiles; + char *s; + word f,x=NIL; + setjmp(env); /* will return here on blankerr (via reset) */ + while(--argc) + if(stat((s=addextn(1,*++argv)),&buf)==0) + { if(s==dicp)keep(dicp); + undump(s); + for(f=files==NIL?oldfiles:files;f!=NIL;f=tl[f]) + if(!member(x,(word)get_fil(hd[f]))) + x=cons((word)get_fil(hd[f]),x), + printf("%s\n",get_fil(hd[f])); + } + exit(0); } + if(making){ extern word oldfiles; + char *s; + setjmp(env); /* will return here on blankerr (via reset) */ + while(--argc) /* where do error messages go?? */ + { s=addextn(1,*++argv); + if(s==dicp)keep(dicp); + undump(s); + if(ND!=NIL||files==NIL&&oldfiles!=NIL) + { if(make_status==1)make_status=0; + make_status=strcons(s,make_status); } + /* keep list of source files with error-dumps */ + } + if(tag[make_status]==STRCONS) + { word h=0,maxw=0,w,n; + printf("errors or undefined names found in:-\n"); + while(make_status) /* reverse to get original order */ + { h=strcons(hd[make_status],h); + w=strlen((char *)hd[h]); + if(w>maxw)maxw=w; + make_status=tl[make_status]; } + maxw++;n=78/maxw;w=0; + while(h) + printf("%*s%s",(int)maxw,(char *)hd[h],(++w%n)?"":"\n"), + h=tl[h]; + if(w%n)printf("\n"); + make_status=1; } + exit(make_status); } + initscript= argc==1?"script.m":magic?argv[1]:addextn(1,argv[1]); + if(initscript==dicp)keep(dicp); +#if sparc8 + fpsetmask(commonmask); +#elif defined sparc + ieee_handler("set","common",(sighandler)fpe_error); +#endif +#if !defined sparc | sparc8 + (void)signal(SIGFPE,(sighandler)fpe_error); /* catch arithmetic overflow */ +#endif + (void)signal(SIGTERM,(sighandler)exit); /* flush buffers if killed */ + commandloop(initscript); + /* parameter is file given as argument */ +} + +int vstack[4]; /* record of miralib versions looked at */ +char *mstack[4]; /* and where found */ +int mvp=0; + +int checkversion(m) +/* returns 1 iff m is directory with .version containing our version number */ +char *m; +{ int v1,read=0,r=0; + FILE *f=fopen(strcat(strcpy(linebuf,m),"/.version"),"r"); + if(f&&fscanf(f,"%u",&v1)==1)r= v1==version, read=1; + if(f)fclose(f); + if(read&&!r)mstack[mvp]=m,vstack[mvp++]=v1; + return r; +} + +void libfails() +{ word i=0; + fprintf(stderr,"found"); + for(;i<mvp;i++)fprintf(stderr,"\tversion %s at: %s\n", + strvers(vstack[i]),mstack[i]); +} + +char *strvers(v) +int v; +{ static char vbuf[12]; + if(v<0||v>999999)return "\?\?\?"; + snprintf(vbuf,12,"%.3f",v/1000.0); + return vbuf; +} + +char *mkabsolute(m) /* make sure m is an absolute pathname */ +char *m; +{ if(m[0]=='/')return(m); + if(!getcwd(dicp,pnlim))fprintf(stderr,"panic: cwd too long\n"),exit(1); + (void)strcat(dicp,"/"); + (void)strcat(dicp,m); + m=dicp; + dicp=dicq+=strlen(dicp)+1; + dic_check(); + return(m); +} + +void missparam(s) +char *s; +{ fprintf(stderr,"mira: missing param after flag \"-%s\"\n",s); + exit(1); } + +int oldversion=0; +#define colmax 400 +#define spaces(s) for(j=s;j>0;j--)putchar(' ') + +void announce() +{ extern char *vdate; + word w,j; +/*clrscr(); /* clear screen on start up */ + w=(twidth()-50)/2; + printf("\n\n"); + spaces(w); printf(" T h e M i r a n d a S y s t e m\n\n"); + spaces(w+5-strlen(vdate)/2); + printf(" version %s last revised %s\n\n",strvers(version),vdate); + spaces(w); printf("Copyright Research Software Ltd 1985-2020\n\n"); + spaces(w); printf(" World Wide Web: http://miranda.org.uk\n\n\n"); + if(SPACELIMIT!=DFLTSPACE) + printf("(%ld cells)\n",SPACELIMIT); + if(!strictif)printf("(-nostrictif : deprecated!)\n"); +/*printf("\t\t\t\t%dbit platform\n",__WORDSIZE); /* temporary */ + if(oldversion<1999) /* pre release two */ + printf("\ +WARNING:\n\ +a new release of Miranda has been installed since you last used\n\ +the system - please read the `CHANGES' section of the /man pages !!!\n\n"); + else + if(version>oldversion) + printf("a new version of Miranda has been installed since you last\n"), + printf("used the system - see under `CHANGES' in the /man pages\n\n"); + if(version<oldversion) + printf("warning - this is an older version of Miranda than the one\n"), + printf("you last used on this machine!!\n\n"); + if(rc_error) + printf("warning: \"%s\" contained bad data (ignored)\n",rc_error); +} + + +word rc_read(rcfile) /* get settings of system parameters from setup file */ +char *rcfile; +{ FILE *in; + char z[20]; + word h,d,v,s,r=0; + oldversion=version; /* default assumption */ + in=fopen(rcfile,"r"); + if(in==NULL||fscanf(in,"%19s",z)!=1) + return(0); /* file not present, or not readable */ + if(strncmp(z,"hdve",4)==0 /* current .mirarc format */ + ||strcmp(z,"lhdve")==0) /* alternative format used at release one */ + { char *z1 = &z[3]; + if(z[0]=='l')listing=1,z1++; + while(*++z1)if(*z1=='l')listing=1; else + if(*z1=='s') /* ignore */; else + if(*z1=='r')rechecking=2; else + rc_error=rcfile; + if(fscanf(in,"%ld%ld%ld%*c",&h,&d,&v)!=3||!getln(in,pnlim-1,ebuf) + ||badval(h)||badval(d)||badval(v))rc_error=rcfile; + else editor=ebuf,SPACELIMIT=h,DICSPACE=d,r=1, + oldversion=v; } else + if(strcmp(z,"ehdsv")==0) /* versions before 550 */ + { if(fscanf(in,"%19s%ld%ld%ld%ld",ebuf,&h,&d,&s,&v)!=5 + ||badval(h)||badval(d)||badval(v))rc_error=rcfile; + else editor=ebuf,SPACELIMIT=h,DICSPACE=d,r=1, + oldversion=v; } else + if(strcmp(z,"ehds")==0) /* versions before 326, "s" was stacklimit (ignore) */ + { if(fscanf(in,"%s%ld%ld%ld",ebuf,&h,&d,&s)!=4 + ||badval(h)||badval(d))rc_error=rcfile; + else editor=ebuf,SPACELIMIT=h,DICSPACE=d,r=1, + oldversion=1; } + else rc_error=rcfile; /* unrecognised format */ + if(editor)fixeditor(); + fclose(in); + return(r); +} + +void fixeditor() +{ if(strcmp(editor,"vi")==0)editor="vi +!"; else + if(strcmp(editor,"pico")==0)editor="pico +!"; else + if(strcmp(editor,"nano")==0)editor="nano +!"; else + if(strcmp(editor,"joe")==0)editor="joe +!"; else + if(strcmp(editor,"jpico")==0)editor="jpico +!"; else + if(strcmp(editor,"vim")==0)editor="vim +!"; else + if(strcmp(editor,"gvim")==0)editor="gvim +! % &"; else + if(strcmp(editor,"emacs")==0)editor="emacs +! % &"; + else { char *p=rindex(editor,'/'); + if(p==0)p=editor; else p++; + if(strcmp(p,"vi")==0)strcat(p," +!"); + } + if(rindex(editor,'&'))rechecking=2; + listing=badeditor(); +} + +int badeditor() /* does editor know how to open file at line? */ +{ char *p=index(editor,'!'); + while(p&&p[-1]=='\\')p=index(p+1,'!'); + return (baded = !p); +} + +int getln(in,n,s) /* reads line (<=n chars) from in into s - returns 1 if ok */ +FILE *in; /* the newline is discarded, and the result '\0' terminated */ +word n; +char *s; +{ while(n--&&(*s=getc(in))!='\n')s++; + if(*s!='\n'||n<0)return(0); + *s='\0'; + return(1); +} + +void rc_write() +{ FILE *out=fopen(home_rc,"w"); + if(out==NULL) + { fprintf(stderr,"warning: cannot write to \"%s\"\n",home_rc); + return; } + fprintf(out,"hdve"); + if(listing)fputc('l',out); + if(rechecking==2)fputc('r',out); + fprintf(out," %ld %ld %ld %s\n",SPACELIMIT,DICSPACE,version,editor); + fclose(out); +} + +word lastid=0; /* first inscope identifier of immediately preceding command */ +word rv_expr=0; + +void commandloop(initscript) +char* initscript; +{ int ch; + void reset(); + extern word cook_stdin; + extern void obey(word); + char *lb; + if(setjmp(env)==0) /* returns here if interrupted, 0 means first time thru */ + { if(magic){ undump(initscript); /* was loadfile() changed 26.11.2019 + to allow dump of magic scripts in ".m"*/ + if(files==NIL||ND!=NIL||id_val(main_id)==UNDEF) + /* files==NIL=>script absent or has syntax errors + ND!=NIL=>script has type errors or undefined names + all reported by undump() or loadfile() on new compile */ + { if(files!=NIL&&ND==NIL&&id_val(main_id)==UNDEF) + fprintf(stderr,"%s: main not defined\n",initscript); + fprintf(stderr,"mira: incorrect use of \"-exec\" flag\n"); + exit(1); } + magic=0; obey(main_id); exit(0); } + /* was obey(lastexp), change to magic scripts 19.11.2013 */ + (void)signal(SIGINT,(sighandler)reset); + undump(initscript); + if(verbosity)printf("for help type /h\n"); } + for(;;) + { resetgcstats(); + if(verbosity)printf("%s",promptstr); + ch = getchar(); + if(rechecking&&src_update())loadfile(current_script); + /* modified behaviour for `2-window' mode */ + while(ch==' '||ch=='\t')ch=getchar(); + switch(ch) + { case '?': ch=getchar(); + if(ch=='?') + { word x; char *aka=NULL; + if(!token()&&!lastid) + { printf("\7identifier needed after `\?\?'\n"); + ch=getchar(); /* '\n' */ + break; } + if(getchar()!='\n'){ xschars(); break; } + if(baded){ ed_warn(); break; } + if(dicp[0])x=findid(dicp); + else printf("??%s\n",get_id(lastid)),x=lastid; + if(x==NIL||id_type(x)==undef_t) + { diagnose(dicp[0]?dicp:get_id(lastid)); + lastid=0; + break; } + if(id_who(x)==NIL) + { /* nb - primitives have NIL who field */ + printf("%s -- primitive to Miranda\n", + dicp[0]?dicp:get_id(lastid)); + lastid=0; + break; } + lastid=x; + x=id_who(x); /* get here info */ + if(tag[x]==CONS)aka=(char *)hd[hd[x]],x=tl[x]; + if(aka)printf("originally defined as \"%s\"\n", + aka); + editfile((char *)hd[x],tl[x]); + break; } + ungetc(ch,stdin); + (void)token(); + lastid=0; + if(dicp[0]=='\0') + { if(getchar()!='\n')xschars(); + else allnamescom(); + break; } + while(dicp[0])finger(dicp),(void)token(); + ch=getchar(); + break; + case ':': /* add (silently) as kindness to Hugs users */ + case '/': (void)token(); + lastid=0; + command(); + break; + case '!': if(!(lb=rdline()))break; /* rdline returns NULL on failure */ + lastid=0; + if(*lb) + { /*system(lb); */ /* always gives /bin/sh */ + static char *shell=NULL; + sighandler oldsig; + word pid; + if(!shell) + { shell=getenv("SHELL"); + if(!shell)shell="/bin/sh"; } + oldsig= signal(SIGINT,SIG_IGN); + if(pid=fork()) + { /* parent */ + if(pid==-1) + perror("UNIX error - cannot create process"); + while(pid!=wait(0)); + (void)signal(SIGINT,oldsig); } + else execl(shell,shell,"-c",lb,(char *)0); + if(src_update())loadfile(current_script); } + else printf( + "No previous shell command to substitute for \"!\"\n"); + break; + case '|': /* lines beginning "||" are comments */ + if((ch=getchar())!='|') + printf("\7unknown command - type /h for help\n"); + while(ch!='\n'&&ch!=EOF)ch=getchar(); + case '\n': break; + case EOF: if(verbosity)printf("\nmiranda logout\n"); + exit(0); + default: ungetc(ch,stdin); + lastid=0; + tl[hd[cook_stdin]]=0; /* unset type of $+ */ + rv_expr=0; + c = EVAL; + echoing=0; + polyshowerror=0; /* gets set by wrong use of $+, readvals */ + commandmode=1; + yyparse(); + if(SYNERR)SYNERR=0; + else if(c!='\n') /* APPARENTLY NEVER TRUE */ + { printf("syntax error\n"); + while(c!='\n'&&c!=EOF) + c=getchar(); /* swallow syntax errors */ + } + commandmode=0; + echoing=verbosity&listing; +}}} + +word parseline(t,f,fil) /* parses next valid line of f at type t, returns EOF + if none found. See READVALS in reduce.c */ +word t; +FILE *f; +word fil; +{ word t1,ch; + lastexp=UNDEF; + for(;;) + { ch=getc(f); + while(ch==' '||ch=='\t'||ch=='\n')ch=getc(f); + if(ch=='|') + { ch=getc(f); + if(ch=='|') /* leading comment */ + { while((ch=getc(f))!='\n'&&ch!=EOF); + if(ch!=EOF)continue; } + else ungetc(ch,f); } + if(ch==EOF)return(EOF); + ungetc(ch,f); + c = VALUE; + echoing=0; + commandmode=1; + s_in=f; + yyparse(); + s_in=stdin; + if(SYNERR)SYNERR=0,lastexp=UNDEF; else + if((t1=type_of(lastexp))==wrong_t)lastexp=UNDEF; else + if(!subsumes(instantiate(t1),t)) + { printf("data has wrong type :: "), out_type(t1), + printf("\nshould be :: "), out_type(t), putc('\n',stdout); + lastexp=UNDEF; } + if(lastexp!=UNDEF)return(codegen(lastexp)); + if(isatty(fileno(f)))printf("please re-enter data:\n"); + else { if(fil)fprintf(stderr,"readvals: bad data in file \"%s\"\n", + getstring(fil,0)); + else fprintf(stderr,"bad data in $+ input\n"); + outstats(); exit(1); } +}} + +void ed_warn() +{ printf( +"The currently installed editor command, \"%s\", does not\n\ +include a facility for opening a file at a specified line number. As a\n\ +result the `\?\?' command and certain other features of the Miranda system\n\ +are disabled. See manual section 31/5 on changing the editor for more\n\ +information.\n",editor); +} + +word fm_time(f) /* time last modified of file f */ +char *f; +{ return(stat(f,&buf)==0?buf.st_mtime:0); + /* non-existent file has conventional mtime of 0 */ +} /* we assume time_t can be stored in a word */ + +#define same_file(x,y) (hd[fil_inodev(x)]==hd[fil_inodev(y)]&& \ + tl[fil_inodev(x)]==tl[fil_inodev(y)]) +#define inodev(f) (stat(f,&buf)==0?datapair(buf.st_ino,buf.st_dev):\ + datapair(0,-1)) + +word oldfiles=NIL; /* most recent set of sources, in case of interrupted or + failed compilation */ +int src_update() /* any sources modified ? */ +{ word ft,f=files==NIL?oldfiles:files; + while(f!=NIL) + { if((ft=fm_time(get_fil(hd[f])))!=fil_time(hd[f])) + { if(ft==0)unlinkx(get_fil(hd[f])); /* tidy up after eg `!rm %' */ + return(1); } + f=tl[f]; } + return(0); +} + +int loading; +char *unlinkme; /* if set, is name of partially created obfile */ + +void reset() /* interrupt catcher - see call to signal in commandloop */ +{ extern word lineptr,ATNAMES,current_id; + extern int blankerr,collecting; + /*if(!making) /* see note below + (void)signal(SIGINT,SIG_IGN); /* dont interrupt me while I'm tidying up */ +/*if(magic)exit(0); *//* signal now not set to reset in magic scripts */ + if(collecting)gcpatch(); + if(loading) + { if(!blankerr) + printf("\n<<compilation interrupted>>\n"); + if(unlinkme)unlink(unlinkme); + /* stackp=dstack; /* add if undump() made interruptible later*/ + oldfiles=files,unload(),current_id=ATNAMES=loading=SYNERR=lineptr=0; + if(blankerr)blankerr=0,makedump(); } + /* magic script cannot be literate so no guard needed on makedump */ + else printf("<<interrupt>>\n"); /* VAX, SUN, ^C does not cause newline */ + reset_state(); /* see LEX */ + if(collecting)collecting=0,gc(); /* to mark stdenv etc as wanted */ + if(making&&!make_status)make_status=1; +#ifdef SYSTEM5 + else (void)signal(SIGINT,(sighandler)reset);/*ready for next interrupt*//*see note*/ +#endif + /* during mira -make blankerr is only use of reset */ + longjmp(env,1); +}/* under BSD and Linux installed signal remains installed after interrupt + and further signals blocked until handler returns */ + +#define checkeol if(getchar()!='\n')break; + +int lose; + +int normal(f) /* s has ".m" suffix */ +char *f; +{ int n=strlen(f); + return n>=2&&strcmp(f+n-2,".m")==0; +} + +void v_info(int full) +{ printf("%s last revised %s\n",strvers(version),vdate); + if(!full)return; + printf("%s",host); + printf("XVERSION %u\n",XVERSION); +} + +void command() +{ char *t; + int ch,ch1; + switch(dicp[0]) + { + case 'a': if(is("a")||is("aux")) + { checkeol; +/* if(verbosity)clrscr(); */ + (void)strcpy(linebuf,miralib); + (void)strcat(linebuf,"/auxfile"); + filecopy(linebuf); + return; } + case 'c': if(is("count")) + { checkeol; atcount=1; return; } + if(is("cd")) + { char *d=token(); + if(!d)d=getenv("HOME"); + else d=addextn(0,d); + checkeol; + if(chdir(d)==-1)printf("cannot cd to %s\n",d); + else if(src_update())undump(current_script); + /* alternative: keep old script and recompute pathname + wrt new directory - LOOK INTO THIS LATER */ + return; } + case 'd': if(is("dic")) + { extern char *dic; + if(!token()) + { lose=getchar(); /* to eat \n */ + printf("%ld chars",DICSPACE); + if(DICSPACE!=DFLTDICSPACE) + printf(" (default=%ld)",DFLTDICSPACE); + printf(" %ld in use\n",(long)(dicq-dic)); + return; } + checkeol; + printf( + "sorry, cannot change size of dictionary while in use\n"); + printf( + "(/q and reinvoke with flag: mira -dic %s ... )\n",dicp); + return; } + case 'e': if(is("e")||is("edit")) + { char *mf=0; + if(t=token())t=addextn(1,t); + else t=current_script; + checkeol; + if(stat(t,&buf)) /* new file */ + { if(!lmirahdr) /* lazy initialisation */ + { dicp=dicq; + (void)strcpy(dicp,getenv("HOME")); + if(strcmp(dicp,"/")==0) + dicp[0]=0; /* root is special case */ + (void)strcat(dicp,"/.mirahdr"); + lmirahdr=dicp; + dicq=dicp=dicp+strlen(dicp)+1; } /* ovflo check? */ + if(!stat(lmirahdr,&buf))mf=lmirahdr; + if(!mf&&!mirahdr) /* lazy initialisation */ + { dicp=dicq; + (void)strcpy(dicp,miralib); + (void)strcat(dicp,"/.mirahdr"); + mirahdr=dicp; + dicq=dicp=dicp+strlen(dicp)+1; } + if(!mf&&!stat(mirahdr,&buf))mf=mirahdr; + /*if(mf)printf("mf=%s\n",mf); /* DEBUG*/ + if(mf&&t!=current_script) + { printf("open new script \"%s\"? [ny]",t); + ch1=ch=getchar(); + while(ch!='\n'&&ch!=EOF)ch=getchar(); + /*eat rest of line */ + if(ch1!='y'&&ch1!='Y')return; } + if(mf)filecp(mf,t); } + editfile(t,strcmp(t,current_script)==0?errline: + errs&&strcmp(t,(char *)hd[errs])==0?tl[errs]: + geterrlin(t)); + return; } + if(is("editor")) + { char *hold=linebuf,*h; + if(!getln(stdin,pnlim-1,hold))break; /*reject if too long*/ + if(!*hold) + { /* lose=getchar(); /* to eat newline */ + printf("%s\n",editor); + return; } + h=hold+strlen(hold); /* remove trailing white space */ + while(h[-1]==' '||h[-1]=='\t')*--h='\0'; + if(*hold=='"'||*hold=='\'') + { printf("please type name of editor without quotation marks\n"); + return; } + printf("change editor to: \"%s\"? [ny]",hold); + ch1=ch=getchar(); + while(ch!='\n'&&ch!=EOF)ch=getchar(); /* eat rest of line */ + if(ch1!='y'&&ch1!='Y') + { printf("editor not changed\n"); + return; } + (void)strcpy(ebuf,hold); + editor=ebuf; + fixeditor(); /* reads "vi" as "vi +!" etc */ + echoing=verbosity&listing; + rc_write(); + printf("editor = %s\n",editor); + return; } + case 'f': if(is("f")||is("file")) + { char *t=token(); + checkeol; + if(t)t=addextn(1,t),keep(t); + /* could get multiple copies of filename in dictionary + - FIX LATER */ + if(t)errs=errline=0; /* moved here from reset() */ + if(t)if(strcmp(t,current_script)||files==NIL&&okdump(t)) + { extern word CLASHES; + CLASHES=NIL; /* normally done by load_script */ + undump(t); /* does not always call load_script */ + if(CLASHES!=NIL)/* pathological case, recompile */ + loadfile(t); } + else loadfile(t); /* force recompilation */ + else printf("%s%s\n",current_script, + files==NIL?" (not loaded)":""); + return; } + if(is("files")) /* info about internal state, not documented */ + { word f=files; + checkeol; + for(;f!=NIL;f=tl[f]) + printf("(%s,%ld,%ld)",get_fil(hd[f]),fil_time(hd[f]), + fil_share(hd[f])),printlist("",fil_defs(hd[f])); + return; } /* DEBUG */ + if(is("find")) + { word i=0; + while(token()) + { word x=findid(dicp),y,f; + i++; + if(x!=NIL) + { char *n=get_id(x); + for(y=primenv;y!=NIL;y=tl[y]) + if(tag[hd[y]]==ID) + if(hd[y]==x||getaka(hd[y])==n) + finger(get_id(hd[y])); + for(f=files;f!=NIL;f=tl[f]) + for(y=fil_defs(hd[f]);y!=NIL;y=tl[y]) + if(tag[hd[y]]==ID) + if(hd[y]==x||getaka(hd[y])==n) + finger(get_id(hd[y])); } + } + ch=getchar(); /* '\n' */ + if(i==0)printf("\7identifier needed after `/find'\n"); + return; } + case 'g': if(is("gc")) + { checkeol; atgc=1; return; } + case 'h': if(is("h")||is("help")) + { checkeol; +/* if(verbosity)clrscr(); */ + (void)strcpy(linebuf,miralib); + (void)strcat(linebuf,"/helpfile"); + filecopy(linebuf); + return; } + if(is("heap")) + { word x; + if(!token()) + { lose=getchar(); /* to eat \n */ + printf("%ld cells",SPACELIMIT); + if(SPACELIMIT!=DFLTSPACE) + printf(" (default=%ld)",DFLTSPACE); + printf("\n"); + return; } + checkeol; + if(sscanf(dicp,"%ld",&x)!=1||badval(x)) + { printf("illegal value (heap unchanged)\n"); return; } + if(x<trueheapsize()) + printf("sorry, cannot shrink heap to %ld at this time\n",x); + else { if(x!=SPACELIMIT) + SPACELIMIT=x,resetheap(); + printf("heaplimit = %ld cells\n",SPACELIMIT), + rc_write(); } + return; } + if(is("hush")) + { checkeol; echoing=verbosity=0; return; } + case 'l': if(is("list")) + { checkeol; listing=1; echoing=verbosity&listing; + rc_write(); return; } + case 'm': if(is("m")||is("man")) + { checkeol; manaction(); return; } + if(is("miralib")) + { checkeol; printf("%s\n",miralib); return; } + case 'n': /* if(is("namebuckets")) + { int i,x; + extern word namebucket[]; + checkeol; + for(i=0;i<128;i++) + if(x=namebucket[i]) + { printf("%d:",i); + while(x) + putchar(' '),out(stdout,hd[x]),x=tl[x]; + putchar('\n'); } + return; } /* DEBUG */ + if(is("nocount")) + { checkeol; atcount=0; return; } + if(is("nogc")) + { checkeol; atgc=0; return; } + if(is("nohush")) + { checkeol; echoing=listing; verbosity=1; return; } + if(is("nolist")) + { checkeol; echoing=listing=0; rc_write(); return; } + if(is("norecheck")) + { checkeol; rechecking=0; rc_write(); return; } +/* case 'o': if(is("object")) + { checkeol; atobject=1; return; } /* now done by flag -object */ + case 'q': if(is("q")||is("quit")) + { checkeol; if(verbosity)printf("miranda logout\n"); exit(0); } + case 'r': if(is("recheck")) + { checkeol; rechecking=2; rc_write(); return; } + case 's': if(is("s")||is("settings")) + { checkeol; + printf("*\theap %ld\n",SPACELIMIT); + printf("*\tdic %ld\n",DICSPACE); + printf("*\teditor = %s\n",editor); + printf("*\t%slist\n",listing?"":"no"); + printf("*\t%srecheck\n",rechecking?"":"no"); + if(!strictif) + printf("\t-nostrictif (deprecated!)\n"); + if(atcount)printf("\tcount\n"); + if(atgc)printf("\tgc\n"); + if(UTF8)printf("\tUTF-8 i/o\n"); + if(!verbosity)printf("\thush\n"); + if(debug)printf("\tdebug 0%o\n",debug); + printf("\n* items remembered between sessions\n"); + return; } + case 'v': if(is("v")||is("version")) + { checkeol; + v_info(0); + return; } + case 'V': if(is("V")) + { checkeol; + v_info(1); + return; } + default: printf("\7unknown command \"%c%s\"\n",(int)c,dicp); + printf("type /h for help\n"); + while((ch=getchar())!='\n'&&ch!=EOF); + return; + } /* end of switch statement */ + xschars(); +} + +void manaction() +{ sprintf(linebuf,"\"%s/menudriver\" \"%s/manual\"",miralib,miralib); + system(linebuf); +} /* put quotes around both pathnames to allow for spaces in miralib 8.5.06 */ + +void editfile(t,line) +char *t; +int line; +{ char *ebuf=linebuf; + char *p=ebuf,*q=editor; + int tdone=0; + if(line==0)line=1; /* avoids warnings in some versions of vi */ + while(*p++ = *q++) + if(p[-1]=='\\'&&(q[0]=='!'||q[0]=='%'))p[-1]= *q++; else + if(p[-1]=='!') + (void) + sprintf(p-1,"%d",line), + p+=strlen(p); else + if(p[-1]=='%')p[-1]='"',*p='\0', /* quote filename 9.5.06 */ + (void)strncat(p,t,BUFSIZE+ebuf-p), + p+=strlen(p), + *p++ = '"',*p='\0', + tdone=1; + if(!tdone) + p[-1] = ' ', + *p++ = '"',*p='\0', /* quote filename 9.5.06 */ + (void)strncat(p,t,BUFSIZE+ebuf-p), + p+=strlen(p), + *p++ = '"',*p='\0'; + /* printf("%s\n",ebuf); /* DEBUG */ + system(ebuf); + if(src_update())loadfile(current_script); + return; +} + +void xschars() +{ word ch; + printf("\7extra characters at end of command\n"); + while((ch=getchar())!='\n'&&ch!=EOF); +} + +word reverse(x) /* x is a cons list */ +word x; +{ word y = NIL; + while(x!=NIL)y = cons(hd[x],y), x = tl[x]; + return(y); +} + +word shunt(x,y) /* equivalent to append(reverse(x),y) */ +word x,y; +{ while(x!=NIL)y = cons(hd[x],y), x = tl[x]; + return(y); +} + +char *presym[] = + {"abstype","div","if","mod","otherwise","readvals","show","type","where", + "with", 0}; +int presym_n[] = + { 21, 8, 15, 8, 15, 31, 23, 22, 15, + 21 }; + +#include <ctype.h> + +void filequote(p) /* write p to stdout with <quotes> if appropriate */ +char *p; /* p is a pathname */ +{ static int mlen=0; + if(!mlen)mlen=(rindex(PRELUDE,'/')-PRELUDE)+1; + if(strncmp(p,PRELUDE,mlen)==0) + printf("<%s>",p+mlen); + else printf("\"%s\"",p); +} /* PRELUDE is a convenient string with the miralib prefix */ + +void finger(n) /* find info about name stored at dicp */ +char *n; +{ word x; int line; + char *s; + x=findid(n); + if(x!=NIL&&id_type(x)!=undef_t) + { if(id_who(x)!=NIL) + s=(char *)hd[line=get_here(x)],line=tl[line]; + if(!lastid)lastid=x; + report_type(x); + if(id_who(x)==NIL)printf(" ||primitive to Miranda\n"); + else { char *aka=getaka(x); + if(aka==get_id(x))aka=NULL; /* don't report alias to self */ + if(id_val(x)==UNDEF&&id_type(x)!=wrong_t) + printf(" ||(UNDEFINED) specified in "); else + if(id_val(x)==FREE) + printf(" ||(FREE) specified in "); else + if(id_type(x)==type_t&&t_class(x)==free_t) + printf(" ||(free type) specified in "); else + printf(" ||%sdefined in ", + id_type(x)==type_t + && t_class(x)==abstract_t?"(abstract type) ": + id_type(x)==type_t + && t_class(x)==algebraic_t?"(algebraic type) ": + id_type(x)==type_t + && t_class(x)==placeholder_t?"(placeholder type) ": + id_type(x)==type_t + && t_class(x)==synonym_t?"(synonym type) ": + ""); + filequote(s); + if(baded||rechecking)printf(" line %d",line); + if(aka)printf(" (as \"%s\")\n",aka); + else putchar('\n'); + } + if(atobject)printf("%s = ",get_id(x)), + out(stdout,id_val(x)),putchar('\n'); + return; } + diagnose(n); +} + +void diagnose(n) +char *n; +{ int i=0; + if(isalpha(n[0])) + while(n[i]&&okid(n[i]))i++; + if(n[i]){ printf("\"%s\" -- not an identifier\n",n); return; } + for(i=0;presym[i];i++) + if(strcmp(n,presym[i])==0) + { printf("%s -- keyword (see manual, section %d)\n",n,presym_n[i]); + return; } + printf("identifier \"%s\" not in scope\n",n); +} + +int sorted=0; /* flag to avoid repeatedly sorting fil_defs */ +int leftist; /* flag to alternate bias of padding in justification */ +int words[colmax]; /* max plausible size of screen */ + +void allnamescom() +{ word s; + word x=ND; + word y=x,z=0; + leftist=0; + namescom(make_fil(nostdenv?0:(word)STDENV,0,0,primenv)); + if(files==NIL)return; else s=tl[files]; + while(s!=NIL)namescom(hd[s]),s=tl[s]; + namescom(hd[files]); + sorted=1; + /* now print warnings, if any */ + /*if(ND!=NIL&&id_type(hd[ND])==type_t) + { printf("ILLEGAL EXPORT LIST - MISSING TYPENAME%s: ",tl[ND]==NIL?"":"S"); + printlist("",ND); + return; } /* install if incomplete export list is escalated to error */ + while(x!=NIL&&id_type(hd[x])==undef_t)x=tl[x]; + while(y!=NIL&&id_type(hd[y])!=undef_t)y=tl[y]; + if(x!=NIL) + { printf("WARNING, SCRIPT CONTAINS TYPE ERRORS: "); + for(;x!=NIL;x=tl[x]) + if(id_type(hd[x])!=undef_t) + { if(!z)z=1; else putchar(','); + out(stdout,hd[x]); } + printf(";\n"); } + if(y!=NIL) + { printf("%s UNDEFINED NAMES: ",z?"AND":"WARNING, SCRIPT CONTAINS"); + z=0; + for(;y!=NIL;y=tl[y]) + if(id_type(hd[y])==undef_t) + { if(!z)z=1; else putchar(','); + out(stdout,hd[y]); } + printf(";\n"); } +} +/* There are two kinds of entry in ND + undefined names: val=UNDEF, type=undef_t + type errors: val=UNDEF, type=wrong_t +*/ + +#define tolerance 3 + /* max number of extra spaces we are willing to insert */ + +void namescom(l) /* l is an element of `files' */ +word l; +{ word n=fil_defs(l),col=0,undefs=NIL,wp=0; + word scrwd = twidth(); + if(!sorted&&n!=primenv) /* primenv already sorted */ + fil_defs(l)=n=alfasort(n); /* also removes pnames */ + if(n==NIL)return; /* skip empty files */ + if(get_fil(l))filequote(get_fil(l)); + else printf("primitive:"); + printf("\n"); + while(n!=NIL) + { if(id_type(hd[n])==wrong_t||id_val(hd[n])!=UNDEF) + { word w=strlen(get_id(hd[n])); + if(col+w<scrwd)col += (col!=0); else + if(wp&&col+w>=scrwd) + { word i,r,j; + if(wp>1)i=(scrwd-col)/(wp-1),r=(scrwd-col)%(wp-1); + if(i+(r>0)>tolerance)i=r=0; + if(leftist) + for(col=0;col<wp;) + { printf("%s",get_id(words[col])); + if(++col<wp) + spaces(1+i+(r-- >0)); } + else + for(r=wp-1-r,col=0;col<wp;) + { printf("%s",get_id(words[col])); + if(++col<wp) + spaces(1+i+(r-- <=0)); } + leftist=!leftist,wp=0,col=0,putchar('\n'); } + col+=w; + words[wp++]=hd[n]; } + else undefs=cons(hd[n],undefs); /* undefined but have good types */ + n = tl[n]; } + if(wp) + for(col=0;col<wp;) + printf("%s",get_id(words[col])),putc(++col==wp?'\n':' ',stdout); + if(undefs==NIL)return; + undefs=reverse(undefs); + printlist("SPECIFIED BUT NOT DEFINED: ",undefs); +} + +word detrop=NIL; /* list of unused local definitions */ +word rfl=NIL; /* list of include components containing type orphans */ +word bereaved; /* typenames referred to in exports and not exported */ +word ld_stuff=NIL; + /* list of list of files, to be unloaded if mkincludes interrupted */ + +void loadfile(t) +char *t; +{ extern word fileq; + extern word current_id,includees,embargoes,exportfiles,freeids,exports; + extern word fnts,FBS,nextpn; + word h=NIL; /* location of %export directive, if present */ + loading=1; + errs=errline=0; + current_script=t; + oldfiles=NIL; + unload(); + if(stat(t,&buf)) + { if(initialising){ fprintf(stderr,"panic: %s not found\n",t); exit(1); } + if(verbosity)printf("new file %s\n",t); + if(magic)fprintf(stderr,"mira -exec %s%s\n",t,": no such file"),exit(1); + if(making&&ideep==0)printf("mira -make %s%s\n",t,": no such file"); + else oldfiles=cons(make_fil(t,0,0,NIL),NIL); + /* for correct record of sources */ + loading=0; + return; } + if(!openfile(t)) + { if(initialising){ fprintf(stderr,"panic: cannot open %s\n",t); exit(1); } + printf("cannot open %s\n",t); + oldfiles=cons(make_fil(t,0,0,NIL),NIL); + loading=0; + return; } + files = cons(make_fil(t,fm_time(t),1,NIL),NIL); + current_file = hd[files],tl[hd[fileq]] = current_file; + if(initialising&&strcmp(t,PRELUDE)==0)privlib(); else + if(initialising||nostdenv==1) + if(strcmp(t,STDENV)==0)stdlib(); + c = ' '; + col = 0; + s_in = (FILE *)hd[hd[fileq]]; + adjust_prefix(t); +/*if(magic&&!initialising) + { if(!(getc(s_in)=='#'&&getc(s_in)=='!')) + { files=NIL; return; } + while(getc(s_in)!='\n'); + commandmode=1; + c=MAGIC; } + else /* change to magic scripts 19.11.2013 */ + commandmode = 0; + if(verbosity||making)printf("compiling %s\n",t); + nextpn=0; /* lose pnames */ + embargoes=detrop= + fnts=rfl=bereaved=ld_stuff=exportfiles=freeids=exports=includees=FBS=NIL; + yyparse(); + if(!SYNERR&&exportfiles!=NIL) + { /* check pathnames in exportfiles have unique bindings */ + word s,i,count; + for(s=exportfiles;s!=NIL;s=tl[s]) + if(hd[s]==PLUS) /* add current script (less freeids) to exports */ + { for(i=fil_defs(hd[files]);i!=NIL;i=tl[i]) + if(isvariable(hd[i])&&!isfreeid(hd[i])) + tl[exports]=add1(hd[i],tl[exports]); + } else + /* pathnames are expanded to their contents in mkincludes */ + { for(count=0,i=includees;i!=NIL;i=tl[i]) + if(!strcmp((char *)hd[hd[hd[i]]],(char *)hd[s])) + hd[s]=hd[hd[hd[i]]]/*sharing*/,count++; + if(count!=1) + SYNERR=1, + printf("illegal fileid \"%s\" in export list (%s)\n", + (char *)hd[s], + count?"ambiguous":"not %included in script"); + } + if(SYNERR) + sayhere(hd[exports],1), + printf("compilation abandoned\n"); + } + if(!SYNERR&&includees!=NIL) + files=append1(files,mkincludes(includees)),includees=NIL; + ld_stuff=NIL; + if(!SYNERR) + { if(verbosity||making&&!mkexports&&!mksources) + printf("checking types in %s\n",t); + checktypes(); + /* printf("typecheck complete\n"); /* DEBUG */ } + if(!SYNERR&&exports!=NIL) + if(ND!=NIL)exports=NIL; else /* skip check, cannot be %included */ + { /* check exports all present and close under type info */ + word e,u=NIL,n=NIL,c=NIL; + h=hd[exports]; exports=tl[exports]; + for(e=embargoes;e!=NIL;e=tl[e]) + { if(id_type(hd[e])==undef_t)u=cons(hd[e],u),ND=add1(hd[e],ND); else + if(!member(exports,hd[e]))n=cons(hd[e],n); } + if(embargoes!=NIL) + exports=setdiff(exports,embargoes); + exports=alfasort(exports); + for(e=exports;e!=NIL;e=tl[e]) + if(id_type(hd[e])==undef_t)u=cons(hd[e],u),ND=add1(hd[e],ND); else + if(id_type(hd[e])==type_t&&t_class(hd[e])==algebraic_t) + c=shunt(t_info(hd[e]),c); /* constructors */ + if(exports==NIL)printf("warning, export list has void contents\n"); + else exports=append1(alfasort(c),exports); + if(n!=NIL) + { printf("redundant entr%s in export list:",tl[n]==NIL?"y":"ies"); + while(n!=NIL)printf(" -%s",get_id(hd[n])),n=tl[n]; n=1; /* flag */ + putchar('\n'); } + if(u!=NIL)exports=NIL, + printlist("undefined names in export list: ",u); + if(u!=NIL)sayhere(h,1),h=NIL; else + if(exports==NIL||n!=NIL)out_here(stderr,h,1),h=NIL; + /* for warnings call out_here not sayhere, so errinfo not saved in dump */ + } + if(!SYNERR&&ND==NIL&&(exports!=NIL||tl[files]!=NIL)) + { /* find out if script can create type orphans when %included */ + word e1,t; + word r=NIL; /* collect list of referenced typenames */ + word e=NIL; /* and list of exported typenames */ + if(exports!=NIL) + for(e1=exports;e1!=NIL;e1=tl[e1]) + { if((t=id_type(hd[e1]))==type_t) + if(t_class(hd[e1])==synonym_t) + r=UNION(r,deps(t_info(hd[e1]))); + else e=cons(hd[e1],e); + else r=UNION(r,deps(t)); } else + for(e1=fil_defs(hd[files]);e1!=NIL;e1=tl[e1]) + { if((t=id_type(hd[e1]))==type_t) + if(t_class(hd[e1])==synonym_t) + r=UNION(r,deps(t_info(hd[e1]))); + else e=cons(hd[e1],e); + else r=UNION(r,deps(t)); } + for(e1=freeids;e1!=NIL;e1=tl[e1]) + if((t=id_type(hd[hd[e1]]))==type_t) + if(t_class(hd[hd[e1]])==synonym_t) + r=UNION(r,deps(t_info(hd[hd[e1]]))); + else e=cons(hd[hd[e1]],e); + else r=UNION(r,deps(t)); + /*printlist("r: ",r); /* DEBUG */ + for(;r!=NIL;r=tl[r]) + if(!member(e,hd[r]))bereaved=cons(hd[r],bereaved); + /*printlist("bereaved: ",bereaved); /* DEBUG */ + } + if(exports!=NIL&&bereaved!=NIL) + { extern word newtyps; + word b=intersection(bereaved,newtyps); + /*printlist("newtyps",newtyps); /* DEBUG */ + if(b!=NIL) + /*ND=b; /* to escalate to type error, see also allnamescom */ + printf("warning, export list is incomplete - missing typename%s: ", + tl[b]==NIL?"":"s"), + printlist("",b); + if(b!=NIL&&h!=NIL)out_here(stdout,h,1); /* sayhere(h,1) for error */ + } + if(!SYNERR&&detrop!=NIL) + { word gd=detrop; + while(detrop!=NIL&&tag[dval(hd[detrop])]==LABEL)detrop=tl[detrop]; + if(detrop!=NIL) + printf("warning, script contains unused local definitions:-\n"); + while(detrop!=NIL) + { out_here(stdout,hd[hd[tl[dval(hd[detrop])]]],0), putchar('\t'); + out_pattern(stdout,dlhs(hd[detrop])), putchar('\n'); + detrop=tl[detrop]; + while(detrop!=NIL&&tag[dval(hd[detrop])]==LABEL) + detrop=tl[detrop]; } + while(gd!=NIL&&tag[dval(hd[gd])]!=LABEL)gd=tl[gd]; + if(gd!=NIL) + printf("warning, grammar contains unused nonterminals:-\n"); + while(gd!=NIL) + { out_here(stdout,hd[dval(hd[gd])],0), putchar('\t'); + out_pattern(stdout,dlhs(hd[gd])), putchar('\n'); + gd=tl[gd]; + while(gd!=NIL&&tag[dval(hd[gd])]!=LABEL)gd=tl[gd]; } + /* note, usual rhs is tries(pat,list(label(here,exp))) + grammar rhs is label(here,...) */ + } + if(!SYNERR) + { word x; extern int lfrule; + /* we invoke the code generator */ + lfrule=0; + for(x=fil_defs(hd[files]);x!=NIL;x=tl[x]) + if(id_type(hd[x])!=type_t) + { current_id=hd[x]; + polyshowerror=0; + id_val(hd[x])=codegen(id_val(hd[x])); + if(polyshowerror)id_val(hd[x])=UNDEF; + /* nb - one remaining class of typerrs trapped in codegen, + namely polymorphic show or readvals */ + } + current_id=0; + if(lfrule&&(verbosity||making)) + printf("grammar optimisation: %d common left factors found\n",lfrule); + if(initialising&&ND!=NIL) + { fprintf(stderr,"panic: %s contains errors\n",okprel?"stdenv":"prelude"); + exit(1); } + if(initialising)makedump(); else + if(normal(t)) /* file ends ".m", formerly if(!magic) */ + fixexports(),makedump(),unfixexports(); + /* changed 26.11.2019 to allow dump of magic scripts ending ".m" */ + if(!errline&&errs&&(char *)hd[errs]==current_script) + errline=tl[errs]; /* soft error (posn not saved in dump) */ + ND=alfasort(ND); + /* we could sort and remove pnames from each defs component immediately + after makedump(), instead of doing this in namescom */ + loading=0; + return; } + /* otherwise syntax error found */ + if(initialising) + { fprintf(stderr,"panic: cannot compile %s\n",okprel?"stdenv":"prelude"); exit(1); } + oldfiles=files; + unload(); + if(normal(t)&&SYNERR!=2)makedump(); /* make syntax error dump */ + /* allow dump of magic script in ".m", was if(!magic&&) 26.11.2019 */ + SYNERR=0; + loading=0; +} + +word isfreeid(x) +word x; +{ return(id_type(x)==type_t?t_class(x)==free_t:id_val(x)==FREE); } + +word internals=NIL; /* used by fix/unfixexports, list of names not exported */ +#define paint(x) id_val(x)=ap(EXPORT,id_val(x)) +#define unpainted(x) (tag[id_val(x)]!=AP||hd[id_val(x)]!=EXPORT) +#define unpaint(x) id_val(x)=tl[id_val(x)] + +void fixexports() +{ extern word exports,exportfiles,embargoes,freeids; + word e=exports,f; + /* printlist("exports: ",e); /* DEBUG */ + for(;e!=NIL;e=tl[e])paint(hd[e]); + internals=NIL; + if(exports==NIL&&exportfiles==NIL&&embargoes==NIL) /*no %export in script*/ + { for(e=freeids;e!=NIL;e=tl[e]) + internals=cons(privatise(hd[hd[e]]),internals); + for(f=tl[files];f!=NIL;f=tl[f]) + for(e=fil_defs(hd[f]);e!=NIL;e=tl[e]) + { if(tag[hd[e]]==ID) + internals=cons(privatise(hd[e]),internals); }} + else for(f=files;f!=NIL;f=tl[f]) + for(e=fil_defs(hd[f]);e!=NIL;e=tl[e]) + { if(tag[hd[e]]==ID&&unpainted(hd[e])) + internals=cons(privatise(hd[e]),internals); } + /* optimisation, need not do this to `silent' components - fix later */ + /*printlist("internals: ",internals); /* DEBUG */ + for(e=exports;e!=NIL;e=tl[e])unpaint(hd[e]); +} /* may not be interrupt safe, re unload() */ + +void unfixexports() +{ /*printlist("internals: ",internals); /* DEBUG */ + word i=internals; + if(mkexports)return; /* in this case don't want internals restored */ + while(i!=NIL) /* lose */ + publicise(hd[i]),i=tl[i]; + internals=NIL; +} /* may not be interrupt safe, re unload() */ + +word privatise(x) /* change id to pname, and return new id holding it as value */ +word x; +{ extern word namebucket[],*pnvec; + word n = make_pn(x),h=namebucket[hash(get_id(x))],i; + if(id_type(x)==type_t) + t_info(x)=cons(datapair(getaka(x),0),get_here(x)); + /* to assist identification of danging type refs - see typesharing code + in mkincludes */ + /* assumption - nothing looks at the t_info after compilation */ + if(id_val(x)==UNDEF) /* name specified but not defined */ + id_val(x)= ap(datapair(getaka(x),0),get_here(x)); + /* this will generate sensible error message on attempt to use value + see reduction rule for DATAPAIR */ + pnvec[i=hd[n]]=x; + tag[n]=ID;hd[n]=hd[x]; + tag[x]=STRCONS;hd[x]=i; + while(hd[h]!=x)h=tl[h]; + hd[h]=n; + return(n); +} /* WARNING - dependent on internal representation of ids and pnames */ +/* nasty problem - privatisation can screw AKA's */ + +word publicise(x) /* converse of the above, applied to the new id */ +word x; +{ extern word namebucket[]; + word i=id_val(x),h=namebucket[hash(get_id(x))]; + tag[i]=ID,hd[i]=hd[x]; + /* WARNING - USES FACT THAT tl HOLDS VALUE FOR BOTH ID AND PNAME */ + if(tag[tl[i]]==AP&&tag[hd[tl[i]]]==DATAPAIR) + tl[i]=UNDEF; /* undo kludge, see above */ + while(hd[h]!=x)h=tl[h]; + hd[h]=i; + return(i); +} + +int sigflag=0; + +void sigdefer() +{ /* printf("sigdefer()\n"); /* DEBUG */ + sigflag=1; } /* delayed signal handler, installed during load_script() */ + +word mkincludes(includees) +word includees; +{ extern word FBS,BAD_DUMP,CLASHES,exportfiles,exports,TORPHANS; + word pid,result=NIL,tclashes=NIL; + includees=reverse(includees); /* process in order of occurrence in script */ + if(pid=fork()) + { /* parent */ + int status; + if(pid==-1) + { perror("UNIX error - cannot create process"); /* will say why */ + if(ideep>6) /* perhaps cyclic %include */ + fprintf(stderr,"error occurs %d deep in %%include files\n",ideep); + if(ideep)exit(2); + SYNERR=2; /* special code to prevent makedump() */ + printf("compilation of \"%s\" abandoned\n",current_script); + return(NIL); } + while(pid!=wait(&status)); + if((WEXITSTATUS(status))==2) /* child aborted */ + if(ideep)exit(2); /* recursive abortion of parent process */ + else { SYNERR=2; + printf("compilation of \"%s\" abandoned\n",current_script); + return(NIL); } + /* if we get to here child completed normally, so carry on */ + } + else { /* child does equivalent of `mira -make' on each includee */ + extern word oldfiles; + (void)signal(SIGINT,SIG_DFL); /* don't trap interrupts */ + ideep++; making=1; make_status=0; echoing=listing=verbosity=magic=0; + setjmp(env); /* will return here on blankerr (via reset) */ + while(includees!=NIL&&!make_status) /* stop at first bad includee */ + { undump((char *)hd[hd[hd[includees]]]); + if(ND!=NIL||files==NIL&&oldfiles!=NIL)make_status=1; + /* any errors in dump? */ + includees=tl[includees]; + } /* obscure bug - undump above can reinvoke compiler, which + side effects compiler variable `includees' - to fix this + had to make sure child is holding local copy of includees*/ + exit(make_status); } + sigflag=0; + for(;includees!=NIL;includees=tl[includees]) + { word x=NIL; + sighandler oldsig; + FILE *f; + char *fn=(char *)hd[hd[hd[includees]]]; + extern word DETROP,MISSING,ALIASES,TSUPPRESSED; + (void)strcpy(dicp,fn); + (void)strcpy(dicp+strlen(dicp)-1,obsuffix); + if(!making) /* cannot interrupt load_script() */ + oldsig=signal(SIGINT,(sighandler)sigdefer); + if(f=fopen(dicp,"r")) + x=load_script(f,fn,hd[tl[hd[includees]]],tl[tl[hd[includees]]],0), + fclose(f); + ld_stuff=cons(x,ld_stuff); + if(!making)(void)signal(SIGINT,oldsig); + if(sigflag)sigflag=0,(* oldsig)(); /* take deferred interrupt */ + if(f&&!BAD_DUMP&&x!=NIL&&ND==NIL&&CLASHES==NIL&&ALIASES==NIL&& + TSUPPRESSED==NIL&&DETROP==NIL&&MISSING==NIL) + /* i.e. if load_script worked ok */ + { /* stuff here is to share repeated file components + issues: + Consider only includees (fil_share=1), not insertees. + Effect of sharing is to replace value fields in later copies + by (pointers to) corresponding ids in first copy - so sharing + transmitted thru dumps. It is illegal to have more than one + copy of a (non-synonym) type in the same scope, even under + different names. */ + word y,z; + /* printf("start share analysis\n"); /* DEBUG */ + if(TORPHANS)rfl=shunt(x,rfl); /* file has type orphans */ + for(y=x;y!=NIL;y=tl[y])fil_inodev(hd[y])=inodev(get_fil(hd[y])); + for(y=x;y!=NIL;y=tl[y]) + if(fil_share(hd[y])) + for(z=result;z!=NIL;z=tl[z]) + if(fil_share(hd[z])&&same_file(hd[y],hd[z]) + &&fil_time(hd[y])==fil_time(hd[z])) + { word p=fil_defs(hd[y]),q=fil_defs(hd[z]); + for(;p!=NIL&&q!=NIL;p=tl[p],q=tl[q]) + if(tag[hd[p]]==ID) + if(id_type(hd[p])==type_t&& + (tag[hd[q]]==ID||tag[pn_val(hd[q])]==ID)) + { /* typeclash - record in tclashes */ + word w=tclashes; + word orig=tag[hd[q]]==ID?hd[q]:pn_val(hd[q]); + if(t_class(hd[p])==synonym_t)continue; + while(w!=NIL&&((char *)hd[hd[w]]!=get_fil(hd[z]) + ||hd[tl[hd[w]]]!=orig)) + w=tl[w]; + if(w==NIL) + w=tclashes=cons(strcons(get_fil(hd[z]), + cons(orig,NIL)),tclashes); + tl[tl[hd[w]]]=cons(hd[p],tl[tl[hd[w]]]); + } + else the_val(hd[q])=hd[p]; + else the_val(hd[p])=hd[q]; + /*following test redundant - remove when sure is ok*/ + if(p!=NIL||q!=NIL) + fprintf(stderr,"impossible event in mkincludes\n"); + /*break; /* z loop -- NO! (see liftbug) */ + } + if(member(exportfiles,(word)fn)) + { /* move ids of x onto exports */ + for(y=x;y!=NIL;y=tl[y]) + for(z=fil_defs(hd[y]);z!=NIL;z=tl[z]) + if(isvariable(hd[z])) + tl[exports]=add1(hd[z],tl[exports]); + /* skip pnames, constructors (expanded later) */ + } + result=append1(result,x); + /* keep `result' in front-first order */ + if(hd[FBS]==NIL)FBS=tl[FBS]; + else hd[FBS]=cons(tl[hd[hd[includees]]],hd[FBS]); /* hereinfo */ + /* printf("share analysis finished\n"); /* DEBUG */ + continue; } + /* something wrong - find out what */ + if(!f)result=cons(make_fil(hd[hd[hd[includees]]], + fm_time(fn),0,NIL),result); else + if(x==NIL&&BAD_DUMP!= -2)result=append1(result,oldfiles),oldfiles=NIL; + else result=append1(result,x); + /* above for benefit of `oldfiles' */ + /* BAD_DUMP -2 is nameclashes due to aliasing */ + SYNERR=1; + printf("unsuccessful %%include directive "); + sayhere(tl[hd[hd[includees]]],1); +/* if(!f)printf("\"%s\" non-existent or unreadable\n",fn), */ + if(!f)printf("\"%s\" cannot be loaded\n",fn), + CLASHES=DETROP=MISSING=NIL; + /* just in case not cleared from a previous load_script() */ + else + if(BAD_DUMP== -2) + printlist("aliasing causes nameclashes: ",CLASHES), + CLASHES=NIL; else + if(ALIASES!=NIL||TSUPPRESSED!=NIL) + { if(ALIASES!=NIL) + printf("alias fails (name%s not found in file", + tl[ALIASES]==NIL?"":"s"), + printlist("): ",ALIASES),ALIASES=NIL; + if(TSUPPRESSED!=NIL) + { printf("illegal alias (cannot suppress typename%s):", + tl[TSUPPRESSED]==NIL?"":"s"); + while(TSUPPRESSED!=NIL) + printf(" -%s",get_id(hd[TSUPPRESSED])), + TSUPPRESSED=tl[TSUPPRESSED]; + putchar('\n'); } + /* if -typename allowed, remember to look for type orphans */ + }else + if(BAD_DUMP)printf("\"%s\" has bad data in dump file\n",fn); else + if(x==NIL)printf("\"%s\" contains syntax error\n",fn); else + if(ND!=NIL) + printf("\"%s\" contains undefined names or type errors\n",fn); + if(ND==NIL&&CLASHES!=NIL) /* can have this and failed aliasing */ + printf("\"%s\" ",fn),printlist("causes nameclashes: ",CLASHES); + while(DETROP!=NIL&&tag[hd[DETROP]]==CONS) + { word fa=hd[tl[hd[DETROP]]],ta=tl[tl[hd[DETROP]]]; + char *pn=get_id(hd[hd[DETROP]]); + if(fa== -1||ta== -1) + printf("`%s' has binding of wrong kind ",pn), + printf(fa== -1?"(should be \"= value\" not \"== type\")\n" + :"(should be \"== type\" not \"= value\")\n"); + else + printf("`%s' has == binding of wrong arity ",pn), + printf("(formal has arity %ld, actual has arity %ld)\n",fa,ta); + DETROP=tl[DETROP]; } + if(DETROP!=NIL) + printf("illegal parameter binding (name%s not %%free in file", + tl[DETROP]==NIL?"":"s"), + printlist("): ",DETROP),DETROP=NIL; + if(MISSING!=NIL) + printf("missing parameter binding%s: ",tl[MISSING]==NIL?"":"s"); + while(MISSING!=NIL) + printf("%s%s",(char *)hd[hd[MISSING]],tl[MISSING]==NIL?";\n":","), + MISSING=tl[MISSING]; + printf("compilation abandoned\n"); + stackp=dstack; /* in case of BAD_DUMP */ + return(result); } /* for unload() */ + if(tclashes!=NIL) + { printf("TYPECLASH - the following type%s multiply named:\n", + tl[tclashes]==NIL?" is":"s are"); + /* structure of tclashes is list of strcons(filname,list-of-ids) */ + for(;tclashes!=NIL;tclashes=tl[tclashes]) + { printf("\'%s\' of file \"%s\", as: ", + getaka(hd[tl[hd[tclashes]]]), + (char *)hd[hd[tclashes]]); + printlist("",alfasort(tl[hd[tclashes]])); } + printf("typecheck cannot proceed - compilation abandoned\n"); + SYNERR=1; + return(result); } /* for unload */ + return(result); +} + +word tlost=NIL; +word pfrts=NIL; /* list of private free types bound in this script */ + +void readoption() /* readopt type orphans */ +{ word f,t; + extern word TYPERRS,FBS; + pfrts=tlost=NIL; + /* exclude anonymous free types, these dealt with later by mcheckfbs() */ + if(FBS!=NIL) + for(f=FBS;f!=NIL;f=tl[f]) + for(t=tl[hd[f]];t!=NIL;t=tl[t]) + if(tag[hd[hd[t]]]==STRCONS&&tl[tl[hd[t]]]==type_t) + pfrts=cons(hd[hd[t]],pfrts); + /* this may needlessly scan `silent' files - fix later */ + for(;rfl!=NIL;rfl=tl[rfl]) + for(f=fil_defs(hd[rfl]);f!=NIL;f=tl[f]) + if(tag[hd[f]]==ID) + if((t=id_type(hd[f]))==type_t) + { if(t_class(hd[f])==synonym_t) + t_info(hd[f])=fixtype(t_info(hd[f]),hd[f]); } + else id_type(hd[f])=fixtype(t,hd[f]); + if(tlost==NIL)return; + TYPERRS++; + printf("MISSING TYPENAME%s\n",tl[tlost]==NIL?"":"S"); + printf("the following type%s no name in this scope:\n", + tl[tlost]==NIL?" is needed but has":"s are needed but have"); + /* structure of tlost is list of cons(losttype,list-of-ids) */ + for(;tlost!=NIL;tlost=tl[tlost]) + { /* printf("tinfo_tlost=");out(stdout,t_info(hd[hd[tlost]])); + putchar(';'); /*DEBUG */ + printf("\'%s\' of file \"%s\", needed by: ", + (char *)hd[hd[t_info(hd[hd[tlost]])]], + (char *)hd[tl[t_info(hd[hd[tlost]])]]); + printlist("",alfasort(tl[hd[tlost]])); } +} + +word fixtype(t,x) /* substitute out any indirected typenames in t */ +word t,x; +{ switch(tag[t]) + { case AP: + case CONS: tl[t]=fixtype(tl[t],x); + hd[t]=fixtype(hd[t],x); + default: return(t); + case STRCONS: if(member(pfrts,t))return(t); /* see jrcfree.bug */ + while(tag[pn_val(t)]!=CONS)t=pn_val(t);/*at most twice*/ + if(tag[t]!=ID) + { /* lost type - record in tlost */ + word w=tlost; + while(w!=NIL&&hd[hd[w]]!=t)w=tl[w]; + if(w==NIL) + w=tlost=cons(cons(t,cons(x,NIL)),tlost); + tl[hd[w]]=add1(x,tl[hd[w]]); + } + return(t); + } +} + +#define mask(c) (c&0xDF) +/* masks out lower case bit, which is 0x20 */ +word alfa_ls(a,b) /* 'DICTIONARY ORDER' - not currently used */ +char *a,*b; +{ while(*a&&mask(*a)==mask(*b))a++,b++; + if(mask(*a)==mask(*b))return(strcmp(a,b)<0); /* lower case before upper */ + return(mask(*a)<mask(*b)); +} + +word alfasort(x) /* also removes non_IDs from result */ +word x; +{ word a=NIL,b=NIL,hold=NIL; + if(x==NIL)return(NIL); + if(tl[x]==NIL)return(tag[hd[x]]!=ID?NIL:x); + while(x!=NIL) /* split x */ + { if(tag[hd[x]]==ID)hold=a,a=cons(hd[x],b),b=hold; + x=tl[x]; } + a=alfasort(a),b=alfasort(b); + /* now merge two halves back together */ + while(a!=NIL&&b!=NIL) + if(strcmp(get_id(hd[a]),get_id(hd[b]))<0)x=cons(hd[a],x),a=tl[a]; + else x=cons(hd[b],x),b=tl[b]; + if(a==NIL)a=b; + while(a!=NIL)x=cons(hd[a],x),a=tl[a]; + return(reverse(x)); +} + +void unsetids(d) /* d is a list of identifiers */ +word d; +{ while(d!=NIL) + { if(tag[hd[d]]==ID)id_val(hd[d])=UNDEF, + id_who(hd[d])=NIL, + id_type(hd[d])=undef_t; + d=tl[d]; } /* should we remove from namebucket ? */ +} + +void unload() /* clear out current script in preparation for reloading */ +{ extern word TABSTRS,SGC,speclocs,newtyps,rv_script,algshfns,nextpn,nolib, + includees,freeids; + word x; + sorted=0; + speclocs=NIL; + nextpn=0; /* lose pnames */ + rv_script=0; + algshfns=NIL; + unsetids(newtyps); + newtyps=NIL; + unsetids(freeids); + freeids=includees=SGC=freeids=TABSTRS=ND=NIL; + unsetids(internals); + internals=NIL; + while(files!=NIL) + { unsetids(fil_defs(hd[files])); + fil_defs(hd[files])=NIL; + files = tl[files]; } + for(;ld_stuff!=NIL;ld_stuff=tl[ld_stuff]) + for(x=hd[ld_stuff];x!=NIL;x=tl[x])unsetids(fil_defs(hd[x])); +} + +void yyerror(s) /* called by YACC in the event of a syntax error */ +char *s; +{ extern int yychar; + if(SYNERR)return; /* error already reported, so shut up */ + if(echoing)printf("\n"); + printf("%s - unexpected ",s); + if(yychar==OFFSIDE&&(c==EOF||c=='|')) + { if(c==EOF) /* special case introduced by fix for dtbug */ + printf("end of file"); else + printf("token '|'"); + /* special case introduced by sreds fix to offside rule */ + } else + { printf(yychar==0?commandmode?"newline":"end of file":"token "); + if(yychar>=256)putchar('\"'); + if(yychar!=0)out2(stdout,yychar); + if(yychar>=256)putchar('\"'); } + printf("\n"); + SYNERR=1; + reset_lex(); +} + +void syntax(s) /* called by actions after discovering a (context sensitive) syntax + error */ +char *s; +{ if(SYNERR)return; + if(echoing)printf("\n"); + printf("syntax error: %s",s); + SYNERR=1; /* this will stop YACC at its next call to yylex() */ + reset_lex(); +} + +void acterror() /* likewise, but assumes error message output by caller */ +{ if(SYNERR)return; + SYNERR=1; /* to stop YACC at next symbol */ + reset_lex(); +} + +void mira_setup() +{ extern word common_stdin,common_stdinb,cook_stdin; + setupheap(); + tsetup(); + reset_pns(); + bigsetup(); + common_stdin= ap(READ,0); + common_stdinb= ap(READBIN,0); + cook_stdin=ap(readvals(0,0),OFFSIDE); + nill= cons(CONST,NIL); + Void=make_id("()"); + id_type(Void)=void_t; + id_val(Void)=constructor(0,Void); + message=make_id("sys_message"); + main_id=make_id("main"); /* change to magic scripts 19.11.2013 */ + concat=make_id("concat"); + diagonalise=make_id("diagonalise"); + standardout=constructor(0,"Stdout"); + indent_fn=make_id("indent"); + outdent_fn=make_id("outdent"); + listdiff_fn=make_id("listdiff"); + shownum1=make_id("shownum1"); + showbool=make_id("showbool"); + showchar=make_id("showchar"); + showlist=make_id("showlist"); + showstring=make_id("showstring"); + showparen=make_id("showparen"); + showpair=make_id("showpair"); + showvoid=make_id("showvoid"); + showfunction=make_id("showfunction"); + showabstract=make_id("showabstract"); + showwhat=make_id("showwhat"); + primlib(); } /* sets up predefined ids, not referred to by rules.y */ + +void dieclean() /* called if evaluation is interrupted - see rules.y */ +{ printf("<<...interrupt>>\n"); +#ifndef NOSTATSONINT + outstats(); /* suppress in presence of segfault on ^C with /count */ +#endif + exit(0); +} + +/* the function process() creates a process and waits for it to die - + returning 1 in the child and 0 in the parent - it is used in the + evaluation command (see rules.y) */ +word process() +{ int pid; + sighandler oldsig; + oldsig = signal(SIGINT,SIG_IGN); + /* do not let parent receive interrupts intended for child */ + if(pid=fork()) + { /* parent */ + int status; /* see man 2 exit, wait, signal */ + if(pid== -1) + { perror("UNIX error - cannot create process"); + return(0); + } + while(pid!=wait(&status)); + /* low byte of status is termination state of child, next byte is the + (low order byte of the) exit status */ + if(WIFSIGNALED(status)) /* abnormal termination status */ + { char *cd=status&0200?" (core dumped)":""; + char *pc=""; /* "probably caused by stack overflow\n";*/ + switch(WTERMSIG(status)) + { case SIGBUS: printf("\n<<...bus error%s>>\n%s",cd,pc); break; + case SIGSEGV: printf("\n<<...segmentation fault%s>>\n%s",cd,pc); break; + default: printf("\n<<...uncaught signal %d>>\n",WTERMSIG(status)); + } } + /*if(status >>= 8)printf("\n(exit status %d)\n",status); */ + (void)signal(SIGINT,oldsig); /* restore interrupt status */ + return(0); } + else return(1); /* child */ +} + +/* Notice that the Miranda system has a two-level interrupt structure. + 1) Each evaluation (see rules.y) is an interruptible process. + 2) If the command loop is interrupted outside an evaluation or during + compilation it reverts to the top level prompt - see set_jmp and + signal(reset) in commandloop() */ + +void primdef(n,v,t) /* used by "primlib", see below */ +char *n; +word v,t; +{ word x; + x= make_id(n); + primenv=cons(x,primenv); + id_val(x)= v; + id_type(x)=t; } + +void predef(n,v,t) /* used by "privlib" and "stdlib", see below */ +char *n; +word v,t; +{ word x; + x= make_id(n); + addtoenv(x); + id_val(x)= isconstructor(x)?constructor(v,x):v; + id_type(x)=t; +} + +void primlib() /* called by "mira_setup", this routine enters + the primitive identifiers into the primitive environment */ +{ primdef("num",make_typ(0,0,synonym_t,num_t),type_t); + primdef("char",make_typ(0,0,synonym_t,char_t),type_t); + primdef("bool",make_typ(0,0,synonym_t,bool_t),type_t); + primdef("True",1,bool_t); /* accessible only to 'finger' */ + primdef("False",0,bool_t); /* likewise - FIX LATER */ +} + +void privlib() /* called when compiling <prelude>, adds some + internally defined identifiers to the environment */ +{ extern word ltchar; + predef("offside",OFFSIDE,ltchar); /* used by `indent' in prelude */ + predef("changetype",I,wrong_t); /* wrong_t to prevent being typechecked */ + predef("first",HD,wrong_t); + predef("rest",TL,wrong_t); +/* the following added to make prelude compilable without stdenv */ + predef("code",CODE,undef_t); + predef("concat",ap2(FOLDR,APPEND,NIL),undef_t); + predef("decode",DECODE,undef_t); + predef("drop",DROP,undef_t); + predef("error",ERROR,undef_t); + predef("filter",FILTER,undef_t); + predef("foldr",FOLDR,undef_t); + predef("hd",HD,undef_t); + predef("map",MAP,undef_t); + predef("shownum",SHOWNUM,undef_t); + predef("take",TAKE,undef_t); + predef("tl",TL,undef_t); +} + +void stdlib() /* called when compiling <stdenv>, adds some + internally defined identifiers to the environment */ +{ predef("arctan",ARCTAN_FN,undef_t); + predef("code",CODE,undef_t); + predef("cos",COS_FN,undef_t); + predef("decode",DECODE,undef_t); + predef("drop",DROP,undef_t); + predef("entier",ENTIER_FN,undef_t); + predef("error",ERROR,undef_t); + predef("exp",EXP_FN,undef_t); + predef("filemode",FILEMODE,undef_t); + predef("filestat",FILESTAT,undef_t); /* added Feb 91 */ + predef("foldl",FOLDL,undef_t); + predef("foldl1",FOLDL1,undef_t); /* new at release 2 */ + predef("hugenum",sto_dbl(DBL_MAX),undef_t); + predef("last",LIST_LAST,undef_t); + predef("foldr",FOLDR,undef_t); + predef("force",FORCE,undef_t); + predef("getenv",GETENV,undef_t); + predef("integer",INTEGER,undef_t); + predef("log",LOG_FN,undef_t); + predef("log10",LOG10_FN,undef_t); /* new at release 2 */ + predef("merge",MERGE,undef_t); /* new at release 2 */ + predef("numval",NUMVAL,undef_t); + predef("read",STARTREAD,undef_t); + predef("readb",STARTREADBIN,undef_t); + predef("seq",SEQ,undef_t); + predef("shownum",SHOWNUM,undef_t); + predef("showhex",SHOWHEX,undef_t); + predef("showoct",SHOWOCT,undef_t); + predef("showfloat",SHOWFLOAT,undef_t); /* new at release 2 */ + predef("showscaled",SHOWSCALED,undef_t); /* new at release 2 */ + predef("sin",SIN_FN,undef_t); + predef("sqrt",SQRT_FN,undef_t); + predef("system",EXEC,undef_t); /* new at release 2 */ + predef("take",TAKE,undef_t); + predef("tinynum",mktiny(),undef_t); /* new at release 2 */ + predef("zip2",ZIP,undef_t); /* new at release 2 */ +} + +word mktiny() +{ volatile + double x=1.0,x1=x/2.0; + while(x1>0.0)x=x1,x1/=2.0; + return(sto_dbl(x)); +} + +word size(x) /* measures the size of a compiled expression */ +word x; +{ word s; + s= 0; + while(tag[x]==CONS||tag[x]==AP) + { s= s+1+size(hd[x]); + x= tl[x]; } + return(s); } + +void makedump() +{ char *obf=linebuf; + FILE *f; + (void)strcpy(obf,current_script); + (void)strcpy(obf+strlen(obf)-1,obsuffix); + f=fopen(obf,"w"); + if(!f){ printf("WARNING: CANNOT WRITE TO %s\n",obf); + if(strcmp(current_script,PRELUDE)==0|| + strcmp(current_script,STDENV)==0) + printf( + "TO FIX THIS PROBLEM PLEASE GET SUPER-USER TO EXECUTE `mira'\n"); + if(making&&!make_status)make_status=1; + return; } + /* printf("dumping to %s\n",obf); /* DEBUG */ + unlinkme=obf; + /* fchmod(fileno(f),0666); /* to make dumps writeable by all */ /* no! */ + setprefix(current_script); + dump_script(files,f); + unlinkme=NULL; + fclose(f); +} + +void undump(t) /* restore t from dump, or recompile if necessary */ +char *t; +{ extern word BAD_DUMP,CLASHES; + if(!normal(t)&&!initialising)return loadfile(t); + /* except for prelude, only .m files have dumps */ + char obf[pnlim]; + FILE *f; + sighandler oldsig; + word flen=strlen(t); + time_t t1=fm_time(t),t2; + if(flen>pnlim) + { printf("sorry, pathname too long (limit=%d): %s\n",pnlim,t); + return; } /* if anyone complains, should remove this limit */ + (void)strcpy(obf,t); + (void)strcpy(obf+flen-1,obsuffix); + t2=fm_time(obf); + if(t2&&!t1)t2=0,unlink(obf); /* dump is orphan - remove */ + if(!t2||t2<t1) /* dump is nonexistent or older than source - ignore */ + { loadfile(t); return; } + f=fopen(obf,"r"); + if(!f){ printf("cannot open %s\n",obf); loadfile(t); return; } + current_script=t; + loading=1; + oldfiles=NIL; + unload(); +/*if(!initialising)printf("undumping from %s\n",obf); /* DEBUG */ + if(!initialising&&!making) /* ie this is the main script */ + sigflag=0, + oldsig=signal(SIGINT,(sighandler)sigdefer); + /* can't take interrupt during load_script */ + files=load_script(f,t,NIL,NIL,!making&!initialising); + fclose(f); + if(BAD_DUMP) + { unlink(obf); unload(); CLASHES=NIL; stackp=dstack; + printf("warning: %s contains incorrect data (file removed)\n",obf); + if(BAD_DUMP== -1)printf("(unrecognised dump format)\n"); else + if(BAD_DUMP==1)printf("(wrong source file)\n"); else + printf("(error %ld)\n",BAD_DUMP); } + if(!initialising&&!making) /* restore interrupt handler */ + (void)signal(SIGINT,oldsig); + if(sigflag)sigflag=0,(*oldsig)(); /* take deferred interrupt */ + /*if(!initialising)printf("%s undumped\n",obf); /* DEBUG */ + if(CLASHES!=NIL) + { if(ideep==0)printf("cannot load %s ",obf), + printlist("due to name clashes: ",alfasort(CLASHES)); + unload(); + loading=0; + return; } + if(BAD_DUMP||src_update())loadfile(t);/* any sources modified since dump? */ + else + if(initialising) + { if(ND!=NIL||files==NIL) /* error in dump of PRELUDE */ + fprintf(stderr,"panic: %s contains errors\n",obf), + exit(1); } /* beware of dangling else ! (whence {}) */ + else + if(verbosity||magic||mkexports) /* for less silent making s/mkexports/making/ */ + if(files==NIL)printf("%s contains syntax error\n",t); else + if(ND!=NIL)printf("%s contains undefined names or type errors\n",t); else + if(!making&&!magic)printf("%s\n",t); /* added &&!magic 26.11.2019 */ + if(files!=NIL&&!making&!initialising)unfixexports(); + loading=0; +} + +void unlinkx(t) /* remove orphaned .x file */ +char *t; +{ char *obf=linebuf; + (void)strcpy(obf,t); + (void)strcpy(obf+strlen(t)-1,obsuffix); + if(!stat(obf,&buf))unlink(obf); +} + +void fpe_error() +{ if(compiling) + { (void)signal(SIGFPE,(sighandler)fpe_error); /* reset SIGFPE trap */ +#ifdef sparc8 + fpsetmask(commonmask); /* to clear sticky bits */ +#endif + syntax("floating point number out of range\n"); + SYNERR=0; longjmp(env,1); + /* go straight back to commandloop - necessary because decoding very + large numbers can cause huge no. of repeated SIGFPE exceptions */ + } + else printf("\nFLOATING POINT OVERFLOW\n"),exit(1); +} + +char fbuf[512]; + +void filecopy(fil) /* copy the file "fil" to standard out */ +char *fil; +{ word in=open(fil,0),n; + if(in== -1)return; + while((n=read(in,fbuf,512))>0)write(1,fbuf,n); + close(in); +} + +void filecp(fil1,fil2) /* copy file "fil1" to "fil2" (like `cp') */ +char *fil1,*fil2; +{ word in=open(fil1,0),n; + word out=creat(fil2,0644); + if(in== -1||out== -1)return; + while((n=read(in,fbuf,512))>0)write(out,fbuf,n); + close(in); + close(out); +} + +/* to define winsize and TIOCGWINSZ for twidth() */ +#include <termios.h> +#include <sys/ioctl.h> + +int twidth() /* returns width (in columns) of current window, less 2 */ +{ +#ifdef TIOCGWINSZ + static struct winsize tsize; + ioctl(fileno(stdout),TIOCGWINSZ,&tsize); + return (tsize.ws_col==0)?78:tsize.ws_col-2; +#else +#error TIOCGWINSZ undefined +/* porting note: if you cannot find how to enable use of TIOCGWINSZ + comment out the above #error line */ + return 78; /* give up, we will assume screen width to be 80 */ +#endif +} + +/* was called when Miranda starts up and before /help, /aux + to clear screen - suppressed Oct 2019 */ +/* clrscr() +{ printf("\x1b[2J\x1b[H"); fflush(stdout); +} */ + +/* the following code tests if we are in a UTF-8 locale */ + +#ifdef CYGWIN +#include <windows.h> + +int utf8test() +{ return GetACP()==65001; } +/* codepage 1252 is Windows version of Latin-1; 65001 is UTF-8 */ + +#else + +int utf8test() +{ char *lang; + if(!(lang=getenv("LC_CTYPE"))) + lang=getenv("LANG"); + if(lang&& + (strstr(lang,"UTF-8")||strstr(lang,"UTF8")|| + strstr(lang,"utf-8")||strstr(lang,"utf8"))) + return 1; + return 0; +} +#endif + +/* end of MIRANDA STEER */ + |