Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!uunet!mcsun!ukc!edcastle!aiai!richard From: richard@aiai.ed.ac.uk (Richard Tobin) Newsgroups: comp.windows.x Subject: Re: I'm looking for code to invoke a slave xterm Message-ID: <3250@skye.ed.ac.uk> Date: 21 Aug 90 13:43:15 GMT References: <2247@esquire.UUCP> Reply-To: richard@aiai.UUCP (Richard Tobin) Organization: AIAI, University of Edinburgh, Scotland Lines: 332 In article <2247@esquire.UUCP> yost@esquire.UUCP (David A. Yost) writes: >I'd like to fork and exec an xterm that has no shell >running in it and whose stdin and stdout are hooked up >to two file descriptors in my process. Anyone done this? Yes, but you have to do it through a pseudo-terminal. Here's a program that illustrates it. -- Richard /* slavewindow.c * * Copyright Richard Tobin / AIAI 1990. * May be freely distributed if this notice remains intact. * * Creates a window (xterm) and accepts commands to display text, * move cursor, etc. Also returns keys typed in the window. * Commands are: * * clear clear the screen * move r c move to row r, col c * string ... display the string (the rest of the line) * char c display the character with ascii code c * format ... set the format for returning characters typed * raw set raw mode (characters returned immediately) * cooked set cooked mode (characters not returned until return typed) * echo set echo mode * noecho set noecho mode (useful with raw) * * The arguments to string and format may contain C style escapes (\n etc). * * The default format for returning characters is * char(%d).\n * In cooked mode, typing an end-of-file causes -1 to be returned. * * Closing the pipe to the process will cause the window to exit. * */ #include #include #include #include #include extern int errno; void process_command(), process_input(), return_char(), copy_term(), escape(); int create_window(); int win; char format[80] = "char(%d).\n"; main() { win = create_window(); while(1) { int stat, bits; char buf[500]; bits = ((1 << 0) | (1 << win)); stat = select(win+1, &bits, (int *)0, (int *)0, (struct timeval *)0); if(stat == -1 && errno == EINTR) continue; if(stat == -1) { perror("slavewindow: select"); exit(1); } if(bits & (1 << win)) { switch(read(win, buf, 1)) { case 0: /* eof typed */ return_char(-1); break; case 1: return_char(((int)buf[0] & 255)); break; default: if(errno == EINTR) break; perror("slavewindow: window read"); exit(1); } } if(bits & (1 << 0)) { stat = read(0, buf, sizeof(buf)); if(stat == 0) /* eof means exit */ exit(0); if(stat == -1) { if(errno == EINTR) continue; perror("slavewindow: pipe read"); exit(1); } process_input(buf, stat); } } } void process_input(data, dlen) char *data; int dlen; { static char buf[500]; static int len = 0; while(dlen > 0 && len < sizeof(buf)-1) { buf[len++] = *data++; dlen--; if(buf[len-1] == '\n') { buf[len] = '\0'; process_command(buf); len = 0; } } } #define Prefix(pre, str) (strncmp(pre, str, sizeof(pre)-1) == 0) #define CLEAR "\033[H\033[2J" #define MOVE "\033[%d;%dH" void process_command(command) char *command; { int row, col, c; char buf[100], *s; struct sgttyb sgtty; if(Prefix("clear", command)) write(win, CLEAR, sizeof(CLEAR)-1); else if(Prefix("move", command)) { sscanf(command, "move %d %d", &row, &col); sprintf(buf, MOVE, row+1, col+1); write(win, buf, strlen(buf)); } else if(Prefix("string ", command)) { s = command+7; escape(s); write(win, s, strlen(s)-1); } else if(Prefix("char", command)) { sscanf(command, "char %d", &c); buf[0] = c; write(win, buf, 1); } else if(Prefix("raw", command)) { ioctl(win, TIOCGETP, &sgtty); sgtty.sg_flags |= RAW; ioctl(win, TIOCSETP, &sgtty); } else if(Prefix("cooked", command)) { ioctl(win, TIOCGETP, &sgtty); sgtty.sg_flags &= ~RAW; ioctl(win, TIOCSETP, &sgtty); } else if(Prefix("echo", command)) { ioctl(win, TIOCGETP, &sgtty); sgtty.sg_flags |= ECHO; ioctl(win, TIOCSETP, &sgtty); } else if(Prefix("noecho", command)) { ioctl(win, TIOCGETP, &sgtty); sgtty.sg_flags &= ~ECHO; ioctl(win, TIOCSETP, &sgtty); } else if(Prefix("format ", command)) { s = command+7; escape(s); strncpy(format, s, sizeof(format)); format[strlen(format)-1] = '\0'; /* get rid of the linefeed */ } else fprintf(stderr, "slavewindow: unknown command %s\n", command); } void escape(s) /* interpret \n etc */ char *s; { char *p = s; int escaped = 0; while(*s) { if(escaped) { char *a = "aenrt\\", *b = "\007\033\n\r\t\\"; char *i = strchr(a, *s); *p++ = i ? b[i-a] : *s; escaped = 0; } else { if(*s == '\\') escaped = 1; else *p++ = *s; } s++; } *p = '\0'; } void return_char(c) { char buf[100]; sprintf(buf, format, c); write(1, buf, strlen(buf)); } #define MASTER "/dev/ptyXY" #define SLAVE "/dev/ttyXY" int create_window() { static char master_path[] = MASTER, slave_path[] = SLAVE, buf[100]; int i, master, slave, oldtty; strcpy(master_path, MASTER); strcpy(slave_path, SLAVE ); for(i=0; i <= ('s'-'p') * 16; ++i) { master_path[strlen(MASTER)-2] = i / 16 + 'p'; master_path[strlen(MASTER)-1] = "0123456789abcdef"[i & 15]; master = open(master_path,O_RDWR); if(master >= 0) break; } if(master < 0) { fprintf(stderr, "slavewindow: can't get a pty\n"); exit(1); } slave_path[strlen(SLAVE)-2] = master_path[strlen(MASTER)-2]; slave_path[strlen(SLAVE)-1] = master_path[strlen(MASTER)-1]; sprintf(buf, "-S%s%d", &slave_path[strlen(slave_path)-2], master); if(vfork() == 0) { setpgrp(0, getpid()); execlp("xterm", "xterm", buf, 0); fprintf(stderr, "xterm exec failed\n"); _exit(1); } close(master); slave = open(slave_path, O_RDWR, 0); read(slave, buf, sizeof(buf)); /* suck xterm junk before echo on */ oldtty = open("/dev/tty", O_RDWR, 0); copy_term(oldtty, slave); close(oldtty); return slave; } void copy_term(from, to) int from, to; { int err; struct sgttyb sb; struct tchars tc; struct ltchars ltc; int bits; err = ioctl(from, TIOCGETP, &sb); err |= ioctl(to, TIOCSETP, &sb); err |= ioctl(from, TIOCGETC, &tc); err |= ioctl(to, TIOCSETC, &tc); err |= ioctl(from, TIOCLGET, &bits); err |= ioctl(to, TIOCLSET, &bits); err |= ioctl(from, TIOCGLTC, <c); err |= ioctl(to, TIOCSLTC, <c); if(err != 0) perror("can't copy terminal modes"); } -- Richard Tobin, JANET: R.Tobin@uk.ac.ed AI Applications Institute, ARPA: R.Tobin%uk.ac.ed@nsfnet-relay.ac.uk Edinburgh University. UUCP: ...!ukc!ed.ac.uk!R.Tobin