home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / forth / pfe-0.000 / pfe-0 / pfe-0.9.13 / src / termunix.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-17  |  23.3 KB  |  957 lines

  1. /*
  2.  * This file is part of the portable Forth environment written in ANSI C.
  3.  * Copyright (C) 1995  Dirk Uwe Zoller
  4.  *
  5.  * This library is free software; you can redistribute it and/or
  6.  * modify it under the terms of the GNU Library General Public
  7.  * License as published by the Free Software Foundation; either
  8.  * version 2 of the License, or (at your option) any later version.
  9.  *
  10.  * This library is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13.  * See the GNU Library General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU Library General Public
  16.  * License along with this library; if not, write to the Free
  17.  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  *
  19.  * This file is version 0.9.13 of 17-July-95
  20.  * Check for the latest version of this package via anonymous ftp at
  21.  *    roxi.rz.fht-mannheim.de:/pub/languages/forth/pfe-VERSION.tar.gz
  22.  * or    sunsite.unc.edu:/pub/languages/forth/pfe-VERSION.tar.gz
  23.  * or    ftp.cygnus.com:/pub/forth/pfe-VERSION.tar.gz
  24.  *
  25.  * Please direct any comments via internet to
  26.  *    duz@roxi.rz.fht-mannheim.de.
  27.  * Thank You.
  28.  */
  29. /*-
  30.  * termunix.c ---    Terminal driver for UNIX-like systems using
  31.  *            termcap or the termcap emulation in curses.
  32.  *
  33.  * Refer to this file as an example of how to write such a driver, though
  34.  * other drivers for non-portable operating systems should be much simpler.
  35.  * E.g. on an IBM-PC you could do everything here with a few INT-10h calls.
  36.  *
  37.  * (duz 24Feb94)
  38.  */
  39.  
  40. #include "forth.h"
  41. #include "support.h"
  42. #include "term.h"
  43.  
  44. #include <stdio.h>        /* putchar() */
  45. #include <stdlib.h>        /* getenv() */
  46. #include <string.h>        /* strlen() */
  47. #include <errno.h>        /* EINTR */
  48.  
  49. #include "nonansi.h"
  50. #include "missing.h"
  51.  
  52. /*-
  53.  * Table of contents
  54.  *
  55.  * 1      Initialization for usage of screen package
  56.  * 1.1      File system, blocking/nonblocking io
  57.  * 1.2      Setting tty driver flags
  58.  * 1.2.1      Old System-V (termio) and Posix (termios) styles
  59.  * 1.2.2      V7 and BSD style
  60.  * 2      Getting information about the terminal
  61.  * 2.1      Simply assume vt100
  62.  * 2.2      Use termcap
  63.  * 2.3      Use terminfo
  64.  */
  65.  
  66.  
  67. /*-
  68.  * 1 Initialization for usage of screen package
  69.  *
  70.  * All this stuff isn't neccessary or very easy on non unix-like machines
  71.  * where there are direct console i/o functions available. See term-emx.c.
  72.  * It implements the three required functions:
  73.  *
  74.  *    - prepare_terminal()      called once at startup
  75.  *    - interactive_terminal()  called to set up the terminal for pfe
  76.  *    - system_terminal()       called to reset the terminal on exit
  77.  *
  78.  * One problem is that in Unix-like OS console i/o is done via the
  79.  * file system. It takes a little hacking to get an interactive
  80.  * flavour from this arrangement. The kind of hacking changes while
  81.  * Unix evolves. The hacking is addressed at two instances:
  82.  *
  83.  *    - UNIX file system. fcntl() calls on the standard input are used
  84.  *    to prevent it from waiting for a pressed key when we only want
  85.  *    to detect if a key is pressed.
  86.  *
  87.  *    - terminal device driver. It must be configured to pass keys
  88.  *    immediately instead of assembling it to lines, to pass through
  89.  *    all keys and not interpret some of them or interpret those keys
  90.  *    we need in a way we want.
  91.  *
  92.  * All what's changed must be carefully restored on exit otherwise
  93.  * the terminal won't work any more.
  94.  */
  95.  
  96. /*
  97.  * 1.1 file system, blocking/nonblocking io:
  98.  */
  99.  
  100. #ifndef O_NONBLOCK
  101. # ifdef O_NDELAY
  102. #   define O_NONBLOCK O_NDELAY
  103. # else
  104. #   error "neither O_NONBLOCK nor O_NDELAY defined"
  105. # endif
  106. #endif
  107.  
  108. static int saved_fcntl[3];
  109.  
  110. static void            /* set file associated with fd to */
  111. waitchar (int fd)        /* "wait for character" */
  112. {
  113.   int flags;
  114.  
  115.   flags = fcntl (fd, F_GETFL, 0);
  116.   fcntl (fd, F_SETFL, flags & ~O_NONBLOCK);
  117. }
  118.  
  119. static void            /* set file associated with fd to */
  120. no_waitchar (int fd)        /* "don't wait for character" */
  121. {
  122.   int flags;
  123.  
  124.   flags = fcntl (fd, F_GETFL, 0);
  125.   fcntl (fd, F_SETFL, flags | O_NONBLOCK);
  126. }
  127.  
  128. /*
  129.  * 1.2 tty hacking, two major styles:
  130.  */
  131.  
  132. #if (defined HAVE_TERMIO_H || defined HAVE_TERMIOS_H) && !defined NeXTstep
  133.  
  134. /*
  135.  * 1.2.1 old System-V (termio) and Posix (termios) styles
  136.  */
  137.  
  138. #if defined HAVE_TERMIOS_H
  139.  
  140. #include <termios.h>
  141.  
  142. #elif defined HAVE_TERMIO_H
  143.  
  144. #include <termio.h>
  145.  
  146. /* make old SysV style look enough like Posix: */
  147. #define termios termio
  148. #define tcsetattr(F,X,M)    ioctl (F, TCSETAW, M)
  149. #define tcgetattr(F,M)        ioctl (F, TCGETA, M)
  150.  
  151. #ifndef VINTR
  152. #define VINTR 0
  153. #endif
  154. #ifndef VQUIT
  155. #define VQUIT 1
  156. #endif
  157. #ifndef VMIN
  158. #define VMIN 4
  159. #endif
  160. #ifndef VTIME
  161. #define VTIME 5
  162. #endif
  163.  
  164. #endif
  165.  
  166. static struct termios tty_system;
  167. #define tty_save()    tcgetattr (STDIN_FILENO, &tty_system)
  168. #define tty_restore()    tcsetattr (STDIN_FILENO, TCSAFLUSH, &tty_system)
  169. #ifdef CBAUD
  170. # define tty_ospeed    (tty_system.c_cflag & CBAUD)
  171. #else
  172. # define tty_ospeed    (tty_system.c_ospeed)
  173. #endif
  174.  
  175. int
  176. tty_interrupt_key (char ch)
  177. /*
  178.  * If ch != 0 enables SIGINT on key ch, else disables SIGINT
  179.  * returns old break key.
  180.  * Porting this function might be hard. It isn't neccessary though.
  181.  * This is needed only by the block editor: What's usually the break key
  182.  * means exit to the block editor. If you have no break key or you have
  183.  * it somewhere else than ^U, just #define this to nothing in term.h.
  184.  */
  185. {
  186.   struct termios tty;
  187.   int old;
  188.  
  189.   if (!isatty (0))
  190.     return -1;
  191.   if (tcgetattr (STDIN_FILENO, &tty) != 0)
  192.     return -1;
  193.   old = tty.c_cc[VINTR];
  194.   if (ch)
  195.     {
  196.       tty.c_iflag |= BRKINT;
  197.       tty.c_cc[VINTR] = 0xFF;
  198.     }
  199.   else
  200.     {
  201.       tty.c_iflag &= ~BRKINT;
  202.       tty.c_cc[VINTR] = ch;
  203.     }
  204.   tcsetattr (0, TCSAFLUSH, &tty);
  205.   return old;
  206. }
  207.  
  208. #define    C_IFLAGS_OFF    (ICRNL | IGNBRK | IGNCR | INLCR | \
  209.              ISTRIP | IXOFF | IXON)
  210. #define C_IFLAGS_ON    (BRKINT)
  211. #define C_OFLAGS_OFF    (0)
  212. #define C_OFLAGS_ON    (0)
  213. #define C_LFLAGS_OFF    (ECHO | ICANON)
  214. #define C_LFLAGS_ON    (ISIG)
  215.  
  216. static void
  217. tty_interactive (void)
  218. {
  219.   struct termios tty = tty_system;
  220.  
  221.   if (!isatty (STDIN_FILENO) || option.canonical)
  222.     return;
  223.   tty.c_iflag &= ~C_IFLAGS_OFF, tty.c_iflag |= C_IFLAGS_ON;
  224.   tty.c_oflag &= ~C_OFLAGS_OFF, tty.c_oflag |= C_OFLAGS_ON;
  225.   tty.c_lflag &= ~C_LFLAGS_OFF, tty.c_lflag |= C_LFLAGS_ON;
  226.   tty.c_cc[VMIN] = 1;
  227.   tty.c_cc[VTIME] = 0;
  228.   tty.c_cc[VINTR] = INTR_KEY;
  229.   tty.c_cc[VQUIT] = QUIT_KEY;
  230. #if defined HAVE_TERMIOS_H
  231.   tty.c_cc[VSUSP] = SUSP_KEY;
  232.   tty.c_cc[VSTART] = '\xFF';
  233.   tty.c_cc[VSTOP] = '\xFF';
  234. #endif
  235.   tcsetattr (STDIN_FILENO, TCSAFLUSH, &tty);
  236. }
  237.  
  238. #elif defined HAVE_SGTTY_H
  239.  
  240. /*
  241.  * 1.2.2 V7 and BSD style, when struct sgttyb is defined
  242.  */
  243.  
  244. #include <sgtty.h>
  245.  
  246. static struct
  247. {
  248.   struct sgttyb sg;
  249.   struct tchars tc;
  250.   int lc;            /* local mode word */
  251. }
  252. tty_system;
  253.  
  254. #define tty_save()    ioctl (STDIN_FILENO, TIOCGETP, &tty_system.sg), \
  255.             ioctl (STDIN_FILENO, TIOCGETC, &tty_system.tc), \
  256.             ioctl (STDIN_FILENO, TIOCLGET, &tty_system.lc)
  257. #define tty_restore()    ioctl (STDIN_FILENO, TIOCSETP, &tty_system.sg), \
  258.             ioctl (STDIN_FILENO, TIOCSETC, &tty_system.tc), \
  259.             ioctl (STDIN_FILENO, TIOCLSET, &tty_system.lc)
  260. #define tty_ospeed    (tty_system.sg.sg_ospeed)
  261.  
  262. int
  263. tty_interrupt_key (char ch)
  264. /*
  265.  * If ch != 0 enables SIGINT on key ch, else disables SIGINT
  266.  * returns old break key.
  267.  * Porting this function might be hard. It isn't neccessary though.
  268.  * This is needed only by the block editor: What's usually the break key
  269.  * means exit to the block editor. If you have no break key or you have
  270.  * it somewhere else than ^U, just #define this to nothing in term.h.
  271.  */
  272. {
  273.   struct tchars tc;
  274.   int old;
  275.  
  276.   if (!isatty (0))
  277.     return -1;
  278.   if (ioctl (STDIN_FILENO, TIOCGETC, &tc) != 0)
  279.     return -1;
  280.   old = tc.t_intrc;
  281.   tc.t_intrc = ch == 0 ? -1 : ch;
  282.   ioctl (STDIN_FILENO, TIOCSETC, &tc);
  283.   return old == -1 ? 0 : old;
  284. }
  285.  
  286. #define    C_FLAGS_OFF    (LCASE | ECHO | RAW)
  287. #define C_FLAGS_ON    (CBREAK)
  288. #define L_FLAGS_ON    (LTOSTOP | LPASS8OUT)
  289.  
  290. static void
  291. tty_interactive (void)
  292. {
  293.   struct sgttyb sg = tty_system.sg;
  294.   struct tchars tc = tty_system.tc;
  295.   int flags;
  296.  
  297.   if (!isatty (STDIN_FILENO) || option.canonical)
  298.     return;
  299.   sg.sg_flags &= ~C_FLAGS_OFF;
  300.   sg.sg_flags |=  C_FLAGS_ON;
  301.   tc.t_intrc = INTR_KEY;
  302.   tc.t_quitc = QUIT_KEY;
  303.   tc.t_startc = -1;
  304.   tc.t_stopc = -1;
  305.   ioctl (STDIN_FILENO, TIOCSETP, &sg);
  306.   ioctl (STDIN_FILENO, TIOCSETC, &tc);
  307.   flags = L_FLAGS_ON;
  308.   ioctl (STDIN_FILENO, TIOCLBIS, &flags);
  309. }
  310.  
  311. #else
  312.  
  313. #error "neither termios.h/termio.h nor sgtty.h available"
  314.  
  315. #endif /* tty hacking, two major styles */
  316.  
  317. /*-
  318.  * 2 Getting information about the terminal
  319.  *
  320.  * The next problem is to get information about the connected terminal.
  321.  * UNIX employs a database file called termcap or (in newer versions)
  322.  * terminfo, where informations on the actually used terminal are
  323.  * stored. Before we begin we must retrieve these information from the
  324.  * data base. Three versions of the retrieval:
  325.  *
  326.  *    - don't read databases but assume vt100
  327.  *    - use termcap or the termcap emulation in curses
  328.  *    - use terminfo
  329.  */
  330.  
  331. /* If nothing is decided yet, make a guess what should work.
  332.    Note that you can override this with -DUSE_TERMCAP even if no
  333.    termcap.h is avavailable in your system. Just in case the terminfo
  334.    calls work worse than the termcap emulation inside curses. */
  335.  
  336. #if !defined ASSUME_VT100 && !defined USE_TERMCAP && !defined USE_TERMINFO
  337. # if defined HAVE_TERMCAP_H
  338. #   define USE_TERMCAP
  339. # elif defined HAVE_CURSES_H && defined HAVE_TERM_H
  340. #   define USE_TERMINFO
  341. # else
  342. #   define ASSUME_VT100
  343. # endif
  344. #endif
  345.  
  346. #if defined ASSUME_VT100 || defined USE_TERMCAP
  347.  
  348. /*
  349.  * We make termcap look like terminfo. The following identifiers stand
  350.  * for capabilities and are declared in term.h when using terminfo.
  351.  * Otherwise they are used as indices to vectors containing capability
  352.  * names or escape sequences. In the latter case they must be declared
  353.  * in the same order as the initializers to the vectors of capability
  354.  * names below.
  355.  */
  356. enum
  357. {
  358.   cursor_address,
  359.   cursor_home,
  360.   cursor_left,
  361.   cursor_right,
  362.   cursor_up,
  363.   cursor_down,
  364.   clear_screen,
  365.   clr_eos,
  366.   clr_eol,
  367.   bell,
  368.   delete_character,
  369.   delete_line,
  370.   scroll_forward,
  371.   scroll_reverse,
  372.   enter_standout_mode,
  373.   exit_standout_mode,
  374.   enter_underline_mode,
  375.   exit_underline_mode,
  376.   enter_bold_mode,
  377.   enter_reverse_mode,
  378.   enter_blink_mode,
  379.   exit_attribute_mode,
  380.   keypad_xmit,
  381.   keypad_local
  382. };
  383.  
  384. #endif
  385.  
  386. /*
  387.  * Strings used to query the termcap database.
  388.  *
  389.  * (In case terminfo is * used, these are only needed to satisfy the
  390.  * show_*_strings * functions.)
  391.  */
  392.  
  393. static char tckeycode[][3] =
  394. {
  395.   "k1", "k2", "k3", "k4", "k5",    /* keys in same order as enum keycode */
  396.   "k6", "k7", "k8", "k9", "k0",    /* from term.h */
  397.   "kl", "kr", "ku", "kd",    /* result is just what has to be exported */
  398.   "kh", "kH", "kN", "kP",    /* via variable rawkey_string */
  399.   "kb", "kD", "kM", "kI",
  400.   "kA", "kE", "kL", "kC"
  401. };
  402.  
  403. static char tcctlcode[][3] =
  404. {
  405.   "cm", "ho",
  406.   "le", "nd", "up", "do",
  407.   "cl", "cd", "ce", "bl",
  408.   "dc", "dl",
  409.   "sf", "sr",
  410.   "so", "se", "us", "ue",
  411.   "md", "mr", "mb", "me",
  412.   "ks", "ke"
  413. };
  414.  
  415. #if defined ASSUME_VT100
  416.  
  417. /*
  418.  * 2.1 simply assume the terminal understands vt100 codes
  419.  */
  420.  
  421. static char *control_string[] =    /* Some hardcoded vt100 sequences. */
  422. {
  423.   "\033[%i%d;%dH",        /* cm - cursor move */
  424.   "\033[H",            /* ho - home position */
  425.  
  426.   "\b",                /* le - cursor left */
  427.   "\033[C",            /* nd - right one column */
  428.   "\033[A",            /* up - up one column */
  429.   "\n",                /* do - down one column */
  430.  
  431.   "\033[H\033[2J",        /* cl - clear screen and home */
  432.   "\033[J",            /* cd - clear down */
  433.   "\033[K",            /* ce - clear to end of line */
  434.   "\a",                /* bl - bell */
  435.  
  436.   "\033[P",            /* dc - delete character in line */
  437.   "\033[M",            /* dl - delete line from screen */
  438.  
  439.   "\033D",            /* sf - scroll screen up */
  440.   "\033M",            /* sr - scroll screen down */
  441.  
  442.   "\033[7m",            /* so - enter standout mode */
  443.   "\033[m",            /* se - leave standout mode */
  444.   "\033[4m",            /* us - turn on underline mode */
  445.   "\033[m",            /* ue - turn off underline mode */
  446.  
  447.   "\033[1m",            /* md - enter double bright mode */
  448.   "\033[7m",            /* mr - enter reverse video mode */
  449.   "\033[5m",            /* mb - enter blinking mode */
  450.   "\033[m",            /* me - turn off all appearance modes */
  451.  
  452.   "\033[?1h\033=",        /* ks - make function keys transmit */
  453.   "\033[?1l\033>"        /* ke - make function keys work locally */
  454. };
  455.  
  456. char *rawkey_string[] =        /* Strings sent by function keys */
  457. {
  458.   "\033OP",            /* k1 - function keys 1 - 4 from vt100 */
  459.   "\033OQ",            /* k2 */
  460.   "\033OR",            /* k3 */
  461.   "\033OS",            /* k4 */
  462.   "\033[15~",            /* k5 - function keys 5 - 10 from xterm */
  463.   "\033[17~",            /* k6 */
  464.   "\033[18~",            /* k7 */
  465.   "\033[19~",            /* k8 */
  466.   "\033[20~",            /* k9 */
  467.   "\033[21~",            /* k0 */
  468.  
  469.   "\033OD",            /* kl - arrow left */
  470.   "\033OC",            /* kr - arrow right */
  471.   "\033OA",            /* ku - arrow up */
  472.   "\033OB",            /* kd - arrow down */
  473.  
  474.   "\033[1~",            /* kh - home key */
  475.   "\033[4~",            /* kH - home down key (end key) */
  476.   "\033[6~",            /* kN - next page */
  477.   "\033[5~",            /* kP - previous page */
  478.  
  479.   "\b",                /* kb - backspace key */
  480.   "\033[3~",            /* kD - delete character key */
  481.   NULL,                /* kM - exit insert mode key */
  482.   "\033[2~",            /* kI - insert character key */
  483.  
  484.   NULL,                /* kA - insert line key */
  485.   NULL,                /* kE - clear end of line key */
  486.   NULL,                /* kL - delete line key */
  487.   NULL,                /* kC - clear screen key */
  488. };
  489.  
  490. #define query_database() 1    /* nothing to query */
  491. #define t_putc(C) putchar (C)
  492.  
  493. #define tputs(S,P,F) fputs (S, stdout)
  494.  
  495. void
  496. t_puts (int cap, int n)
  497. {
  498.   fputs (control_string[cap], stdout);
  499. }
  500.  
  501. char *
  502. tparm (int cap, int x, int y)    /* call this only with `cm' */
  503. {
  504.   static char buf[12];
  505.   sprintf (buf, "\033[%d;%dH", x + 1, y + 1);
  506.   return buf;
  507. }
  508.  
  509. #elif defined USE_TERMCAP
  510.  
  511. /*
  512.  * 2.2 Use termcap calls to retrieve information about the terminal
  513.  */
  514.  
  515. #ifdef HAVE_TERMCAP_H
  516. #include <termcap.h>
  517. #else
  518.  
  519. /* Systems emulating the termcap functions in the curses library
  520.  * usually haven't declared them in a file <termcap.h>, so here are
  521.  * these declarations: */
  522. #ifdef __cplusplus
  523. extern "C"
  524. {
  525. #endif
  526.   int tgetent (char *, char *);
  527.   int tgetnum (char *);
  528.   int tgetflag (char *);
  529.   char *tgetstr (char *, char **);
  530.   int tputs (char *, int, int (*)(int));
  531.   char *tgoto (char *, int, int);
  532. #ifdef __cplusplus
  533. }
  534. #endif
  535.  
  536. #endif
  537.  
  538. static char *control_string[DIM (tcctlcode)];
  539. char *rawkey_string[NO_OF_KEYS];
  540.  
  541. static int
  542. query_database (void)
  543. /*
  544.  * Get the strings needed out of termcap/terminfo.
  545.  * Store them for later use in control_string[] and rawkey_string[].
  546.  */
  547. {
  548.   char *ttype = getenv ("TERM");
  549.   char tcent[2048];
  550.   static char tcbuf[2048];
  551.   char *tctop = tcbuf;
  552.   int i;
  553.  
  554.   if (ttype == NULL || tgetent (tcent, ttype) <= 0)
  555.     return 0;
  556.  
  557. #if HAVE_TERMCAP_H
  558.   {
  559.     char *pc = tgetstr ("pc", &tctop);
  560.     if (pc != NULL)
  561.       PC = *pc;
  562.     else
  563.       PC = 0;
  564.   }
  565. #endif
  566.  
  567.   rows = tgetnum ("li");
  568.   cols = tgetnum ("co");
  569.  
  570.   /* Read all termcap strings we need, */
  571.   for (i = 0; i < NO_OF_KEYS; i++)
  572.     rawkey_string[i] = tgetstr (tckeycode[i], &tctop);
  573.   /* rawkey_string [EKEY_enter - EKEY_k1] = "\r"; */
  574.   for (i = 0; i < DIM (tcctlcode); i++)
  575.     control_string[i] = tgetstr (tcctlcode[i], &tctop);
  576.   if (control_string [cursor_left] == NULL)
  577.     control_string [cursor_left] = "\b";
  578.  
  579. #if defined HAVE_OSPEED
  580.   {
  581.     char *pc = tgetstr ("pc", &tctop);
  582.     /* these are defined inside the termcap-library: */
  583.     ospeed = tty_ospeed;
  584.     PC = pc ? *pc : 0;
  585.     BC = control_string[cursor_left];
  586.     UP = control_string[cursor_up];
  587.   }
  588. #endif
  589.   return 1;
  590. }
  591.  
  592. static int            /* utility function for tputs(): */
  593. t_putc (int c)            /* putchar() is a macro, we need a function */
  594. {
  595.   return putchar (c);
  596. }
  597.  
  598. static void
  599. t_puts (int tcidx, int n)    /* issue termcap string to terminal */
  600. {
  601.   if (!control_string[tcidx])
  602.     /* no harm if feature not available */
  603.     return;
  604.   tputs (control_string[tcidx], n, t_putc);
  605.   fflush (stdout);
  606. }
  607.  
  608. #define tparm(CAP,X,Y) tgoto (control_string [CAP], Y, X)
  609.  
  610. #elif defined USE_TERMINFO
  611.  
  612. /*
  613.  * 2.3 Use terminfo calls to retrieve information about the terminal
  614.  */
  615.  
  616. #include <curses.h>
  617. #include <term.h>
  618.  
  619. static char *control_string[DIM (tcctlcode)];
  620. char *rawkey_string[NO_OF_KEYS];
  621.  
  622. static int
  623. query_database (void)
  624. {
  625.   int errret;
  626.  
  627.   setupterm (NULL, STDOUT_FILENO, &errret);
  628.   if (errret != 1)
  629.     return 0;
  630.  
  631. #define KINIT(key, ti_capability) \
  632.   rawkey_string [APPEND (EKEY_,key) - EKEY_k1] = ti_capability
  633.  
  634.   KINIT (k1, key_f1);        /* function keys F1 through F10 */
  635.   KINIT (k2, key_f2);
  636.   KINIT (k3, key_f3);
  637.   KINIT (k4, key_f4);
  638.   KINIT (k5, key_f5);
  639.   KINIT (k6, key_f6);
  640.   KINIT (k7, key_f7);
  641.   KINIT (k8, key_f8);
  642.   KINIT (k9, key_f9);
  643.   KINIT (k0, key_f10);
  644.  
  645.   KINIT (kl, key_left);        /* arrow key left */
  646.   KINIT (kr, key_right);    /* arrow key right */
  647.   KINIT (ku, key_up);        /* arrow key up */
  648.   KINIT (kd, key_down);        /* arrow key down */
  649.  
  650.   KINIT (kh, key_home);        /* home key */
  651.   KINIT (kH, key_end);        /* home down key */
  652.   KINIT (kN, key_npage);    /* next page key */
  653.   KINIT (kP, key_ppage);    /* previous page key */
  654.  
  655.   KINIT (kb, key_backspace);    /* backspace key */
  656.   KINIT (kD, key_dc);        /* delete character key */
  657.   KINIT (kM, key_eic);        /* exit insert mode key */
  658.   KINIT (kI, key_ic);        /* insert character key */
  659.  
  660.   KINIT (kA, key_il);        /* insert line key */
  661.   KINIT (kE, key_eol);        /* clear to end of line key */
  662.   KINIT (kL, key_dl);        /* delete line key */
  663.   KINIT (kC, key_clear);    /* clear screen key */
  664.  
  665. #undef KINIT
  666.   if (cursor_left == NULL)
  667.     cursor_left = "\b";
  668.   return 1;
  669. }
  670.  
  671. static int            /* utility function for tputs(): */
  672. t_putc (char c)            /* putchar() is a macro, we need a function */
  673. {
  674.   return putchar (c);
  675. }
  676.  
  677. static void            /* output capability with affected lines cnt */
  678. t_puts (char *s, int n)
  679. {
  680.   tputs (s, n, t_putc);
  681.   fflush (stdout);
  682. }
  683.  
  684. #else
  685.  
  686. #error "One of ASSUME_VT100, USE_TERMCAP or USE_TERMINFO must be set."
  687.  
  688. #endif
  689.  
  690. void
  691. show_control_strings (int (*outf) (const char *fmt,...))
  692. /* for your information why screen manipulation doesn't work :-) */
  693. {
  694.   int i;
  695.   char *p;
  696.  
  697.   for (i = 0; i < DIM (tcctlcode); i++)
  698.     {
  699.       outf ("\n\"%s\"=", tcctlcode[i]);
  700.       if ((p = control_string[i]))
  701.     while (*p)
  702.       c_putc_printable (*p++);
  703.       else
  704.     c_puts ("undefined");
  705.     }
  706. }
  707.  
  708. void
  709. show_rawkey_strings (int (*outf) (const char *fmt,...))
  710. /* for your information why function keys don't work :-) */
  711. {
  712.   int i;
  713.   char *p;
  714.  
  715.   for (i = 0; i < DIM (tckeycode); i++)
  716.     {
  717.       outf ("\n\"%s\"=", tckeycode[i]);
  718.       if ((p = rawkey_string[i]))
  719.     while (*p)
  720.       c_putc_printable (*p++);
  721.       else
  722.     c_puts ("undefined");
  723.     }
  724. }
  725.  
  726. int                /* Prepares usage of all other functions. */
  727. prepare_terminal (void)        /* Call only once at startup. */
  728. {
  729.   int fd;
  730.  
  731.   /* save state before all changes */
  732.   for (fd = 0; fd < 3; fd++)
  733.     saved_fcntl[fd] = fcntl (fd, F_GETFL, 0);
  734.   tty_save ();
  735.  
  736.   /* initialize termcap-stuff */
  737.   return query_database ();
  738. }
  739.  
  740. void                /* set terminal to the sort of */
  741. interactive_terminal (void)    /* `cbreak' mode used by pfe */
  742. {
  743.   tty_interactive ();
  744.   t_puts (keypad_xmit, 0);
  745. }
  746.  
  747. void                /* resets terminal state to */
  748. system_terminal (void)        /* as it was before */
  749. {
  750.   int fd;
  751.  
  752.   if (!isatty (STDIN_FILENO) || option.canonical)
  753.     return;
  754.   fflush (stdout);
  755.   tty_restore ();
  756.   t_puts (keypad_local, 0);
  757.   for (fd = 0; fd < 3; fd++)
  758.     fcntl (fd, F_SETFL, saved_fcntl[fd]);
  759. }
  760.  
  761. /*
  762.  * Handle window size change, see also signal.c:
  763.  */
  764. #ifdef TIOCGWINSZ
  765. void
  766. query_winsize (void)
  767. {
  768.   struct winsize size;
  769.  
  770.   if (ioctl (1, TIOCGWINSZ, (char *) &size) >= 0)
  771.     {
  772.       rows = size.ws_row;
  773.       cols = size.ws_col;
  774.       xmax = size.ws_xpixel;
  775.       ymax = size.ws_ypixel;
  776.     }
  777. }
  778. #else
  779. void
  780. query_winsize (void)
  781. {
  782. }
  783. #endif
  784.  
  785. /************************************************************************/
  786. /* Input from keyboard.                                                 */
  787. /************************************************************************/
  788.  
  789. /*
  790.  * Having hacked the terminal driver and the standard input in the
  791.  * appropiate modes all we have to do is read one character from
  792.  * standard input to get a key.
  793.  * The only problem is: we can't detect if a key is available without
  794.  * reading it. So we have to store it in this case.
  795.  */
  796.  
  797. #define NOCH ((unsigned short)0xABCD)
  798.                 /* encodes 'no character available' */
  799.  
  800. static unsigned short        /* the next character when read by */
  801.   nxch = NOCH;            /* keypressed() */
  802.  
  803. static int
  804. nextch (void)
  805. /*
  806.  * Read a single character from standard input.
  807.  */
  808. {
  809.   unsigned char c;
  810.  
  811.   if (nxch != NOCH)
  812.     {
  813.       c = (unsigned char) nxch;
  814.       nxch = NOCH;
  815.       return c;
  816.     }
  817.   else
  818.     for (;;)
  819.       switch (read (0, &c, 1))
  820.     {
  821.     case -1:
  822.       switch (errno)
  823.         {
  824.         default:
  825.           return -1;
  826.         case EAGAIN:
  827.         case EINTR:
  828.           continue;
  829.         }
  830.     case 0:
  831.       return -1;
  832.     default:
  833.       return c;
  834.     }
  835. }
  836.  
  837. int
  838. c_keypressed (void)
  839. /*
  840.  * Checks if a character is available in the standard input. Saves it in nxch.
  841.  * Returns: 1 if char available, 0 otherwise
  842.  */
  843. {
  844.   unsigned char c;        /* place to read the character */
  845.   int result;
  846.  
  847.   fflush (stdout);
  848.   if (nxch != NOCH)
  849.     return 1;            /* char from previos keypressed() */
  850.   no_waitchar (STDIN_FILENO);
  851.   result = read (0, &c, 1);
  852.   waitchar (STDIN_FILENO);
  853.   if (result != 1)
  854.     return 0;
  855.   nxch = c;
  856.   return 1;
  857. }
  858.  
  859. int
  860. c_getkey (void)
  861. {
  862.   fflush (stdout);
  863.   return nextch ();
  864. }
  865.  
  866. /************************************************************************/
  867. /* Output to screen, control with termcap:                              */
  868. /************************************************************************/
  869.  
  870. static int row, col;        /* position of curser as tracked */
  871.  
  872. void                /* output character and */
  873. c_putc_noflush (char c)        /* trace the cursor position */
  874. {
  875.   putchar (c);
  876.   switch (c)
  877.     {
  878.     case '\a':            /* bell, no change of cursor position */
  879.       break;
  880.     case '\b':            /* backspace, move cursor left */
  881.       if (col > 0)
  882.     col--;
  883.       break;
  884.     case '\r':            /* carriage return, ->column 0 */
  885.       col = 0;
  886.       break;
  887.     default:            /* ordinary character: */
  888.       if (col < cols - 1)    /* at right edge of screen? */
  889.     {
  890.       col++;        /* no: increment column */
  891.       break;
  892.     }            /* yes: like line feed */
  893.     case '\n':            /* line feed */
  894.       col = 0;
  895.       if (row < rows - 1)    /* if not at bottom of screen: */
  896.     row++;            /* increment row */
  897.     }                /* otherwise terminal is supposed to scroll */
  898. }
  899.  
  900. void
  901. c_flush (void)
  902. {
  903.   fflush (stdout);
  904. }
  905.  
  906. void
  907. c_putc (char c)
  908. {
  909.   c_putc_noflush (c);
  910.   fflush (stdout);
  911. }
  912.  
  913. void
  914. c_puts (const char *s)
  915. {
  916.   while (*s)
  917.     c_putc_noflush (*s++);
  918.   fflush (stdout);
  919. }
  920.  
  921. void
  922. c_gotoxy (int x, int y)
  923. {
  924.   tputs (tparm (cursor_address, y, x), 1, t_putc);
  925.   fflush (stdout);
  926.   col = x;
  927.   row = y;
  928. }
  929.  
  930. void
  931. c_wherexy (int *x, int *y)
  932. {
  933.   *x = col;
  934.   *y = row;
  935. }
  936.  
  937. /* *INDENT-OFF* */
  938. void c_goleft (void)        { t_puts (cursor_left,  0); --col; }
  939. void c_goright (void)        { t_puts (cursor_right, 0); ++col; }
  940. void c_goup (void)        { t_puts (cursor_up,    0); --row; }
  941. void c_godown (void)        { t_puts (cursor_down,  0); ++row; }
  942.  
  943. void c_home (void)        { t_puts (cursor_home, 1); row = col = 0; }
  944. void c_clrscr (void)        { t_puts (clear_screen, rows); c_home (); }
  945. void c_clreol (void)        { t_puts (clr_eol, 1); }
  946. void c_clrdown (void)        { t_puts (clr_eos, rows - row); }
  947. void c_bell (void)        { t_puts (bell, 0); }
  948.  
  949. void c_standout_on (void)    { t_puts (enter_standout_mode,    0); }
  950. void c_standout_off (void)    { t_puts (exit_standout_mode,    0); }
  951. void c_underline_on (void)    { t_puts (enter_underline_mode,    0); }
  952. void c_underline_off (void)    { t_puts (exit_underline_mode,    0); }
  953. void c_bright (void)        { t_puts (enter_bold_mode,    0); }
  954. void c_reverse (void)        { t_puts (enter_reverse_mode,    0); }
  955. void c_blinking (void)        { t_puts (enter_blink_mode,    0); }
  956. void c_normal (void)        { t_puts (exit_attribute_mode,    0); }
  957.