/** 
   -- terminal driver for unix-like systems
  
    Copyright (C) Tektronix, Inc. 1998 - 2001. All rights reserved.
  
    @see     GNU LGPL
    @author  Tektronix CTE              @(#) %derived_by: guidod %
    @version %version: bln_mpt1!5.16 %
      (%date_modified: Tue Sep 10 12:31:47 2002 %)
  
    @description
                        Terminal driver for UNIX-like systems using
  			termcap or the termcap emulation in curses.
  
   Refer to this file as an example of how to write such a driver, though
   other drivers for non-portable operating systems should be much simpler.
   E.g. on an IBM-PC you could do everything here with a few INT-10h calls.
 */
/*@{*/
#if defined(__version_control__) && defined(__GNUC__)
static char* id __attribute__((unused)) = 
"@(#) $Id: %full_filespec:  term-lib.c~bln_mpt1!5.16:csrc:bln_12xx!1 % $";
#endif

#define _P4_SOURCE 1
#include <pfe/pfe-base.h>
#include <pfe/def-xtra.h>

#include <stdio.h>		/* putchar() */
#include <stdlib.h>		/* getenv() */
#include <string.h>		/* strlen() */
#include <errno.h>		/* EINTR */

#ifdef PFE_HAVE_UNISTD_H 
#include <unistd.h>
#endif
#ifdef PFE_HAVE_FCNTL_H
#include <fcntl.h>
#endif

#include <pfe/term-sub.h>
#include <pfe/_nonansi.h>
#include <pfe/_missing.h>

#if ! defined PFE_HAVE_FCNTL && defined F_GETFL && defined F_SETFL
#define PFE_HAVE_FCNTL 1 /* should have been done by configure... */
#endif

#ifdef PFE_HAVE_FCNTL
#define P4_fcntl_F_GETFL(_FD_,_ARG_) (fcntl((_FD_),F_GETFL,(_ARG_)))
#define P4_fcntl_F_SETFL(_FD_,_ARG_) (fcntl((_FD_),F_SETFL,(_ARG_)))
#else
int P4_fcntl_F_GETFL(int fd, int arg)
{
    static seen;
    if (! seen)
    
{
        P4_warn2 ("no fcntl - ignored F_GETFL %i on fd %i", arg, fd);
        seen++;
    }
return 0; }
int P4_fcntl_F_SETFL(int fd, int arg)
{
    static seen;
    if (! seen)
    
{
        P4_warn2 ("no fcntl - ignored F_SETFL %i on fd %i", arg, fd);
        seen++;
    }
}
#endif
/*-
   Table of contents
  
   1      Initialization for usage of screen package
   1.1      File system, blocking/nonblocking io
   1.2      Setting tty driver flags
   1.2.1      Old System-V (termio) and Posix (termios) styles
   1.2.2      V7 and BSD style
   2      Getting information about the terminal
   2.1      Simply assume vt100
   2.2      Use termcap
   2.3      Use terminfo
 */
/* forward */ static int c_interrupt_key (char ch); static int c_prepare_terminal (void); static void c_cleanup_terminal (void); static void c_interactive_terminal (void); static void c_system_terminal (void); static void c_query_winsize (void); static int c_keypressed (void); static int c_getkey (void); static void c_putc_noflush (char c); static void c_put_flush (void); static void c_putc (char c); static void c_puts (const char *s); static void c_gotoxy (int x, int y); static void c_wherexy (int *x, int *y); static void c_tput (int);
/*-
   1 Initialization for usage of screen package
  
   All this stuff isn't neccessary or very easy on non unix-like machines
   where there are direct console i/o functions available. See term-emx.c.
   It implements the three required functions:
  
  	- prepare_terminal()      called once at startup
  	- interactive_terminal()  called to set up the terminal for p4th
  	- system_terminal()       called to reset the terminal on exit
  
   One problem is that in Unix-like OS console i/o is done via the
   file system. It takes a little hacking to get an interactive
   flavour from this arrangement. The kind of hacking changes while
   Unix evolves. The hacking is addressed at two instances:
  
      - UNIX file system. fcntl() calls on the standard input are used
  	to prevent it from waiting for a pressed key when we only want
  	to detect if a key is pressed.
  
      - terminal device driver. It must be configured to pass keys
  	immediately instead of assembling it to lines, to pass through
  	all keys and not interpret some of them or interpret those keys
  	we need in a way we want.
  
   All what's changed must be carefully restored on exit otherwise
   the terminal won't work any more.
 */
