home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-385-Vol-1of3.iso
/
v
/
vim_src.zip
/
TERM.C
< prev
next >
Wrap
C/C++ Source or Header
|
1993-01-12
|
17KB
|
763 lines
/* vi:sw=4:ts=4:
*
* term.c -- VIM - Vi IMitation
*
* primitive termcap support added
*
* NOTE: padding and variable substitution is not performed,
* when compiling without TERMCAP, we use tputs() and tgoto() dummies.
*
* 14.6.92
*/
#include "vim.h"
#include "globals.h"
#include "param.h"
#include "proto.h"
#ifdef TERMCAP
# ifdef linux
# include <termcap.h>
# define TPUTSFUNCAST (outfuntype)
# else
# define TPUTSFUNCAST
# ifdef AMIGA
# include "proto/termlib.pro"
# endif
# endif
#endif
/* buffer size for termcap entry */
#ifdef linux
# define TBUFSZ 2048
#else
# define TBUFSZ 1024
#endif
#ifdef DEBUG
# define TTEST(a) debug1("%s: ", "a"); if (a) {debug2("%02x %s\n", *a, a + 1);} else debug("NULL\n");
#endif
static void parse_builtin_tcap __ARGS((Tcarr *tc, char *s));
/*
* Builtin_tcaps must always contain DFLT_TCAP as the first entry!
* DFLT_TCAP is used, when no terminal is specified with -T option or $TERM.
* The entries are compact, therefore they normally are included even when
* TERMCAP is defined.
* When TERMCAP is defined, the builtin entries can be accessed with
* "builtin_amiga", "builtin_ansi", "builtin_debug", etc.
*/
static char *builtin_tcaps[] =
{
#ifndef NO_BUILTIN_TCAPS
DFLT_TCAP, /* almost allways included */
# if !defined(UNIX) && (defined(ALL_BUILTIN_TCAPS) || defined(SOME_BUILTIN_TCAPS))
ANSI_TCAP, /* default for unix */
# endif
# if !defined(AMIGA) && (defined(ALL_BUILTIN_TCAPS) || defined(SOME_BUILTIN_TCAPS))
AMIGA_TCAP, /* default for amiga */
# endif
# if !defined(MSDOS) && (defined(ALL_BUILTIN_TCAPS) || defined(SOME_BUILTIN_TCAPS))
PCTERM_TCAP, /* default for MSdos */
# endif
# if !defined(ATARI) && defined(ALL_BUILTIN_TCAPS)
ATARI_TCAP, /* default for Atari */
# endif
# if defined(UNIX) || defined(ALL_BUILTIN_TCAPS) || defined(SOME_BUILTIN_TCAPS)
XTERM_TCAP, /* always included on unix */
# endif
# ifdef ALL_BUILTIN_TCAPS
VT52_TCAP,
# endif
# if defined(DEBUG) || defined(ALL_BUILTIN_TCAPS)
DEBUG_TCAP, /* always included when debugging */
# endif
#else /* NO_BUILTIN_TCAPS */
"dump\0\0\0",
#endif /* NO_BUILTIN_TCAPS */
NULL,
};
/*
* Term_strings contains currently used terminal strings.
* It is initialized with the default values by parse_builtin_tcap().
* The values can be changed by setting the parameter with the same name.
*/
Tcarr term_strings;
/*
* Parsing of the builtin termcap entries.
* The terminal's name is not set, as this is already done in termcapinit().
* Chop builtin termcaps, string entries are already '\0' terminated.
* not yet implemented:
* boolean entries could be empty strings;
* numeric entries would need a flag (e.g. high bit of the skip byte),
* so that parse_builtin_tcap can handle them.
*/
static void
parse_builtin_tcap(tc, s)
Tcarr *tc;
char *s;
{
char **p = &tc->t_name;
p++;
for (;;)
{
while (*s++)
;
p += *s++;
if (!*s)
return;
*p++ = s;
}
}
#ifdef TERMCAP
# ifndef linux /* included in <termlib.h> */
# ifndef AMIGA /* included in proto/termlib.pro */
int tgetent();
int tgetnum();
char *tgetstr();
int tputs();
# endif /* AMIGA */
extern short ospeed;
# endif /* linux */
extern char *UP, *BC, PC;
#endif /* TERMCAP */
void
set_term(term)
char *term;
{
char **p = builtin_tcaps;
#ifdef TERMCAP
int builtin = 0;
#endif
int width = 0, height = 0;
clear_termparam(); /* clear old parameters */
if (!strncmp(term, "builtin_", (size_t)8))
{
term += 8;
#ifdef TERMCAP
builtin = 1;
#endif
}
#ifdef TERMCAP
else
{
for (;;)
{
char *p;
static char tstrbuf[TBUFSZ];
char tbuf[TBUFSZ];
char *tp = tstrbuf;
int i;
i = tgetent(tbuf, term);
if (i == -1)
{
emsg("Cannot open termcap file");
builtin = 1;
break;
}
if (i == 0)
{
emsg("terminal entry not found");
builtin = 1;
break;
}
/* output strings */
T_EL = tgetstr("ce", &tp);
T_IL = tgetstr("al", &tp);
T_CIL = tgetstr("AL", &tp);
T_DL = tgetstr("dl", &tp);
T_CDL = tgetstr("DL", &tp);
T_ED = tgetstr("cl", &tp);
T_CI = tgetstr("vi", &tp);
T_CV = tgetstr("ve", &tp);
T_TP = tgetstr("me", &tp);
T_TI = tgetstr("mr", &tp);
T_CM = tgetstr("cm", &tp);
T_SR = tgetstr("sr", &tp);
T_CRI = tgetstr("RI", &tp);
T_VB = tgetstr("vb", &tp);
T_KS = tgetstr("ks", &tp);
T_KE = tgetstr("ke", &tp);
/* key codes */
term_strings.t_ku = tgetstr("ku", &tp);
term_strings.t_kd = tgetstr("kd", &tp);
term_strings.t_kl = tgetstr("kl", &tp);
term_strings.t_kr = tgetstr("kr", &tp);
/* term_strings.t_sku = tgetstr("", &tp); termcap code unknown */
/* term_strings.t_skd = tgetstr("", &tp); termcap code unknown */
term_strings.t_sku = NULL;
term_strings.t_skd = NULL;
term_strings.t_skl = tgetstr("#4", &tp);
term_strings.t_skr = tgetstr("%i", &tp);
term_strings.t_f1 = tgetstr("k1", &tp);
term_strings.t_f2 = tgetstr("k2", &tp);
term_strings.t_f3 = tgetstr("k3", &tp);
term_strings.t_f4 = tgetstr("k4", &tp);
term_strings.t_f5 = tgetstr("k5", &tp);
term_strings.t_f6 = tgetstr("k6", &tp);
term_strings.t_f7 = tgetstr("k7", &tp);
term_strings.t_f8 = tgetstr("k8", &tp);
term_strings.t_f9 = tgetstr("k9", &tp);
term_strings.t_f10 = tgetstr("k;", &tp);
term_strings.t_sf1 = tgetstr("F1", &tp); /* really function keys 11-20 */
term_strings.t_sf2 = tgetstr("F2", &tp);
term_strings.t_sf3 = tgetstr("F3", &tp);
term_strings.t_sf4 = tgetstr("F4", &tp);
term_strings.t_sf5 = tgetstr("F5", &tp);
term_strings.t_sf6 = tgetstr("F6", &tp);
term_strings.t_sf7 = tgetstr("F7", &tp);
term_strings.t_sf8 = tgetstr("F8", &tp);
term_strings.t_sf9 = tgetstr("F9", &tp);
term_strings.t_sf10 = tgetstr("FA", &tp);
term_strings.t_help = tgetstr("%1", &tp);
term_strings.t_undo = tgetstr("&8", &tp);
height = tgetnum("li");
width = tgetnum("co");
BC = tgetstr("bc", &tp);
UP = tgetstr("up", &tp);
p = tgetstr("pc", &tp);
if (p)
PC = *p;
ospeed = 0;
break;
}
}
if (builtin)
#endif
{
while (*p && strcmp(term, *p))
p++;
if (!*p)
{
fprintf(stderr, "'%s' not builtin. Available terminals are:\n", term);
for (p = builtin_tcaps; *p; p++)
fprintf(stderr, "\t%s\n", *p);
sleep(2);
fprintf(stderr, "defaulting to '%s'\n", *builtin_tcaps);
sleep(2);
p = builtin_tcaps;
if (term_strings.t_name)
free(term_strings.t_name);
term_strings.t_name = strsave(term = *p);
}
parse_builtin_tcap(&term_strings, *p);
}
#if defined(AMIGA) || defined(MSDOS)
/* DFLT_TCAP indicates that it is the machine console. */
if (strcmp(term, *builtin_tcaps))
term_console = FALSE;
else
term_console = TRUE;
#endif
ttest(TRUE);
/* display initial screen after ttest() checking. jw. */
if (width <= 0 || height <= 0)
{
/* termcap failed to report size */
/* set defaults, in case mch_get_winsize also fails */
width = 80;
height = 24;
}
set_winsize(width, height, FALSE);
}
#ifndef TERMCAP
static char *titoa __PARMS((int));
/*
* minimal tgoto() implementation.
* no padding and we only parse for %i %d and %+char
*/
static char *
titoa(i)
int i;
{
static char buf[10];
char *p;
p = buf + 9;
*p = '\0';
do
{
--p;
*p = i % 10 + '0';
i /= 10;
}
while (i > 0 && p > buf);
return p;
}
char *
tgoto(cm, x, y)
char *cm;
int x, y;
{
static char buf[30];
char *p, *s, *e;
if (!cm)
return "OOPS";
e = buf + 29;
for (s = buf; s < e && *cm; cm++)
{
if (*cm != '%')
{
*s++ = *cm;
continue;
}
switch (*++cm)
{
case 'd':
p = titoa(y);
y = x;
while (*p)
*s++ = *p++;
break;
case 'i':
x++;
y++;
break;
case '+':
*s++ = (char)(*++cm + y);
y = x;
break;
case '%':
*s++ = *cm;
break;
default:
return "OOPS";
}
}
*s = '\0';
return buf;
}
#endif /* TERMCAP */
/*
* termcapinit is called from main() to initialize the terminal
* the optional argument is given with the -T command line option
*/
void
termcapinit(term)
char *term;
{
if (!term)
term = getenv("TERM");
if (!term || !*term)
term = *builtin_tcaps;
term_strings.t_name = strsave(term);
set_term(term);
}
/*
* a never-padding outstr.
* use this whenever you don't want to run the string through tputs.
* tputs above is harmless, but tputs from the termcap library
* is likely to strip off leading digits, that it mistakes for padding
* information. (jw)
*/
void
outstrn(s)
char *s;
{
while (*s)
outchar(*s++);
}
#ifndef MSDOS /* there is a special version of outstr() in msdos.c */
/*
* outstr(s): put a string character at a time into the output buffer.
* If TERMCAP is defined use the termcap parser. (jw)
*/
void
outstr(s)
register char *s;
{
if (s)
#ifdef TERMCAP
tputs(s, 1, TPUTSFUNCAST outchar);
#else
while (*s)
outchar(*s++);
#endif
}
#endif /* MSDOS */
/*
* cursor positioning using termcap parser. (jw)
*/
void
windgoto(row, col)
int row;
int col;
{
#ifdef TERMCAP
tputs(tgoto(T_CM, col, row), 1, TPUTSFUNCAST outchar);
#else
outstr(tgoto(T_CM, col, row));
#endif
}
/*
* Set cursor to current position.
* Should be optimized for minimal terminal output.
*/
void
setcursor()
{
if (!RedrawingDisabled)
windgoto(Cursrow, Curscol);
}
void
ttest(pairs)
int pairs;
{
char buf[70];
char *s = "terminal capability %s required.\n";
char *t = NULL;
#ifdef TTEST
TTEST(T_EL);
TTEST(T_IL);
TTEST(T_CIL);
TTEST(T_DL);
TTEST(T_CDL);
TTEST(T_ED);
TTEST(T_CI);
TTEST(T_CV);
TTEST(T_TP);
TTEST(T_TI);
TTEST(T_CM);
TTEST(T_SR);
TTEST(T_CRI);
#endif /* TTEST */
/* hard requirements */
if (!T_ED || !*T_ED) /* erase display */
t = "cl";
if (!T_CM || !*T_CM) /* cursor motion */
t = "cm";
if (t)
{
sprintf(buf, s, t);
emsg(buf);
}
if (pairs)
{
/* optional pairs */
if ((!T_TP || !*T_TP) ^ (!T_TI || !*T_TI))
{
debug2("cap :me=%s:mr=%s: ignored\n", T_TP, T_TI);
T_TP = T_TI = NULL;
}
if ((!T_CI || !*T_CI) ^ (!T_CV || !*T_CV))
{
debug2("cap :vi=%s:ve=%s: ignored\n", T_CI, T_CV);
T_CI = T_CV = NULL;
}
}
}
/*
* inchar() - get a character from
* 1. a scriptfile
* 2. the keyboard - take care of interpreting escape sequences
*
* If we got an interrupt all input is read until none is available.
* If async is TRUE there is no waiting for the char and we keep the
* character.
*/
#define INBUFLEN 50 /* buffer lengt, must be enough to contain an
Amiga raw keycode report */
int
inchar(async)
int async;
{
static u_char buf[INBUFLEN+1]; /* already entered characters */
static int len = 0; /* number of valid chars in buf */
static int lastc = -1; /* last character from script file */
register int c;
register int slen = 0;
register char **p;
#ifdef AMIGA
char *s;
#endif /* AMIGA */
int first;
int mode_deleted = FALSE;
if (!async)
flushbuf();
/*
* Check for interrupt.
*/
breakcheck();
/*
* first try script file
* If interrupted: Stop reading script files.
*/
retry:
if (scriptin[curscript] != NULL)
{
if (!got_int && lastc >= 0)
c = lastc;
else if (got_int || (c = getc(scriptin[curscript])) < 0) /* reached EOF */
{
lastc = -1;
fclose(scriptin[curscript]);
scriptin[curscript] = NULL;
if (curscript > 0)
--curscript;
/* recovery may be delayed till after reading a script file */
if (recoverymode)
openrecover();
goto retry; /* may read other script if this one was nested */
}
if (c == 0)
c = K_ZERO; /* replace ^@ with special code */
if (async)
lastc = c;
else
lastc = -1;
return c;
}
/*
* If we got an interrupt, skip all previously typed characters and
* reset interrupt flag.
*/
if (got_int) /* skip typed characters */
{
while (GetChars((char *)buf, INBUFLEN, T_PEEK))
;
got_int = FALSE;
len = 0;
}
/*
* get character(s) from the keyboard
* this is repeated until we have found that
* - it is not an escape sequence, return the first char
* - it is the start of an escape sequence, but it is not completed within
* a short time, return the first char
* - it is an escape seqence, translate it into a single byte code
*/
first = TRUE;
for (;;)
{
if (!first || len == 0)
{
/*
* special case: if we get an <ESC> in insert mode and there are
* no more characters at once, we pretend to go out of insert mode.
* This prevents the one second delay after typing an <ESC>.
* If we get something after all, we may have to redisplay the
* mode. That the cursor is in the wrong place does not matter.
*/
if (!async && len == 1 && buf[0] == ESC && (State == INSERT || State == REPLACE) && p_timeout)
{
slen = GetChars((char *)buf + len, INBUFLEN - len, T_PEEK);
if (slen != 0)
{
len += slen;
goto gotchar;
}
if (p_mo)
{
delmode();
mode_deleted = TRUE;
}
if (Curscol) /* move cursor one left if possible */
--Curscol;
else if (Curpos.col != 0 && Cursrow)
{
--Cursrow;
Curscol = Columns - 1;
}
setcursor();
flushbuf();
}
/*
* When async == TRUE do not wait for characters to arrive: T_PEEK
* When getting the first character wait until one arrives: T_BLOCK
* When waiting for a key sequence to complete
* and 'timeout' set wait a short time: T_WAIT
* and 'timeout' not set wait until one arrives: T_BLOCK
*/
slen = GetChars((char *)buf + len, INBUFLEN - len, async ? T_PEEK : (len == 0 || !p_timeout) ? T_BLOCK : T_WAIT);
len += slen;
if (async && len == 0) /* was async and nothing typed */
return NUL;
if (slen == 0) /* escape sequence not completed in time */
break;
}
first = FALSE;
if (async) /* when we get here: len != 0 */
return (int)buf[0];
gotchar:
/* search in the escape sequences for the characters we have */
for (p = (char **)&term_strings.t_ku; p != (char **)&term_strings.t_undo + 1; ++p)
{
if (*p == NULL)
continue;
slen = strlen(*p);
if (strncmp(*p, (char *)buf, (size_t)(slen > len ? len : slen)) == 0)
break;
}
if (p == (char **)&term_strings.t_undo + 1) /* no match */
{
#ifdef AMIGA /* check for window bounds report */
buf[len] = 0;
if (buf[0] == CSI && ((s = strchr((char *)buf, 'r')) || (s = strchr((char *)buf, '|'))))
{
slen = s - buf + 1;
p = NULL;
}
else
#endif
break;
}
if (slen <= len) /* got the complete sequence */
{
if (mode_deleted)
showmode();
len -= slen;
memmove((char *)buf, (char *)buf + slen, (size_t)len);
#ifdef AMIGA
if (p == NULL) /* got window bounds report */
{
set_winsize(0, 0, FALSE); /* get size and redraw screen */
continue;
}
#endif
/* this relies on the Key numbers to be consecutive! */
return (int)(K_UARROW + (p - (char **)&term_strings.t_ku));
}
/*
* get here when we got an incomplete sequence,
* we need some more characters
*/
}
c = buf[0];
memmove((char *)buf, (char *)buf+1, (size_t)--len);
if (c == 0)
c = K_ZERO; /* replace ^@ with special code */
/* delete "INSERT" message if we return an ESC */
if (c == ESC && p_mo && !mode_deleted && (State == INSERT || State == REPLACE))
delmode();
return c;
}
/*
* outnum - output a number fast (used for row and column numbers) (jw)
*/
void
outnum(n)
register int n;
{
outstrn(tgoto("%d", 0, n));
}
void
check_winsize()
{
if (Columns < 5)
Columns = 5;
else if (Columns > MAX_COLUMNS)
Columns = MAX_COLUMNS;
if (Rows < 2)
Rows = 2;
p_scroll = Rows >> 1;
}
/*
* set window size
* If 'mustset' is TRUE, we must set Rows and Columns, do not get real
* window size (this is used for the :win command during recovery).
* If 'mustset' is FALSE, we may try to get the real window size and if
* it fails use 'width' and 'height'.
*/
void
set_winsize(width, height, mustset)
int width, height;
int mustset;
{
register int tmp;
if (width < 0 || height < 0) /* just checking... */
return;
if (State == HITRETURN) /* postpone the resizing */
{
State = SETWINSIZE;
return;
}
screenclear();
#ifdef AMIGA
flushbuf(); /* must do this before mch_get_winsize for some obscure reason */
#endif /* AMIGA */
if (mustset || mch_get_winsize())
{
debug("mch_get_win failed\n");
Rows = height;
Columns = width;
mch_set_winsize();
}
check_winsize(); /* always check, to get p_scroll right */
if (State == HELP)
redrawhelp();
else
{
tmp = RedrawingDisabled;
RedrawingDisabled = FALSE;
updateScreen(CURSUPD);
RedrawingDisabled = tmp;
if (State == CMDLINE)
redrawcmdline();
else
setcursor();
}
flushbuf();
}
void
settmode(raw)
int raw;
{
static int oldraw = FALSE;
if (oldraw == raw) /* skip if already in desired mode */
return;
oldraw = raw;
if (raw)
outstr(T_KS); /* start "keypad transmit" mode */
else
outstr(T_KE); /* stop "keypad transmit" mode */
mch_settmode(raw); /* machine specific function */
}