1: /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1984. */ 2: static char rcsid[] = "$Header: comm.c,v 2.4 85/08/22 16:00:49 timo Exp $"; 3: 4: /* 5: * B editor -- Communication with B interpreter. 6: */ 7: 8: #include "feat.h" 9: #ifdef BTOP 10: 11: #include <signal.h> 12: #include <setjmp.h> 13: #include <ctype.h> 14: 15: #include "b.h" 16: #include "node.h" 17: #include "supr.h" 18: #include "unix.h" 19: #include "cell.h" /* For winheight */ 20: 21: #define TABS 8 22: 23: string unixerror(); 24: 25: 26: /* 27: * Communication to other modules (demo, getc, ...): 28: */ 29: 30: Visible bool interrupted; /* Set when interrupt caught but not propagated */ 31: Visible bool canjump; /* Set when disrupt() can safely longjmp(jumpback) */ 32: Visible jmp_buf jumpback; /* Set by other module where to jump */ 33: 34: /* 35: * Pipeline protocol with interpreter: 36: */ 37: 38: #define ESCAPE '\001' /* Character signalling special function */ 39: #define RESYNC '\177' /* Character signalling acknowledge of interrupt */ 40: #define INTRCHILD SIGTRAP /* Signal to send as interrupt */ 41: 42: #ifndef INTERPRETER 43: #define INTERPRETER "/usr/new/lib/B/bint" 44: #endif 45: 46: /* 47: * Local definitions: 48: */ 49: 50: #ifndef INTRMSG 51: #define INTRMSG "*** Interrupted" /* Acknowledges interrupt */ 52: #endif INTRMSG 53: 54: #define Moreinput(stream) ((stream)->_cnt > 0) 55: 56: Hidden int fdown[2]; /* File descriptors for pipe down */ 57: Hidden int fup[2]; /* Pipe up */ 58: 59: Hidden int pid; /* Process id of child */ 60: 61: Hidden FILE *pdown; /* FILE pointer for pipe down to child process */ 62: Hidden FILE *pup; /* Pipe up */ 63: 64: Hidden string interpreter; /* Name of interpreter to be used */ 65: 66: 67: Hidden char pushback[100]; /* Limited pushback facility */ 68: Hidden int npushback; /* Number of characters pushed back */ 69: 70: 71: /* 72: * Routine to set canjump, do a getc, and clear canjump. 73: */ 74: 75: Visible int 76: ffgetc(fp) 77: FILE *fp; 78: { 79: register int c; 80: 81: canjump = Yes; 82: c = getc(fp); 83: canjump = No; 84: return c; 85: } 86: 87: 88: /* 89: * Similar for fgets. 90: */ 91: 92: Visible string 93: ffgets(buf, len, fp) 94: string buf; 95: int len; 96: FILE *fp; 97: { 98: canjump = Yes; 99: buf = fgets(buf, len, fp); 100: canjump = No; 101: return buf; 102: } 103: 104: 105: /* 106: * Assign values to `fdown' and `fup'. 107: */ 108: 109: Hidden Procedure 110: getdevices() 111: { 112: if (pipe(fdown) < 0 || pipe(fup) < 0) 113: syserr("%s", unixerror("can't pipe")); 114: } 115: 116: 117: /* 118: * Do the magic required for child-birth. 119: */ 120: 121: Hidden Procedure 122: makechild() 123: { 124: #ifdef VFORK 125: pid = vfork(); 126: #else VFORK 127: pid = fork(); 128: #endif VFORK 129: if (pid == -1) 130: syserr("%s", unixerror("can't fork")); 131: if (pid == 0) /* Child */ 132: exec_b(); /* Does not return */ 133: /* Parent */ 134: close(fdown[0]); 135: close(fup[1]); 136: } 137: 138: 139: /* 140: * Code executed in the child process. Never returns. 141: * Just dup the pipe ends to files 0, a and 2 (stdin, stdout and stderr), 142: * then close the original pipes. 143: */ 144: 145: Hidden Procedure 146: exec_b() 147: { 148: close(fdown[1]), close(fup[0]); 149: close(0), close(1), close(2); 150: dup(fdown[0]), dup(fup[1]), dup(fup[1]); 151: close(fdown[0]), close(fup[1]); 152: execl(interpreter, interpreter, "-i", (char*)NULL); 153: fprintf(stderr, "*** "); 154: perror(interpreter); 155: _exit(1); 156: } 157: 158: 159: /* 160: * Interrupt handler. 161: * Usually only the flag `interrupted' is set. 162: * 163: * When `canjump' is on, it is cleared and we do a longjmp 164: * back to where jumpbuf leads us (usually done when a read 165: * system call is interrupted, as 4.2BSD tends to continue 166: * these rather than have them return with errno = EINTR). 167: */ 168: 169: Hidden Procedure 170: disrupt() 171: { 172: interrupted = Yes; 173: signal(SIGINT, disrupt); 174: if (canjump) { 175: canjump = No; 176: longjmp(jumpback, 1); 177: } 178: } 179: 180: 181: /* 182: * Start the B interpreter as a subprocess. 183: * Set up communication pipes in pdown, pup. 184: */ 185: 186: Visible Procedure 187: start_b(ppdown, ppup) 188: FILE **ppdown; 189: FILE **ppup; 190: { 191: interpreter = getenv("B_INTERPRETER"); 192: if (!interpreter) 193: interpreter = INTERPRETER; 194: getdevices(); 195: makechild(); 196: pdown = fdopen(fdown[1], "w"); 197: pup = fdopen(fup[0], "r"); 198: if (!pdown || !pup) 199: syserr("%s", unixerror("can't fdopen")); 200: *ppdown = pdown; 201: *ppup = pup; 202: signal(SIGINT, disrupt); 203: } 204: 205: 206: /* 207: * Routine to be called after each line of data has been passed 208: * to the B interpreter; it checks whether the immediate next 209: * output is a request for an immediate command, and if so, 210: * eats the request and returns Yes. Otherwise it pushes back the 211: * request for later processing by sleur(), and returns No. 212: * ***** The prompt parameter is a relict of old times. ***** 213: */ 214: 215: Visible bool 216: expect(prompt) 217: string prompt; /* Only first char used; should be ">" */ 218: { 219: register int c; 220: 221: fflush(pdown); 222: if (setjmp(jumpback)) 223: return No; 224: if (npushback) 225: c = pushback[--npushback]; 226: else 227: c = ffgetc(pup); 228: if (c != ESCAPE) { 229: if (c != EOF) 230: pushback[npushback++] = c; 231: return No; 232: } 233: if (npushback) 234: c = pushback[--npushback]; 235: else 236: c = ffgetc(pup); 237: if (c == *prompt) 238: return Yes; 239: if (c != EOF) 240: pushback[npushback++] = c; 241: pushback[npushback++] = ESCAPE; 242: return No; 243: } 244: 245: 246: Visible int 247: sleur() 248: { 249: register int c; 250: register int x = 0; 251: bool show = Yes; /* No when looking for interrupt sync char */ 252: bool idle = Yes; /* Yes when no output done yet this call */ 253: 254: fflush(pdown); 255: 256: for (;;) { 257: if (interrupted) { 258: interrupted = No; 259: intrchild(); 260: show = No; 261: } 262: if (show && npushback == 0 && !Moreinput(pup)) 263: fflush(stdout); 264: if (setjmp(jumpback)) 265: continue; 266: if (npushback > 0) 267: c = pushback[--npushback]; 268: else 269: c = ffgetc(pup); 270: if (c == EOF) { /* End-of-file: B interpreter has terminated. */ 271: fflush(stdout); 272: return EOF; 273: } 274: if (c == RESYNC) { 275: /* B interpreter acknowledges interrupt. */ 276: if (!show) { 277: if (x != 0) putchar('\n'); 278: fputs(INTRMSG, stdout); 279: putchar('\n'); 280: x = 0; 281: show = Yes; 282: } 283: continue; 284: } 285: if (show) { 286: if (c != ESCAPE) { 287: putchar(c); 288: switch (c) { 289: case '\t': 290: x = (x/TABS + 1)*TABS; 291: break; 292: case '\b': 293: if (x > 0) --x; 294: break; 295: case '\r': 296: case '\n': 297: x = 0; 298: break; 299: default: 300: if (isascii(c) && isprint(c) 301: || c == ' ') ++x; 302: break; 303: } 304: } 305: else { 306: /* Control-A: B interpreter needs input. */ 307: if (setjmp(jumpback)) 308: continue; 309: if (npushback) 310: c = pushback[--npushback]; 311: else { 312: c = ffgetc(pup); 313: if (c == EOF) { 314: return EOF; 315: } 316: } 317: if (c == '>') { 318: /* Newline before command prompt */ 319: if (x != 0) putchar('\n'); 320: x = 0; 321: } 322: setindent(x); 323: fflush(stdout); 324: return c; 325: } 326: } 327: } 328: } 329: 330: 331: /* 332: * Send the child a termination signal (SIGTERM). 333: */ 334: 335: Visible Procedure 336: termchild() 337: { 338: if (pid) { 339: kill(pid, SIGTERM); 340: pid = 0; 341: } 342: } 343: 344: 345: /* 346: * Send the child an interrupt signal. (By convention, this is SIGTRAP). 347: */ 348: 349: Visible Procedure 350: intrchild() 351: { 352: if (pid) { 353: kill(pid, INTRCHILD); 354: fflush(stdout); 355: } 356: } 357: 358: 359: /* 360: * Wait for child process and report abnormal exit statuses. 361: */ 362: 363: Visible Procedure 364: waitchild() 365: { 366: int k; 367: int status; 368: 369: if (pid) { 370: while ((k = wait(&status)) != -1) { 371: if (k != pid) 372: #ifndef SMALLSYS 373: fprintf(stderr, "*** [Pid %d status 0%o]\n", pid, status) 374: #endif SMALLSYS 375: ; 376: else { 377: #ifndef SMALLSYS 378: if (status&0377) 379: fprintf(stderr, "*** Interpreter killed by signal %d%s\n", 380: status&0177, status&0200 ? " - core dumped" : ""); 381: else if (status) 382: fprintf(stderr, "*** Interpreter exit(%d)\n", status>>8); 383: #endif SMALLSYS 384: pid = 0; 385: break; 386: } 387: } 388: #ifndef SMALLSYS 389: if (pid) 390: fprintf(stderr, "*** Can't get interpreter status\n"); 391: #endif SMALLSYS 392: pid = 0; 393: } 394: } 395: 396: #endif BTOP