/*
   1.1 file system, blocking/nonblocking io:
 */
#ifndef O_NONBLOCK # ifdef O_NDELAY # define O_NONBLOCK O_NDELAY # else # error "neither O_NONBLOCK nor O_NDELAY defined" # endif #endif
static int saved_fcntl[3] = 
{0}
;
/* fixme: in multithreaded, use only once */
static void			/* set file associated with fd to */
waitchar (int fd)		/* "wait for character" */
{
    int flags = P4_fcntl_F_GETFL (fd, 0); /* fcntl (fd, F_GETFL, 0); */
    P4_fcntl_F_SETFL (fd, flags & ~O_NONBLOCK);
}
static void			/* set file associated with fd to */
no_waitchar (int fd)		/* "don't wait for character" */
{
    int flags = P4_fcntl_F_GETFL (fd, 0);  /* fcntl (fd, F_GETFL, 0); */
    P4_fcntl_F_SETFL (fd, flags | O_NONBLOCK);
}
/*
   1.2 tty hacking, two major styles:
 */
#if (defined PFE_HAVE_TERMIO_H || defined PFE_HAVE_TERMIOS_H) \ && !defined __target_os_nextstep
/*
   1.2.1 old System-V (termio) and Posix (termios) styles
 */
#if defined PFE_HAVE_TERMIOS_H #include <termios.h> #elif defined PFE_HAVE_TERMIO_H #include <termio.h> /* make old SysV style look enough like Posix: */ #define termios termio #define tcsetattr(F,X,M) ioctl (F, TCSETAW, M) #define tcgetattr(F,M) ioctl (F, TCGETA, M) #ifndef VINTR #define VINTR 0 #endif #ifndef VQUIT #define VQUIT 1 #endif #ifndef VERASE #define VERASE 2 #endif #ifndef VMIN #define VMIN 4 #endif #ifndef VTIME #define VTIME 5 #endif #endif static struct termios tty_system; #define tty_save() tcgetattr (STDIN_FILENO, &tty_system) #define tty_restore() tcsetattr (STDIN_FILENO, TCSAFLUSH, &tty_system) #ifdef CBAUD # define tty_ospeed (tty_system.c_cflag & CBAUD) #else # define tty_ospeed (tty_system.c_ospeed) #endif #define tty_erasechar (tty_system.c_cc[VERASE])
int
c_interrupt_key (char ch)
/*
   If ch != 0 enables SIGINT on key ch, else disables SIGINT
   returns old break key.
   Porting this function might be hard. It isn't neccessary though.
   This is needed only by the block editor: What's usually the break key
   means exit to the block editor. If you have no break key or you have
   it somewhere else than ^U, just #define this to nothing in term.h.
 */
{
  struct termios tty;
  int old;

  if (!isatty (0))
    return -1;
  if (tcgetattr (STDIN_FILENO, &tty) != 0)
    return -1;
  old = tty.c_cc[VINTR];
  if (ch)
    
{
      tty.c_iflag |= BRKINT;
      tty.c_cc[VINTR] = 0xFF;
    }
else
{
      tty.c_iflag &= ~BRKINT;
      tty.c_cc[VINTR] = ch;
    }
tcsetattr (0, TCSAFLUSH, &tty); return old; }
#define C_IFLAGS_OFF (ICRNL | IGNBRK | IGNCR | INLCR | \ ISTRIP | IXOFF | IXON) #define C_IFLAGS_ON (BRKINT) #define C_OFLAGS_OFF (0) #define C_OFLAGS_ON (0) #define C_LFLAGS_OFF (ECHO | ICANON) #define C_LFLAGS_ON (ISIG)
/*
   Special keys for this unix-prone driver...
 */
