/***********************/ /* dvorak.c 1.00 */ /* by Adam M. Costello */ /* **********************/ /* Starts a new shell in a new pty and */ /* translates from qwerty to Dvorak between */ /* the original tty and the new pty. */ /* */ /* Warning: Does not re-map the control */ /* characters because that's impossible. */ /* */ /* Warning: This code is BSD-specific. */ /********************************************/ #include #include #include #include #include #include #include #include #define BUFSZ 256 char map[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 95, 35, 36, 37, 38, 45, 40, 41, 42, 43, 119, 93, 118, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 83, 115, 87, 61, 86, 90, 64, 65, 88, 74, 69, 62, 85, 73, 68, 67, 72, 84, 78, 77, 66, 82, 76, 34, 80, 79, 89, 71, 75, 60, 81, 70, 58, 47, 92, 125, 94, 91, 96, 97, 120, 106, 101, 46, 117, 105, 100, 99, 104, 116, 110, 109, 98, 114, 108, 39, 112, 111, 121, 103, 107, 44, 113, 102, 59, 63, 124, 123, 126, 127 }; void fail(msg) char *msg; { fputs(msg,stderr); exit(1); } char *nopty = "Couldn't open pty.\n"; struct termios tiobak; void bailout() { tcsetattr(0, 0, &tiobak); /* Restore original terminal settings. */ kill(0,SIGKILL); /* Kill all processes in this group. */ } main() { struct termios tio; struct winsize ws; int c, r, td, pd, shpid, copid, nr, nw; char *p, *q, *shname, buf[BUFSZ]; static char name[] = { "/dev/ptyxx" }; fd_set pdset, stdset; /* First, make sure stdin is a tty: */ if (!isatty(0)) fail("stdin is not a tty.\n"); /* Next, find an available pty: */ for (c = 0; c < 176 ; ++c) { name[8] = 'p' + c / 16; r = c % 16; name[9] = r < 10 ? '0' + r : 'a' - 10 + r; pd = open(name, O_RDWR | O_NONBLOCK); if (pd >= 0) break; if (errno == ENOENT) fail(nopty); } if (pd < 0) fail(nopty); /* Make a backup copy of the tty's settings and size, */ /* then change them so that no input processing, echoing, */ /* or buffering is done, and bit 7 is always 0: */ tcgetattr(0, &tio); ioctl(0, TIOCGWINSZ, &ws); tiobak = tio; tio.c_lflag &= ~ISIG & ~ICANON & ~ECHO; tio.c_iflag |= ISTRIP; tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; tcsetattr(0, 0, &tio); /* Before we spawn any child processes we must set up our SIGCHLD */ /* handler, which will clean up if any of our children die: */ signal(SIGCHLD,bailout); /* The first child becomes the shell process: */ shpid = fork(); if (shpid < 0) fail("Couldn't fork().\n"); if (shpid == 0) { close(pd); /* Shell doesn't need the master. */ setsid(); /* Shell should be a session leader. */ name[5] = 't'; td = open(name, O_RDWR); /* Slave becomes controlling terminal. */ if (td < 0) fail("Couldn't open tty.\n"); tcsetattr(td, 0, &tiobak); /* Use original terminal settings */ ioctl(td, TIOCSWINSZ, &ws); /* and size. */ dup2(td,0); /* Slave should be stdin, */ dup2(td,1); /* stdout, */ dup2(td,2); /* and stderr. */ if (td > 2) close(td); /* No other descriptors needed. */ shname = getenv("SHELL"); /* Get user's preferred shell. */ if (!shname) shname = "/bin/sh"; /* Use Bourne shell as default. */ p = shname + strlen(shname); while (p != shname && *p != '/') --p; if (*p == '/') ++p; execl(shname,p,NULL); /* Exec shell with basename as arg 0. */ fail("Couldn't execute shell.\n"); } FD_ZERO(&pdset); FD_SET(pd, &pdset); /* The next child becomes the process which */ /* copies the pty output to stdout: */ copid = fork(); if (copid < 0) { kill(shpid,SIGKILL); tcsetattr(0, 0, &tiobak); fail("Couldn't fork().\n"); } if (copid == 0) { FD_ZERO(&stdset); FD_SET(1, &stdset); for (;;) { nr = read(pd,buf,BUFSZ); /* Read some bytes. */ if (nr <= 0) /* If none were read, */ select(pd+1, &pdset, NULL, NULL, NULL); /* wait until some can be. */ else { /* But if some were read, */ p = buf; q = buf + nr; do { /* repeat: */ nw = write(1,p,nr); /* Write some bytes. */ if (nw <= 0) /* If none were written, */ select(2, NULL, &stdset, NULL, NULL); /* wait until some can be. */ else p += nw; } while (p < q); /* until all are written. */ } } /* Loop forever. */ } /* The parent process reads from stdin, does */ /* the Dvorak mapping, and writes to the pty: */ FD_ZERO(&stdset); FD_SET(0, &stdset); for(;;) { nr = read(0,buf,BUFSZ); /* Read some bytes. */ if (nr <= 0) /* If none were read, */ select(1, &stdset, NULL, NULL, NULL); /* wait until some can be. */ else { /* But if some were read, */ q = buf + nr; /* Translate the bytes */ for (p = buf; p < q; ++p) *p = map[*p]; /* from qwerty to Dvorak. */ p = buf; do { /* repeat: */ nw = write(pd,p,nr); /* Write some bytes. */ if (nw <= 0) /* If none were written, */ select(pd+1, NULL, &pdset, NULL, NULL); /* wait until some can be. */ else p += nw; } while (p < q); /* until all are written. */ } } /* Loop forever. */ }