/* Open a pty, fork a child that frobs the pty's settings then exec()s, and see what we get back in a short conversation with the child. */ #include #include #include #include #include #include #include #include void * lose (char *s) { fprintf(stderr, "%d\n", errno); fprintf(stderr, "%s: %s\n", s, strerror(errno)); exit(1); } /* set some flags on a terminal */ int set_noecho (int fd) { struct termios stermios; if (tcgetattr(fd, &stermios) < 0) return 0; stermios.c_lflag &= ~(ECHO | ECHONL); stermios.c_iflag &= ~(BRKINT); stermios.c_iflag |= (ICANON|ICRNL); stermios.c_oflag |= (OPOST|ONLCR); /* We want CR/LF-terminated lines. */ stermios.c_cc[VERASE]=0177; if (tcsetattr(fd, TCSANOW, &stermios) < 0) return 0; return 1; } /* For some reason, I need to declare this... */ extern char *ptsname(); /* Open a pty with a pipe(2)-like interface. */ int open_pty(int fildes[2]) { /* pty mumbojumbo */ int mtrfd = open("/dev/ptmx", O_RDWR, 0); char *pts; int slvfd; if (mtrfd == -1) return -1; if (grantpt(mtrfd) != 0) return -1; if (unlockpt(mtrfd) != 0) return -1; if ((pts = ptsname(mtrfd)) == NULL) return -1; slvfd = open(pts, O_RDWR, 0666); if (slvfd == -1) return -1; fildes[0] = mtrfd; fildes[1] = slvfd; return 0; } /* yes, this is written somewhat strangely. */ int main (int argc, char **argv) { int fds[2]; pid_t pid; (open_pty(fds) == 0) || lose("open_pty"); /* make a test file. */ (system("echo foo > openpty.test") == 0) || lose("system"); pid = fork(); if (pid < -1) lose("fork"); if (pid == 0) { dup2(fds[1], 0); (set_noecho(0) != 0) || lose("set_noecho"); dup2(fds[1], 1); dup2(fds[1], 2); close(fds[1]); execl("/bin/ed", "/bin/ed", "openpty.test", NULL); } else { FILE *f; int status; (f = fdopen(fds[0], "w+")) || lose("fdopen"); printf("%d\n", fgetc(f)); printf("%d\n", fgetc(f)); printf("%d\n", fgetc(f)); fprintf(f, "q\n"); fflush(f); wait(&status); (unlink("openpty.test")==0) || lose("unlink"); } } /* Expected output (which I get on Linux and NetBSD) 52 13 10 What I get on Solaris: 52 10 52 That is, the child's terminal isn't converting newlines to CR/LFs. */