#ifndef INTR_KEY /* USER-CONFIG: */ #define INTR_KEY '\025' /* (^U) key to send a SIGINT */ #endif #ifndef QUIT_KEY /* USER-CONFIG: */ #define QUIT_KEY '\034' /* (^\) key to send a SIGQUIT */ #endif #ifndef SUSP_KEY /* USER-CONFIG: */ #define SUSP_KEY '\032' /* (^Z) key to send a SIGTSTP */ #endif
static void
tty_interactive (void)
{
  /* struct termios tty = tty_system; */
  struct termios tty; memcpy(&tty, &tty_system, sizeof(tty));

  if (! isatty (STDIN_FILENO)) return;

  tty.c_iflag &= ~C_IFLAGS_OFF, tty.c_iflag |= C_IFLAGS_ON;
  tty.c_oflag &= ~C_OFLAGS_OFF, tty.c_oflag |= C_OFLAGS_ON;
  tty.c_lflag &= ~C_LFLAGS_OFF, tty.c_lflag |= C_LFLAGS_ON;
  tty.c_cc[VMIN] = 1;
  tty.c_cc[VTIME] = 0;
  tty.c_cc[VINTR] = INTR_KEY;
  tty.c_cc[VQUIT] = QUIT_KEY;
#if defined PFE_HAVE_TERMIOS_H
  tty.c_cc[VSUSP] = SUSP_KEY;
  tty.c_cc[VSTART] = '\xFF';
  tty.c_cc[VSTOP] = '\xFF';
#endif
  tcsetattr (STDIN_FILENO, TCSAFLUSH, &tty);
}
#elif defined PFE_HAVE_SGTTY_H
/*
   1.2.2 V7 and BSD style, when struct sgttyb is defined
 */
#include <sgtty.h>
static struct
{
  struct sgttyb sg;
  struct tchars tc;
  int lc;			/* local mode word */
}
tty_system; #define tty_save() ioctl (STDIN_FILENO, TIOCGETP, &tty_system.sg), \ ioctl (STDIN_FILENO, TIOCGETC, &tty_system.tc), \ ioctl (STDIN_FILENO, TIOCLGET, &tty_system.lc) #define tty_restore() ioctl (STDIN_FILENO, TIOCSETP, &tty_system.sg), \ ioctl (STDIN_FILENO, TIOCSETC, &tty_system.tc), \ ioctl (STDIN_FILENO, TIOCLSET, &tty_system.lc) #define tty_ospeed (tty_system.sg.sg_ospeed) #define tty_erasechar (tty_system.sg.sg_erase)
static int
c_interrupt_key (char ch)
/*
   If ch != 0 enables SIGINT on key ch, else disables SIGINT
   returns old break key.
   Porting this function might be hard. It isn't neccessary though.
   This is needed only by the block editor: What's usually the break key
   means exit to the block editor. If you have no break key or you have
   it somewhere else than ^U, just #define this to nothing in term.h.
 */
{
  struct tchars tc;
  int old;

  if (!isatty (0))
    return -1;
  if (ioctl (STDIN_FILENO, TIOCGETC, &tc) != 0)
    return -1;
  old = tc.t_intrc;
  tc.t_intrc = ch == 0 ? -1 : ch;
  ioctl (STDIN_FILENO, TIOCSETC, &tc);
  return old == -1 ? 0 : old;
}
#define C_FLAGS_OFF (LCASE | ECHO | RAW) #define C_FLAGS_ON (CBREAK) #define L_FLAGS_ON (LTOSTOP | LPASS8)
static void
tty_interactive (void)
{
  struct sgttyb sg = tty_system.sg;
  struct tchars tc = tty_system.tc;
  int flags;

  if (! isatty (STDIN_FILENO)) return;

  sg.sg_flags &= ~C_FLAGS_OFF;
  sg.sg_flags |=  C_FLAGS_ON;
  tc.t_intrc = INTR_KEY;
  tc.t_quitc = QUIT_KEY;
  tc.t_startc = -1;
  tc.t_stopc = -1;
  ioctl (STDIN_FILENO, TIOCSETP, &sg);
  ioctl (STDIN_FILENO, TIOCSETC, &tc);
  flags = L_FLAGS_ON;
  ioctl (STDIN_FILENO, TIOCLBIS, &flags);
}
#else #if defined VxWorks #include <vxWorks.h> #include <ioLib.h> static int tty_system = 0; /* not reentrant */ #define tty_save() (tty_system = ioctl(0,FIOGETOPTIONS,0)) #define tty_restore() (ioctl(0,FIOSETOPTIONS,tty_system))
static int
c_interrupt_key (char ch)
{
  return 0;
}
static void
tty_interactive (void)
{
  ioctl (0, FIOSETOPTIONS, ioctl (0, FIOGETOPTIONS, 0) & ~OPT_LINE);
  ioctl (0, FIOSETOPTIONS, ioctl (0, FIOGETOPTIONS, 0) & ~OPT_ECHO);
}
#else /* VxWorks */ #error "neither termios.h/termio.h nor sgtty.h available" #endif /* VxWorks */ #endif /* tty hacking, two major styles */
/*-
   2 Getting information about the terminal
  
   The next problem is to get information about the connected terminal.
   UNIX employs a database file called termcap or (in newer versions)
   terminfo, where informations on the actually used terminal are
   stored. Before we begin we must retrieve these information from the
   data base. Three versions of the retrieval:
  
  	- don't read databases but assume vt100
  	- use termcap or the termcap emulation in curses
  	- use terminfo
 */
