home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / termio.c < prev    next >
C/C++ Source or Header  |  1998-10-01  |  26KB  |  1,201 lines

  1. /*
  2.  * The functions in this file negotiate with the operating system for
  3.  * characters, and write characters in a barely buffered fashion on the display.
  4.  * All operating systems.
  5.  *
  6.  * $Header: /usr/build/vile/vile/RCS/termio.c,v 1.158 1998/10/01 10:56:23 tom Exp $
  7.  *
  8.  */
  9. #include    "estruct.h"
  10. #include    "edef.h"
  11. #include    "nefunc.h"
  12.  
  13. #if CC_DJGPP
  14. # include <pc.h>   /* for kbhit() */
  15. #endif
  16.  
  17. #if SYS_VMS
  18. #include <starlet.h>
  19. #include <lib$routines.h>
  20. #endif
  21.  
  22. static    int    was_clean = FALSE;    /* suppress the first TTkopen */
  23.  
  24. #if SYS_UNIX
  25.  
  26. static void ttmiscinit (void);
  27.  
  28. /* there are three copies of the tt...() routines here -- one each for
  29.     POSIX termios, traditional termio, and sgtty.  If you have a
  30.     choice, I recommend them in that order. */
  31.  
  32. /* ttopen() and ttclose() are responsible for putting the terminal in raw
  33.     mode, setting up terminal signals, etc.
  34.    ttclean() prepares the terminal for shell escapes, and ttunclean() gets
  35.     us back into vile's mode
  36. */
  37.  
  38. /* I suppose this config stuff should move to estruct.h */
  39.  
  40. #if HAVE_TERMIOS_H && HAVE_TCGETATTR
  41. /* Note: <termios.h> is available on some systems, but in order to use it
  42.  * a special library needs to be linked in.  This is the case on the NeXT
  43.  * where libposix.a needs to be linked in.  Unfortunately libposix.a is buggy.
  44.  * So we have the configuration script test to make sure that tcgetattr is
  45.  * available through the standard set of libraries in order to help us
  46.  * determine whether or not to use <termios.h>.
  47.  */
  48. # define USE_POSIX_TERMIOS 1
  49. # define USE_FCNTL 1
  50. #else
  51. # if HAVE_TERMIO_H
  52. #  define USE_TERMIO 1
  53. #  define USE_FCNTL 1
  54. # else
  55. #  if HAVE_SGTTY_H
  56. #   define USE_SGTTY 1
  57. #   define USE_FIONREAD 1
  58. #  else
  59.  huh?
  60. #  endif
  61. # endif
  62. #endif
  63.  
  64. /* FIXME: There used to be code here which dealt with OSF1 not working right
  65.  * with termios.  We will have to determine if this is still the case and
  66.  * add code here to deal with it if so.
  67.  */
  68.  
  69. #if !defined(FIONREAD)
  70. /* try harder to get it */
  71. # if HAVE_SYS_FILIO_H
  72. #  include "sys/filio.h"
  73. # else /* if you have trouble including ioctl.h, try "sys/ioctl.h" instead */
  74. #  if HAVE_IOCTL_H
  75. #   include <ioctl.h>
  76. #  else
  77. #   if HAVE_SYS_IOCTL_H
  78. #    include <sys/ioctl.h>
  79. #   endif
  80. #  endif
  81. # endif
  82. #endif
  83.  
  84. #if DISP_X11    /* don't use either one */
  85. # undef USE_FCNTL
  86. # undef USE_FIONREAD
  87. #else
  88. # if defined(FIONREAD)
  89.   /* there seems to be a bug in someone's implementation of fcntl -- it
  90.    * causes output to be flushed if you change to ndelay input while output
  91.    * is pending.  for these systems, we use FIONREAD instead, if possible.
  92.    * In fact, if try and use FIONREAD in any case if present.  If you have
  93.    * the problem with fcntl, you'll notice that the screen doesn't always
  94.    * refresh correctly, as if the fcntl is interfering with the output drain
  95.    */
  96. #  undef USE_FCNTL
  97. #  define USE_FIONREAD 1
  98. # endif
  99. #endif
  100.  
  101. #if USE_FCNTL
  102. /* this is used to determine whether input is pending from the user */
  103. #include    <fcntl.h>
  104. int kbd_flags;            /* saved keyboard fcntl flags    */
  105. int kbd_is_polled;        /* in O_NDELAY mode?        */
  106. int kbd_char_present;        /* there is a char in kbd_char    */
  107. char kbd_char;            /* the char we've already read    */
  108. #endif
  109.  
  110. #define SMALL_STDOUT 1
  111.  
  112. #if defined(SMALL_STDOUT) && (defined (USE_FCNTL) || defined(USE_FIONREAD))
  113. #define TBUFSIZ 128 /* Provide a smaller terminal output buffer so that
  114.        the type-ahead detection works better (more often).
  115.        That is, we overlap screen writing with more keyboard polling */
  116. #else
  117. #define TBUFSIZ 1024    /* reduces the number of writes */
  118. #endif
  119.  
  120. #if USE_POSIX_TERMIOS
  121.  
  122. #include <termios.h>
  123.  
  124. #if NEED_PTEM_H
  125. /* they neglected to define struct winsize in termios.h -- it's only
  126.    in termio.h    */
  127. #include    <sys/stream.h>
  128. #include    <sys/ptem.h>
  129. #endif
  130.  
  131. #ifndef VDISABLE
  132. # ifdef    _POSIX_VDISABLE
  133. #  define VDISABLE _POSIX_VDISABLE
  134. # else
  135. #  define VDISABLE '\0'
  136. # endif
  137. #endif
  138.  
  139. struct termios otermios, ntermios;
  140.  
  141. char tobuf[TBUFSIZ];        /* terminal output buffer */
  142.  
  143. void
  144. ttopen(void)
  145. {
  146.     int s;
  147.     s = tcgetattr(0, &otermios);
  148.     if (s < 0) {
  149.         perror("ttopen tcgetattr");
  150.         ExitProgram(BADEXIT);
  151.     }
  152. #if !DISP_X11
  153. #if HAVE_SETVBUF
  154. # if SETVBUF_REVERSED
  155.     setvbuf(stdout, _IOFBF, tobuf, TBUFSIZ);
  156. # else
  157.     setvbuf(stdout, tobuf, _IOFBF, TBUFSIZ);
  158. # endif
  159. #else /* !HAVE_SETVBUF */
  160.     setbuffer(stdout, tobuf, TBUFSIZ);
  161. #endif /* !HAVE_SETVBUF */
  162. #endif /* !DISP_X11 */
  163.  
  164.     suspc =   otermios.c_cc[VSUSP];
  165.     intrc =   otermios.c_cc[VINTR];
  166.     killc =   otermios.c_cc[VKILL];
  167.     startc =  otermios.c_cc[VSTART];
  168.     stopc =   otermios.c_cc[VSTOP];
  169.     backspc = otermios.c_cc[VERASE];
  170. #ifdef VWERASE    /* Sun has it.    any others? */
  171.     wkillc = otermios.c_cc[VWERASE];
  172. #else
  173.     wkillc =  tocntrl('W');
  174. #endif
  175.  
  176.     /* this could probably be done more POSIX'ish? */
  177. #if OPT_SHELL && defined(SIGTSTP) && defined(SIGCONT)
  178.     setup_handler(SIGTSTP,SIG_DFL);     /* set signals so that we can */
  179.     setup_handler(SIGCONT,rtfrmshell);    /* suspend & restart */
  180. #endif
  181. #ifdef SIGTTOU
  182.     setup_handler(SIGTTOU,SIG_IGN);     /* ignore output prevention */
  183. #endif
  184.  
  185. #if USE_FCNTL
  186.     kbd_flags = fcntl( 0, F_GETFL, 0 );
  187.     kbd_is_polled = FALSE;
  188. #endif
  189.  
  190. #if ! DISP_X11
  191.     ntermios = otermios;
  192.  
  193.     /* new input settings: turn off crnl mapping, cr-ignoring,
  194.      * case-conversion, and allow BREAK
  195.      */
  196.     ntermios.c_iflag = BRKINT | (otermios.c_iflag &
  197.         (unsigned long) ~(INLCR | IGNCR | ICRNL
  198. #ifdef IUCLC
  199.                     | IUCLC
  200. #endif
  201.                  ));
  202.  
  203.     ntermios.c_oflag = 0;
  204.     ntermios.c_lflag = ISIG;
  205.     ntermios.c_cc[VMIN] = 1;
  206.     ntermios.c_cc[VTIME] = 0;
  207. #ifdef    VSWTCH
  208.     ntermios.c_cc[VSWTCH] = VDISABLE;
  209. #endif
  210.     ntermios.c_cc[VSUSP]  = VDISABLE;
  211. #if defined (VDSUSP) && defined(NCCS) && VDSUSP < NCCS
  212.     ntermios.c_cc[VDSUSP]  = VDISABLE;
  213. #endif
  214.     ntermios.c_cc[VSTART] = VDISABLE;
  215.     ntermios.c_cc[VSTOP]  = VDISABLE;
  216. #endif /* ! DISP_X11 */
  217.  
  218.     ttmiscinit();
  219.  
  220.     ttunclean();
  221. }
  222.  
  223. /* we disable the flow control chars so we can use ^S as a command, but
  224.   some folks still need it.  they can put "flow-control-enable" in their
  225.   .vilerc
  226.   no argument:    re-enable ^S/^Q processing in the driver
  227.   with arg:    disable it
  228. */
  229. /* ARGSUSED */
  230. int
  231. flow_control_enable(int f, int n GCC_UNUSED)
  232. {
  233. #if !DISP_X11
  234.     if (!f) {
  235.         ntermios.c_cc[VSTART] = (char)startc;
  236.         ntermios.c_cc[VSTOP]  = (char)stopc;
  237.     } else {
  238.         ntermios.c_cc[VSTART] = VDISABLE;
  239.         ntermios.c_cc[VSTOP]  = VDISABLE;
  240.     }
  241.     ttunclean();
  242. #endif
  243.     return TRUE;
  244. }
  245.  
  246. void
  247. ttclose(void)
  248. {
  249.     ttclean(TRUE);
  250. }
  251.  
  252. /*
  253.  * Clean up in anticipation for a return to the
  254.  * operating system. Move down to the last line and advance, to make room
  255.  * for the system prompt. Shut down the channel to the
  256.  * terminal.
  257.  */
  258. /*ARGSUSED*/
  259. void
  260. ttclean(int f)
  261. {
  262. #if !DISP_X11
  263.     if (f)
  264.         kbd_openup();
  265.  
  266.     (void)fflush(stdout);
  267.     tcdrain(1);
  268.     tcsetattr(0, TCSADRAIN, &otermios);
  269.     TTflush();
  270.     TTclose();
  271.     TTkclose();
  272. #if USE_FCNTL
  273.     fcntl(0, F_SETFL, kbd_flags);
  274.     kbd_is_polled = FALSE;
  275. #endif
  276. #endif
  277.     was_clean = TRUE;
  278. }
  279.  
  280. void
  281. ttunclean(void)
  282. {
  283. #if ! DISP_X11
  284.     tcdrain(1);
  285.     tcsetattr(0, TCSADRAIN, &ntermios);
  286.     if (was_clean)
  287.         TTkopen();
  288. #endif
  289. }
  290.  
  291.  
  292. #endif /* USE_POSIX_TERMIOS */
  293.  
  294. #if USE_TERMIO
  295.  
  296. #include    <termio.h>
  297.  
  298. /* original terminal characteristics and characteristics to use inside */
  299. struct    termio    otermio, ntermio;
  300.  
  301. #ifdef AVAILABLE  /* setbuffer() isn't on most termio systems */
  302. char tobuf[TBUFSIZ];        /* terminal output buffer */
  303. #endif
  304.  
  305. void
  306. ttopen(void)
  307. {
  308.  
  309.     ioctl(0, TCGETA, (char *)&otermio);    /* save old settings */
  310. #if defined(AVAILABLE) && !DISP_X11
  311.     setbuffer(stdout, tobuf, TBUFSIZ);
  312. #endif
  313.  
  314.     intrc =   otermio.c_cc[VINTR];
  315.     killc =   otermio.c_cc[VKILL];
  316.     startc =  tocntrl('Q');
  317.     stopc =   tocntrl('S');
  318.     backspc = otermio.c_cc[VERASE];
  319.     wkillc =  tocntrl('W');
  320.  
  321. #if USE_FCNTL
  322.     kbd_flags = fcntl( 0, F_GETFL, 0 );
  323.     kbd_is_polled = FALSE;
  324. #endif
  325.  
  326. #if SIGTSTP
  327. /* be careful here -- VSUSP is sometimes out of the range of the c_cc array */
  328. #ifdef V_SUSP
  329.     ntermio.c_cc[V_SUSP] = -1;
  330.     suspc = otermio.c_cc[V_SUSP];
  331. #else
  332.     suspc = -1;
  333. #endif
  334. #ifdef V_DSUSP
  335.     ntermio.c_cc[V_DSUSP] = -1;
  336. #endif
  337.  
  338. #if OPT_SHELL
  339.     setup_handler(SIGTSTP,SIG_DFL);     /* set signals so that we can */
  340.     setup_handler(SIGCONT,rtfrmshell);    /* suspend & restart */
  341. #endif
  342.     setup_handler(SIGTTOU,SIG_IGN);     /* ignore output prevention */
  343.  
  344. #else /* no SIGTSTP */
  345.     suspc =   tocntrl('Z');
  346. #endif
  347.  
  348. #if ! DISP_X11
  349.     ntermio = otermio;
  350.  
  351.     /* setup new settings, allow BREAK */
  352.     ntermio.c_iflag = BRKINT;
  353.     ntermio.c_oflag = 0;
  354.     ntermio.c_lflag = ISIG;
  355.     ntermio.c_cc[VMIN] = 1;
  356.     ntermio.c_cc[VTIME] = 0;
  357. #ifdef    VSWTCH
  358.     ntermio.c_cc[VSWTCH] = -1;
  359. #endif
  360. #endif
  361.  
  362.     ttmiscinit();
  363.     ttunclean();
  364.  
  365. }
  366.  
  367. /* we disable the flow control chars so we can use ^S as a command, but
  368.   some folks still need it.  they can put "flow-control-enable" in their
  369.   .vilerc */
  370. /* ARGSUSED */
  371. int
  372. flow_control_enable(int f, int n)
  373. {
  374. #if !DISP_X11
  375.     ntermio.c_iflag &= ~(IXON|IXANY|IXOFF);
  376.     if (!f)
  377.         ntermio.c_iflag |= otermio.c_iflag & (IXON|IXANY|IXOFF);
  378.     ttunclean();
  379. #endif
  380.     return TRUE;
  381. }
  382.  
  383. void
  384. ttclose(void)
  385. {
  386.     ttclean(TRUE);
  387. }
  388.  
  389. void
  390. ttclean(int f)
  391. {
  392. #if ! DISP_X11
  393.     if (f)
  394.         kbd_openup();
  395.  
  396.     (void)fflush(stdout);
  397.     TTflush();
  398.     TTclose();
  399.     TTkclose();    /* xterm */
  400.     ioctl(0, TCSETAF, (char *)&otermio);
  401. #if USE_FCNTL
  402.     fcntl(0, F_SETFL, kbd_flags);
  403.     kbd_is_polled = FALSE;
  404. #endif
  405. #endif    /* DISP_X11 */
  406.     was_clean = TRUE;
  407. }
  408.  
  409. void
  410. ttunclean(void)
  411. {
  412. #if ! DISP_X11
  413.     ioctl(0, TCSETAW, (char *)&ntermio);
  414. #endif
  415. }
  416.  
  417. #endif /* USE_TERMIO */
  418.  
  419. #if USE_SGTTY
  420.  
  421. #if USE_FIONREAD
  422. char tobuf[TBUFSIZ];        /* terminal output buffer */
  423. #endif
  424.  
  425. #undef    CTRL
  426. #include    <sgtty.h>     /* for stty/gtty functions */
  427. struct    sgttyb    ostate;      /* saved tty state */
  428. struct    sgttyb    nstate;      /* values for editor mode */
  429. struct    sgttyb    rnstate;      /* values for raw editor mode */
  430. int olstate;        /* Saved local mode values */
  431. int nlstate;        /* new local mode values */
  432. struct ltchars    oltchars;    /* Saved terminal special character set */
  433. struct ltchars    nltchars = { -1, -1, -1, -1, -1, -1 }; /* a lot of nothing */
  434. struct tchars    otchars;    /* Saved terminal special character set */
  435. struct tchars    ntchars; /*  = { -1, -1, -1, -1, -1, -1 }; */
  436.  
  437. void
  438. ttopen(void)
  439. {
  440.     ioctl(0,TIOCGETP,(char *)&ostate); /* save old state */
  441.     killc = ostate.sg_kill;
  442.     backspc = ostate.sg_erase;
  443.  
  444. #if ! DISP_X11
  445.     nstate = ostate;
  446.     nstate.sg_flags |= CBREAK;
  447.     nstate.sg_flags &= ~(ECHO|CRMOD);    /* no echo for now... */
  448.     ioctl(0,TIOCSETN,(char *)&nstate); /* set new state */
  449. #endif
  450.  
  451.     rnstate = nstate;
  452.     rnstate.sg_flags &= ~CBREAK;
  453.     rnstate.sg_flags |= RAW;
  454.  
  455.     ioctl(0, TIOCGETC, (char *)&otchars);    /* Save old characters */
  456.     intrc =  otchars.t_intrc;
  457.     startc = otchars.t_startc;
  458.     stopc =  otchars.t_stopc;
  459.  
  460. #if ! DISP_X11
  461.     ntchars = otchars;
  462.     ntchars.t_brkc = -1;
  463.     ntchars.t_eofc = -1;
  464.     ntchars.t_startc = -1;
  465.     ntchars.t_stopc = -1;
  466.     ioctl(0, TIOCSETC, (char *)&ntchars);    /* Place new character into K */
  467. #endif
  468.  
  469.     ioctl(0, TIOCGLTC, (char *)&oltchars);    /* Save old characters */
  470.     wkillc = oltchars.t_werasc;
  471.     suspc = oltchars.t_suspc;
  472. #if ! DISP_X11
  473.     ioctl(0, TIOCSLTC, (char *)&nltchars);    /* Place new character into K */
  474. #endif
  475.  
  476. #ifdef    TIOCLGET
  477.     ioctl(0, TIOCLGET, (char *)&olstate);
  478. #if ! DISP_X11
  479.     nlstate = olstate;
  480.     nlstate |= LLITOUT;
  481.     ioctl(0, TIOCLSET, (char *)&nlstate);
  482. #endif
  483. #endif
  484. #if USE_FIONREAD
  485.     setbuffer(stdout, tobuf, TBUFSIZ);
  486. #endif
  487. #if ! DISP_X11
  488.  
  489. #if OPT_SHELL
  490.     setup_handler(SIGTSTP,SIG_DFL);     /* set signals so that we can */
  491.     setup_handler(SIGCONT,rtfrmshell);    /* suspend & restart */
  492. #endif
  493.     setup_handler(SIGTTOU,SIG_IGN);     /* ignore output prevention */
  494.  
  495. #endif
  496.  
  497.     ttmiscinit();
  498. }
  499.  
  500. /* we disable the flow control chars so we can use ^S as a command, but
  501.   some folks still need it.  they can put "flow-control-enable" in their
  502.   .vilerc */
  503. /* ARGSUSED */
  504. int
  505. flow_control_enable(int f, int n)
  506. {
  507. #if !DISP_X11
  508.     if (!f) {
  509.         ntchars.t_startc = startc;
  510.         ntchars.t_stopc = stopc;
  511.     } else {
  512.         ntchars.t_startc = -1;
  513.         ntchars.t_stopc = -1;
  514.     }
  515.     ttunclean();
  516. #endif
  517.     return TRUE;
  518. }
  519.  
  520. void
  521. ttclose(void)
  522. {
  523.     ttclean(TRUE);
  524. }
  525.  
  526. void
  527. ttclean(int f)
  528. {
  529. #if ! DISP_X11
  530.     if (f)
  531.         kbd_openup();
  532.  
  533.     TTflush();
  534.     TTclose();
  535.     TTkclose();    /* xterm */
  536.     ioctl(0, TIOCSETN, (char *)&ostate);
  537.     ioctl(0, TIOCSETC, (char *)&otchars);
  538.     ioctl(0, TIOCSLTC, (char *)&oltchars);
  539. #ifdef    TIOCLSET
  540.     ioctl(0, TIOCLSET, (char *)&olstate);
  541. #endif
  542. #if SYS_APOLLO
  543.     TTflush();
  544. #endif
  545. #endif
  546.     was_clean = TRUE;
  547. }
  548.  
  549. void
  550. ttunclean(void)
  551. {
  552. #if ! DISP_X11
  553. #if SYS_APOLLO
  554.     int literal = LLITOUT;
  555.  
  556.     (void)fflush(stdout);
  557.     ioctl(0, TIOCLSET, (caddr_t)&olstate);
  558.     ioctl(0, TIOCSETP, (caddr_t)&nstate);    /* setting nlstate changes sb_flags */
  559.     TTflush();
  560.     ioctl(0, TIOCLBIS, (caddr_t)&literal);    /* set this before nltchars! */
  561.     ioctl(0, TIOCSETC, (caddr_t)&ntchars);
  562.     ioctl(0, TIOCSLTC, (caddr_t)&nltchars);
  563.  
  564. #else
  565.  
  566.     ioctl(0, TIOCSETN, (char *)&nstate);
  567.     ioctl(0, TIOCSETC, (char *)&ntchars);
  568.     ioctl(0, TIOCSLTC, (char *)&nltchars);
  569. #ifdef    TIOCLSET
  570.     ioctl(0, TIOCLSET, (char *)&nlstate);
  571. #endif
  572.  
  573. #endif    /* SYS_APOLLO */
  574. #endif    /* !DISP_X11 */
  575. }
  576.  
  577. #endif /* USE_SGTTY */
  578.  
  579. #if !DISP_X11
  580. OUTC_DCL
  581. ttputc(OUTC_ARGS)
  582. {
  583.     OUTC_RET putchar((char)c);
  584. }
  585.  
  586. void
  587. ttflush(void)
  588. {
  589.     (void)fflush(stdout);
  590. }
  591.  
  592. /*
  593.  * Read a character from the terminal, performing no editing and doing no echo
  594.  * at all.
  595.  */
  596. int
  597. ttgetc(void)
  598. {
  599. #if    USE_FCNTL
  600.     int n;
  601.     if( kbd_char_present ) {
  602.         kbd_char_present = FALSE;
  603.     } else {
  604.         if( kbd_is_polled && fcntl( 0, F_SETFL, kbd_flags ) < 0 )
  605.             imdying(SIGINT);
  606.         kbd_is_polled = FALSE;
  607.         n = read(0, &kbd_char, 1);
  608.         if (n <= 0) {
  609.             if (n < 0 && errno == EINTR)
  610.                 return -1;
  611.             imdying(SIGINT);
  612.         }
  613.     }
  614.     return ( kbd_char );
  615. #else /* USE_FCNTL */
  616. #if SYS_APOLLO
  617.     /*
  618.      * If we try to read a ^C in cooked mode it will echo anyway.  Also,
  619.      * the 'getchar()' won't be interruptable.  Setting raw-mode
  620.      * temporarily here still allows the program to be interrupted when we
  621.      * are not actually looking for a character.
  622.      */
  623.     int    c;
  624.     ioctl(0, TIOCSETN, (char *)&rnstate);
  625.     c = getchar();
  626.     ioctl(0, TIOCSETN, (char *)&nstate);
  627. #else
  628.     int c;
  629.     c = getchar();
  630. #endif
  631.     if (c == EOF) {
  632. #ifdef linux
  633.         /*
  634.          * The command "^X!vile -V" makes vile receive an EOF at this
  635.          * point.
  636.          */
  637.         return -1;
  638. #else
  639.         if (errno == EINTR)
  640.             return -1;
  641.         imdying(SIGINT);
  642. #endif
  643.     }
  644.     return c;
  645. #endif
  646. }
  647. #endif /* !DISP_X11 */
  648.  
  649. /* tttypahead:    Check to see if any characters are already in the
  650.         keyboard buffer
  651. */
  652. int
  653. tttypahead(void)
  654. {
  655.  
  656. #if DISP_X11
  657.     return x_typahead(0);
  658. #else
  659.  
  660. # if    USE_FIONREAD
  661.     {
  662.     long x;
  663.     return((ioctl(0,FIONREAD,(caddr_t)&x) < 0) ? 0 : (int)x);
  664.     }
  665. # else
  666. #  if    USE_FCNTL
  667.     if( !kbd_char_present )
  668.     {
  669.         if( !kbd_is_polled &&
  670.                 fcntl(0, F_SETFL, kbd_flags|O_NDELAY ) < 0 )
  671.             return(FALSE);
  672.         kbd_is_polled = TRUE;  /* I think */
  673.         kbd_char_present = (1 == read( 0, &kbd_char, 1 ));
  674.     }
  675.     return ( kbd_char_present );
  676. #  else
  677.     return FALSE;
  678. #  endif/* USE_FCNTL */
  679. # endif/* USE_FIONREAD */
  680. #endif    /* DISP_X11 */
  681. }
  682.  
  683. /* this takes care of some stuff that's common across all ttopen's.  Some of
  684.     it should arguably be somewhere else, but... */
  685. static void
  686. ttmiscinit(void)
  687. {
  688.     /* make sure backspace is bound to backspace */
  689.     asciitbl[backspc] = &f_backchar_to_bol;
  690.  
  691. #if !DISP_X11
  692.     /* no buffering on input */
  693.     setbuf(stdin, (char *)0);
  694. #endif
  695. }
  696.  
  697. #else /* not SYS_UNIX */
  698.  
  699. #if SYS_MSDOS || SYS_OS2 || SYS_WINNT
  700. # if CC_DJGPP
  701. #  include <gppconio.h>
  702. # else
  703. #  if CC_NEWDOSCC
  704. #   include <conio.h>
  705. #  endif
  706. # endif
  707. #endif
  708.  
  709.  
  710.  
  711. #if    SYS_VMS
  712. #include    <stsdef.h>
  713. #include    <ssdef.h>
  714. #include    <descrip.h>
  715. #include    <iodef.h>
  716. #include    <ttdef.h>
  717. #include    <tt2def.h>
  718.  
  719. typedef struct    {
  720.     unsigned short int status;    /* I/O completion status */
  721.     unsigned short int count;    /* byte transfer count     */
  722.     int dev_dep_data;        /* device dependant data */
  723.     } QIO_SB;            /* This is a QIO I/O Status Block */
  724.  
  725. #define NIBUF    1024            /* Input buffer size        */
  726. #define NOBUF    1024            /* MM says big buffers win!    */
  727. #define EFN    0            /* Event flag            */
  728.  
  729. char    obuf[NOBUF];            /* Output buffer        */
  730. int    nobuf;                /* # of bytes in above        */
  731. char    ibuf[NIBUF];            /* Input buffer         */
  732. int    nibuf;                /* # of bytes in above        */
  733. int    ibufi;                /* Read index            */
  734. int    oldmode[3];            /* Old TTY mode bits        */
  735. int    newmode[3];            /* New TTY mode bits        */
  736. short    iochan;             /* TTY I/O channel        */
  737. #endif
  738.  
  739.  
  740. #if    SYS_MSDOS && CC_NEWDOSCC && !CC_MSC
  741. union REGS rg;        /* cpu register for use of DOS calls (ibmpc.c) */
  742. int nxtchar = -1;    /* character held from type ahead    */
  743. #endif
  744.  
  745. void
  746. ttopen(void)
  747. {
  748. #if    SYS_VMS
  749.     struct    dsc$descriptor    idsc;
  750.     struct    dsc$descriptor    odsc;
  751.     char    oname[40];
  752.     QIO_SB    iosb;
  753.     int    status;
  754.  
  755.     odsc.dsc$a_pointer = "TT";
  756.     odsc.dsc$w_length  = strlen(odsc.dsc$a_pointer);
  757.     odsc.dsc$b_dtype    = DSC$K_DTYPE_T;
  758.     odsc.dsc$b_class    = DSC$K_CLASS_S;
  759.     idsc.dsc$b_dtype    = DSC$K_DTYPE_T;
  760.     idsc.dsc$b_class    = DSC$K_CLASS_S;
  761.     do {
  762.         idsc.dsc$a_pointer = odsc.dsc$a_pointer;
  763.         idsc.dsc$w_length  = odsc.dsc$w_length;
  764.         odsc.dsc$a_pointer = &oname[0];
  765.         odsc.dsc$w_length  = sizeof(oname);
  766.         status = lib$sys_trnlog(&idsc, &odsc.dsc$w_length, &odsc);
  767.         if (status!=SS$_NORMAL && status!=SS$_NOTRAN)
  768.             tidy_exit(status);
  769.         if (oname[0] == 0x1B) {
  770.             odsc.dsc$a_pointer += 4;
  771.             odsc.dsc$w_length  -= 4;
  772.         }
  773.     } while (status == SS$_NORMAL);
  774.     status = sys$assign(&odsc, &iochan, 0, 0);
  775.     if (status != SS$_NORMAL)
  776.         tidy_exit(status);
  777.     status = sys$qiow(EFN, iochan, IO$_SENSEMODE, &iosb, 0, 0,
  778.               oldmode, sizeof(oldmode), 0, 0, 0, 0);
  779.     if (status != SS$_NORMAL
  780.      || iosb.status != SS$_NORMAL)
  781.         tidy_exit(status);
  782.     newmode[0] = oldmode[0];
  783.     newmode[1] = oldmode[1];
  784.     newmode[1] &= ~(TT$M_TTSYNC|TT$M_HOSTSYNC);
  785.     newmode[2] = oldmode[2] | TT2$M_PASTHRU;
  786.     status = sys$qiow(EFN, iochan, IO$_SETMODE, &iosb, 0, 0,
  787.               newmode, sizeof(newmode), 0, 0, 0, 0);
  788.     if (status != SS$_NORMAL
  789.      || iosb.status != SS$_NORMAL)
  790.         tidy_exit(status);
  791.     term.t_nrow = (newmode[1]>>24);
  792.     term.t_ncol = newmode[0]>>16;
  793.  
  794. #endif
  795.  
  796.     /* make sure backspace is bound to backspace */
  797.     asciitbl[backspc] = &f_backchar_to_bol;
  798.  
  799. }
  800.  
  801. void
  802. ttclose(void)
  803. {
  804.  
  805. #if    SYS_VMS
  806.     /*
  807.      * Note: this code used to check for errors when closing the output,
  808.      * but it didn't work properly (left the screen set in 1-line mode)
  809.      * when I was running as system manager, so I took out the error
  810.      * checking -- T.Dickey 94/7/15.
  811.      */
  812.     int    status;
  813.     QIO_SB    iosb;
  814.  
  815.     ttflush();
  816.     status = sys$qiow(EFN, iochan, IO$_SETMODE, &iosb, 0, 0,
  817.          oldmode, sizeof(oldmode), 0, 0, 0, 0);
  818.     if (status == SS$_IVCHAN)
  819.         return; /* already closed it */
  820.     (void) sys$dassgn(iochan);
  821. #endif
  822. #if    !SYS_VMS
  823.     ttclean(TRUE);
  824. #endif
  825. }
  826.  
  827. void
  828. ttclean(int f)
  829. {
  830.     if (f)
  831.         kbd_openup();
  832.  
  833.     TTflush();
  834.     TTclose();
  835.     TTkclose();
  836.     was_clean = TRUE;
  837. }
  838.  
  839. void
  840. ttunclean(void)
  841. {
  842. }
  843.  
  844. /*
  845.  * Write a character to the display. On VMS, terminal output is buffered, and
  846.  * we just put the characters in the big array, after checking for overflow.
  847.  * On CPM terminal I/O unbuffered, so we just write the byte out. Ditto on
  848.  * MS-DOS (use the very very raw console output routine).
  849.  */
  850. OUTC_DCL
  851. ttputc(OUTC_ARGS)
  852. {
  853. #if    SYS_VMS
  854.     if (nobuf >= NOBUF)
  855.         ttflush();
  856.     obuf[nobuf++] = c;
  857.     OUTC_RET c;
  858. #endif
  859. #if    SYS_OS2 && !DISP_VIO
  860.     OUTC_RET putch(c);
  861. #endif
  862. #if    SYS_MSDOS
  863. # if DISP_IBMPC
  864.     /* unneeded currently -- output is memory-mapped */
  865. # endif
  866. # if DISP_ANSI
  867.     OUTC_RET putchar(c);
  868. # endif
  869. #endif
  870. }
  871.  
  872.  
  873. /*
  874.  * Flush terminal buffer. Does real work where the terminal output is buffered
  875.  * up. A no-operation on systems where byte at a time terminal I/O is done.
  876.  */
  877. void
  878. ttflush(void)
  879. {
  880. #if    SYS_VMS
  881.     QIO_SB    iosb;
  882.  
  883.     if (nobuf != 0) {
  884.         (void) sys$qiow(EFN, iochan, IO$_WRITELBLK|IO$M_NOFORMAT,
  885.              &iosb, 0, 0, obuf, nobuf, 0, 0, 0, 0);
  886.         nobuf = 0;
  887.     }
  888. #endif
  889.  
  890. #if    SYS_MSDOS
  891. # if DISP_ANSI
  892.     fflush(stdout);
  893. # endif
  894. #endif
  895. }
  896.  
  897. #if SYS_VMS
  898. static void
  899. read_vms_tty(int length)
  900. {
  901.     int    status;
  902.     QIO_SB    iosb;
  903.     int    term[2] = {0,0};
  904.  
  905.     status = sys$qiow(EFN, iochan,
  906.         (length == 1)
  907.             ? IO$_READLBLK|IO$M_NOECHO|IO$M_NOFILTR
  908.             : IO$_READLBLK|IO$M_NOECHO|IO$M_NOFILTR|IO$M_TIMED,
  909.         &iosb, 0, 0, ibuf, length, 0, term, 0, 0);
  910.  
  911.     if (status != SS$_NORMAL)
  912.         tidy_exit(status);
  913.     if (iosb.status == SS$_ENDOFFILE)
  914.         tidy_exit(status);
  915.  
  916.         /* FIXME: check documentation */
  917.     nibuf = iosb.count + (iosb.dev_dep_data >> 16);
  918.     ibufi = 0;
  919. }
  920. #endif
  921.  
  922. /*
  923.  * Read a character from the terminal, performing no editing and doing no echo
  924.  * at all. More complex in VMS that almost anyplace else, which figures. Very
  925.  * simple on CPM, because the system can do exactly what you want.
  926.  * This should be a terminal dispatch function.
  927.  */
  928. int
  929. ttgetc(void)
  930. {
  931. #if    SYS_VMS
  932.     while (ibufi >= nibuf) {
  933.         if (!tttypahead())
  934.             read_vms_tty(1);
  935.     }
  936.     return (ibuf[ibufi++] & 0xFF);      /* Allow multinational  */
  937. #else
  938. #if SYS_MSDOS || SYS_OS2
  939.     /*
  940.      * If we've got a mouse, poll waiting for mouse movement and mouse
  941.      * clicks until we've got a character to return.
  942.      */
  943. # if OPT_MS_MOUSE
  944.     if (ms_exists()) {
  945.         for_ever {
  946.             if (tttypahead())
  947.                 break;
  948.             ms_processing();
  949.         }
  950.     }
  951. # endif /* OPT_MS_MOUSE */
  952. #if    CC_MSC || CC_TURBO || SYS_OS2
  953.     return getch();
  954. #endif
  955. #if    CC_NEWDOSCC && !(CC_MSC||CC_TURBO||SYS_OS2||SYS_WINNT)
  956.     {
  957.     int c;
  958.  
  959.     /* if a char already is ready, return it */
  960.     if (nxtchar >= 0) {
  961.         c = nxtchar;
  962.         nxtchar = -1;
  963.         return(c);
  964.     }
  965.  
  966.     /* call the dos to get a char */
  967.     rg.h.ah = 7;        /* dos Direct Console Input call */
  968.     intdos(&rg, &rg);
  969.     c = rg.h.al;        /* grab the char */
  970.     return(c & 0xff);
  971.     }
  972. #endif
  973. #else /* ! (SYS_MSDOS || SYS_OS2) */
  974.     /* Not used. */
  975.     return 0;
  976. #endif
  977. #endif
  978.  
  979. }
  980.  
  981.  
  982. /* tttypahead:    Check to see if any characters are already in the
  983.         keyboard buffer
  984. */
  985. #if    ! SYS_WINNT
  986. int
  987. tttypahead(void)
  988. {
  989.  
  990. #if    DISP_X11
  991.     return x_typahead(0);
  992. #endif
  993.  
  994. #if    DISP_VMSVT
  995.     if (ibufi >= nibuf) {
  996.         read_vms_tty(NIBUF);
  997.         return (nibuf > 0);
  998.     }
  999.     return TRUE;
  1000. #endif
  1001.  
  1002. #if    SYS_MSDOS || SYS_OS2
  1003.     return (kbhit() != 0);
  1004. #endif
  1005.  
  1006. }
  1007. #endif
  1008.  
  1009. #endif /* not SYS_UNIX */
  1010.  
  1011.  
  1012. /* Get terminal size from system, first trying the driver, and then
  1013.  * the environment.  Store number of lines into *heightp and width
  1014.  * into *widthp.  If zero or a negative number is stored, the value
  1015.  * is not valid.  This may be fixed (in the tcap.c case) by the TERM
  1016.  * variable.
  1017.  */
  1018. #if ! DISP_X11
  1019. void
  1020. getscreensize (int *widthp, int *heightp)
  1021. {
  1022.     char *e;
  1023. #ifdef TIOCGWINSZ
  1024.     struct winsize size;
  1025. #endif
  1026.     *widthp = 0;
  1027.     *heightp = 0;
  1028. #ifdef TIOCGWINSZ
  1029.     if (ioctl (0, TIOCGWINSZ, (caddr_t)&size) == 0) {
  1030.         if ((int)(size.ws_row) > 0)
  1031.             *heightp = size.ws_row;
  1032.         if ((int)(size.ws_col) > 0)
  1033.             *widthp = size.ws_col;
  1034.     }
  1035.     if (*widthp <= 0) {
  1036.         e = getenv("COLUMNS");
  1037.         if (e)
  1038.             *widthp = atoi(e);
  1039.     }
  1040.     if (*heightp <= 0) {
  1041.         e = getenv("LINES");
  1042.         if (e)
  1043.             *heightp = atoi(e);
  1044.     }
  1045. #else
  1046.     e = getenv("COLUMNS");
  1047.     if (e)
  1048.         *widthp = atoi(e);
  1049.     e = getenv("LINES");
  1050.     if (e)
  1051.         *heightp = atoi(e);
  1052. #endif
  1053. }
  1054. #endif
  1055.  
  1056. /******************************************************************************/
  1057.  
  1058. /*
  1059.  * Define an empty terminal type for machines where we cannot use 'dumb_term',
  1060.  * so that command-line prompting will have something to talk to.
  1061.  */
  1062.  
  1063. static int  null_cres     (const char *res);
  1064. static int  null_getc     (void);
  1065. static OUTC_DCL null_putc (OUTC_ARGS);
  1066. static int  null_typahead (void);
  1067. static void null_beep     (void);
  1068. static void null_close    (void);
  1069. static void null_eeol     (void);
  1070. static void null_eeop     (void);
  1071. static void null_flush    (void);
  1072. static void null_kclose   (void);
  1073. static void null_kopen    (void);
  1074. static void null_move     (int row, int col);
  1075. static void null_open     (void);
  1076. static void null_rev      (UINT state);
  1077.  
  1078. TERM null_term = {
  1079.     1,
  1080.     1,
  1081.     80,
  1082.     80,
  1083.     0/*MARGIN*/,
  1084.     0/*SCRSIZ*/,
  1085.     0/*NPAUSE*/,
  1086.     null_open,
  1087.     null_close,
  1088.     null_kopen,
  1089.     null_kclose,
  1090.     null_getc,
  1091.     null_putc,
  1092.     null_typahead,
  1093.     null_flush,
  1094.     null_move,
  1095.     null_eeol,
  1096.     null_eeop,
  1097.     null_beep,
  1098.     null_rev,
  1099.     null_cres,
  1100.     null_t_setfor,
  1101.     null_t_setback,
  1102.     null_t_setpal,
  1103.     null_t_scroll,
  1104.     null_t_pflush,
  1105.     null_t_icursor,
  1106.     null_t_title,
  1107.     null_t_watchfd,
  1108.     null_t_unwatchfd,
  1109. };
  1110.  
  1111. static void null_open(void)    { }
  1112. static void null_close(void)    { }
  1113. static void null_kopen(void)    { }
  1114. static void null_kclose(void)    { }
  1115. static int  null_getc(void)    { return abortc; }
  1116. /*ARGSUSED*/
  1117. static OUTC_DCL null_putc(OUTC_ARGS) { OUTC_RET c; }
  1118. static int  null_typahead(void)    { return FALSE; }
  1119. static void null_flush(void)    { }
  1120. /*ARGSUSED*/
  1121. static void null_move(int row GCC_UNUSED, int col GCC_UNUSED) { }
  1122. static void null_eeol(void)    { }
  1123. static void null_eeop(void)    { }
  1124. static void null_beep(void)    { }
  1125. /*ARGSUSED*/
  1126. static void null_rev(UINT state GCC_UNUSED) { }
  1127. /*ARGSUSED*/
  1128. static int null_cres(const char *res GCC_UNUSED) { return(FALSE); }
  1129.  
  1130. /*
  1131.  * These are public, since we'll use them as placeholders for unimplemented
  1132.  * device methods.
  1133.  */
  1134. /*ARGSUSED*/ void null_t_setfor (int f GCC_UNUSED) { }
  1135. /*ARGSUSED*/ void null_t_setback (int b GCC_UNUSED) { }
  1136. /*ARGSUSED*/ void null_t_setpal (const char *p GCC_UNUSED) { }
  1137. /*ARGSUSED*/ void null_t_scroll (int f GCC_UNUSED, int t GCC_UNUSED, int n GCC_UNUSED) { }
  1138. /*ARGSUSED*/ void null_t_pflush (void) { }
  1139. /*ARGSUSED*/ void null_t_icursor (int c GCC_UNUSED) { }
  1140. /*ARGSUSED*/ void null_t_title (char *t GCC_UNUSED) { }
  1141. /*ARGSUSED*/ int  null_t_watchfd (int fd GCC_UNUSED, WATCHTYPE type GCC_UNUSED, long *idp GCC_UNUSED) { return 0; }
  1142. /*ARGSUSED*/ void null_t_unwatchfd (int fd GCC_UNUSED, long id GCC_UNUSED) { }
  1143.  
  1144. /******************************************************************************/
  1145.  
  1146. /*
  1147.  * This function is used during terminal initialization to allow us to setup
  1148.  * either a dumb or null terminal driver to handle stray command-line and other
  1149.  * debris, then (in the second call), open the screen driver.
  1150.  */
  1151. int
  1152. open_terminal(TERM *termp)
  1153. {
  1154.     static    TERM    save_term;
  1155.     static int    initialized;
  1156.  
  1157.     if (!initialized++) {
  1158.  
  1159.         /*
  1160.          * If the open and/or close slots are empty, fill them in with
  1161.          * the screen driver's functions.
  1162.          */
  1163.         if (termp->t_open == 0)
  1164.             termp->t_open = term.t_open;
  1165.  
  1166.         if (termp->t_close == 0)
  1167.             termp->t_close = term.t_close;
  1168.  
  1169.         /*
  1170.          * If the command-line driver is the same as the screen driver,
  1171.          * then all we're really doing is opening the terminal in raw
  1172.          * mode (e.g., the termcap driver), but with restrictions on
  1173.          * cursor movement, etc.  We do a bit of juggling, because the
  1174.          * screen driver typically sets entries (such as the screen
  1175.          * size) in the 'term' struct.
  1176.          */
  1177.         if (term.t_open == termp->t_open) {
  1178.             TTopen();
  1179.             save_term = term;
  1180.             term = *termp;
  1181.         } else {
  1182.             save_term = term;
  1183.             term = *termp;
  1184.             TTopen();
  1185.         }
  1186.     } else {
  1187.         /*
  1188.          * If the command-line driver isn't the same as the screen
  1189.          * driver, reopen the terminal with the screen driver.
  1190.          */
  1191.         if (save_term.t_open != term.t_open) {
  1192.             TTclose();
  1193.             term = save_term;
  1194.             TTopen();
  1195.         } else {
  1196.             term = save_term;
  1197.         }
  1198.     }
  1199.     return TRUE;
  1200. }
  1201.