/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1984. */ static char rcsid[] = "$Header: comm.c,v 2.4 85/08/22 16:00:49 timo Exp $"; /* * B editor -- Communication with B interpreter. */ #include "feat.h" #ifdef BTOP #include #include #include #include "b.h" #include "node.h" #include "supr.h" #include "unix.h" #include "cell.h" /* For winheight */ #define TABS 8 string unixerror(); /* * Communication to other modules (demo, getc, ...): */ Visible bool interrupted; /* Set when interrupt caught but not propagated */ Visible bool canjump; /* Set when disrupt() can safely longjmp(jumpback) */ Visible jmp_buf jumpback; /* Set by other module where to jump */ /* * Pipeline protocol with interpreter: */ #define ESCAPE '\001' /* Character signalling special function */ #define RESYNC '\177' /* Character signalling acknowledge of interrupt */ #define INTRCHILD SIGTRAP /* Signal to send as interrupt */ #ifndef INTERPRETER #define INTERPRETER "/usr/new/lib/B/bint" #endif /* * Local definitions: */ #ifndef INTRMSG #define INTRMSG "*** Interrupted" /* Acknowledges interrupt */ #endif INTRMSG #define Moreinput(stream) ((stream)->_cnt > 0) Hidden int fdown[2]; /* File descriptors for pipe down */ Hidden int fup[2]; /* Pipe up */ Hidden int pid; /* Process id of child */ Hidden FILE *pdown; /* FILE pointer for pipe down to child process */ Hidden FILE *pup; /* Pipe up */ Hidden string interpreter; /* Name of interpreter to be used */ Hidden char pushback[100]; /* Limited pushback facility */ Hidden int npushback; /* Number of characters pushed back */ /* * Routine to set canjump, do a getc, and clear canjump. */ Visible int ffgetc(fp) FILE *fp; { register int c; canjump = Yes; c = getc(fp); canjump = No; return c; } /* * Similar for fgets. */ Visible string ffgets(buf, len, fp) string buf; int len; FILE *fp; { canjump = Yes; buf = fgets(buf, len, fp); canjump = No; return buf; } /* * Assign values to `fdown' and `fup'. */ Hidden Procedure getdevices() { if (pipe(fdown) < 0 || pipe(fup) < 0) syserr("%s", unixerror("can't pipe")); } /* * Do the magic required for child-birth. */ Hidden Procedure makechild() { #ifdef VFORK pid = vfork(); #else VFORK pid = fork(); #endif VFORK if (pid == -1) syserr("%s", unixerror("can't fork")); if (pid == 0) /* Child */ exec_b(); /* Does not return */ /* Parent */ close(fdown[0]); close(fup[1]); } /* * Code executed in the child process. Never returns. * Just dup the pipe ends to files 0, a and 2 (stdin, stdout and stderr), * then close the original pipes. */ Hidden Procedure exec_b() { close(fdown[1]), close(fup[0]); close(0), close(1), close(2); dup(fdown[0]), dup(fup[1]), dup(fup[1]); close(fdown[0]), close(fup[1]); execl(interpreter, interpreter, "-i", (char*)NULL); fprintf(stderr, "*** "); perror(interpreter); _exit(1); } /* * Interrupt handler. * Usually only the flag `interrupted' is set. * * When `canjump' is on, it is cleared and we do a longjmp * back to where jumpbuf leads us (usually done when a read * system call is interrupted, as 4.2BSD tends to continue * these rather than have them return with errno = EINTR). */ Hidden Procedure disrupt() { interrupted = Yes; signal(SIGINT, disrupt); if (canjump) { canjump = No; longjmp(jumpback, 1); } } /* * Start the B interpreter as a subprocess. * Set up communication pipes in pdown, pup. */ Visible Procedure start_b(ppdown, ppup) FILE **ppdown; FILE **ppup; { interpreter = getenv("B_INTERPRETER"); if (!interpreter) interpreter = INTERPRETER; getdevices(); makechild(); pdown = fdopen(fdown[1], "w"); pup = fdopen(fup[0], "r"); if (!pdown || !pup) syserr("%s", unixerror("can't fdopen")); *ppdown = pdown; *ppup = pup; signal(SIGINT, disrupt); } /* * Routine to be called after each line of data has been passed * to the B interpreter; it checks whether the immediate next * output is a request for an immediate command, and if so, * eats the request and returns Yes. Otherwise it pushes back the * request for later processing by sleur(), and returns No. * ***** The prompt parameter is a relict of old times. ***** */ Visible bool expect(prompt) string prompt; /* Only first char used; should be ">" */ { register int c; fflush(pdown); if (setjmp(jumpback)) return No; if (npushback) c = pushback[--npushback]; else c = ffgetc(pup); if (c != ESCAPE) { if (c != EOF) pushback[npushback++] = c; return No; } if (npushback) c = pushback[--npushback]; else c = ffgetc(pup); if (c == *prompt) return Yes; if (c != EOF) pushback[npushback++] = c; pushback[npushback++] = ESCAPE; return No; } Visible int sleur() { register int c; register int x = 0; bool show = Yes; /* No when looking for interrupt sync char */ bool idle = Yes; /* Yes when no output done yet this call */ fflush(pdown); for (;;) { if (interrupted) { interrupted = No; intrchild(); show = No; } if (show && npushback == 0 && !Moreinput(pup)) fflush(stdout); if (setjmp(jumpback)) continue; if (npushback > 0) c = pushback[--npushback]; else c = ffgetc(pup); if (c == EOF) { /* End-of-file: B interpreter has terminated. */ fflush(stdout); return EOF; } if (c == RESYNC) { /* B interpreter acknowledges interrupt. */ if (!show) { if (x != 0) putchar('\n'); fputs(INTRMSG, stdout); putchar('\n'); x = 0; show = Yes; } continue; } if (show) { if (c != ESCAPE) { putchar(c); switch (c) { case '\t': x = (x/TABS + 1)*TABS; break; case '\b': if (x > 0) --x; break; case '\r': case '\n': x = 0; break; default: if (isascii(c) && isprint(c) || c == ' ') ++x; break; } } else { /* Control-A: B interpreter needs input. */ if (setjmp(jumpback)) continue; if (npushback) c = pushback[--npushback]; else { c = ffgetc(pup); if (c == EOF) { return EOF; } } if (c == '>') { /* Newline before command prompt */ if (x != 0) putchar('\n'); x = 0; } setindent(x); fflush(stdout); return c; } } } } /* * Send the child a termination signal (SIGTERM). */ Visible Procedure termchild() { if (pid) { kill(pid, SIGTERM); pid = 0; } } /* * Send the child an interrupt signal. (By convention, this is SIGTRAP). */ Visible Procedure intrchild() { if (pid) { kill(pid, INTRCHILD); fflush(stdout); } } /* * Wait for child process and report abnormal exit statuses. */ Visible Procedure waitchild() { int k; int status; if (pid) { while ((k = wait(&status)) != -1) { if (k != pid) #ifndef SMALLSYS fprintf(stderr, "*** [Pid %d status 0%o]\n", pid, status) #endif SMALLSYS ; else { #ifndef SMALLSYS if (status&0377) fprintf(stderr, "*** Interpreter killed by signal %d%s\n", status&0177, status&0200 ? " - core dumped" : ""); else if (status) fprintf(stderr, "*** Interpreter exit(%d)\n", status>>8); #endif SMALLSYS pid = 0; break; } } #ifndef SMALLSYS if (pid) fprintf(stderr, "*** Can't get interpreter status\n"); #endif SMALLSYS pid = 0; } } #endif BTOP