Category : C Source Code
Archive   : VIM20SRC.ZIP
Filename : TERM.C

 
Output of file : TERM.C contained in archive : VIM20SRC.ZIP
/* vi:sw=4:ts=4:
*
* term.c -- VIM - Vi IMproved
*
* 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
# define TPUTSFUNCAST (outfuntype)
# else
# define TPUTSFUNCAST
# ifdef AMIGA
# include "proto/termlib.pro"
# endif
# endif
#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(MSDOS) || defined(ALL_BUILTIN_TCAPS)
PCANSI_TCAP,
# 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 */
DUMB_TCAP, /* minimal termcap, used when everything else fails */
#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 */
# ifndef AMIGA /* included in proto/termlib.pro */
int tgetent();
int tgetnum();
char *tgetstr();
int tputs();
# endif /* AMIGA */
# ifndef hpux
extern short ospeed;
# endif
# endif /* linux */
# ifndef hpux
char *UP, *BC, PC; /* should be extern, but some don't have them */
# endif
#endif /* TERMCAP */

void
set_term(term)
char *term;
{
char **p = builtin_tcaps;
#ifdef TERMCAP
int builtin = 0;
#endif
int width = 0, height = 0;

if (!strncmp(term, "builtin_", (size_t)8))
{
term += 8;
#ifdef TERMCAP
builtin = 1;
#endif
}
#ifdef TERMCAP
else
{
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;
}
else if (i == 0)
{
emsg("terminal entry not found");
builtin = 1;
}
else
{
clear_termparam(); /* clear old parameters */
/* 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);
/* if 'mr' or 'me' is not defined use 'so' and 'se' */
if (T_TP == NULL || *T_TP == NUL || T_TI == NULL || *T_TI == NUL)
{
T_TP = tgetstr("se", &tp);
T_TI = tgetstr("so", &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);
T_TS = tgetstr("ti", &tp);
T_TE = tgetstr("te", &tp);

/* key codes */
term_strings.t_ku = tgetstr("ku", &tp);
term_strings.t_kd = tgetstr("kd", &tp);
term_strings.t_kl = tgetstr("kl", &tp);
/* if cursor-left == backspace, ignore it (televideo 925) */
if (term_strings.t_kl != NULL && *term_strings.t_kl == Ctrl('H'))
term_strings.t_kl = NULL;
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");

# ifndef hpux
BC = tgetstr("bc", &tp);
UP = tgetstr("up", &tp);
p = tgetstr("pc", &tp);
if (p)
PC = *p;
ospeed = 0;
# endif
}
}
if (builtin)
#endif
{
while (*p && strcmp(term, *p))
p++;
if (!*p)
{
fprintf(stderr, "'%s' not builtin. Available terminals are:\r\n", term);
for (p = builtin_tcaps; *p; p++)
#ifdef TERMCAP
fprintf(stderr, "\tbuiltin_%s\r\n", *p);
#else
fprintf(stderr, "\t%s\r\n", *p);
#endif
if (!starting) /* when user typed :set term=xxx, quit here */
{
wait_return(TRUE);
return;
}
sleep(2);
fprintf(stderr, "defaulting to '%s'\r\n", *builtin_tcaps);
sleep(2);
p = builtin_tcaps;
free(term_strings.t_name);
term_strings.t_name = strsave(term = *p);
}
clear_termparam(); /* clear old parameters */
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;
# ifdef AMIGA
win_resize_on(); /* enable window resizing reports */
# endif
}
#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;
#ifdef MSDOS
height = 25; /* console is often 25 lines */
#else
height = 24; /* most terminals are 24 lines */
#endif
}
Rows_max = Rows; /* remember max. physical nr. of Rows */
set_winsize(width, height, FALSE); /* may change Rows_max */
}

#if defined(TERMCAP) && defined(UNIX)
/*
* Get Columns and Rows from the termcap. Used after a window signal if the
* ioctl() fails. It doesn't make sense to call tgetent each time if the "co"
* and "li" entries never change. But this may happen on some systems.
*/
void
getlinecol()
{
char tbuf[TBUFSZ];

if (term_strings.t_name && tgetent(tbuf, term_strings.t_name) > 0)
{
if (Columns == 0)
Columns = tgetnum("co");
if (Rows == 0)
Rows = tgetnum("li");
}
}
#endif

static char *tltoa __PARMS((unsigned long));

static char *
tltoa(i)
unsigned long i;
{
static char buf[16];
char *p;

p = buf + 15;
*p = '\0';
do
{
--p;
*p = i % 10 + '0';
i /= 10;
}
while (i > 0 && p > buf);
return p;
}

#ifndef TERMCAP

/*
* minimal tgoto() implementation.
* no padding and we only parse for %i %d and %+char
*/

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 = tltoa((unsigned long)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 = (char *)vimgetenv("TERM");
if (!term || !*term)
term = *builtin_tcaps;
term_strings.t_name = strsave(term);
set_term(term);
}

/*
* the number of calls to mch_write is reduced by using the buffer "outbuf"
*/
#undef BSIZE /* hpux has BSIZE in sys/param.h */
#define BSIZE 2048
static u_char outbuf[BSIZE];
static int bpos = 0; /* number of chars in outbuf */

/*
* flushbuf(): flush the output buffer
*/
void
flushbuf()
{
if (bpos != 0)
{
mch_write((char *)outbuf, bpos);
bpos = 0;
}
}

