/*
$NiH: tty.c,v 1.24 2002/09/16 12:42:45 dillo Exp $
tty.c -- lowlevel tty handling
Copyright (C) 1996-2002 Dieter Baron
This file is part of cftp, a fullscreen ftp client
The author can be contacted at <dillo@giga.or.at>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <termios.h>
#include <sys/ioctl.h>
#include "config.h"
#ifdef HAVE_TERMCAP_H
#include <termcap.h>
#endif
#ifndef HAVE_DECL_OSPEED
char PC, *BC, *UP;
short ospeed;
#endif
#ifndef HAVE_FPUTCHAR
int fputchar(int c);
#endif
#if !defined(HAVE_TPARAM) && !defined(HAVE_TPARM)
char *tparam (char *string, char *outstring, int len,
int arg0, int arg1, int arg2, int arg3);
#endif
#include "keys.h"
#include "tty.h"
#ifndef VWERASE
#ifdef VWERSE
#define VWERASE VWERSE
#endif
#endif
#ifndef _POSIX_VDISABLED
#define _POSIX_VDISABLED -1
#endif
extern char *prg;
void tty_keypad_init(void);
void _tty_capinit(void);
int tty_ispref(char *s, int l);
#ifdef SIGWINCH
void tty_winch(int s);
#endif
enum tty_am tty_am;
volatile int tty_cols, tty_lines;
int tty_metap, tty_noLP,
tty_verase, tty_vwerase, tty_vkill;
char termcap_entry[4196];
char termcap_area[4196], *termcap_str;
struct termios tty_tio, tty_tio_ext;
speed_t tty_baud;
#define KEY_PREF 1
#define KEY_SLPREF 2
char keyflag[256];
struct cap {
char *name;
char *cap;
};
#define CAP_SIZE 253
#define CAP_G 17
struct cap cap[253];
void (*tty_redraw)(void) = NULL;
int
tty_init(void)
{
char *term, *pc, *env;
#ifdef SIGWINCH
struct winsize ws;
#endif
/* get termcap entry */
if ((term=getenv("TERM")) == NULL) {
fprintf(stderr, "%s: no terminal type specified\n",
prg);
return -1;
}
switch (tgetent(termcap_entry, term)) {
case -1:
fprintf(stderr, "%s: can't access termcap: %s\n",
prg, strerror(errno));
return -1;
case 0:
fprintf(stderr, "%s: unknown terminal: %s\n",
prg, term);
return -1;
}
termcap_str = termcap_area;
/* meta and function keys */
tty_metap = tgetflag("km");
tty_keypad_init();
if (tcgetattr(0, &tty_tio_ext) < 0) {
fprintf(stderr, "%s: can't get terminal attributes: %s\n",
prg, strerror(errno));
return -1;
}
tty_tio = tty_tio_ext;
tty_baud = cfgetospeed(&tty_tio);
/* globals for tputs(3) and tgoto */
ospeed = tty_baud;
pc = tty_getcap("pc");
if (pc)
PC = *pc;
else
PC = 0;
BC = tty_getcap("le");
UP = tty_getcap("up");
/* other capabilities we need (mostly in the output macros) */
if (tgetflag("am")) {
if (tgetflag("xn"))
tty_am = TTY_AMXN;
else
tty_am = TTY_AM;
}
else
tty_am = TTY_AMNONE;
tty_noLP = !tgetflag("LP");
/* screen size */
tty_cols = tgetnum("co");
tty_lines = tgetnum("li");
#ifdef SIGWINCH
if (ioctl(0, TIOCGWINSZ, &ws) == 0) {
if (ws.ws_col && ws.ws_row) {
tty_cols = ws.ws_col;
tty_lines = ws.ws_row;
}
}
signal(SIGWINCH, tty_winch);
#endif
if ((env=getenv("LINES")))
tty_lines = atoi(env);
if ((env=getenv("COLUMNS")))
tty_cols = atoi(env);
if (tty_lines == 0 && tty_cols == 0)
return -1;
/* erase, werase, kill */
if ((tty_verase=tty_tio.c_cc[VERASE]) == _POSIX_VDISABLED)
tty_verase = -1;
#ifdef VWERASE
if ((tty_vwerase=tty_tio.c_cc[VWERASE]) == _POSIX_VDISABLED)
tty_vwerase = -1;
#else
tty_vwerase = -1;
#endif
if ((tty_vkill=tty_tio.c_cc[VKILL]) == _POSIX_VDISABLED)
tty_vkill = -1;
/* input mode (cbreak, noecho) */
tty_tio.c_cc[VMIN] = 1;
tty_tio.c_cc[VTIME] = 0;
tty_tio.c_lflag &= ~(ECHO|ICANON|ECHONL);
tty_tio.c_iflag &= ~(IXON|IXOFF);
tty_tio.c_lflag |= ISIG;
if (tcsetattr(0, TCSANOW, &tty_tio_ext) < 0) {
fprintf(stderr, "%s: can't set terminal attribues: %s\n",
prg, strerror(errno));
return -1;
}
_tty_capinit();
return 0;
}
void
tty_keypad_init(void)
{
int i;
char *seq;
for (i=0; i<256; i++)
keyflag[0] = 0;
for (i=0; i<max_fnkey; i++) {
seq = tty_getcap(fnkey[i].cap);
if (seq && strlen(seq) > 1) {
fnkey[i].seq = seq;
keyflag[(int)seq[0]] = KEY_PREF;
}
}
}
int
tty_setup(void)
{
tty_put("ks", 1);
tty_put("mm", 1);
return tcsetattr(0, TCSANOW, &tty_tio);
}
int
tty_restore(void)
{
tty_put("ke", 1);
tty_put("mo", 1);
return tcsetattr(0, TCSANOW, &tty_tio_ext);
}
void
tty_put(char *name, int lines)
{
tputs(tty_getcap(name), lines, fputchar);
}
void
tty_put0(char *cap, int lines)
{
tputs(cap, lines, fputchar);
}
void
tty_goto(int x, int y)
{
tputs(tgoto(tty_getcap("cm"), x, y), 1, fputchar);
}
int
tty_echo(void)
{
if ((tty_tio.c_lflag & ECHO) == 0) {
tty_tio.c_lflag |= ECHO;
if (tcsetattr(0, TCSANOW, &tty_tio) < 0) {
fprintf(stderr, "%s: can't set terminal "
"attribues: %s\n",
prg, strerror(errno));
return -1;
}
}
return 0;
}
int
tty_noecho(void)
{
if (tty_tio.c_lflag & ECHO) {
tty_tio.c_lflag &= ~ECHO;
if (tcsetattr(0, TCSANOW, &tty_tio) < 0) {
fprintf(stderr, "%s: can't set terminal "
"attribues: %s\n",
prg, strerror(errno));
return -1;
}
}
return 0;
}
int tty_cbreak(void)
{
tty_tio.c_lflag &= ~ECHO;
if (tcsetattr(0, TCSANOW, &tty_tio_ext) < 0) {
fprintf(stderr, "%s: can't set terminal "
"attribues: %s\n",
prg, strerror(errno));
return -1;
}
return 0;
}
int
tty_readkey(void)
{
static unsigned char s[128];
static int l = 0;
int c, len;
int vmin = 0;
if (l == 0) {
while ((c=getchar())==EOF && errno == EINTR)
errno = 0;
if (c == EOF)
return -1;
s[l++] = c;
}
len = 0;
if (keyflag[(int)s[0]] == KEY_PREF) {
while ((c=tty_ispref((char *)s, l)) == EOF) {
if (!vmin) {
tty_vmin(0, 5);
vmin = 1;
}
while ((c=getchar())==EOF && errno == EINTR)
errno = 0;
if (c == EOF) {
clearerr(stdin);
c = s[0];
break;
}
s[l++] = c;
}
if (c == -2) {
c = s[0];
}
}
else {
c = s[0];
l = 1;
}
if (c > 256)
len =(fnkey[c-256].seq ? strlen(fnkey[c-256].seq) : 1);
else
len = 1;
if (vmin)
tty_vmin(1, 0);
l -= len;
if (l)
memmove(s, s+len, l);
return c;
}
int tty_ispref(char *s, int l)
{
int j;
for (j=0; j<max_fnkey; j++)
if (fnkey[j].seq) {
if (!strncmp(s, fnkey[j].seq, l) && fnkey[j].seq[l] == '\0')
return 256+j;
else if (!strncmp(fnkey[j].seq, s, l))
return EOF;
}
return -2;
}
int
tty_vmin(int min, int tim)
{
if (tty_tio.c_cc[VMIN] != min || tty_tio.c_cc[VTIME] != tim) {
tty_tio.c_cc[VMIN] = min;
tty_tio.c_cc[VTIME] = tim;
if (tcsetattr(0, TCSANOW, &tty_tio) < 0) {
fprintf(stderr, "%s: can't set terminal "
"attribues: %s\n",
prg, strerror(errno));
return -1;
}
}
return 0;
}
char *
tty_getcap(char *name)
{
int i, j;
i = j = (name[0]*name[1]) % CAP_SIZE;
while (cap[i].name) {
if (strcmp(cap[i].name, name) == 0)
return cap[i].cap;
i = (i+CAP_G) % CAP_SIZE;
if (i == j) /* cap array full! */
return NULL;
}
if ((cap[i].name=strdup(name)) == NULL)
return NULL;
cap[i].cap = (char *)tgetstr(name, &termcap_str);
return cap[i].cap;
}
int
tty_iscap(char *name)
{
return (tgetflag(name) == 1);
}
#ifdef SIGWINCH
void
tty_winch(int s)
{
int change = 0;
struct winsize ws;
if (ioctl(0, TIOCGWINSZ, &ws) == 0) {
if (tty_cols != ws.ws_col
|| tty_lines != ws.ws_row)
change = 1;
tty_cols = ws.ws_col;
tty_lines = ws.ws_row;
}
if (change && tty_redraw)
tty_redraw();
}
#endif
void
tty_parp(char *cap, char *pcap, int n, int pad)
{
int i;
if (*pcap)
tty_putp(pcap, pad, n, 0, 0, 0);
else
for (i=0; i<n; i++)
tputs(cap, pad, fputchar);
}
char *_tty_capnames[] = {
"cl", "ho", "cd", "ce", "so", "se", "vi", "ve", "cs",
"sf", "sr", "SF", "SR", "ll", "al", "dl", "AL", "DL"
};
char *_tty_caps[sizeof(_tty_capnames)/sizeof(_tty_capnames[0])];
void
_tty_capinit()
{
int i;
for (i=0; i<sizeof(_tty_capnames)/sizeof(_tty_capnames[0]); i++) {
_tty_caps[i] = tty_getcap(_tty_capnames[i]);
if (_tty_caps[i] == NULL)
_tty_caps[i] = "";
}
/* screen removes `sf' capability, so we compensate */
if (!*TTY_CAP(sf)) {
TTY_CAP(sf) = tty_getcap("do");
if (TTY_CAP(sf) == NULL)
TTY_CAP(sf) = "";
}
}
void
tty_putp(char *cap, int lines, int arg0, int arg1, int arg2, int arg3)
{
#ifndef HAVE_TPARM
static char buf[40];
#endif
char *s;
#ifndef HAVE_TPARM
s = (char *)tparam(cap, buf, sizeof(buf), arg0, arg1, arg2, arg3);
#else
s = (char *)tparm(cap, arg0, arg1, arg2, arg3);
#endif
tputs(s, lines, fputchar);
#ifndef HAVE_TPARM
if (s != buf)
free(s);
#endif
}
void
tty_lowleft(void)
{
if (*TTY_CAP(ll))
tty_put0(TTY_CAP(ll), 1);
else
tty_goto(0, tty_lines-1);
}
syntax highlighted by Code2HTML, v. 0.9.1