home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2916 < prev    next >
Encoding:
Text File  |  1991-03-03  |  13.3 KB  |  658 lines

  1. Newsgroups: comp.lang.c,alt.sources
  2. From: scs@adam.mit.edu (Steve Summit)
  3. Subject: trivial "portable" terminal driver interface package (cbreak, noecho)
  4. Message-ID: <1991Mar2.065303.1709@athena.mit.edu>
  5. Date: Sat, 2 Mar 91 06:53:03 GMT
  6.  
  7. This posting contains sources and documentation for a small
  8. package which implements a quasi-portable interface to the
  9. notoriously variegated terminal driver facilities provided by
  10. various operating systems.  This code springs from a widespread
  11. desire, voiced frequently on comp.lang.c (though not, I hasten to
  12. add, by me) for a defined, standard way to ask for certain
  13. services of the terminal driver, so that "simple" programs having
  14. "simple" needs (i.e. character-at-a-time input) would not need to
  15. have #ifdefs in their low-level I/O code for 37 different
  16. operating systems.
  17.  
  18. This is not, by any stretch of the imagination, production-
  19. quality code.  It is intended mainly as a pedagogical example.
  20.  
  21. To use this simple package, a program must #include "ttyio.h",
  22. and call tty_init.  It can then call tty_setmode to control tty
  23. operating modes (only two are defined: character-at-a-time-ness
  24. and echo), and tty_getchar to read single characters.  tty_navail
  25. reports the number of characters immediately available, if known.
  26. tty_reset cleans up the terminal driver before exiting.
  27.  
  28. This posting contains implementations for V7/bsd Unix and MS-DOS,
  29. an untested implementation based on curses, and a dubious,
  30. completely untested implementation for Posix.  (The MS-DOS
  31. version has been tested under Microsoft C, but I believe most if
  32. not all PC C RTL's supply getch and kbhit.)  A System V version
  33. would probably be very close to Posix, but I don't have access to
  34. a System V system for testing.  It has been several years since I
  35. worked with VMS, and I have forgotten the details of its terminal
  36. driver, so I am not able to provide a VMS implementation at the
  37. moment.
  38.  
  39. The shar file following my signature contains these seven files:
  40.  
  41.     ttyio.3        man page
  42.     ttyio.h        header file
  43.     ttytest.c    test program
  44.     ttyio.c        BSD/V7 implementation
  45.     ttyio.curses.c    curses implementation (untested)
  46.     ttyio.msdos.c    MS-DOS implementation
  47.     ttyio.posix.c    Posix/SysVr4 implementation (completely untested)
  48.  
  49. After writing ttyio.curses.c, I discovered (because this system
  50. does not have them!) that the cbreak(), nocbreak(), echo(),
  51. noecho(), and getch() curses functions upon which it is based do
  52. not appear in all versions of curses.  Caveat emptor.
  53.  
  54. Anyone who knows anything about Posix/SysV termios is going to
  55. look at ttyio.posix.c and laugh.  This is the first time I've
  56. actually read the termios documentation and tried to write any
  57. code against it, and since I don't have a system to test it on,
  58. it is guaranteed to be wrong.  I confess that I don't understand
  59. the distinction between c_cc[MIN] and c_cc[VMIN] (similarly for
  60. TIME and VTIME), and I know that there is some ineffable nonsense
  61. having to do with the possibility of VMIN and VTIME overlapping
  62. VEOF and VEOL, requiring circumlocutions when playing with
  63. ICANON, which I have certainly gotten wrong.  (Since this is the
  64. first time I've looked at termios, I'll withhold judgement on it;
  65. perhaps it has attractions I'm overlooking.  Presumably it was
  66. defined and adopted in its present form because of its inherent
  67. advantages and superiority over other alternatives.)
  68.  
  69. This package is in the public domain; use it in good health.
  70. I couldn't put copyright notices on it if I wanted to; the code
  71. (with the exception of the termios stuff, which is wrong, anyway)
  72. is obvious, trivial, and appears as examples in countless other
  73. published works.
  74.  
  75. I'm not sure what to suggest be done with the bugfixes and
  76. improvements to this package that some people are going to insist
  77. upon providing.  Definitely don't post them to comp.lang.c .
  78. Post them to alt.sources if you must.  You can mail them to me,
  79. and I'll mail them back out to anyone who asks, but I'm not
  80. going to spend much, if any, time integrating them or maintaining
  81. this "package."
  82.  
  83.                                             Steve Summit
  84.                                             scs@adam.mit.edu
  85.  
  86. echo extracting ttyio.3
  87. cat > ttyio.3 <<\%
  88. .TH TTYIO 3
  89. .SH NAME
  90. ttyio \- trivial terminal driver interface
  91. .SH SYNOPSIS
  92. .nf
  93. #include "ttyio.h"
  94.  
  95. tty_init()
  96.  
  97. tty_setmode(mode)
  98. int mode;
  99.  
  100. tty_getchar()
  101.  
  102. tty_navail()
  103.  
  104. tty_reset()
  105. .fi
  106. .SH DESCRIPTION
  107. .PP
  108. These routines implement a trivial interface
  109. to a few popular features
  110. of the terminal drivers
  111. usually supplied with interactive operating systems.
  112. All facilities are defined
  113. in terms of the "controlling terminal"
  114. of the calling process
  115. (standard input,
  116. or file descriptor 0,
  117. for Unix systems).
  118. .PP
  119. The header file "ttyio.h" must be #included by any source file
  120. making use of these facilities.
  121. It contains the symbolic constants used by the
  122. tty_setmode
  123. routine,
  124. and may also implement some of these routines
  125. as function-like macros.
  126. .PP
  127. tty_init
  128. must be called first,
  129. before any of the other routines.
  130. .PP
  131. tty_setmode
  132. is used to set operating modes.
  133. The
  134. mode
  135. argument is formed by or-ing together symbolic constants,
  136. which are #defined in "ttyio.h".
  137. Separate values are used to turn on and off each mode.
  138. The available values are:
  139. .sp
  140. .nf
  141. .ta 1i +\w'TTY_NOCANON'u+3m
  142.     TTY_ECHO
  143.     TTY_NOECHO
  144.     TTY_CANON    "canonical" erase/newline processing
  145.     TTY_NOCANON    character-at-a-time processing
  146. .fi
  147. .sp
  148. TTY_ECHO and TTY_NOECHO have the obvious meanings.
  149. TTY_NOCANON turns off the "canonical" processing
  150. of the various line editing characters
  151. (backspace,
  152. delete/rubout,
  153. and any word or line kill characters)
  154. and makes input characters available immediately,
  155. without waiting for a newline (carriage return) character.
  156. (Interrupt characters, however, remain active.)
  157. TTY_CANON returns to "canonical" processing.
  158. .PP
  159. tty_setmode returns 0 if it is successful
  160. and -1 if the requested mode(s) could not be set.
  161. After a failure,
  162. errno may or may not be useful;
  163. the most likely cause for failure
  164. is that the "controlling terminal" is not really a terminal (ENOTTY)
  165. but is rather a file or a pipe.
  166. .PP
  167. tty_getchar
  168. gets and returns one character,
  169. with processing performed
  170. according to the modes set by
  171. previous calls to
  172. tty_setmode.
  173. tty_getchar blocks until character(s) are available.
  174. If TTY_NOCANON mode is in effect,
  175. the operating system's end-of-file character
  176. (usually control-D for Unix,
  177. control-Z for VMS and MS-DOS)
  178. may or may not be translated
  179. to the <stdio.h> value EOF.
  180. .PP
  181. (tty_getchar must be used for input
  182. while this package is being used;
  183. any other input routines --
  184. getchar(3),
  185. read(2),
  186. etc. --
  187. may behave unpredictably.)
  188. .PP
  189. tty_navail
  190. returns the number of characters
  191. which are immediately available
  192. for reading via
  193. tty_getchar,
  194. if this number can be determined.
  195. tty_navail
  196. is not implementable under all systems,
  197. and may return an approximation
  198. on systems where it does work.
  199. It returns -1
  200. if it cannot determine
  201. the number of characters available;
  202. the calling program will then have to make
  203. its own conservative approximation.
  204. (Whether it is better to assume
  205. that characters are or aren't available
  206. when the answer is not definitively determinable
  207. is a decision which is best made by the calling program.)
  208. Some operating systems can report
  209. that there are characters available,
  210. but not how many;
  211. tty_navail
  212. returns 1 in this case.
  213. .PP
  214. tty_reset
  215. should be called before the calling program exits,
  216. to restore the terminal driver
  217. to its default or initial state.
  218. tty_reset can also be called
  219. before the calling program suspends operation
  220. or otherwise gives up control.
  221. It is permissible to call
  222. tty_setmode
  223. again,
  224. to re-establish non-default modes,
  225. after calling
  226. tty_reset,
  227. as long as tty_reset is called
  228. a final time before exiting.
  229. .SH BUGS
  230. .PP
  231. Not general enough to satisfy anybody
  232. (let alone "Tenex fans").
  233. .PP
  234. Under some systems, calling
  235. setmode
  236. with only the value TTY_NOECHO
  237. may implicitly turn on TTY_NOCANON.
  238. .PP
  239. There is no way to specify
  240. the file descriptor
  241. of the tty to be controlled;
  242. standard input (fd 0) is assumed.
  243. .PP
  244. No provision for output or output modes.
  245. %
  246. chmod 664 ttyio.3
  247. if test `wc -c < ttyio.3` -ne 3878; then
  248.     echo "error extracting ttyio.3" 1>&2
  249. fi
  250. echo extracting ttyio.h
  251. cat > ttyio.h <<\%
  252. #ifndef TTYIO_H
  253. #define TTYIO_H
  254.  
  255. /* values for tty_setmode(): */
  256.  
  257. #define TTY_ECHO    0x01
  258. #define TTY_NOECHO    0x02
  259. #define TTY_CANON    0x04    /* "canonical" erase/newline processing */
  260. #define TTY_NOCANON    0x08    /* character-at-a-time processing */
  261.  
  262. #endif
  263. %
  264. chmod 664 ttyio.h
  265. if test `wc -c < ttyio.h` -ne 248; then
  266.     echo "error extracting ttyio.h" 1>&2
  267. fi
  268. echo extracting ttytest.c
  269. cat > ttytest.c <<\%
  270. #include <stdio.h>
  271. #include <ctype.h>
  272. #include "ttyio.h"
  273.  
  274. #define Streq(s1, s2) (strcmp(s1, s2) == 0)
  275.  
  276. main(argc, argv)
  277. int argc;
  278. char *argv[];
  279. {
  280. int flags = 0;
  281. int i;
  282. int c;
  283. int r;
  284.  
  285. for(i = 1; i < argc; i++)
  286.     {
  287.     if(Streq(argv[i], "echo"))
  288.         flags |= TTY_ECHO;
  289.     else if(Streq(argv[i], "noecho"))
  290.         flags |= TTY_NOECHO;
  291.     else if(Streq(argv[i], "canon"))
  292.         flags |= TTY_CANON;
  293.     else if(Streq(argv[i], "nocanon"))
  294.         flags |= TTY_NOCANON;
  295.     else    fprintf(stderr, "ttytest: unknown mode \"%s\"\n", argv[i]);
  296.     }
  297.  
  298. tty_init();
  299.  
  300. if(tty_setmode(flags) < 0)
  301.     {
  302.     fprintf(stderr, "ttytest: can't set requested mode(s)\n");
  303.     exit(1);
  304.     }
  305.  
  306. while(1)
  307.     {
  308.     while((r = tty_navail()) == 0)
  309.         ;
  310.  
  311.     printf("tty_navail returned %d\n", r);
  312.  
  313.     if(r <= 0)
  314.         r = 1;
  315.  
  316.     for(i = 0; i < r; i++)
  317.         {
  318.         c = tty_getchar();
  319.  
  320.         if(isprint(c))
  321.             printf("you typed %c\n", c);
  322.         else    printf("you typed %d\n", c);
  323.  
  324.         if(c == EOF)
  325.             break;
  326.         }
  327.  
  328.     if(c == EOF)
  329.         break;
  330.     }
  331.  
  332. tty_reset();
  333. }
  334. %
  335. chmod 664 ttytest.c
  336. if test `wc -c < ttytest.c` -ne 942; then
  337.     echo "error extracting ttytest.c" 1>&2
  338. fi
  339. echo extracting ttyio.c
  340. cat > ttyio.c <<\%
  341. #include <stdio.h>
  342. #include <sgtty.h>
  343. #include "ttyio.h"
  344.  
  345. #define TRUE 1
  346. #define FALSE 0
  347.  
  348. static struct sgttyb savetty;
  349. static struct sgttyb tty;
  350. static int havetty = FALSE;
  351.  
  352. tty_init()
  353. {
  354. }
  355.  
  356. tty_setmode(flags)
  357. int flags;
  358. {
  359. if(!havetty)
  360.     {
  361.     if(ioctl(0, TIOCGETP, &savetty) < 0)
  362.         return -1;
  363.  
  364.     tty = savetty;
  365.     havetty = TRUE;
  366.     }
  367.  
  368. if(flags & TTY_ECHO)
  369.     tty.sg_flags |= ECHO;
  370.  
  371. if(flags & TTY_NOECHO)
  372.     tty.sg_flags &= ~ECHO;
  373.  
  374. if(flags & TTY_CANON)
  375.     {
  376.     tty.sg_flags &= ~CBREAK;
  377.     tty.sg_flags |= CRMOD;
  378.     }
  379.  
  380. if(flags & TTY_NOCANON)
  381.     {
  382.     tty.sg_flags |= CBREAK;
  383.     tty.sg_flags &= ~CRMOD;
  384.     }
  385.  
  386. if(ioctl(0, TIOCSETN, &tty) < 0)
  387.     return -1;
  388.  
  389. return 0;
  390. }
  391.  
  392. tty_getchar()
  393. {
  394. return getchar();
  395. }
  396.  
  397. tty_navail()
  398. {
  399. #ifdef FIONREAD
  400.  
  401. int nchars;
  402.  
  403. if(ioctl(0, FIONREAD, &nchars) < 0)
  404.     return -1;
  405.  
  406. return nchars;
  407.  
  408. #else
  409.  
  410. return -1;
  411.  
  412. #endif
  413. }
  414.  
  415. tty_reset()
  416. {
  417. if(havetty)
  418.     ioctl(0, TIOCSETN, &savetty);
  419. }
  420. %
  421. chmod 664 ttyio.c
  422. if test `wc -c < ttyio.c` -ne 875; then
  423.     echo "error extracting ttyio.c" 1>&2
  424. fi
  425. echo extracting ttyio.curses.c
  426. cat > ttyio.curses.c <<\%
  427. #include <stdio.h>
  428. #include "ttyio.h"
  429.  
  430. tty_init()
  431. {
  432. initscr();
  433. }
  434.  
  435. tty_setmode(flags)
  436. int flags;
  437. {
  438. if(flags & TTY_ECHO)
  439.     echo();
  440.  
  441. if(flags & TTY_NOECHO)
  442.     noecho();
  443.  
  444. if(flags & TTY_CANON)
  445.     {
  446.     nocbreak();
  447.     nl();
  448.     }
  449.  
  450. if(flags & TTY_NOCANON)
  451.     {
  452.     cbreak();
  453.     nonl();
  454.     }
  455.  
  456. return 0;
  457. }
  458.  
  459. tty_getchar()
  460. {
  461. return getch();
  462. }
  463.  
  464. tty_navail()
  465. {
  466. /* I don't know how to do this in curses */
  467. return -1;
  468. }
  469.  
  470. tty_reset()
  471. {
  472. endwin();
  473. }
  474. %
  475. chmod 664 ttyio.curses.c
  476. if test `wc -c < ttyio.curses.c` -ne 411; then
  477.     echo "error extracting ttyio.curses.c" 1>&2
  478. fi
  479. echo extracting ttyio.msdos.c
  480. cat > ttyio.msdos.c <<\%
  481. #include <stdio.h>
  482.  
  483. #include "ttyio.h"
  484.  
  485. static int mode = 0;
  486.  
  487. /* values for internal mode word: */
  488.  
  489. #define NOCANON    1
  490. #define NOECHO    2
  491.  
  492. tty_init()
  493. {
  494. }
  495.  
  496. tty_setmode(flags)
  497. int flags;
  498. {
  499. if(!isatty(0))        /* if you don't have isatty(), just delete this test */
  500.     return -1;
  501.  
  502. if(flags & TTY_ECHO)
  503.     mode &= ~NOECHO;
  504.  
  505. if(flags & TTY_NOECHO)
  506.     mode |= NOECHO;
  507.  
  508. if(flags & TTY_CANON)
  509.     mode &= ~NOCANON;
  510.  
  511. if(flags & TTY_NOCANON)
  512.     mode |= NOCANON;
  513.  
  514. return 0;
  515. }
  516.  
  517. tty_getchar()
  518. {
  519. switch(mode)
  520.     {
  521.     case 0:
  522.         return getchar();
  523.  
  524.     case NOCANON:
  525.         return getche();
  526.  
  527.     case NOCANON | NOECHO:
  528.     case NOECHO:        /* oh, well, noecho gets you nocanon */
  529.         return getch();
  530.     }
  531. }
  532.  
  533. tty_navail()
  534. {
  535. if(!(mode & NOCANON))
  536.     return -1;
  537.  
  538. return kbhit() ? 1 : 0;
  539. }
  540.  
  541. tty_reset()
  542. {
  543. mode = 0;
  544. }
  545.  
  546. %
  547. chmod 664 ttyio.msdos.c
  548. if test `wc -c < ttyio.msdos.c` -ne 753; then
  549.     echo "error extracting ttyio.msdos.c" 1>&2
  550. fi
  551. echo extracting ttyio.posix.c
  552. cat > ttyio.posix.c <<\%
  553. #include <stdio.h>
  554. #include <termios.h>
  555. #include "ttyio.h"
  556.  
  557. #define TRUE 1
  558. #define FALSE 0
  559.  
  560. static struct termios savetty;
  561. static struct termios tty;
  562. static int havetty = FALSE;
  563.  
  564. static int savevmin, savevtime;
  565. static int havevminvtime = FALSE;
  566.  
  567. tty_init()
  568. {
  569. }
  570.  
  571. tty_setmode(flags)
  572. int flags;
  573. {
  574. if(!havetty)
  575.     {
  576.     if(tcgetattr(0, &savetty) < 0)
  577.         return -1;
  578.  
  579.     tty = savetty;
  580.     havetty = TRUE;
  581.     }
  582.  
  583. if(flags & TTY_ECHO)
  584.     tty.c_lflag |= ECHO;
  585.  
  586. if(flags & TTY_NOECHO)
  587.     tty.c_lflag &= ~ECHO;
  588.  
  589. if(flags & TTY_CANON)
  590.     {
  591.     tty.c_lflag |= ICANON;
  592.     tty.c_iflag |= ICRNL;
  593.     if(havevminvtime)
  594.         {
  595.         tty.c_cc[VMIN] = savevmin;
  596.         tty.c_cc[VTIME] = savevtime;
  597.         }
  598.     }
  599.  
  600. if(flags & TTY_NOCANON)
  601.     {
  602.     tty.c_lflag &= ~ICANON;
  603.     tty.c_iflag &= ~ICRNL;
  604.     savevmin = tty.c_cc[VMIN];
  605.     savevtime = tty.c_cc[VTIME];
  606.     havevminvtime = TRUE;
  607.     tty.c_cc[VMIN] = 1;
  608.     tty.c_cc[VTIME] = 0;
  609.     }
  610.  
  611. if(tcsetattr(0, TCSANOW, &tty) < 0)
  612.     return -1;
  613.  
  614. return 0;
  615. }
  616.  
  617. tty_getchar()
  618. {
  619. return getchar();
  620. }
  621.  
  622. tty_navail()
  623. {
  624. #ifdef FIONREAD
  625.  
  626. int nchars;
  627.  
  628. if(ioctl(0, FIONREAD, &nchars) < 0)
  629.     return -1;
  630.  
  631. return nchars;
  632.  
  633. #else
  634.  
  635. return -1;
  636.  
  637. #endif
  638. }
  639.  
  640. tty_reset()
  641. {
  642. if(havetty)
  643.     {
  644.     if(!(tty.c_lflag & ICANON) && havevminvtime)
  645.         {
  646.         savetty.c_cc[VMIN] = savevmin;
  647.         savetty.c_cc[VTIME] = savevtime;
  648.         }
  649.  
  650.     tcsetattr(0, TCSANOW, &savetty);
  651.     }
  652. }
  653. %
  654. chmod 664 ttyio.posix.c
  655. if test `wc -c < ttyio.posix.c` -ne 1280; then
  656.     echo "error extracting ttyio.posix.c" 1>&2
  657. fi
  658.