/* If nothing is decided yet, make a guess what should work.
   Note that you can override this with -DUSE_TERMCAP even if no
   termcap.h is avavailable in your system. Just in case the terminfo
   calls work worse than the termcap emulation inside curses. */
#if !defined ASSUME_VT100 && !defined USE_TERMCAP && !defined USE_TERMINFO # if defined PFE_HAVE_TERMCAP_H # define USE_TERMCAP # elif defined VxWorks # define ASSUME_DUMBTERM # else # if defined PFE_HAVE_CURSES_H && defined PFE_HAVE_TERM_H # define USE_TERMINFO # else # define ASSUME_VT100 # endif # endif #endif #if defined ASSUME_VT100 || defined USE_TERMCAP || defined ASSUME_DUMBTERM
/*
   We make termcap look like terminfo. The following identifiers stand
   for capabilities and are declared in term.h when using terminfo.
   Otherwise they are used as indices to vectors containing capability
   names or escape sequences. In the latter case they must be declared
   in the same order as the initializers to the vectors of capability
   names below.
 */
enum
{
  cursor_address,
  cursor_home,
  cursor_left,
  cursor_right,
  cursor_up,
  cursor_down,
  clear_screen,
  clr_eos,
  clr_eol,
  bell,
  delete_character,
  delete_line,
  scroll_forward,
  scroll_reverse,
  enter_standout_mode,
  exit_standout_mode,
  enter_underline_mode,
  exit_underline_mode,
  enter_bold_mode,
  enter_reverse_mode,
  enter_blink_mode,
  exit_attribute_mode,
  keypad_xmit,
  keypad_local
}
; #endif
/*
   Strings used to query the termcap database.
  
   (In case terminfo is used, these are only needed to satisfy the
   show_*_strings functions.)
 */
#ifdef USE_TERMCAP
static char tckeycode[][3] =
{
  "k1", "k2", "k3", "k4", "k5",	/* keys in same order as enum keycode */
  "k6", "k7", "k8", "k9", "k0",	/* from term.h */
  "F1", "F2", "F3", "F4", "F5",
  "F6", "F7", "F8", "F9", "FA",
  "kl", "kr", "ku", "kd",	/* result is just what has to be exported */
  "kh", "kH", "kN", "kP",	/* via variable rawkey_string */
  "kb", "kD", "kM", "kI",
  "kA", "kE", "kL", "kC"
}
; #endif /* USE_TERMCAP */ #if defined USE_TERMCAP || defined USE_TERMINFO
static char tcctlcode[][3] =
{
  "cm", "ho",
  "le", "nd", "up", "do",
  "cl", "cd", "ce", "bl",
  "dc", "dl",
  "sf", "sr",
  "so", "se", "us", "ue",
  "md", "mr", "mb", "me",
  "ks", "ke"
}
; #endif /* USE_TERMCAP || USE_TERMINFO */ #if defined ASSUME_DUMBTERM || defined ASSUME_VT100
/*
   terminal is 
     a) dumb/unknown
     b) vt100 like
     c) hopefully one of them
 */
