/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. */ #include #include #include #include #include #include #include #ifdef LINUX #define USE_SIGACTION #endif #ifdef SYSV #include #else #include #endif #ifndef O_NDELAY #include #endif #define import_kernel #define import_knames #define import_zfstat #define import_spp #include /* * ZFIOTX -- File i/o to textfiles, for UNIX 4.1BSD. This driver is used for * both terminals and ordinary disk text files. I/O is via the C library * stdio routines, which provide buffering. * * On input, we must check for newline and terminate when it has been read, * including the newline character in the return buffer and in the character * count. If the buffer limit is reached before newline is found, we return * the line without a newline, and return the rest of the line in the next * read request. If a single character is requested character mode is * asserted, i.e., if the device is a terminal RAW is enabled and ECHO and * CRMOD (CR to LF mapping) are disabled. A subsequent request to read or * write more than one character restores line mode. * * On output, we transmit the specified number of characters, period. * No checking for newline or EOS is performed. Text may contain null * characters (note that EOS == NUL). There is no guarantee that the * output line will contain a newline. When flushing partial lines to a * terminal, this is often not the case. When FIO writes a very long * line and the FIO buffer overflows, it will flush a partial line, * passing the rest of the line (with newline delimiter) in the next * write request. The newline should be included in the data as stored * on disk, if possible. It is not necessary to map newlines upon output * because UNIX does this already. * * N.B.: An SPP char is usually not a byte, so these routines usually * perform type conversion. The actual SPP datatype is defined in language.h * An SPP char is a signed integer, but only values in the range 0-127 may * be written to a text file. We assume that character set conversion (e.g., * ASCII to EBCDIC) is not necessary since all 4.1BSD hosts we are aware of * are ASCII machines. */ #define MAXOTTYS 20 /* max simultaneous open ttys */ #define MAX_TRYS 2 /* max interrupted trys to read */ #define NEWLINE '\n' /* The ttyport descriptor is used by the tty driver (zfiotx). */ struct ttyport { int inuse; /* port in use */ int chan; /* host channel (file descrip.) */ unsigned device; /* tty device number */ int flags; /* port flags */ char redraw; /* redraw char, sent after susp */ #ifdef SYSV struct termios tc; /* normal tty state */ struct termios save_tc; /* saved rawmode tty state */ #else struct sgttyb tc; /* normal tty state */ struct sgttyb save_tc; /* saved rawmode tty state */ #endif }; #define CTRLC 3 extern int errno; static jmp_buf jmpbuf; static int tty_getraw = 0; /* raw getc in progress */ #ifdef USE_SIGACTION static struct sigaction sigint, sigterm; static struct sigaction sigtstp, sigcont; static struct sigaction oldact; #else static SIGFUNC sigint, sigterm; static SIGFUNC sigtstp, sigcont; #endif static void tty_rawon(), tty_reset(), uio_bwrite(); static void tty_onsig(), tty_stop(), tty_continue(); /* The ttyports array describes up to MAXOTTYS open terminal i/o ports. * Very few processes will ever have more than one or two ports open at * one time. ttyport descriptors are allocated one per tty device, using * the minor device number to identify the device. This serves to * uniquely identify each tty device regardless of the file descriptor * used to access it. Multiple file descriptors (e.g. stdin, stdout, * stderr) used to access a single device must use description of the * device state. */ struct ttyport ttyports[MAXOTTYS]; struct ttyport *lastdev = NULL; /* Omit this for now; it was put in for an old Linux libc bug, and since libc * is completely different now the need for it has probably gone away. * * #ifdef LINUX * #define FCANCEL * #endif */ #ifdef FCANCEL /* The following definition has intimate knowledge of the STDIO structures. */ #define fcancel(fp) ( \ (fp)->_IO_read_ptr = (fp)->_IO_read_end = (fp)->_IO_read_base,\ (fp)->_IO_write_ptr = (fp)->_IO_write_end = (fp)->_IO_write_base) #endif /* ZOPNTX -- Open or create a text file. The pseudo-files "unix-stdin", * "unix-stdout", and "unix-stderr" are treated specially. These denote the * UNIX stdio streams of the process. These streams are not really opened * by this driver, but ZOPNTX should be called on these streams to * properly initialize the file descriptor. */ int ZOPNTX ( PKCHAR *osfn, /* UNIX filename */ XINT *mode, /* file access mode */ XINT *chan /* UNIX channel of file (output) */ ) { register int fd; register FILE *fp; struct stat filestat; int newmode, maskval; FILE *fopen(); char *fmode; /* Map FIO access mode into UNIX/stdio access mode. */ switch (*mode) { case READ_ONLY: fmode = "r"; break; case READ_WRITE: fmode = "r+"; /* might not work on old systems */ break; case APPEND: fmode = "a"; break; case WRITE_ONLY: fmode = "w"; break; case NEW_FILE: /* create for appending */ fmode = "w"; break; default: goto error; } /* Open or create file. If a new file is created it is necessary * to explicitly set the file access permissions as indicated by * FILE_MODEBITS in kernel.h. This is done in such a way that the * user's UMASK bits are preserved. */ if (strcmp ((char *)osfn, U_STDIN) == 0) { fp = stdin; } else if (strcmp ((char *)osfn, U_STDOUT) == 0) { fp = stdout; } else if (strcmp ((char *)osfn, U_STDERR) == 0) { fp = stderr; } else if ((fp = fopen ((char *)osfn, fmode)) == NULL) goto error; fd = fileno (fp); if (fstat (fd, &filestat) == ERR) { if (fd > 2) fclose (fp); goto error; } else if (fd >= MAXOFILES) { if (fd > 2) fclose (fp); goto error; } /* If regular file apply the umask. */ if (fd > 2 && S_ISREG(filestat.st_mode) && (*mode == WRITE_ONLY || *mode == NEW_FILE)) { umask (maskval = umask (022)); newmode = ((filestat.st_mode | 066) & ~maskval); (void) chmod ((char *)osfn, newmode); } /* Set up kernel file descriptor. */ zfd[fd].fp = fp; zfd[fd].fpos = 0; zfd[fd].nbytes = 0; zfd[fd].io_flags = 0; zfd[fd].port = (char *) NULL; zfd[fd].flags = (KF_NOSEEK | KF_NOSTTY); if (S_ISREG (filestat.st_mode)) zfd[fd].flags &= ~KF_NOSEEK; if (S_ISCHR (filestat.st_mode)) zfd[fd].flags &= ~KF_NOSTTY; /* If we are opening a terminal device set up ttyport descriptor. */ if (!(zfd[fd].flags & KF_NOSTTY)) { register struct ttyport *port; register unsigned device; register int i; /* Check if we already have a descriptor for this device. */ device = (filestat.st_dev << 16) + filestat.st_rdev; for (i=0, port = &ttyports[0]; i < MAXOTTYS; i++, port++) if (port->inuse && port->device == device) { zfd[fd].port = (char *) port; port->inuse++; goto done; } /* Fill in a fresh descriptor. */ port = &ttyports[0]; for (i=MAXOTTYS; --i >= 0 && port->inuse; port++) ; if (i >= MAXOTTYS) goto error; port->chan = fd; port->device = device; port->flags = 0; port->redraw = 0; port->inuse = 1; zfd[fd].port = (char *) port; } done: *chan = fd; return (*chan); error: *chan = XERR; return (*chan); } /* ZCLSTX -- Close a text file. */ int ZCLSTX (XINT *fd, XINT *status) { register struct fiodes *kfp = &zfd[*fd]; register struct ttyport *port = (struct ttyport *) kfp->port; /* Disable character mode if still in effect. If this is necessary * then we have already saved the old tty status flags in sg_flags. */ if (port && (port->flags & KF_CHARMODE)) tty_reset (port); /* Close the file. Set file pointer field of kernel file descriptor * to null to indicate that the file is closed. * * [NOTE] -- fclose errors are ignored if we are closing a terminal * device. This was necessary on the Suns and it was not clear why * a close error was occuring (errno was EPERM - not owner). */ *status = (fclose(kfp->fp) == EOF && kfp->flags&KF_NOSTTY) ? XERR : XOK; kfp->fp = NULL; if (port) { kfp->port = NULL; if (--port->inuse < 0) port->inuse = 0; if (lastdev == port) lastdev = NULL; } return (*status); } /* ZFLSTX -- Flush any buffered textual output. */ int ZFLSTX (XINT *fd, XINT *status) { *status = (fflush (zfd[*fd].fp) == EOF) ? XERR : XOK; return ((int) *status); } /* ZGETTX -- Get a line of text from a text file. Unpack machine chars * into type XCHAR. If output buffer is filled before newline is encountered, * the remainder of the line will be returned in the next call, and the * current line will NOT be newline terminated. If maxchar==1 assert * character mode, otherwise assert line mode. */ int ZGETTX (XINT *fd, XCHAR *buf, XINT *maxchars, XINT *status) { register FILE *fp; register XCHAR *op; register int ch, maxch = *maxchars; register struct fiodes *kfp; struct ttyport *port; int nbytes, ntrys; if (maxch <= 0) { *status = 0; return (*status); } kfp = &zfd[*fd]; port = (struct ttyport *) kfp->port; fp = kfp->fp; /* If maxch=1 assert char mode if legal on device, otherwise clear * char mode if set. Ignore ioctl errors if ioctl is illegal on * device. */ if (port) { if (maxch == 1 && !(port->flags & KF_CHARMODE)) tty_rawon (port, 0); else if (maxch > 1 && (port->flags & KF_CHARMODE)) { /* Disable character mode. If this is necessary then we have * already saved the old tty status flags in sg_flags. */ tty_reset (port); } } /* Copy the next line of text into the user buffer. Keep track of * file offsets of current line and next line for ZNOTTX. A call to * ZNOTTX will return the value of kfp->fpos, the file offset of the * next line to be read (unless newline has not yet been seen on the * current line due to a partial read). The following while loop is * the inner loop of text file input, hence is heavily optimized at * the expense of some clarity. If the host is non-ASCII add a lookup * table reference to this loop to map chars from the host character * set to ASCII. * * N.B.: If an interrupt occurs during the read and the output buffer * is still empty, try to complete the read. This can happen when * an interrupt occurs while waiting for input from the terminal, in * which case recovery is probably safe. */ if (!port || !(port->flags & KF_CHARMODE)) { /* Read in line mode. */ ntrys = MAX_TRYS; do { clearerr (fp); op = buf; errno = 0; while (*op++ = ch = getc(fp), ch != EOF) { if (--maxch <= 0 || ch == NEWLINE) break; } #ifdef FCANCEL if (errno == EINTR) fcancel (fp); #endif } while (errno == EINTR && op-1 == buf && --ntrys >= 0); *op = XEOS; nbytes = *maxchars - maxch; } else if (kfp->flags & KF_NDELAY) { /* Read a single character in nonblocking raw mode. Zero * bytes are returned if there is no data to be read. */ struct timeval timeout; int chan = fileno(fp); fd_set rfds; char data[1]; FD_ZERO (&rfds); FD_SET (chan, &rfds); timeout.tv_sec = 0; timeout.tv_usec = 0; clearerr (fp); if (select (chan+1, &rfds, NULL, NULL, &timeout)) { if (read (chan, data, 1) != 1) { *status = XERR; return (XERR); } ch = *data; goto outch; } else { *buf = XEOS; *status = 0; return (*status); } } else { /* Read a single character in raw mode. Catch interrupts and * return the interrupt character as an ordinary character. * We map interrupts in this way, rather than use raw mode * (which disables interrupts and all other input processing) * because ctrl/s ctrl/q is disabled in raw mode, and that can * cause sporadic protocol failures. */ #ifdef USE_SIGACTION sigint.sa_handler = (SIGFUNC) tty_onsig; sigemptyset (&sigint.sa_mask); sigint.sa_flags = SA_NODEFER; sigaction (SIGINT, &sigint, &oldact); sigterm.sa_handler = (SIGFUNC) tty_onsig; sigemptyset (&sigterm.sa_mask); sigterm.sa_flags = SA_NODEFER; sigaction (SIGTERM, &sigterm, &oldact); #else sigint = (SIGFUNC) signal (SIGINT, (SIGFUNC)tty_onsig); sigterm = (SIGFUNC) signal (SIGTERM, (SIGFUNC)tty_onsig); #endif tty_getraw = 1; /* Async mode can be cleared by other processes (e.g. wall), * so reset it on every nonblocking read. This code should * never be executed as KF_NDELAY is handled specially above, * but it does no harm to leave it in here anyway. */ if (kfp->flags & KF_NDELAY) fcntl (*fd, F_SETFL, kfp->io_flags | O_NDELAY); if ((ch = setjmp (jmpbuf)) == 0) { clearerr (fp); ch = getc (fp); } #ifdef FCANCEL if (ch == CTRLC) fcancel (fp); #endif #ifdef USE_SIGACTION sigaction (SIGINT, &oldact, NULL); sigaction (SIGTERM, &oldact, NULL); #else signal (SIGINT, sigint); signal (SIGTERM, sigterm); #endif tty_getraw = 0; /* Clear parity bit just in case raw mode is used. */ outch: op = buf; if (ch == EOF) { *op++ = ch; nbytes = 0; } else { *op++ = (ch & 0177); nbytes = 1; } *op = XEOS; } /* Check for errors and update offsets. If the last char copied * was EOF step on it with an EOS. In nonblocking raw mode EOF is * indistinguishable from a no-data read, but it doesn't matter * especially since a ctrl/d, ctrl/z, etc. typed by the user is * passed through as data. */ if (ferror (fp)) { clearerr (fp); *op = XEOS; nbytes = (kfp->flags&KF_NDELAY && errno==EWOULDBLOCK) ? 0 : XERR; } else { kfp->nbytes += nbytes; switch (*--op) { case NEWLINE: kfp->fpos += kfp->nbytes; kfp->nbytes = 0; break; case EOF: *op = XEOS; break; } } *status = nbytes; return (*status); } /* ZNOTTX -- Return the seek offset of the beginning of the current line * of text (file offset of char following last newline seen). */ int ZNOTTX (XINT *fd, XLONG *offset) { *offset = zfd[*fd].fpos; return ((int) *offset); } /* ZPUTTX -- Put "nchars" characters into the text file "fd". The final * character will always be a newline, unless the FIO line buffer overflowed, * in which case the correct thing to do is to write out the line without * artificially adding a newline. We do not check for newlines in the text, * hence ZNOTTX will return the offset of the next write, which will be the * offset of the beginning of a line of text only if we are called to write * full lines of text. */ int ZPUTTX ( XINT *fd, /* file to be written to */ XCHAR *buf, /* data to be output */ XINT *nchars, /* nchars to write to file */ XINT *status /* return status */ ) { register FILE *fp; register int nbytes; register struct fiodes *kfp = &zfd[*fd]; struct ttyport *port; int count, ch; XCHAR *ip; char *cp; count = nbytes = *nchars; port = (struct ttyport *) kfp->port; fp = kfp->fp; /* Clear raw mode if raw mode is set, the output file is a tty, and * more than one character is being written. We must get an exact * match to the RAWOFF string for the string to be recognized. * The string is recognized and removed whether or not raw mode is * in effect. The RAWON sequence may also be issued to turn raw * mode on. The SETREDRAW sequence is used to permit an automatic * screen redraw when a suspended process receives SIGCONT. */ if ((*buf == '\033' && count == LEN_RAWCMD) || count == LEN_SETREDRAW) { /* Note that we cannot use strcmp since buf is XCHAR. */ /* The disable rawmode sequence. */ for (ip=buf, cp=RAWOFF; *cp && (*ip == *cp); ip++, cp++) ; if (*cp == EOS) { tty_reset (port); *status = XOK; return (XOK); } /* The enable rawmode sequence. The control sequence is followed * by a single modifier character, `D' denoting a normal blocking * character read, and `N' nonblocking character reads. */ for (ip=buf, cp=RAWON; *cp && (*ip == *cp); ip++, cp++) ; if (*cp == EOS) { tty_rawon (port, (*ip++ == 'N') ? KF_NDELAY : 0); *status = XOK; return (XOK); } /* The set-redraw control sequence. If the redraw code is * set to a nonnull value, that value will be returned to the * reader in the next GETC call following a process * suspend/continue, as if the code had been typed by the user. */ for (ip=buf, cp=SETREDRAW; *cp && (*ip == *cp); ip++, cp++) ; if (*cp == EOS) { if (port) port->redraw = *ip; *status = XOK; return (XOK); } } /* Do not check for EOS; merely output the number of characters * requested. Checking for EOS prevents output of nulls, used for * padding to create delays on terminals. We must pass all legal * ASCII chars, i.e., all chars in the range 0-127. The results of * storing non-ASCII chars in a text file are system dependent. * If the host is not ASCII then a lookup table reference should be * added to this loop to map ASCII chars to the host character set. * * The uio_bwrite function moves the characters in a block and is * more efficient than character at a time output with putc, if the * amount of text to be moved is large. */ if (nbytes < 10) { for (ip=buf; --nbytes >= 0; ) { ch = *ip++; putc (ch, fp); } } else uio_bwrite (fp, buf, nbytes); kfp->fpos += count; /* If an error occurred while writing to the file, clear the error * on the host stream and report a file write error to the caller. * Ignore the special case of an EBADF occuring while writing to the * terminal, which occurs when a background job writes to the terminal * after the user has logged out. */ if (ferror (fp)) { clearerr (fp); if (errno == EBADF && (fp == stdout || fp == stderr)) *status = count; else *status = XERR; } else *status = count; return (*status); } /* ZSEKTX -- Seek on a text file to the character offset given by a prior * call to ZNOTTX. This offset should always refer to the beginning of a line. */ int ZSEKTX (XINT *fd, XLONG *znottx_offset, XINT *status) { register struct fiodes *kfp = &zfd[*fd]; /* Clear the byte counter, used to keep track of offsets within * a line of text when reading sequentially. */ kfp->nbytes = 0; /* Ignore seeks to the beginning or end of file on special devices * (pipes or terminals). A more specific seek on such a device * is an error. */ if (kfp->flags & KF_NOSEEK) { /* Seeks are illegal on this device. Seeks to BOF or EOF are * permitted but are ignored. */ switch (*znottx_offset) { case XBOF: case XEOF: kfp->fpos = 0; break; default: kfp->fpos = ERR; } } else { /* Seeks are permitted on this device. The seek offset is BOF, * EOF, or a value returned by ZNOTTX. */ switch (*znottx_offset) { case XBOF: kfp->fpos = fseek (kfp->fp, 0L, 0); break; case XEOF: kfp->fpos = fseek (kfp->fp, 0L, 2); break; default: kfp->fpos = fseek (kfp->fp, *znottx_offset, 0); } } if (kfp->fpos == ERR) { *status = XERR; } else if (kfp->flags & KF_NOSEEK) { kfp->fpos = 0; *status = XOK; } else { kfp->fpos = ftell (kfp->fp); *status = XOK; } return (*status); } /* ZSTTTX -- Get file status for a text file. */ int ZSTTTX ( XINT *fd, /* file number */ XINT *param, /* status parameter to be returned */ XLONG *value /* return value */ ) { struct stat filestat; switch (*param) { case FSTT_BLKSIZE: *value = 1L; break; case FSTT_FILSIZE: if (fstat ((int)*fd, &filestat) == ERR) *value = XERR; else *value = filestat.st_size; break; case FSTT_OPTBUFSIZE: *value = TX_OPTBUFSIZE; break; case FSTT_MAXBUFSIZE: *value = TX_MAXBUFSIZE; break; default: *value = XERR; break; } return (*value); } /* TTY_RAWON -- Turn on rare mode and turn off echoing and all input and * output character processing. Interrupts are caught and the interrupt * character is returned like any other character. Save sg_flags for * subsequent restoration. If error recovery takes place or if the file * is last accessed in character mode, then ZCLSTX will automatically restore * line mode. */ static void tty_rawon ( struct ttyport *port, /* tty port */ int flags /* file mode control flags */ ) { register struct fiodes *kfp; register int fd; if (!port) return; fd = port->chan; kfp = &zfd[fd]; if (!(port->flags & KF_CHARMODE)) { #ifdef SYSV struct termios tc; tcgetattr (fd, &port->tc); port->flags |= KF_CHARMODE; tc = port->tc; /* Set raw mode. */ tc.c_lflag &= ~(0 | ICANON | ECHO | ECHOE | ECHOK | ECHONL); tc.c_iflag &= ~(0 | ICRNL | INLCR | IUCLC); tc.c_oflag |= (0 | TAB3 | OPOST | ONLCR); tc.c_oflag &= ~(0 | OCRNL | ONOCR | ONLRET); tc.c_cc[VMIN] = 1; tc.c_cc[VTIME] = 0; tc.c_cc[VLNEXT] = 0; tcsetattr (fd, TCSADRAIN, &tc); #else struct sgttyb tc; ioctl (fd, TIOCGETP, &tc); port->flags |= KF_CHARMODE; port->tc = tc; /* Set raw mode in the terminal driver. */ if ((flags & KF_NDELAY) && !(kfp->flags & KF_NDELAY)) tc.sg_flags |= (RAW|TANDEM); else tc.sg_flags |= CBREAK; tc.sg_flags &= ~(ECHO|CRMOD); ioctl (fd, TIOCSETN, &tc); #endif /* Set pointer to raw mode tty device. */ lastdev = port; /* Post signal handlers to clear/restore raw mode if process is * suspended. */ #ifdef USE_SIGACTION sigtstp.sa_handler = (SIGFUNC) tty_stop; sigemptyset (&sigtstp.sa_mask); sigtstp.sa_flags = SA_NODEFER; sigaction (SIGINT, &sigtstp, &oldact); sigcont.sa_handler = (SIGFUNC) tty_continue; sigemptyset (&sigcont.sa_mask); sigcont.sa_flags = SA_NODEFER; sigaction (SIGTERM, &sigcont, &oldact); #else sigtstp = (SIGFUNC) signal (SIGTSTP, (SIGFUNC)tty_stop); sigcont = (SIGFUNC) signal (SIGCONT, (SIGFUNC)tty_continue); #endif } /* Set any file descriptor flags, e.g., for nonblocking reads. */ if ((flags & KF_NDELAY) && !(kfp->flags & KF_NDELAY)) { kfp->io_flags = fcntl (fd, F_GETFL, 0); fcntl (fd, F_SETFL, kfp->io_flags | O_NDELAY); kfp->flags |= KF_NDELAY; } else if (!(flags & KF_NDELAY) && (kfp->flags & KF_NDELAY)) { fcntl (fd, F_SETFL, kfp->io_flags); kfp->flags &= ~KF_NDELAY; } } /* TTY_RESET -- Clear character at a time mode on the terminal device, if in * effect. This will restore normal line oriented terminal i/o, even if raw * mode i/o was set on the physical device when the ioctl status flags were * saved. */ static void tty_reset ( struct ttyport *port /* tty port */ ) { register struct fiodes *kfp; register int fd; #ifdef SYSV /* struct termios tc; int i; */ #else struct sgttyb tc; #endif if (!port) return; fd = port->chan; kfp = &zfd[fd]; #ifdef SYSV /* Restore saved port status. */ if (port->flags & KF_CHARMODE) tcsetattr (fd, TCSADRAIN, &port->tc); #else if (ioctl (fd, TIOCGETP, &tc) == -1) return; if (!(port->flags & KF_CHARMODE)) port->tc = tc; tc.sg_flags = (port->tc.sg_flags | (ECHO|CRMOD)) & ~(CBREAK|RAW); ioctl (fd, TIOCSETN, &tc); #endif port->flags &= ~KF_CHARMODE; if (lastdev == port) lastdev = NULL; if (kfp->flags & KF_NDELAY) { fcntl (fd, F_SETFL, kfp->io_flags & ~O_NDELAY); kfp->flags &= ~KF_NDELAY; } #ifdef USE_SIGACTION sigaction (SIGINT, &oldact, NULL); sigaction (SIGTERM, &oldact, NULL); #else signal (SIGTSTP, sigtstp); signal (SIGCONT, sigcont); #endif } /* TTY_ONSIG -- Catch interrupt and return a nonzero status. Active only while * we are reading from the terminal in raw mode. */ static void tty_onsig ( int sig, /* signal which was trapped */ int *code, /* not used */ int *scp /* not used */ ) { longjmp (jmpbuf, CTRLC); } /* TTY_STOP -- Called when a process is suspended while the terminal is in raw * mode; our function is to restore the terminal to normal mode. */ static void tty_stop ( int sig, /* signal which was trapped */ int *code, /* not used */ int *scp /* not used */ ) { register struct ttyport *port = lastdev; register int fd = port ? port->chan : 0; /* register struct fiodes *kfp = port ? &zfd[fd] : NULL; */ #ifdef SYSV struct termios tc; #else struct sgttyb tc; #endif if (!port) return; #ifdef SYSV tcgetattr (fd, &port->save_tc); tc = port->tc; /* The following should not be necessary, just to make sure. */ tc.c_iflag = (port->tc.c_iflag | ICRNL); tc.c_oflag = (port->tc.c_oflag | OPOST); tc.c_lflag = (port->tc.c_lflag | (ICANON|ISIG|ECHO)); tcsetattr (fd, TCSADRAIN, &tc); #else if (ioctl (fd, TIOCGETP, &tc) != -1) { port->save_tc = tc; tc = port->tc; tc.sg_flags = (port->tc.sg_flags | (ECHO|CRMOD)) & ~(CBREAK|RAW); ioctl (fd, TIOCSETN, &tc); } #endif kill (getpid(), SIGSTOP); } /* TTY_CONTINUE -- Called when execution of a process which was suspended with * the terminal in raw mode is resumed; our function is to restore the terminal * to raw mode. */ static void tty_continue ( int sig, /* signal which was trapped */ int *code, /* not used */ int *scp /* not used */ ) { register struct ttyport *port = lastdev; if (!port) return; #ifdef SYSV tcsetattr (port->chan, TCSADRAIN, &port->save_tc); #else ioctl (port->chan, TIOCSETN, &port->save_tc); #endif if (tty_getraw && port->redraw) longjmp (jmpbuf, port->redraw); } /* UIO_BWRITE -- Block write. Pack xchars into chars and write the data out * as a block with fwrite. The 4.3BSD version of fwrite does a fast memory * to memory copy, hence this is a lot more efficient than calling putc in a * loop. */ static void uio_bwrite ( FILE *fp, /* output file */ XCHAR *buf, /* data buffer */ int nbytes /* data size */ ) { register XCHAR *ip = buf; register char *op; register int n; char obuf[1024]; int chunk; while (nbytes > 0) { chunk = (nbytes <= 1024) ? nbytes : 1024; for (op=obuf, n=chunk; --n >= 0; ) *op++ = *ip++; fwrite (obuf, 1, chunk, fp); nbytes -= chunk; } }