/*
* outchar(c): put a character into the output buffer.
* Flush it if it becomes full.
*/
void
outchar(c)
unsigned c;
{
#ifdef UNIX
if (c == '\n') /* turn LF into CR-LF (CRMOD does not seem to do this) */
outchar('\r');
#endif
outbuf[bpos] = c;
++bpos;
if (bpos >= BSIZE)
flushbuf();
if (c == '\n')
char_count += Columns;
else
++char_count;
}

/*
* 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;
{
if (bpos > BSIZE - 20) /* avoid terminal strings being split up */
flushbuf();
while (*s)
outchar(*s++);
}

/*
* 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 (bpos > BSIZE - 20) /* avoid terminal strings being split up */
flushbuf();
if (s)
#ifdef TERMCAP
tputs(s, 1, TPUTSFUNCAST outchar);
#else
while (*s)
outchar(*s++);
#endif
}

/*
* cursor positioning using termcap parser. (jw)
*/
void
windgoto(row, col)
int row;
int col;
{
outstr(tgoto(T_CM, col, row));
}

/*
* 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 one character from
* 1. a scriptfile
* 2. the keyboard
*
* As much characters as we can get (upto 'maxlen') are put in buf and
* NUL terminated (buffer length must be 'maxlen' + 1).
*
* If we got an interrupt all input is read until none is available.
*
* If time == 0 there is no waiting for the char.
* If time == n we wait for n msec for a character to arrive.
* If time == -1 we wait forever for a character to arrive.
*
* Return the number of obtained characters.
*/

int
inchar(buf, maxlen, time)
char *buf;
int maxlen;
int time; /* milli seconds */
{
int len;
int retesc = FALSE; /* return ESC with gotint */
register int c;
register int i;

if (time == -1) /* flush output before blocking */
flushbuf();
did_outofmem_msg = FALSE; /* display out of memory message (again) */

/*
* first try script file
* If interrupted: Stop reading script files.
*/
retry:
if (scriptin[curscript] != NULL)
{
if (got_int || (c = getc(scriptin[curscript])) < 0) /* reached EOF */
{
/* when reading script file is interrupted, return an ESC to
get back to normal mode */
if (got_int)
retesc = TRUE;
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 */
*buf++ = c;
*buf = NUL;
return 1;
}

/*
* If we got an interrupt, skip all previously typed characters and
* return TRUE if quit reading script file.
*/
if (got_int) /* skip typed characters */
{
while (GetChars(buf, maxlen, T_PEEK))
;
return retesc;
}
len = GetChars(buf, maxlen, time);

for (i = len; --i >= 0; ++buf)
if (*buf == 0)
*(u_char *)buf = K_ZERO; /* replace ^@ with special code */
*buf = NUL; /* add trailing NUL */
return len;
}

/*
* Check if buf[] begins with a terminal key code.
* Return 0 for no match, -1 for partial match, > 0 for full match.
* With a match the replacement code is put in buf[0], the match is
* removed and the number characters in buf is returned.
*/
int
check_termcode(buf)
char *buf;
{
char **p;
int slen;
int len;

len = strlen(buf);
for (p = (char **)&term_strings.t_ku; p != (char **)&term_strings.t_undo + 1; ++p)
{
if (*p == NULL || (slen = strlen(*p)) == 0) /* empty entry */
continue;
if (strncmp(*p, buf, (size_t)(slen > len ? len : slen)) == 0)
{
if (len >= slen) /* got the complete sequence */
{
len -= slen;
memmove(buf + 1, buf + slen, (size_t)(len + 1));
/* this relies on the Key numbers to be consecutive! */
buf[0] = K_UARROW + (p - (char **)&term_strings.t_ku);
return (len + 1);
}
return -1; /* got a partial sequence */
}
}
return 0; /* no match found */
}

/*
* outnum - output a (big) number fast
*/
void
outnum(n)
register long n;
{
outstrn(tltoa((unsigned long)n));
}

/*
* outnuml - output a (big) number fast and return the number of characters
*/
int
outnuml(n)
register long n;
{
char *s;

s = tltoa((unsigned long)n);
outstrn(s);
return (int)strlen(s);
}

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 || State == SETWSIZE) /* postpone the resizing */
{
State = SETWSIZE;
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 if (!starting)
{
tmp = RedrawingDisabled;
RedrawingDisabled = FALSE;
comp_Botline();
updateScreen(CURSUPD);
RedrawingDisabled = tmp;
if (State == CMDLINE)
redrawcmdline();
else
setcursor();
}
flushbuf();
}

/*
* set active window height (for "z" command)
*/
void
set_winheight(height)
int height;
{
if (height > Rows_max) /* can't make it larger */
height = Rows_max;
Rows = height;
check_winsize();
updateScreen(CLEAR);
}

void
settmode(raw)
int raw;
{
static int oldraw = FALSE;

if (oldraw == raw) /* skip if already in desired mode */
return;
oldraw = raw;

mch_settmode(raw); /* machine specific function */
}

void
starttermcap()
{
outstr(T_KS); /* start "keypad transmit" mode */
outstr(T_TS); /* start termcap mode */
flushbuf();
termcap_active = TRUE;
}

void
stoptermcap()
{
outstr(T_KE); /* stop "keypad transmit" mode */
outstr(T_TE); /* stop termcap mode */
flushbuf();
termcap_active = FALSE;
}

/*
* enable cursor, unless in Visual mode or no inversion possible
*/
void
cursor_on()
{
if (!Visual.lnum || T_TI == NULL || *T_TI == NUL)
outstr(T_CV);
}

void
cursor_off()
{
outstr(T_CI); /* disable cursor */
}


  3 Responses to “Category : C Source Code
Archive   : VIM20SRC.ZIP
Filename : TERM.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/