extern char const * p4_vt100_controls[]; extern char const * p4_vt100_rawkeys[]; #define query_database() 1 /* nothing to query */ #define t_putc(C) putchar (C) #define tputs(S,P,F) fputs (S, stdout)
static void
t_puts (int cap, int n)
{
  fputs (PFE.control_string[cap], stdout);
}
static char *
tparm (int cap, int x, int y)	/* call this only with `cm' */
{
  static char buf[12] = 
{0}
; /*not reentrant */ if (PFE.control_string == p4_vt100_controls) sprintf (buf, "\033[%d;%dH", x + 1, y + 1); return buf; }
#ifdef __GNUC__ #define INTO(x) .x = #else #define INTO(x) #endif #ifdef ASSUME_DUMBTERM p4_term_struct p4_term_ios = #else p4_term_struct p4_term_dumbterm = #endif
{
  "dumbterm",
  p4_dumbterm_controls,
  p4_dumbterm_rawkeys,
  INTO(init) 		c_prepare_terminal, 
  INTO(fini) 		c_cleanup_terminal,
  INTO(tput)		c_tput,

  INTO(tty_interrupt_key) c_interrupt_key,
  INTO(interactive_terminal) c_interactive_terminal,
  INTO(system_terminal)   c_system_terminal,
  INTO(query_winsize)     c_query_winsize,

  INTO(c_keypressed)	c_keypressed,
  INTO(c_getkey)	c_getkey,
  INTO(c_putc_noflush)  c_putc_noflush,
  INTO(c_put_flush)	c_put_flush,
  INTO(c_putc)		c_putc,
  INTO(c_puts)		c_puts,
  INTO(c_gotoxy)	c_gotoxy,
  INTO(c_wherexy)	c_wherexy

}
; #ifdef ASSUME_VT100 p4_term_struct p4_term_ios = #else p4_term_struct p4_term_vt100 = #endif
{
  "vt100",
  p4_vt100_controls,
  p4_vt100_rawkeys,
  INTO(init) 		c_prepare_terminal, 
  INTO(fini) 		c_cleanup_terminal,
  INTO(tput)		c_tput,

  INTO(tty_interrupt_key) c_interrupt_key,
  INTO(interactive_terminal) c_interactive_terminal,
  INTO(system_terminal)   c_system_terminal,
  INTO(query_winsize)     c_query_winsize,

  INTO(c_keypressed)	c_keypressed,
  INTO(c_getkey)	c_getkey,
  INTO(c_putc_noflush)  c_putc_noflush,
  INTO(c_put_flush)	c_put_flush,
  INTO(c_putc)		c_putc,
  INTO(c_puts)		c_puts,
  INTO(c_gotoxy)	c_gotoxy,
  INTO(c_wherexy)	c_wherexy

}
; #elif defined USE_TERMCAP
/*
   2.2 Use termcap calls to retrieve information about the terminal
 */
