static char sccsid[] = "%M% %I% %E%"; /* * Dynamic loader for Forth. This program reads in a binary image of * a Forth system and executes it. It connects standard input to the * Forth input stream (key and expect) and puts the Forth output stream * on standard output. * * An array of entry points for Unix system calls is provided to the * Forth system, so that Forth may conveniently access certain Unix * system calls. * * Synopsis: * * forth [ .exe ] [ -v ] [ file ] * [ -s 'string of forth commands' ] ... * * is the name of the ".exe" file containing the forth binary * image. It is NOT in a.out format. It is an exact image of the forth * dictionary, with an image of the initial user area appended to the end. * If the first argument does not end in ".exe", the default binary file * DEFAULT_EXE is used. * * If additional arguments are provided, they are taken to be the names of * files to be successively provided to the Forth system as it's input * stream. If a "-" appears as one of the file arguments, the standard * input is connected to the the Forth input stream. * * An argument of the form -s "string of forth commands" * will pass the given string to the forth system when the argument is * encountered. * * An argument -v turns on reporting of file names as they are opened. * * If there are no additional file arguments, standard input is connected * the the Forth input stream. * * The Forth system may determine whether the input stream is coming from * a file or from standard input by calling the function "input_is_file()". * This is useful for deciding whether or not to prompt. */ #include #include #include #include #include #ifdef sun #include #include #include #else #include #include #endif #include #ifdef SUN #define DEFAULT_PATH ".:/usr/local/lib/forth" #else #define DEFAULT_PATH ".:/usr/local/lib/forth" #endif #define DEFAULT_EXE "files.exe" extern char * substr(); extern int open(), creat(); extern int close(), read(), write(); extern int ioctl(); extern FILE * open_next_file(); extern int nextchar(); extern int outputchar(); extern int checkterm(); extern int exit(); extern int lseek(); extern int unlink(); extern int input_is_file(); extern int getenv(); #ifdef sun extern char *valloc(); extern int mmap(); extern struct tm *today(); extern int timez(); extern char *timezstr(); extern char *timezone(); #endif extern int fork(); extern int execve(); extern int spawn(); extern int system(); extern int chdir(); extern int getwd(); extern caddr_t sbrk(); extern int cexpect(); #ifdef USECOMMAND extern int scommand(); #endif extern int ctype(); /* * Only the first 13 (0-48) of these functions are absolutely necessary. * Number 108 is necessary too, now. * The rest are used by various extension packages and can be replaced * by dummy entries, with the loss of only that extension package */ int ( (*functions[])()) = { /* 0 4 */ nextchar, outputchar, /* 8 12 16 20 24 28 32 */ open, creat, close, read, write, ioctl, checkterm, /* 36 40 44 48 52 */ exit, lseek, unlink, input_is_file, getenv, #ifdef sun /* 56, 60, */ valloc, mmap, /* 64 68 72 */ today, timez, timezstr, #else 0, 0, 0, 0, 0, #endif /* 76, 80, 84, 88, 92, 96, 100, */ fork, execve, spawn, system, signal, chdir, getwd, /* 104, */ sbrk, /* 108 */ cexpect, #ifdef USECOMMAND /* 112 */ scommand, #else 0, #endif /* 116 */ ctype, }; #define MAX_KERNEL_SIZE 512*1024 extern handle_signal(); /* * Insane is used by the error handling code to decide whether to do a * warm start or to give up. */ int insane; int ip_is_file; int gargc; char **gargv; FILE *fi; char * faddr; main(argc, argv) int argc; char **argv; { int cnt; int f; int catch_signals; char * dictionary_file; /* If forth is invoked as "bomb", then we don't catch signals */ catch_signals = strcmp(argv[0], "bomb"); if( argc < 2 || strcmp(substr(argv[1],-4,4),".exe") != 0 ) { dictionary_file = DEFAULT_EXE; } else { dictionary_file = argv[1]; argc -= 1; argv += 1; } if( (f = path_open(dictionary_file) ) < 0 ) { printf("forth: Can't open %s\n",dictionary_file); perror(""); exit(1); } get_workspace(MAX_KERNEL_SIZE); if( (cnt = read(f, faddr, MAX_KERNEL_SIZE)) < 0 ) { printf("forth: Read failed\n"); exit(1); } gargc = argc; gargv = argv; ip_is_file = 0; fi = (argc <= 1) ? stdin : open_next_file() ; setbuf(stdout, NULL); #ifdef CATCHINT if ( catch_signals ) { signal(SIGINT,handle_signal); signal(SIGILL,handle_signal); signal(SIGIOT,handle_signal); signal(SIGEMT,handle_signal); signal(SIGSEGV,handle_signal); signal(SIGBUS,handle_signal); } #endif CATCHINT exit((*(int (*) ())faddr)(faddr+cnt,functions,&gargc,&gargv)); } #ifdef CATCHINT handle_signal(sig,code,scp) int sig, code; struct sigcontext * scp; { switch(sig) { case SIGINT: break; case SIGILL: printf("Illegal Instruction\n"); break; case SIGSEGV: printf("Segmentation violation\n"); break; case SIGBUS: printf("Bus Error\n"); break; case SIGIOT: printf("IOT Instruction Trap\n"); break; case SIGEMT: printf("EMT Instruction Trap\n"); break; } /* * If the interrupt occurs when taking input from a file, * recovery is probably not possible, * so doing a warm start is inappropriate. */ if( ip_is_file || insane++ ) exit(1); else { #ifdef sun scp->sc_pc = (int)(faddr+4); #else /* Unblock all signals; the signal we're processing is blocked */ sigsetmask(0); ( *(int (*)())(faddr+4) )(); #endif sun } } #endif CATCHINT checkterm() { int nchars; if ( fi != stdin || (nchars = stdin->_cnt) == 0 ) ioctl(0,FIONREAD,&nchars); return (nchars); } char strbuf[128]; char *argstring; char *strptr; #define STRINGINPUT (FILE *) -5 int nextchar() { register int c; insane = 0; do { if ( fi == STRINGINPUT ) { if( (c = *strptr++) != '\0') return(c); } else { if((c = getc(fi)) != EOF ) return(c); else if (fi!=stdin) fclose(fi); } fi = open_next_file(); } while (fi != NULL); exit(0); } int cexpect(max, buffer) register int max; char * buffer; { register int c; register char *p = buffer; insane = 0; if ( fi == NULL ) if ((fi = open_next_file()) == NULL) exit(0); while ( fi == STRINGINPUT || feof( fi ) ) { if ( fi == STRINGINPUT ) { while (max--) if ( (*p++ = *argstring++) == '\0' ) break; if ( max >= 0 ) --p; fi = NULL; return ( p - buffer ); } if ( feof( fi ) ) { if (fi!=stdin) fclose(fi); if ((fi = open_next_file()) == NULL) exit(0); } } while (max-- && (c = getc(fi)) != '\n' && c != EOF ) *p++ = c; return ( p - buffer ); } FILE * open_next_file() { register FILE * fi; static int vflag = 0; /* controls reporting of file names */ while (--gargc > 0) { if ( (*++gargv)[0]=='-' ) { switch((*gargv)[1]) { case '\0': ip_is_file = 0; fi = stdin; return(fi); case 's': ip_is_file = 1; ++gargv; --gargc; argstring = *gargv; strcpy(strbuf, *gargv); strptr = strcat(strbuf, "\n"); return(STRINGINPUT); case 'v': vflag = 1; break; default: fprintf(stderr,"Unknown flag %s\n",*gargv); } } else { if ((fi = fopen(*gargv, "r")) == NULL) { perror(*gargv); continue; } else { if (vflag) printf("File: %s\n",*gargv); /* input is coming from a file */ ip_is_file = 1; return(fi); } } } return(NULL); } outputchar(c) int c; { putchar(c); } ctype(len, addr) register int len; register char * addr; { while (len--) putchar(*addr++); } /* * This routine is called by the forth system to determine whether * its input stream is connected to a file or to the possibly-interactive * stdin stream. It uses this information to decide whether or not to * prompt at the beginning of a line. */ int input_is_file() { return(ip_is_file); } char * substr(str, pos, n) char *str; int pos, n; { register int len = strlen(str); static char outstr[128]; if( pos < 0 ) pos += len+1; if( pos <= 0 ) pos = 1; if (n < 0) n += len; if (pos + n - 1 > len) { n = len + 1 - pos; if (n < 0) n = 0; } strncpy(outstr, str + pos - 1, n); outstr[n] = '\0'; return(outstr); } align (current,boundary) int current, boundary; { return( ((current + boundary - 1) / boundary ) * boundary ); } get_workspace(size) int size; { int current; current = (int)sbrk(0); faddr = (char *) align(current,64*1024); if( brk(faddr) != 0 ) { fprintf(stderr,"forth: brk: couldn't get memory\n"); exit(1); } if( sbrk(2*size) == -1 ) { /* get extra space for metacompiler */ fprintf(stderr,"forth: sbrk: couldn't get memory\n"); exit(1); } } #ifdef sun struct tm *today() { struct timeval tp; struct timezone tzp; gettimeofday(&tp, &tzp); return(localtime(&tp.tv_sec)); } long timez() { struct timeval tp; struct timezone tzp; gettimeofday(&tp, &tzp); return(tzp.tz_minuteswest); } char *timezstr() { struct timeval tp; struct timezone tzp; struct tm *tb; gettimeofday(&tp, &tzp); tb = localtime(&tp.tv_sec); return(timezone(tzp.tz_minuteswest,tb->tm_isdst)); } #endif sun int spawn(name, argv, envp) char *name, *argv[], *envp[]; { int pid; union wait status; if( (pid = vfork()) == -1 ) return(0); else if (pid == 0) { /* Child process */ execve(name, argv, envp); _exit(1); /* Random magic number */ } else { /* parent process */ wait(&status); return( status.w_T.w_Retcode == 0 ); } } /* * Tries to open the named file looking in each directory of the * search path specified by the environment variable FPATH. * Returns file descriptor or -1 if not found */ char fnb[300]; path_open(fn) register char *fn; { static char *path; register char *dp; int fd; register char *lpath; if (fn == 0 ) return -1; if (path == 0) { path = (char *) getenv ("FPATH"); if (path == 0) path = DEFAULT_PATH; } lpath = *fn == '/' ? "" : path; do { dp = fnb; while (*lpath && *lpath != ':') *dp++ = *lpath++; if (dp != fnb) *dp++ = '/'; strcpy (dp, fn); if ((fd = open (fnb, 0)) >= 0) break; } while (*lpath++); }