home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1619 < prev    next >
Encoding:
Internet Message Format  |  1990-12-28  |  34.4 KB

  1. From: maart@cs.vu.nl (Maarten Litmaath)
  2. Newsgroups: alt.sources
  3. Subject: mtty - mimic a tty - a public domain pseudo tty utility
  4. Message-ID: <7171@star.cs.vu.nl>
  5. Date: 24 Jul 90 20:50:38 GMT
  6.  
  7. : This is a shar archive.  Extract with sh, not csh.
  8. : This archive ends with exit, so do not worry about trailing junk.
  9. : --------------------------- cut here --------------------------
  10. PATH=/bin:/usr/bin:/usr/ucb
  11. echo Extracting 'data_flow.c'
  12. sed 's/^X//' > 'data_flow.c' << '+ END-OF-FILE ''data_flow.c'
  13. X#include    "extern.h"
  14. X
  15. X
  16. Xvoid    data_flow(pty, tty, death)
  17. Xint    pty, tty, *death;
  18. X{
  19. X    fd_set    rfds, wfds;
  20. X    int    width, flags, left = 0, status = 0;
  21. X
  22. X
  23. X    width = getdtablesize();
  24. X    (void) signal(SIGPIPE, SIG_IGN);
  25. X
  26. X    flags = fcntl(pty, F_GETFL, 0);
  27. X    flags |= FNDELAY;
  28. X    fcntl(pty, F_SETFL, flags);
  29. X
  30. X    for (;;) {
  31. X        FD_ZERO(&rfds);
  32. X        FD_ZERO(&wfds);
  33. X        FD_SET(death[0], &rfds);
  34. X        if (Do_stdin) {
  35. X            /*
  36. X             * If there is nothing left from a previous read from
  37. X             * stdin, wait for data to arrive on stdin, else wait
  38. X             * till the pty becomes writable again.
  39. X             * left < 0 denotes an error has occurred.
  40. X             */
  41. X            if (left == 0)
  42. X                FD_SET(0, &rfds);
  43. X            else if (left > 0)
  44. X                FD_SET(pty, &wfds);
  45. X        }
  46. X        if (Do_stdout && status == 0)
  47. X            FD_SET(pty, &rfds);
  48. X        if (select(width, &rfds, &wfds, (fd_set *) 0,
  49. X            (struct timeval *) 0) < 0) {
  50. X            /*
  51. X             * `impossible'
  52. X             */
  53. X            fprintf(stderr, "%s: select: %s\n", Prog,
  54. X                strerror(errno));
  55. X            exit(1);
  56. X        }
  57. X        /*
  58. X         * If our parent has detected the death of the other child,
  59. X         * death[0] will be ready for reading.
  60. X         */
  61. X        if (FD_ISSET(death[0], &rfds))
  62. X            status = do_stdout(pty, 1);
  63. X        if (FD_ISSET(pty, &rfds))
  64. X            status = do_stdout(pty, 0);
  65. X        if (FD_ISSET(pty, &wfds) || FD_ISSET(0, &rfds))
  66. X            left = do_stdin(pty, tty);
  67. X    }
  68. X}
  69. + END-OF-FILE data_flow.c
  70. chmod 'u=rw,g=r,o=r' 'data_flow.c'
  71. set `wc -c 'data_flow.c'`
  72. count=$1
  73. case $count in
  74. 1255)    :;;
  75. *)    echo 'Bad character count in ''data_flow.c' >&2
  76.         echo 'Count should be 1255' >&2
  77. esac
  78. echo Extracting 'do_child.c'
  79. sed 's/^X//' > 'do_child.c' << '+ END-OF-FILE ''do_child.c'
  80. X#include    "extern.h"
  81. X
  82. X
  83. Xvoid    do_child(pty, tty, name, message, argp)
  84. Xint    pty, tty, *message;
  85. Xchar    *name, **argp;
  86. X{
  87. X    int    fd;
  88. X    char    c;
  89. X
  90. X
  91. X    /*
  92. X     * Wait for a message that all is well, i.e. our parent has been
  93. X     * able to create another pipe and to fork again.
  94. X     */
  95. X    close(message[1]);
  96. X    if (read(message[0], &c, 1) != 1)
  97. X        exit(1);
  98. X    close(message[0]);
  99. X
  100. X    close(pty);
  101. X    if (Do_stdin)
  102. X        dup2(tty, 0);
  103. X    if (Do_stdout)
  104. X        dup2(tty, 1);
  105. X    if (Do_stderr)
  106. X        dup2(tty, 2);
  107. X    close(tty);
  108. X
  109. X    if (Mode == INTERACTIVE) {
  110. X        if ((fd = open("/dev/tty", 1)) >= 0) {
  111. X            /*
  112. X             * Relinquish controlling tty.
  113. X             */
  114. X            ioctl(fd, TIOCNOTTY, (caddr_t) 0);
  115. X            close(fd);
  116. X        } else
  117. X            setpgrp(0, 0);
  118. X        /*
  119. X         * Acquire new controlling tty.
  120. X         * Our process group has been set to 0, either implicitly
  121. X         * by the TIOCNOTTY ioctl(), or explicitly by the setpgrp().
  122. X         * When we reopen the pseudo terminal, it will become our
  123. X         * controlling tty and our process group gets set to the tty
  124. X         * process group.  If the latter had not been set yet, it
  125. X         * will have been set to our process id first.
  126. X         */
  127. X        if ((fd = open(name, 2)) < 0) {
  128. X            fprintf(stderr, "%s: open %s failed: %s\n",
  129. X                Prog, name, strerror(errno));
  130. X            exit(1);
  131. X        }
  132. X        close(fd);
  133. X    }
  134. X
  135. X    execvp(*argp, argp);
  136. X    fprintf(stderr, "%s: cannot exec %s: %s\n",
  137. X        Prog, *argp, strerror(errno));
  138. X    exit(1);
  139. X}
  140. + END-OF-FILE do_child.c
  141. chmod 'u=rw,g=r,o=r' 'do_child.c'
  142. set `wc -c 'do_child.c'`
  143. count=$1
  144. case $count in
  145. 1323)    :;;
  146. *)    echo 'Bad character count in ''do_child.c' >&2
  147.         echo 'Count should be 1323' >&2
  148. esac
  149. echo Extracting 'do_parent.c'
  150. sed 's/^X//' > 'do_parent.c' << '+ END-OF-FILE ''do_parent.c'
  151. X#include    "extern.h"
  152. X
  153. X
  154. Xvoid    do_parent(pty, tty, message)
  155. Xint    pty, tty, *message;
  156. X{
  157. X    union    wait    w, dummy;
  158. X    int    child, n, death[2];
  159. X    char    c = '!';
  160. X
  161. X
  162. X    close(message[0]);
  163. X
  164. X    if (pipe(death) < 0) {
  165. X        fprintf(stderr, "%s: cannot create a pipe: %s\n",
  166. X            Prog, strerror(errno));
  167. X        exit(1);
  168. X    }
  169. X
  170. X    switch (child = fork()) {
  171. X    case -1:
  172. X        fprintf(stderr, "%s: cannot fork: %s\n",
  173. X            Prog, strerror(errno));
  174. X        exit(1);
  175. X    case 0:
  176. X        close(message[1]);
  177. X        close(death[1]);
  178. X        data_flow(pty, tty, death);
  179. X    }
  180. X
  181. X    /*
  182. X     * Send a message to the other child, indicating all is well.
  183. X     */
  184. X    write(message[1], &c, 1);
  185. X    close(message[1]);
  186. X    close(death[0]);
  187. X
  188. X    close(pty);
  189. X    close(tty);
  190. X
  191. X    while ((n = wait(&w)) != Pid)
  192. X        if (n == child)
  193. X            child = -1;
  194. X    /*
  195. X     * To signal the child that the process has died, we close our side
  196. X     * of the pipe.  A read() in the child will return 0 immediately.
  197. X     * Then wait for the child to finish the IO.
  198. X     */
  199. X    if (child != -1) {
  200. X        close(death[1]);
  201. X        while (wait(&dummy) != child)
  202. X            ;
  203. X    }
  204. X
  205. X    exit(w.w_termsig ? w.w_termsig + 0200 : w.w_retcode);
  206. X}
  207. + END-OF-FILE do_parent.c
  208. chmod 'u=rw,g=r,o=r' 'do_parent.c'
  209. set `wc -c 'do_parent.c'`
  210. count=$1
  211. case $count in
  212. 1043)    :;;
  213. *)    echo 'Bad character count in ''do_parent.c' >&2
  214.         echo 'Count should be 1043' >&2
  215. esac
  216. echo Extracting 'do_stdin.c'
  217. sed 's/^X//' > 'do_stdin.c' << '+ END-OF-FILE ''do_stdin.c'
  218. X#include    "extern.h"
  219. X
  220. X
  221. Xint    do_stdin(pty, tty)
  222. Xint    pty, tty;
  223. X{
  224. X    static    char    buf[MAXBUF], *leftbuf;
  225. X    static    int    left = 0, first = 1;
  226. X    static    void    reset_tty();
  227. X    int    pass2 = 0;
  228. X
  229. X
  230. X    for (;;) {
  231. X        if (left < 0 || left > 0
  232. X            && (left = to_pty(pty, &leftbuf, left)) != 0 || pass2)
  233. X            return left;
  234. X
  235. X        leftbuf = buf;
  236. X        if ((left = read(0, buf, MAXBUF)) < 0)
  237. X            fprintf(stderr, "%s: read stdin: %s\n",
  238. X                Prog, strerror(errno));
  239. X        else if (left == 0) {
  240. X            if (Mode == INTERACTIVE && first) {
  241. X                reset_tty(tty);
  242. X                first = 0;
  243. X            }
  244. X            *buf = Tc[Mode].t_eofc;
  245. X            left = 1;
  246. X        }
  247. X        pass2 = 1;
  248. X    }
  249. X}
  250. X
  251. X
  252. Xstatic    void    reset_tty(tty)
  253. Xint    tty;
  254. X{
  255. X    ioctl(tty, TIOCGETP, (caddr_t) &Sg[INTERACTIVE]);
  256. X    if (Sg[INTERACTIVE].sg_flags & (RAW | CBREAK)) {
  257. X        Sg[INTERACTIVE].sg_flags &= ~(RAW | CBREAK);
  258. X        ioctl(tty, TIOCSETN, (caddr_t) &Sg[INTERACTIVE]);
  259. X    }
  260. X    ioctl(tty, TIOCGETC, (caddr_t) &Tc[INTERACTIVE]);
  261. X    if (Tc[INTERACTIVE].t_eofc == -1 || Tc[INTERACTIVE].t_eofc == 0) {
  262. X        Tc[INTERACTIVE].t_eofc = Eofc;
  263. X        ioctl(tty, TIOCSETC, (caddr_t) &Tc[INTERACTIVE]);
  264. X    }
  265. X}
  266. + END-OF-FILE do_stdin.c
  267. chmod 'u=rw,g=r,o=r' 'do_stdin.c'
  268. set `wc -c 'do_stdin.c'`
  269. count=$1
  270. case $count in
  271. 1034)    :;;
  272. *)    echo 'Bad character count in ''do_stdin.c' >&2
  273.         echo 'Count should be 1034' >&2
  274. esac
  275. echo Extracting 'do_stdout.c'
  276. sed 's/^X//' > 'do_stdout.c' << '+ END-OF-FILE ''do_stdout.c'
  277. X#include    "extern.h"
  278. X
  279. X
  280. Xint    do_stdout(pty, child_died)
  281. Xint    pty, child_died;
  282. X{
  283. X    char    buf[MAXBUF];
  284. X    int    n;
  285. X
  286. X
  287. X    for (;;) {
  288. X        if ((n = read(pty, buf, (int) sizeof buf)) <= 0)
  289. X            exit(!child_died);
  290. X        if (write(1, buf, n) < 0) {
  291. X            if (errno == EPIPE) {
  292. X                if (child_died)
  293. X                    exit(1);
  294. X                kill(Pid, SIGPIPE);
  295. X            } else
  296. X                fprintf(stderr, "%s: write stdout: %s\n",
  297. X                    Prog, strerror(errno));
  298. X            return -1;
  299. X        }
  300. X        if (!child_died)
  301. X            break;
  302. X    }
  303. X    return 0;
  304. X}
  305. + END-OF-FILE do_stdout.c
  306. chmod 'u=rw,g=r,o=r' 'do_stdout.c'
  307. set `wc -c 'do_stdout.c'`
  308. count=$1
  309. case $count in
  310. 451)    :;;
  311. *)    echo 'Bad character count in ''do_stdout.c' >&2
  312.         echo 'Count should be 451' >&2
  313. esac
  314. echo Extracting 'extern.c'
  315. sed 's/^X//' > 'extern.c' << '+ END-OF-FILE ''extern.c'
  316. X#include    "extern.h"
  317. X
  318. X#undef    CTRL
  319. X#define        CTRL(x)        ((x) & 037)
  320. X#undef    DEL
  321. X#define        DEL        0177
  322. X
  323. Xint    Lw[] = {
  324. X    (LLITOUT | LPASS8),
  325. X    (LCRTERA | LCRTKIL | LCTLECH)
  326. X};
  327. Xstruct    sgttyb    Sg[] = {
  328. X    { B9600, B9600, -1, -1, 0 },
  329. X    { B9600, B9600, CTRL('H'), CTRL('U'), ECHO | CRMOD | ANYP | XTABS }
  330. X};
  331. Xstruct    tchars    Tc[] = {
  332. X    { -1, -1, -1, -1, CTRL('D'), -1 },
  333. X    { DEL, CTRL('\\'), CTRL('S'), CTRL('Q'), CTRL('D'), -1 }
  334. X};
  335. Xstruct    ltchars    Ltc[] = {
  336. X    { -1, -1, -1, -1, -1, -1 },
  337. X    { CTRL('Z'), CTRL('Y'), CTRL('R'), CTRL('O'), CTRL('W'), CTRL('V') }
  338. X};
  339. Xstruct    winsize    Win = {
  340. X    24, 80, 0, 0
  341. X};
  342. Xchar    Eofc = -1;
  343. Xint    Mode = !INTERACTIVE;
  344. Xint    Do_stdin = 0, Do_stdout = 0, Do_stderr = 0;
  345. Xint    Pid;
  346. Xchar    *Prog;
  347. + END-OF-FILE extern.c
  348. chmod 'u=rw,g=r,o=r' 'extern.c'
  349. set `wc -c 'extern.c'`
  350. count=$1
  351. case $count in
  352. 684)    :;;
  353. *)    echo 'Bad character count in ''extern.c' >&2
  354.         echo 'Count should be 684' >&2
  355. esac
  356. echo Extracting 'main.c'
  357. sed 's/^X//' > 'main.c' << '+ END-OF-FILE ''main.c'
  358. X#include    "extern.h"
  359. X
  360. X
  361. Xstatic    char    Usage[] =
  362. X        "Usage: %s [-i] [-012] [-E \\ooo] [-V] [--] command\n";
  363. X
  364. X
  365. Xstatic    void    usage()
  366. X{
  367. X    fprintf(stderr, Usage, Prog);
  368. X    exit(2);
  369. X}
  370. X
  371. X
  372. Xstatic    void    geteofc(p)
  373. Xchar    *p;
  374. X{
  375. X    int    i;
  376. X
  377. X
  378. X    if (!p || sscanf(p, "\\%o", &i) != 1)
  379. X        usage();
  380. X    if (i == -1 || i == 0) {
  381. X        fprintf(stderr, "%s: the EOF character cannot be -1 or 0\n",
  382. X            Prog);
  383. X        exit(2);
  384. X    }
  385. X    Eofc = i;
  386. X}
  387. X
  388. X
  389. Xmain(argc, argv)
  390. Xint    argc;
  391. Xchar    **argv;
  392. X{
  393. X    int    stop = 0, pty, tty, message[2];
  394. X    char    name[PTYMAXNAME], **argp, *p, c;
  395. X
  396. X
  397. X    Prog = argv[0];
  398. X
  399. X    for (argp = &argv[1]; !stop && *argp && **argp == '-'; ++argp)
  400. X        for (p = *argp + 1; c = *p; ++p)
  401. X            switch (c) {
  402. X            case 'E':
  403. X                geteofc(*++argp);
  404. X                break;
  405. X            case 'V':
  406. X                printf("%s\n", Version);
  407. X                exit(0);
  408. X            case 'i':
  409. X                Mode = INTERACTIVE;
  410. X                break;
  411. X            case '0':
  412. X                Do_stdin = 1;
  413. X                break;
  414. X            case '1':
  415. X                Do_stdout = 1;
  416. X                break;
  417. X            case '2':
  418. X                Do_stderr = 1;
  419. X                break;
  420. X            case '-':
  421. X                stop = 1;
  422. X                break;
  423. X            default:
  424. X                usage();
  425. X            }
  426. X
  427. X    if (!(Do_stdin | Do_stdout | Do_stderr)) {
  428. X        Do_stdin = Do_stdout = 1;        /* default */
  429. X        if (Mode == INTERACTIVE)
  430. X            Do_stderr = 1;
  431. X    }
  432. X
  433. X    if (!*argp)
  434. X        usage();
  435. X
  436. X    if ((pty = getpty(&tty, name)) < 0) {
  437. X        fprintf(stderr, "%s: cannot allocate a pty.  Exit.\n", Prog);
  438. X        exit(1);
  439. X    }
  440. X
  441. X    set_modes(tty);
  442. X
  443. X    if (pipe(message) < 0) {
  444. X        fprintf(stderr, "%s: cannot create a pipe: %s\n",
  445. X            Prog, strerror(errno));
  446. X        exit(1);
  447. X    }
  448. X
  449. X    switch (Pid = fork()) {
  450. X    case -1:
  451. X        fprintf(stderr, "%s: cannot fork: %s\n",
  452. X            Prog, strerror(errno));
  453. X        exit(1);
  454. X    case 0:
  455. X        do_child(pty, tty, name, message, argp);
  456. X    }
  457. X
  458. X    do_parent(pty, tty, message);
  459. X}
  460. + END-OF-FILE main.c
  461. chmod 'u=rw,g=r,o=r' 'main.c'
  462. set `wc -c 'main.c'`
  463. count=$1
  464. case $count in
  465. 1614)    :;;
  466. *)    echo 'Bad character count in ''main.c' >&2
  467.         echo 'Count should be 1614' >&2
  468. esac
  469. echo Extracting 'set_modes.c'
  470. sed 's/^X//' > 'set_modes.c' << '+ END-OF-FILE ''set_modes.c'
  471. X#include    "extern.h"
  472. X
  473. X
  474. Xvoid    set_modes(tty)
  475. Xint    tty;
  476. X{
  477. X    int    fd, devtty;
  478. X
  479. X
  480. X    if (Mode == INTERACTIVE) {
  481. X        if ((fd = devtty = open("/dev/tty", 0)) < 0)
  482. X            fd = Do_stdin ? Do_stdout ? 2 : 1 : 0;
  483. X        if (ioctl(fd, TIOCLGET, (caddr_t) &Lw[INTERACTIVE]) == 0) {
  484. X            ioctl(fd, TIOCGETC, (caddr_t) &Tc[INTERACTIVE]);
  485. X            ioctl(fd, TIOCGLTC, (caddr_t) &Ltc[INTERACTIVE]);
  486. X            ioctl(fd, TIOCGETP, (caddr_t) &Sg[INTERACTIVE]);
  487. X            Sg[INTERACTIVE].sg_flags |= ECHO;
  488. X            Sg[INTERACTIVE].sg_flags &= ~(RAW | CBREAK);
  489. X            ioctl(fd, TIOCGWINSZ, (caddr_t) &Win);
  490. X            ioctl(tty, TIOCSWINSZ, (caddr_t) &Win);
  491. X            if (devtty >= 0)
  492. X                close(devtty);
  493. X        }
  494. X    } else
  495. X        ioctl(tty, TIOCEXCL, (caddr_t) 0);
  496. X        /*
  497. X         * Put the tty into exclusive use.  There is a race
  498. X         * condition, but this ioctl() is better than nothing,
  499. X         * I guess.  Opening the master should set the access mode
  500. X         * of the slave (via the third argument to open(2))!
  501. X         * Furthermore opening the tty side before the pty side
  502. X         * should be impossible.
  503. X         */
  504. X
  505. X    if (Eofc != -1)
  506. X        Tc[Mode].t_eofc = Eofc;
  507. X    else
  508. X        Eofc = Tc[Mode].t_eofc;
  509. X
  510. X    ioctl(tty, TIOCLSET, (caddr_t) &Lw[Mode]);
  511. X    ioctl(tty, TIOCSETC, (caddr_t) &Tc[Mode]);
  512. X    ioctl(tty, TIOCSLTC, (caddr_t) &Ltc[Mode]);
  513. X    ioctl(tty, TIOCSETP, (caddr_t) &Sg[Mode]);
  514. X}
  515. + END-OF-FILE set_modes.c
  516. chmod 'u=rw,g=r,o=r' 'set_modes.c'
  517. set `wc -c 'set_modes.c'`
  518. count=$1
  519. case $count in
  520. 1245)    :;;
  521. *)    echo 'Bad character count in ''set_modes.c' >&2
  522.         echo 'Count should be 1245' >&2
  523. esac
  524. echo Extracting 'to_pty.c'
  525. sed 's/^X//' > 'to_pty.c' << '+ END-OF-FILE ''to_pty.c'
  526. X#include    "extern.h"
  527. X
  528. X
  529. X/*
  530. X * Try to write `n' bytes to `pty', starting at `*buf'.
  531. X * Increment `*buf' with the number of bytes actually written.
  532. X * Return the number of bytes still to be written, -1 on error.
  533. X */
  534. Xint    to_pty(pty, buf, n)
  535. Xint    pty, n;
  536. Xchar    **buf;
  537. X{
  538. X    register char    *p;
  539. X    register int    m;
  540. X    char    *q;
  541. X    int    backslash;
  542. X
  543. X
  544. X    for (backslash = 0; n > 0 && !backslash; *buf += m, n -= m) {
  545. X        /*
  546. X         * Write 1 line at a time.
  547. X         */
  548. X        for (p = *buf, m = n; m > 0 && *p++ != '\n'; m--)
  549. X            ;
  550. X        if (*--p == '\\') {
  551. X            for (q = p; p > *buf && *--p == '\\'; )
  552. X                ;
  553. X            m = q - p;
  554. X            if (*p == '\\')
  555. X                m++;
  556. X            /*
  557. X             * Only if the number of backslashes ending the
  558. X             * block is uneven, we have to watch out: the tty
  559. X             * driver translates a backslash-EOF sequence to a
  560. X             * literal EOF character.
  561. X             */
  562. X            p = (m & 1) ? q : q + 1;
  563. X            backslash = 1;
  564. X        } else
  565. X            p++;
  566. X        if ((m = write(pty, *buf, p - *buf)) < 0)
  567. X            return (errno == EWOULDBLOCK) ? n : -1;
  568. X    }
  569. X
  570. X    /*
  571. X     * If the last chunk didn't end in a `break',
  572. X     * write an EOF character if we're not interactive.
  573. X     */
  574. X    if (*--p != '\n' && *p != Eofc && Mode != INTERACTIVE)
  575. X        if (write(pty, &Eofc, 1) < 0)
  576. X            return (errno == EWOULDBLOCK) ? n : -1;
  577. X
  578. X    /*
  579. X     * Write the final backslash.
  580. X     */
  581. X    if (backslash) {
  582. X        if (write(pty, *buf, 1) < 0)
  583. X            return (errno == EWOULDBLOCK) ? n : -1;
  584. X        *buf += 1;
  585. X        n -= 1;
  586. X    }
  587. X
  588. X    return n;
  589. X}
  590. + END-OF-FILE to_pty.c
  591. chmod 'u=rw,g=r,o=r' 'to_pty.c'
  592. set `wc -c 'to_pty.c'`
  593. count=$1
  594. case $count in
  595. 1366)    :;;
  596. *)    echo 'Bad character count in ''to_pty.c' >&2
  597.         echo 'Count should be 1366' >&2
  598. esac
  599. echo Extracting 'version.c'
  600. sed 's/^X//' > 'version.c' << '+ END-OF-FILE ''version.c'
  601. Xchar    Version[] =
  602. X    "@(#)mtty 1.1 90/07/24 Maarten Litmaath @ VU Dept. of CS, Amsterdam";
  603. + END-OF-FILE version.c
  604. chmod 'u=rw,g=r,o=r' 'version.c'
  605. set `wc -c 'version.c'`
  606. count=$1
  607. case $count in
  608. 88)    :;;
  609. *)    echo 'Bad character count in ''version.c' >&2
  610.         echo 'Count should be 88' >&2
  611. esac
  612. echo Extracting 'strerror.c'
  613. sed 's/^X//' > 'strerror.c' << '+ END-OF-FILE ''strerror.c'
  614. Xchar    *strerror(n)
  615. Xint    n;
  616. X{
  617. X    static    char    buf[32];
  618. X    extern    int    sys_nerr;
  619. X    extern    char    *sys_errlist[];
  620. X
  621. X
  622. X    if ((unsigned) n < sys_nerr)
  623. X        return sys_errlist[n];
  624. X    (void) sprintf(buf, "Unknown error %d", n);
  625. X    return buf;
  626. X}
  627. + END-OF-FILE strerror.c
  628. chmod 'u=rw,g=r,o=r' 'strerror.c'
  629. set `wc -c 'strerror.c'`
  630. count=$1
  631. case $count in
  632. 217)    :;;
  633. *)    echo 'Bad character count in ''strerror.c' >&2
  634.         echo 'Count should be 217' >&2
  635. esac
  636. echo Extracting 'getpty.c'
  637. sed 's/^X//' > 'getpty.c' << '+ END-OF-FILE ''getpty.c'
  638. X#include    <sys/file.h>
  639. X#include    <errno.h>
  640. X#include    <stdio.h>
  641. X
  642. Xstatic    char    pty_name[] = "/dev/ptyPQ";
  643. X
  644. X#define        T_INDEX        (sizeof pty_name - 6)
  645. X#define        P_INDEX        (sizeof pty_name - 3)
  646. X#define        Q_INDEX        (sizeof pty_name - 2)
  647. X
  648. Xstruct    range {
  649. X    char    lower, upper;
  650. X};
  651. Xstatic    struct    range    P_range[] = {
  652. X    { 'p', 'z' },
  653. X    { 'P', 'Z' },
  654. X    { 'a', 'o' },
  655. X    { 'A', 'O' },
  656. X};
  657. Xstatic    struct    range    Q_range[] = {
  658. X    { '0', '9' },
  659. X    { 'a', 'f' },
  660. X    { 'A', 'F' },
  661. X    { 'g', 'z' },
  662. X    { 'G', 'Z' },
  663. X};
  664. Xextern    int    errno;
  665. X
  666. X
  667. X#define        SIZE(a)        (sizeof(a) / sizeof((a)[0]))
  668. X
  669. X#ifdef    DEBUG
  670. X#define     FPRINTF(x)    fprintf x
  671. X#else    /* DEBUG */
  672. X#define     FPRINTF(x)
  673. X#endif    /* DEBUG */
  674. X#if    defined(DEBUG) || defined(LIST)
  675. Xextern    char    *strerror();
  676. X#endif    /* defined(DEBUG) || defined(LIST) */
  677. X
  678. X
  679. Xint    getpty(tty, s)
  680. Xint    *tty;
  681. Xchar    *s;
  682. X{
  683. X    register char    P, P_max;
  684. X    register struct range    *pr;
  685. X    int    pty;
  686. X    extern    int    try_pty();
  687. X
  688. X
  689. X    strcpy(s, pty_name);
  690. X    for (pr = P_range; pr < &P_range[SIZE(P_range)]; ++pr) {
  691. X        for (P = pr->lower, P_max = pr->upper; P <= P_max; ++P) {
  692. X            s[P_INDEX] = P;
  693. X            switch (pty = try_pty(tty, s)) {
  694. X            case -1:
  695. X                continue;
  696. X            case -2:
  697. X                break;
  698. X            default:
  699. X                return pty;
  700. X            }
  701. X            break;
  702. X        }
  703. X    }
  704. X    return -1;
  705. X}
  706. X
  707. X
  708. Xstatic    int    try_pty(tty, s)
  709. Xint    *tty;
  710. Xchar    *s;
  711. X{
  712. X    extern    int    errno;
  713. X    register char    Q, Q_max;
  714. X    register struct range    *qr;
  715. X    int    pty;
  716. X
  717. X
  718. X    for (qr = Q_range; qr < &Q_range[SIZE(Q_range)]; ++qr) {
  719. X        for (Q = qr->lower, Q_max = qr->upper; Q <= Q_max; ++Q) {
  720. X            s[Q_INDEX] = Q;
  721. X#ifdef    LIST
  722. X            fprintf(stderr, "%s", s);
  723. X            if (access(s, 0) == 0) {
  724. X                fprintf(stderr, "\n");
  725. X                continue;
  726. X            }
  727. X            fprintf(stderr, ": %s\n", strerror(errno));
  728. X            if (Q == qr->lower && qr == Q_range)
  729. X                return -2;
  730. X            break;
  731. X#else    /* LIST */
  732. X            if ((pty = open(s, 2)) >= 0) {
  733. X
  734. X                FPRINTF((stderr, "opening %s succeeded\n", s));
  735. X
  736. X                s[T_INDEX] = 't';
  737. X                if (access(s, R_OK | W_OK) == 0
  738. X                    && (*tty = open(s, 2)) >= 0) {
  739. X                    FPRINTF((stderr,
  740. X                        "opening %s succeeded\n", s));
  741. X                    return pty;
  742. X                }
  743. X
  744. X                FPRINTF((stderr, "opening %s failed: %s\n",
  745. X                    s, strerror(errno)));
  746. X
  747. X                close(pty);
  748. X                s[T_INDEX] = 'p';
  749. X                continue;
  750. X            }
  751. X            FPRINTF((stderr, "opening %s failed: %s\n",
  752. X                s, strerror(errno)));
  753. X            if (errno == ENOENT) {
  754. X                if (Q == qr->lower && qr == Q_range)
  755. X                    return -2;
  756. X                break;
  757. X            }
  758. X#endif    /* LIST */
  759. X        }
  760. X    }
  761. X    return -1;
  762. X}
  763. X
  764. X
  765. X#if    defined(DEBUG) || defined(LIST)
  766. X
  767. Xmain()
  768. X{
  769. X    char    buf[sizeof pty_name];
  770. X    int    pty, tty;
  771. X
  772. X    pty = getpty(&tty, buf);
  773. X}
  774. X
  775. X#endif    /* defined(DEBUG) || defined(LIST) */
  776. X
  777. X#ifdef    STRERROR
  778. X
  779. Xchar    *strerror(errno)
  780. X{
  781. X    extern    int    sys_nerr;
  782. X    extern    char    *sys_errlist[];
  783. X    static    char    buf[32];
  784. X
  785. X    if ((unsigned) errno < sys_nerr)
  786. X        return sys_errlist[errno];
  787. X    sprintf(buf, "Unknown error %d", errno);
  788. X    return buf;
  789. X}
  790. X
  791. X#endif    /* STRERROR */
  792. + END-OF-FILE getpty.c
  793. chmod 'u=rw,g=r,o=r' 'getpty.c'
  794. set `wc -c 'getpty.c'`
  795. count=$1
  796. case $count in
  797. 2730)    :;;
  798. *)    echo 'Bad character count in ''getpty.c' >&2
  799.         echo 'Count should be 2730' >&2
  800. esac
  801. echo Extracting 'extern.h'
  802. sed 's/^X//' > 'extern.h' << '+ END-OF-FILE ''extern.h'
  803. X#include    <sys/param.h>
  804. X#include    <sys/time.h>
  805. X#include    <sys/wait.h>
  806. X#include    <sys/file.h>
  807. X#include    <errno.h>
  808. X#include    <signal.h>
  809. X#include    <sys/ioctl.h>
  810. X#include    <stdio.h>
  811. X
  812. X
  813. X#ifndef    CANBSIZ
  814. X#define        CANBSIZ        256
  815. X#endif    /* !CANBSIZ */
  816. X#define        MAXBUF        (CANBSIZ - 1)
  817. X#define        PTYMAXNAME    32
  818. X#define        INTERACTIVE    1
  819. X
  820. X
  821. Xextern    int    Lw[];
  822. Xextern    struct    sgttyb    Sg[];
  823. Xextern    struct    tchars    Tc[];
  824. Xextern    struct    ltchars    Ltc[];
  825. Xextern    struct    winsize    Win;
  826. Xextern    char    Eofc;
  827. Xextern    int    Mode;
  828. Xextern    int    Do_stdin, Do_stdout, Do_stderr;
  829. Xextern    int    Pid;
  830. Xextern    char    *Prog, Version[];
  831. Xextern    int    errno;
  832. X
  833. Xextern    char    *strerror();
  834. Xextern    int    getpty(), do_stdin(), do_stdout(), to_pty();
  835. Xextern    void    set_modes(), do_child(), do_parent(), data_flow();
  836. + END-OF-FILE extern.h
  837. chmod 'u=rw,g=r,o=r' 'extern.h'
  838. set `wc -c 'extern.h'`
  839. count=$1
  840. case $count in
  841. 725)    :;;
  842. *)    echo 'Bad character count in ''extern.h' >&2
  843.         echo 'Count should be 725' >&2
  844. esac
  845. echo Extracting 'notty.c'
  846. sed 's/^X//' > 'notty.c' << '+ END-OF-FILE ''notty.c'
  847. X#include    <sys/types.h>
  848. X#include    <sys/ioctl.h>
  849. X#include    <stdio.h>
  850. X
  851. X/*
  852. X * Relinquish controlling tty.  Exec args.
  853. X */
  854. X
  855. Xmain(argc, argv)
  856. Xint    argc;
  857. Xchar    **argv;
  858. X{
  859. X    int    fd;
  860. X
  861. X
  862. X    if (argc < 2) {
  863. X        fprintf(stderr, "Usage: %s command\n", argv[0]);
  864. X        exit(2);
  865. X    }
  866. X    if ((fd = open("/dev/tty", 1)) >= 0) {
  867. X        ioctl(fd, TIOCNOTTY, (caddr_t) 0);
  868. X        close(fd);
  869. X        /*
  870. X         * Now prevent our process group getting set to that of
  871. X         * the first tty opened by the command.
  872. X         */
  873. X        setpgrp(0, getpid());
  874. X    }
  875. X    execvp(argv[1], &argv[1]);
  876. X    perror(argv[1]);
  877. X    exit(1);
  878. X}
  879. + END-OF-FILE notty.c
  880. chmod 'u=rw,g=r,o=r' 'notty.c'
  881. set `wc -c 'notty.c'`
  882. count=$1
  883. case $count in
  884. 538)    :;;
  885. *)    echo 'Bad character count in ''notty.c' >&2
  886.         echo 'Count should be 538' >&2
  887. esac
  888. echo Extracting 'frontend.c'
  889. sed 's/^X//' > 'frontend.c' << '+ END-OF-FILE ''frontend.c'
  890. X#include    <sys/types.h>
  891. X#include    <signal.h>
  892. X#include    <sys/ioctl.h>
  893. X#include    <stdio.h>
  894. X
  895. X
  896. Xchar    *Prog;
  897. X
  898. X
  899. Xvoid    usage()
  900. X{
  901. X    fprintf(stderr, "Usage: %s [-ec]\n", Prog);
  902. X    exit(1);
  903. X}
  904. X
  905. X
  906. Xmain(argc, argv)
  907. Xint    argc;
  908. Xchar    **argv;
  909. X{
  910. X    register char    esc = '~', susp, eof, killc, intr, quit;
  911. X    char    c, prev = '\n';
  912. X    int    save = 0;
  913. X    struct    sgttyb    osg, nsg;
  914. X    struct    tchars    tc;
  915. X    struct    ltchars    ltc;
  916. X
  917. X
  918. X    Prog = argv[0];
  919. X
  920. X    if (argc > 2)
  921. X        usage();
  922. X    if (argc == 2 && (argv[1][0] != '-' || argv[1][1] != 'e'
  923. X        || !(esc = argv[1][2]) || argv[1][3]))
  924. X        usage();
  925. X
  926. X    ioctl(0, TIOCGETP, (caddr_t) &osg);
  927. X    killc = osg.sg_kill;
  928. X    nsg = osg;
  929. X    nsg.sg_flags |= RAW;
  930. X    nsg.sg_flags &= ~ECHO;
  931. X    ioctl(0, TIOCSETN, (caddr_t) &nsg);
  932. X    ioctl(0, TIOCGETC, (caddr_t) &tc);
  933. X    intr = tc.t_intrc;
  934. X    quit = tc.t_quitc;
  935. X    eof = tc.t_eofc;
  936. X    ioctl(0, TIOCGLTC, (caddr_t) <c);
  937. X    susp = ltc.t_suspc;
  938. X
  939. X    signal(SIGPIPE, SIG_IGN);
  940. X
  941. X#define        BOL(c)        (c == '\n' || c == '\r' || c == killc \
  942. X                || c == eof || c == intr || c == quit \
  943. X                || c == susp)
  944. X
  945. X    while (read(0, &c, 1) == 1) {
  946. X        if (c == '.' || c == susp) {
  947. X            if (save) {
  948. X                if (c == '.')
  949. X                    break;
  950. X                ioctl(0, TIOCSETN, (caddr_t) &osg);
  951. X                kill(0, SIGTSTP);
  952. X                ioctl(0, TIOCSETN, (caddr_t) &nsg);
  953. X                prev = '\n';
  954. X                continue;
  955. X            }
  956. X        } else if (c == esc) {
  957. X            if (BOL(prev)) {
  958. X                prev = esc;
  959. X                save = 1;
  960. X                continue;
  961. X            }
  962. X        }
  963. X        if (save) {
  964. X            if (write(1, &prev, 1) != 1)
  965. X                break;
  966. X            save = 0;
  967. X        }
  968. X        if (write(1, &c, 1) != 1)
  969. X            break;
  970. X        prev = c;
  971. X    }
  972. X
  973. X    ioctl(0, TIOCSETN, (caddr_t) &osg);
  974. X}
  975. + END-OF-FILE frontend.c
  976. chmod 'u=rw,g=r,o=r' 'frontend.c'
  977. set `wc -c 'frontend.c'`
  978. count=$1
  979. case $count in
  980. 1497)    :;;
  981. *)    echo 'Bad character count in ''frontend.c' >&2
  982.         echo 'Count should be 1497' >&2
  983. esac
  984. echo Extracting 'irsh'
  985. sed 's/^X//' > 'irsh' << '+ END-OF-FILE ''irsh'
  986. X#!/bin/sh
  987. X
  988. Xtest $# != 1 && {
  989. X    echo "Usage: `basename $0` machine" >&2
  990. X    exit 2
  991. X}
  992. X
  993. Xcase ${SHELL-sh} in
  994. X*csh)
  995. X    term="set term=$TERM"
  996. X    ;;
  997. X*)
  998. X    term="TERM=$TERM; export TERM"
  999. Xesac
  1000. X
  1001. Xfrontend | rsh $1 "$term;" exec mtty -i ${SHELL-sh}
  1002. X
  1003. X# `rsh' may exit first, making `frontend' a child of init;
  1004. X# if so, it will be killed when it attempts to read from or reset the
  1005. X# tty while the job control shell already has taken it over
  1006. X
  1007. Xstty -raw echo
  1008. + END-OF-FILE irsh
  1009. chmod 'u=rwx,g=rx,o=rx' 'irsh'
  1010. set `wc -c 'irsh'`
  1011. count=$1
  1012. case $count in
  1013. 433)    :;;
  1014. *)    echo 'Bad character count in ''irsh' >&2
  1015.         echo 'Count should be 433' >&2
  1016. esac
  1017. echo Extracting 'myscript'
  1018. sed 's/^X//' > 'myscript' << '+ END-OF-FILE ''myscript'
  1019. X#!/bin/sh
  1020. X
  1021. Xopen='>'
  1022. X
  1023. Xcase $1 in
  1024. X-a)
  1025. X    open='>>'
  1026. X    shift
  1027. Xesac
  1028. X
  1029. Xfile=${1-myscript.out}
  1030. X
  1031. Xeval exec 3$open '$file'
  1032. X
  1033. Xecho "Script started, file is $file"
  1034. X
  1035. X(echo -n 'Script started on '; date) >&3
  1036. Xexec 3>&-
  1037. X
  1038. Xstty raw -echo
  1039. Xmtty -i ${SHELL-sh} | tee -a $file
  1040. Xstty -raw echo
  1041. X
  1042. X(echo ''; echo -n 'Script done on '; date) >> $file
  1043. X
  1044. Xecho "Script done, file is $file"
  1045. + END-OF-FILE myscript
  1046. chmod 'u=rwx,g=rx,o=rx' 'myscript'
  1047. set `wc -c 'myscript'`
  1048. count=$1
  1049. case $count in
  1050. 353)    :;;
  1051. *)    echo 'Bad character count in ''myscript' >&2
  1052.         echo 'Count should be 353' >&2
  1053. esac
  1054. echo Extracting 'Makefile'
  1055. sed 's/^X//' > 'Makefile' << '+ END-OF-FILE ''Makefile'
  1056. X# Makefile for mtty and notty
  1057. X
  1058. X# if your system doesn't have a strerror(3) function,
  1059. X# uncomment the next 2 lines
  1060. XSTRERROR =    strerror.o
  1061. XDSTRERROR =    -DSTRERROR
  1062. X
  1063. XSRCS =        data_flow.c do_child.c do_parent.c do_stdin.c do_stdout.c \
  1064. X        extern.c main.c set_modes.c to_pty.c version.c strerror.c \
  1065. X        getpty.c extern.h notty.c frontend.c irsh myscript Makefile \
  1066. X        mtty.1 Implementation Sketch README notty.1
  1067. X
  1068. XOBJS =        data_flow.o do_child.o do_parent.o do_stdin.o do_stdout.o \
  1069. X        extern.o main.o set_modes.o to_pty.o version.o $(STRERROR) \
  1070. X        getpty.o
  1071. X
  1072. Xmtty:        $(OBJS)
  1073. X        $(CC) $(OBJS) -o mtty
  1074. X
  1075. Xfrontend:
  1076. X        $(CC) frontend.c -o frontend
  1077. X
  1078. Xptylist:    getpty.c
  1079. X        $(CC) -DLIST $(DSTRERROR) getpty.c -o ptylist
  1080. X
  1081. Xptytest:    getpty.c
  1082. X        $(CC) -DDEBUG $(DSTRERROR) getpty.c -o ptytest
  1083. X
  1084. Xnotty:
  1085. X        $(CC) notty.c -o notty
  1086. X
  1087. Xshar:
  1088. X        shar $(SRCS) > mtty.sh
  1089. X
  1090. Xtar:
  1091. X        tar cvf mtty.t $(SRCS)
  1092. X
  1093. Xdata_flow.o:    extern.h
  1094. Xdo_child.o:    extern.h
  1095. Xdo_parent.o:    extern.h
  1096. Xdo_stdout.o:    extern.h
  1097. Xdo_stdin.o:    extern.h
  1098. Xextern.o:    extern.h
  1099. Xmain.o:        extern.h
  1100. Xset_modes.o:    extern.h
  1101. Xto_pty.o:    extern.h
  1102. + END-OF-FILE Makefile
  1103. chmod 'u=rw,g=r,o=r' 'Makefile'
  1104. set `wc -c 'Makefile'`
  1105. count=$1
  1106. case $count in
  1107. 1043)    :;;
  1108. *)    echo 'Bad character count in ''Makefile' >&2
  1109.         echo 'Count should be 1043' >&2
  1110. esac
  1111. echo Extracting 'mtty.1'
  1112. sed 's/^X//' > 'mtty.1' << '+ END-OF-FILE ''mtty.1'
  1113. X.\" Uses -man.
  1114. X.TH MTTY 1 Jul\ 24\ 1990
  1115. X.SH NAME
  1116. Xmtty \- mimic a tty and execute a command
  1117. X.SH SYNOPSIS
  1118. X.B mtty
  1119. X[
  1120. X.B \-i
  1121. X] [
  1122. X.B \-012
  1123. X] [
  1124. X.BI "\-E \e" ooo
  1125. X] [
  1126. X.B \-V
  1127. X] [
  1128. X.B \-\-
  1129. X]
  1130. X.I command
  1131. X.SH DESCRIPTION
  1132. X.I Mtty
  1133. Xallocates a \fIpseudo terminal\fR (see \fIpty\fR(4)) and executes the given
  1134. X.I command
  1135. Xwith (a subset of) its standard input, output and error output connected to
  1136. Xthe pseudo terminal: default \fIstdin\fR and \fIstdout\fR, and under
  1137. Xthe \-\fIi\fR option \fIstderr\fR as well.  \fIMtty\fR then waits
  1138. Xfor \fIcommand\fR to finish and reports
  1139. Xthe exit status in its own exit status.  If \fIcommand\fR died due to a signal,
  1140. Xthe exit status of \fImtty\fR will be the signal number plus 0200.
  1141. X.PP
  1142. XDefault the pseudo terminal operates in non-interactive mode.  In this mode
  1143. Xall special processing of input and output characters is turned off, except
  1144. Xfor the EOF character on input.  Although the pseudo tty actually has been put
  1145. Xinto \fIcanonical\fR mode, it behaves as if it were in \fIraw\fR mode, in the
  1146. Xsense that every character written on the standard input of \fImtty\fR is
  1147. Xmade available to \fIcommand\fR immediately.  Non-interactive operation is
  1148. Xtypically used in pipelines in which \fIfully\fR (\fIblock\fR-) \fIbuffered
  1149. XIO\fR is undesirable: \fImtty\fR
  1150. Xtries to lure the application into using \fIline-buffered\fR output.  Normally
  1151. Xthis scheme will be successful, because the \fIstdio\fR(3) library
  1152. Xautomatically selects line-buffering when the standard output is a terminal.
  1153. X.SH OPTIONS
  1154. X.TP
  1155. X.B \-i
  1156. XSelect interactive mode: the pseudo terminal will be made the \fIcontrolling
  1157. Xterminal\fR (see \fItty\fR(4)) for \fIcommand\fR.  The terminal modes,
  1158. Xincluding for example the special characters like interrupt and quit, will be
  1159. Xcopied from the controlling tty of \fImtty\fR itself (if possible), with the
  1160. Xfollowing exception: the pseudo tty will always be put into canonical
  1161. Xecho mode, i.e. its \fBRAW\fR and \fBCBREAK\fR bits will be turned off and
  1162. Xits \fBECHO\fR bit will be turned on.
  1163. X.TP
  1164. X.B \-012
  1165. XConnect only the specified \fIfile descriptors\fR to the pseudo tty.
  1166. X.TP
  1167. X.BI "\-E \e" ooo
  1168. XSet the EOF character of the pseudo tty to the byte whose octal representation
  1169. Xis `\fIooo\fR' (default ^D).
  1170. X.TP
  1171. X.B \-V
  1172. XPrint the version number and exit.
  1173. X.SH EXAMPLES
  1174. XThe following Bourne shell command will hang without producing any output at
  1175. Xall:
  1176. X.sp
  1177. X.RS
  1178. X.nf
  1179. Xtail -f /etc/passwd | tr a A |
  1180. Xwhile read line
  1181. Xdo
  1182. X    echo $line
  1183. Xdone
  1184. X.fi
  1185. X.RE
  1186. X.sp
  1187. XExplanation:
  1188. X.IP \-
  1189. Xbecause the output of `\fItr\fR' is a pipe, it will be block-buffered
  1190. X.IP \-
  1191. Xthe last 10 lines of /etc/passwd together contain fewer bytes than the
  1192. Xblocksize used by the \fIstdio\fR(3) library
  1193. X.IP \-
  1194. Xnow the \fIwhile\fR loop must wait until the output buffer of `\fItr\fR' fills
  1195. Xup, which will not happen unless `\fItr\fR' receives enough extra input (or
  1196. XEOF), which cannot happen while `\fItail\fR' is waiting for /etc/passwd to
  1197. Xgrow.
  1198. X.PP
  1199. XTo lure `\fItr\fR' into using line-buffered output we can use \fImtty\fR:
  1200. X.sp
  1201. X.RS
  1202. X.nf
  1203. Xtail -f /etc/passwd | mtty tr a A |
  1204. Xwhile read line
  1205. Xdo
  1206. X    echo $line
  1207. Xdone
  1208. X.fi
  1209. X.RE
  1210. X.sp
  1211. XSince only the output of `\fItr\fR' needs to be connected to a pseudo tty,
  1212. Xwe could have invoked \fImtty\fR with the \-\fI1\fR option.
  1213. X.PP
  1214. XHow \fImtty\fR can be used interactively is shown in the next example.
  1215. XThe following commands could be the heart of an alternative implementation
  1216. Xof \fIscript\fR(1):
  1217. X.sp
  1218. X.RS
  1219. X.nf
  1220. Xstty raw \-echo
  1221. Xmtty \-i ${SHELL\-sh} | tee typescript
  1222. Xstty \-raw echo
  1223. X.fi
  1224. X.RE
  1225. X.SH BUGS
  1226. XBecause of problems too complicated to discuss here, \fImtty\fR will close the
  1227. Xpseudo tty channel as soon as \fIcommand\fR has finished, i.e. a \fIbackground
  1228. Xprocess\fR started by \fIcommand\fR will not be able to use the pseudo tty any
  1229. Xlonger.
  1230. X.PP
  1231. XThough in non-interactive operation the pseudo tty will be put into
  1232. X\fIexclusive use\fR by means of a \fBTIOCEXCL\fR \fIioctl\fR(), there is
  1233. Xcurrently no guarantee that the channel will be exclusive indeed: there is a
  1234. Xrace condition and the tty might have been open already before \fImtty\fR
  1235. Xhad even started.
  1236. X.SH AUTHOR
  1237. XMaarten Litmaath @ VU Dept. of CS, Amsterdam
  1238. + END-OF-FILE mtty.1
  1239. chmod 'u=rw,g=r,o=r' 'mtty.1'
  1240. set `wc -c 'mtty.1'`
  1241. count=$1
  1242. case $count in
  1243. 4096)    :;;
  1244. *)    echo 'Bad character count in ''mtty.1' >&2
  1245.         echo 'Count should be 4096' >&2
  1246. esac
  1247. echo Extracting 'Implementation'
  1248. sed 's/^X//' > 'Implementation' << '+ END-OF-FILE ''Implementation'
  1249. XSome notes on the implementation of mtty.
  1250. X-----------------------------------------
  1251. XAs a read() from the pty (master) side of a pty-tty pair will return -1 with
  1252. Xerrno set to EIO, as soon as the last file descriptor to the tty (slave) side
  1253. Xhas been closed, the master has to keep the tty side open himself all the
  1254. Xtime!  But that means he cannot detect the slave side closing its last fd to
  1255. Xthe tty: as the master still has the tty open, a read() from the pty will not
  1256. Xreturn.  My solution is to let the master first fork() off the command, then
  1257. Xfork() off the IO handler, and wait() for both of them itself.  De IO handler
  1258. Xselect()s on 3 file descriptors (at most): stdin, the pty and a pipe set up
  1259. Xbetween the master and itself.  If stdin is selected, data are read from fd 0
  1260. Xand written to the pty to become input for the command.  If the pty is
  1261. Xselected for reading, data read from it will be written on stdout: the output
  1262. Xfrom the command is passed on.  If the pipe is selected for reading, it is a
  1263. Xsignal from the master that the command has finished, so the IO handler can
  1264. Xread any remaining data from the pty and write them to stdout.
  1265. XWe cannot let the master handle the IO, informed of the command's death by
  1266. Xa SIGCHLD: if stdout is a `slow' device and the master would just be writing
  1267. Xto it when the signal arrives, output would be lost; to `hold' the signal
  1268. Xbefore each write() and releasing it immediately afterward would be gross.
  1269. XBy letting the IO handler take care of all IO (including stdin), the master
  1270. Xcan step out of the way: it can get swapped out while wait()ing for its two
  1271. Xchildren to finish.
  1272. XOne more thing: if there should be no activity (when stdin is empty, or the
  1273. Xpty has been filled up with data yet unread by the command, and when there is
  1274. Xno unread output from the command), the IO handler will not take up cpu cycles,
  1275. Xbut instead will hang on the select().  This fact is not so trivial as it
  1276. Xseems; it involves non-blocking IO.
  1277. X
  1278. XBugs in the pty(4) driver.
  1279. X--------------------------
  1280. XThere's no distinction between the tty side being opened for reading and
  1281. Xit being opened for writing: only the fact that it is open counts when
  1282. Xdetermining if a read or write on the pty side should fail.
  1283. X
  1284. XWhen the last tty file descriptor is closed any unread output is lost!
  1285. XThe next read() on the pty fd should return the remaining output instead
  1286. Xof an IO error!
  1287. X
  1288. XOne should be able to open the pty side for reading and writing separately.
  1289. X
  1290. XAccording to pty(4):
  1291. X
  1292. X     Input flow control is  automatically  performed;  a  process
  1293. X     that  attempts  to  write  to  the controller device will be
  1294. X     blocked if too much unconsumed data is buffered on the slave
  1295. X     device.
  1296. X
  1297. XThis does not work if the slave device is in canonical mode: extra input
  1298. Xcharacters are thrown away.
  1299. X
  1300. XTIOCSETN on the tty slave flushes input!
  1301. X
  1302. XAnyone can open the tty slave; the mode of the tty side should be set
  1303. Xaccording to the third argument of open(2)!
  1304. + END-OF-FILE Implementation
  1305. chmod 'u=rw,g=r,o=r' 'Implementation'
  1306. set `wc -c 'Implementation'`
  1307. count=$1
  1308. case $count in
  1309. 2949)    :;;
  1310. *)    echo 'Bad character count in ''Implementation' >&2
  1311.         echo 'Count should be 2949' >&2
  1312. esac
  1313. echo Extracting 'Sketch'
  1314. sed 's/^X//' > 'Sketch' << '+ END-OF-FILE ''Sketch'
  1315. X
  1316. X          +------------------+
  1317. X          |                  |
  1318. X          |                  v
  1319. X    +------+  |  +--------+     +------+
  1320. X    |      | <-- |        | <-- |      | <-- real stdin
  1321. X    | proc |     |tty  pty|     |master|
  1322. X    |      | --> |        | --> |      | --> real stdout
  1323. X    +------+  ^  +--------+     +------+
  1324. X          |                  |
  1325. X          |                  |
  1326. X          +------------------+
  1327. X
  1328. X1) If the process dies, we must deselect stdin and drain the output from the
  1329. X   process, present on the read side of the pty.
  1330. X
  1331. X2) If EOF is detected on stdin, we write the tty's EOF character to the write
  1332. X   side of the pty to signal EOF to the process.
  1333. + END-OF-FILE Sketch
  1334. chmod 'u=rw,g=r,o=r' 'Sketch'
  1335. set `wc -c 'Sketch'`
  1336. count=$1
  1337. case $count in
  1338. 627)    :;;
  1339. *)    echo 'Bad character count in ''Sketch' >&2
  1340.         echo 'Count should be 627' >&2
  1341. esac
  1342. echo Extracting 'README'
  1343. sed 's/^X//' > 'README' << '+ END-OF-FILE ''README'
  1344. XCurrently mtty is BSD-oriented.  I have tested it on SunOS 4.0.3c.
  1345. X
  1346. XCheck the Makefile regarding strerror(3).
  1347. X
  1348. XYou should check if on your system ptys are allocated `the BSD way'.
  1349. XTry `make ptylist ptytest' and run the 2 resulting programs.
  1350. X
  1351. XI have included 2 examples of how mtty can be used: `myscript' is an
  1352. Xalternative implementation of `script'; `irsh' starts an `interactive rsh',
  1353. Xi.e. it is if you rlogin to the remote machine, only you will not get logged
  1354. Xin the utmp file (interesting!), and a `.rhosts' file is required.
  1355. X`frontend.c' is the source of `frontend' (how unexpected!), used by `irsh'.
  1356. X
  1357. X`notty.c' is included as a bonus!  See the manual.
  1358. + END-OF-FILE README
  1359. chmod 'u=rw,g=r,o=r' 'README'
  1360. set `wc -c 'README'`
  1361. count=$1
  1362. case $count in
  1363. 659)    :;;
  1364. *)    echo 'Bad character count in ''README' >&2
  1365.         echo 'Count should be 659' >&2
  1366. esac
  1367. echo Extracting 'notty.1'
  1368. sed 's/^X//' > 'notty.1' << '+ END-OF-FILE ''notty.1'
  1369. X.\" Uses -man.
  1370. X.TH NOTTY 1 Jul\ 24\ 1990
  1371. X.SH NAME
  1372. Xnotty \- detach /dev/tty and execute a command
  1373. X.SH SYNOPSIS
  1374. X.B notty
  1375. X.I command
  1376. X.SH DESCRIPTION
  1377. X.I Notty
  1378. Xdetaches the \fIcontrolling tty\fR (see \fItty\fR(4)) and executes the
  1379. X\fIcommand\fR specified.
  1380. X.SH EXAMPLE
  1381. X.RS
  1382. X.nf
  1383. X# the file `pw' contains the password of `joe'
  1384. X# the file `cmds' contains shell commands,
  1385. X# to be executed by `joe'
  1386. Xnotty su joe cmds < pw
  1387. X.fi
  1388. X.RE
  1389. X.PP
  1390. XThe following form is wrong:
  1391. X.sp
  1392. X.RS
  1393. Xcat pw cmds | notty su joe
  1394. X.RE
  1395. X.sp
  1396. XAs `\fIsu\fR' uses the \fIstdio\fR(3) library to read the password from
  1397. Xstandard input, and as stdin is a pipe, it will be \fIblock-buffered\fR,
  1398. Xand more than 1 line will be read, so part of `\fIcmds\fR' is consumed
  1399. Xby `\fIsu\fR'.
  1400. X.PP
  1401. XThe \fImtty\fR(1) utility cannot help here: if `\fIsu\fR' discovers
  1402. Xstdin is a tty, it will put it into non-echo mode using a \fBTIOCSETP\fR
  1403. X\fIioctl\fR(), which may flush unread input.
  1404. X.SH AUTHOR
  1405. XMaarten Litmaath @ VU Dept. of CS, Amsterdam
  1406. + END-OF-FILE notty.1
  1407. chmod 'u=rw,g=r,o=r' 'notty.1'
  1408. set `wc -c 'notty.1'`
  1409. count=$1
  1410. case $count in
  1411. 971)    :;;
  1412. *)    echo 'Bad character count in ''notty.1' >&2
  1413.         echo 'Count should be 971' >&2
  1414. esac
  1415. exit 0
  1416. --
  1417.  "and with a sudden plop it lands on usenet.  what is it? omigosh, it must[...]
  1418.    be a new user! quick kill it before it multiplies!"      (Loren J. Miller)
  1419.