#ifdef PFE_HAVE_TERMCAP_H #include <termcap.h> #else /* Systems emulating the termcap functions in the curses library usually haven't declared them in a file <termcap.h>, so here are these declarations: */ #ifdef __cplusplus extern "C" { #endif int tgetent (char *, char *); int tgetnum (char *); int tgetflag (char *); char *tgetstr (char *, char **); int tputs (char *, int, int (*)(int)); char *tgoto (char *, int, int); #ifdef __cplusplus } #endif #endif #define __using_termcap 1 static char const * termcap_control_string[DIM (tcctlcode)]; static char const * termcap_rawkey_string[P4_NUM_KEYS];
static int
query_database (void)
/*
   Get the strings needed out of termcap/terminfo.
   Store them for later use in control_string[] and rawkey_string[].
 */
{
  char *ttype = getenv ("TERM");
  char tcent[2048];
  static char tcbuf[2048];
  static char erase[] = "\b";
  char *tctop = tcbuf;
  int i;

  if (ttype == NULL || tgetent (tcent, ttype) <= 0)
    return 0;
    
  PFE.term->name = ttype;

#ifdef PFE_HAVE_TERMCAP_H
  
{
    char *pc = tgetstr ("pc", &tctop);
    if (pc != NULL)
      PC = *pc;
    else
      PC = 0;
  }
#endif PFE.rows = tgetnum ("li"); PFE.cols = tgetnum ("co"); /* Read all termcap strings we need, */ for (i = 0; i < P4_NUM_KEYS; i++) PFE.rawkey_string[i] = tgetstr (tckeycode[i], &tctop); /* another chance for F10: */ if (PFE.rawkey_string [P4_KEY_k0 - P4_KEY_k1] == NULL) PFE.rawkey_string [P4_KEY_k0 - P4_KEY_k1] = tgetstr ("k;", &tctop); /* better believe stty which is the erase character: */ if (tty_erasechar)
{
      erase[0] = tty_erasechar;
      PFE.rawkey_string [P4_KEY_kb - P4_KEY_k1] = erase;
    }
for (i = 0; i < DIM (tcctlcode); i++) PFE.control_string[i] = tgetstr (tcctlcode[i], &tctop); if (PFE.control_string [cursor_left] == NULL) PFE.control_string [cursor_left] = "\b"; #if defined PFE_HAVE_OSPEED
{
    char *pc = tgetstr ("pc", &tctop);
    /* these are defined inside the termcap-library: */
    ospeed = tty_ospeed;
    PC = pc ? *pc : 0;
    BC = PFE.control_string[cursor_left];
    UP = PFE.control_string[cursor_up];
  }
#endif return 1; }
static int			/* utility function for tputs(): */
t_putc (int c)			/* putchar() is a macro, we need a function */
{
  return putchar (c);
}
static void
t_puts (int tcidx, int n)	/* issue termcap string to terminal */
{
  if (!PFE.control_string[tcidx])
    /* no harm if feature not available */
    return;
  tputs (PFE.control_string[tcidx], n, t_putc);
  fflush (stdout);
}
#define tparm(CAP,X,Y) tgoto (PFE.control_string [CAP], Y, X) #ifdef __GNUC__ #define INTO(x) .x = #else #define INTO(x) #endif
p4_term_struct p4_term_ios =
{
  "termcap",
  termcap_control_string,
  termcap_rawkey_string,
  INTO(init) 		c_prepare_terminal, 
  INTO(fini) 		c_cleanup_terminal,
  INTO(tput)		c_tput,

  INTO(tty_interrupt_key) c_interrupt_key,
  INTO(interactive_terminal) c_interactive_terminal,
  INTO(system_terminal)   c_system_terminal,
  INTO(query_winsize)     c_query_winsize,

  INTO(c_keypressed)	c_keypressed,
  INTO(c_getkey)	c_getkey,
  INTO(c_putc_noflush)  c_putc_noflush,
  INTO(c_put_flush)	c_put_flush,
  INTO(c_putc)		c_putc,
  INTO(c_puts)		c_puts,
  INTO(c_gotoxy)	c_gotoxy,
  INTO(c_wherexy)	c_wherexy

}
; #elif defined USE_TERMINFO
/*
   2.3 Use terminfo calls to retrieve information about the terminal
 */
#if SunOS || defined __target_os_solaris #undef TRUE /* These confuse Sun's cc in curses.h */ #undef FALSE #include <sys/termio.h> /* Solaris' curses.h seems to need this */ #endif #include <curses.h> #include <term.h> #define __using_terminfo static char const * terminfo_control_string[DIM (tcctlcode)]; static char const * terminfo_rawkey_string[P4_NUM_KEYS];
static int
query_database (void)
{
  int errret;
  static char erase[] = "\b";

  setupterm (NULL, STDOUT_FILENO, &errret);
  if (errret != 1)
    return 0;

#define KINIT(key, ti_capability) \
  PFE.rawkey_string [P4CAT (P4_KEY_,key) - P4_KEY_k1] = ti_capability

  KINIT (k1, key_f1);		/* function keys F1 through F10 */
  KINIT (k2, key_f2);
  KINIT (k3, key_f3);
  KINIT (k4, key_f4);
  KINIT (k5, key_f5);
  KINIT (k6, key_f6);
  KINIT (k7, key_f7);
  KINIT (k8, key_f8);
  KINIT (k9, key_f9);
  KINIT (k0, key_f10);

  KINIT (kl, key_left);		/* arrow key left */
  KINIT (kr, key_right);	/* arrow key right */
  KINIT (ku, key_up);		/* arrow key up */
  KINIT (kd, key_down);		/* arrow key down */

  KINIT (kh, key_home);		/* home key */
#ifdef key_end
  KINIT (kH, key_end);		/* home down key */
#else /* on HPUX9 */
  KINIT (kH, key_ll);           /* the same, 'lower left' */
#endif
  KINIT (kN, key_npage);	/* next page key */
  KINIT (kP, key_ppage);	/* previous page key */

  KINIT (kb, key_backspace);	/* backspace key */
  KINIT (kD, key_dc);		/* delete character key */
  KINIT (kM, key_eic);		/* exit insert mode key */
  KINIT (kI, key_ic);		/* insert character key */

  KINIT (kA, key_il);		/* insert line key */
  KINIT (kE, key_eol);		/* clear to end of line key */
  KINIT (kL, key_dl);		/* delete line key */
  KINIT (kC, key_clear);	/* clear screen key */

#undef KINIT
  /* better believe stty which is the erase character: */
  if (tty_erasechar)
    
{
      erase[0] = tty_erasechar;
      PFE.rawkey_string [P4_KEY_kb - P4_KEY_k1] = erase;
    }
if (cursor_left == NULL) cursor_left = strdup ("\b"); /* fixmee: memoryleak on nonunix systems */ return 1; }
static int			/* utility function for tputs(): */
t_putc (char c)			/* putchar() is a macro, we need a function */
{
  return putchar (c);
}
static void			/* output capability with affected lines cnt */
t_puts (char *s, int n)
{
  tputs (s, n, t_putc);
  fflush (stdout);
}
#ifdef __GNUC__ #define INTO(x) .x = #else #define INTO(x) #endif
p4_term_struct p4_term_ios =
{
  "terminfo",
  terminfo_control_string,
  terminfo_rawkey_string,
  INTO(init) 		c_prepare_terminal, 
  INTO(fini) 		c_cleanup_terminal,
  INTO(tput)		c_tput,

  INTO(tty_interrupt_key) c_interrupt_key,
  INTO(interactive_terminal) c_interactive_terminal,
  INTO(system_terminal)   c_system_terminal,
  INTO(query_winsize)     c_query_winsize,

  INTO(c_keypressed)	c_keypressed,
  INTO(c_getkey)	c_getkey,
  INTO(c_putc_noflush)  c_putc_noflush,
  INTO(c_put_flush)	c_put_flush,
  INTO(c_putc)		c_putc,
  INTO(c_puts)		c_puts,
  INTO(c_gotoxy)	c_gotoxy,
  INTO(c_wherexy)	c_wherexy
}
; #else #error "One of ASSUME_VT100, USE_TERMCAP or USE_TERMINFO must be set." #endif
static int				/* Prepares usage of all other functions. */
c_prepare_terminal (void)		/* Call only once at startup. */
{
    int fd;
  
#  if defined ASSUME_DUMBTERM
    PFE.control_string = p4_dumbterm_controls;
    PFE.rawkey_string  = p4_dumbterm_rawkeys;
#  elif defined ASSUME_VT100
    PFE.control_string = p4_vt100_controls;
    PFE.rawkey_string  = p4_vt100_rawkeys;
#  endif
  

    /* save state before all changes */
    for (fd = 0; fd < 3; fd++)
        saved_fcntl[fd] = P4_fcntl_F_GETFL (fd, 0); /* fcntl(fd, F_GETFL, 0) */
    tty_save ();

    /* initialize termcap-stuff */
    return query_database ();
}
static void
c_cleanup_terminal (void)
{
  return; /* nothing to do here */
}
static void			/* set terminal to the sort of */
c_interactive_terminal (void)	/* `cbreak' mode used by p4th */
{
  tty_interactive ();
  t_puts (keypad_xmit, 0);
}
static void			/* resets terminal state to */
c_system_terminal (void)		/* as it was before */
{
    int fd;

    if (! isatty (STDIN_FILENO)) return;

    fflush (stdout);
    tty_restore ();
    t_puts (keypad_local, 0);
    for (fd = 0; fd < 3; fd++)
        P4_fcntl_F_SETFL (fd, saved_fcntl[fd]); /* fcntl (fd, F_SETFL, ..) */
}
/* Handle window size change, see also signal.c: */ #ifdef TIOCGWINSZ
static void
c_query_winsize (void)
{
  struct winsize size;

  if (ioctl (1, TIOCGWINSZ, (char *) &size) >= 0)
    
{
      PFE.rows = size.ws_row;
      PFE.cols = size.ws_col;
      PFE.xmax = size.ws_xpixel;
      PFE.ymax = size.ws_ypixel;
    }
}
#else
static void
c_query_winsize (void)
{
}
#endif /************************************************************************/ /* Input from keyboard. */
/************************************************************************/
/*
   Having hacked the terminal driver and the standard input in the
   appropiate modes all we have to do is read one character from
   standard input to get a key.
   The only problem is: we can't detect if a key is available without
   reading it. So we have to store it in this case.
 */
#define NOCH ((unsigned short)0xABCD)
/* encodes 'no character available' */
static unsigned short /* the next character when read by */ nxch = NOCH;
/* keypressed() */
static int
nextch (void)
/*
   Read a single character from standard input.
 */
{
  unsigned char c;

  if (nxch != NOCH)
    
{
      c = (unsigned char) nxch;
      nxch = NOCH;
      return c;
    }
else for (;;) switch (read (0, (void *) &c, 1))
{
	case -1:
	  switch (errno)
	    
{
	    default:
	      return -1;
	    case EAGAIN:
	    case EINTR:
	      continue;
	    }
case 0: return -1; default: return c; }
}
static int
c_keypressed (void)
/*
   Checks if a character is available in the standard input. Saves it in nxch.
   Returns: 1 if char available, 0 otherwise
 */
{
  unsigned char c;		/* place to read the character */
  int result;

  fflush (stdout);
  if (nxch != NOCH)
    return 1;			/* char from previos keypressed() */
  no_waitchar (STDIN_FILENO);
  result = read (0, (void *) &c, 1);
  waitchar (STDIN_FILENO);
  if (result != 1)
    return 0;
  nxch = c;
  return 1;
}
static int
c_getkey (void)
{
  fflush (stdout);

  if (PFE.wait_for_stdin)
      PFE.wait_for_stdin ();

  return nextch ();
}
/************************************************************************/ /* Output to screen, control with termcap: */
/************************************************************************/
static int row, col;
/* position of curser as tracked */
static void			/* output character and */
c_putc_noflush (char c)		/* trace the cursor position */
{
  putchar (c);
  switch (c)
    
{
    case '\a':			/* bell, no change of cursor position */
      break;
    case '\b':			/* backspace, move cursor left */
      if (col > 0)
	col--;
      break;
    case '\r':			/* carriage return, ->column 0 */
      col = 0;
      break;
    default:			/* ordinary character: */
      if (col < PFE.cols - 1)	/* at right edge of screen? */
	
{
	  col++;		/* no: increment column */
	  break;
	}
/* yes: like line feed */ case '\n': /* line feed */ col = 0; if (row < PFE.rows - 1) /* if not at bottom of screen: */ row++; /* increment row */ }
/* otherwise terminal is supposed to scroll */ }
static void
c_put_flush (void)
{
  fflush (stdout);
}
static void
c_putc (char c)
{
  c_putc_noflush (c);
  fflush (stdout);
}
static void
c_puts (const char *s)
{
  while (*s)
    c_putc_noflush (*s++);
  fflush (stdout);
}
static void
c_gotoxy (int x, int y)
{
  tputs (tparm (cursor_address, y, x), 1, t_putc);
  fflush (stdout);
  col = x;
  row = y;
}
static void
c_wherexy (int *x, int *y)
{
  *x = col;
  *y = row;
}
static void 
c_tput (int attr)
{
  switch (attr)
  
{
  case P4_TERM_GOLEFT:		t_puts (cursor_left,  	0); --col; break;
  case P4_TERM_GORIGHT:		t_puts (cursor_right, 	0); ++col; break;
  case P4_TERM_GOUP:		t_puts (cursor_up,	0); --row; break;
  case P4_TERM_GODOWN:		t_puts (cursor_down,	0); ++row; break;

  case P4_TERM_CLRSCR:		t_puts (clear_screen, PFE.rows); /*->HOME*/
  case P4_TERM_HOME:		t_puts (cursor_home, 1); 
                                        	   row = col = 0;  break;
  case P4_TERM_CLREOL:		t_puts (clr_eol, 1); 		   break;
  case P4_TERM_CLRDOWN:		t_puts (clr_eos, PFE.rows - row);  break;
  case P4_TERM_BELL:		t_puts (bell, 0); 		   break;
  
  case P4_TERM_NORMAL:		t_puts (exit_attribute_mode,	0); break;
  case P4_TERM_BOLD_ON:		t_puts (enter_standout_mode,	0); break;
  case P4_TERM_BOLD_OFF:	t_puts (exit_standout_mode,	0); break;
  case P4_TERM_UNDERLINE_ON:	t_puts (enter_underline_mode,	0); break;
  case P4_TERM_UNDERLINE_OFF:	t_puts (exit_underline_mode,	0); break;
  case P4_TERM_BRIGHT:		t_puts (enter_bold_mode,	0); break;
  case P4_TERM_REVERSE:		t_puts (enter_reverse_mode,	0); break;
  case P4_TERM_BLINKING:	t_puts (enter_blink_mode,	0); break;
  default: break;
  }
}
/*@}*/