mirror of
https://github.com/vim/vim
synced 2025-04-30 05:17:45 +02:00
628 lines
14 KiB
C
628 lines
14 KiB
C
/* vi:set ts=8 sts=4 sw=4: */
|
|
/*
|
|
* The following software is (C) 1984 Peter da Silva, the Mad Australian, in
|
|
* the public domain. It may be re-distributed for any purpose with the
|
|
* inclusion of this notice.
|
|
*/
|
|
|
|
/* Modified by Bram Moolenaar for use with VIM - Vi Improved. */
|
|
/* A few bugs removed by Olaf 'Rhialto' Seibert. */
|
|
|
|
/* TERMLIB: Terminal independant database. */
|
|
|
|
#include "vim.h"
|
|
#include "termlib.pro"
|
|
|
|
#if !defined(AMIGA) && !defined(VMS) && !defined(MACOS) && !defined(RISCOS)
|
|
# include <sgtty.h>
|
|
#endif
|
|
|
|
static int getent __ARGS((char *, char *, FILE *, int));
|
|
static int nextent __ARGS((char *, FILE *, int));
|
|
static int _match __ARGS((char *, char *));
|
|
static char *_addfmt __ARGS((char *, char *, int));
|
|
static char *_find __ARGS((char *, char *));
|
|
|
|
/*
|
|
* Global variables for termlib
|
|
*/
|
|
|
|
char *tent; /* Pointer to terminal entry, set by tgetent */
|
|
char PC = 0; /* Pad character, default NULL */
|
|
char *UP = 0, *BC = 0; /* Pointers to UP and BC strings from database */
|
|
short ospeed; /* Baud rate (1-16, 1=300, 16=19200), as in stty */
|
|
|
|
/*
|
|
* Module: tgetent
|
|
*
|
|
* Purpose: Get termcap entry for <term> into buffer at <tbuf>.
|
|
*
|
|
* Calling conventions: char tbuf[TBUFSZ+], term=canonical name for terminal.
|
|
*
|
|
* Returned values: 1 = success, -1 = can't open file,
|
|
* 0 = can't find terminal.
|
|
*
|
|
* Notes:
|
|
* - Should probably supply static buffer.
|
|
* - Uses environment variables "TERM" and "TERMCAP". If TERM = term (that is,
|
|
* if the argument matches the environment) then it looks at TERMCAP.
|
|
* - If TERMCAP begins with a slash, then it assumes this is the file to
|
|
* search rather than /etc/termcap.
|
|
* - If TERMCAP does not begin with a slash, and it matches TERM, then this is
|
|
* used as the entry.
|
|
* - This could be simplified considerably for non-UNIX systems.
|
|
*/
|
|
|
|
#ifndef TERMCAPFILE
|
|
# ifdef AMIGA
|
|
# define TERMCAPFILE "s:termcap"
|
|
# else
|
|
# ifdef VMS
|
|
# define TERMCAPFILE "VIMRUNTIME:termcap"
|
|
# else
|
|
# define TERMCAPFILE "/etc/termcap"
|
|
# endif
|
|
# endif
|
|
#endif
|
|
|
|
int
|
|
tgetent(tbuf, term)
|
|
char *tbuf; /* Buffer to hold termcap entry, TBUFSZ bytes max */
|
|
char *term; /* Name of terminal */
|
|
{
|
|
char tcbuf[32]; /* Temp buffer to handle */
|
|
char *tcptr = tcbuf; /* extended entries */
|
|
char *tcap = TERMCAPFILE; /* Default termcap file */
|
|
char *tmp;
|
|
FILE *termcap;
|
|
int retval = 0;
|
|
int len;
|
|
|
|
if ((tmp = (char *)mch_getenv((char_u *)"TERMCAP")) != NULL)
|
|
{
|
|
if (*tmp == '/') /* TERMCAP = name of termcap file */
|
|
{
|
|
tcap = tmp ;
|
|
#if defined(AMIGA)
|
|
/* Convert /usr/share/lib/termcap to usr:share/lib/termcap */
|
|
tcap++;
|
|
tmp = strchr(tcap, '/');
|
|
if (tmp)
|
|
*tmp = ':';
|
|
#endif
|
|
}
|
|
else /* TERMCAP = termcap entry itself */
|
|
{
|
|
int tlen = strlen(term);
|
|
|
|
while (*tmp && *tmp != ':') /* Check if TERM matches */
|
|
{
|
|
char *nexttmp;
|
|
|
|
while (*tmp == '|')
|
|
tmp++;
|
|
nexttmp = _find(tmp, ":|"); /* Rhialto */
|
|
if (tmp+tlen == nexttmp && _match(tmp, term) == tlen)
|
|
{
|
|
strcpy(tbuf, tmp);
|
|
tent = tbuf;
|
|
return 1;
|
|
}
|
|
else
|
|
tmp = nexttmp;
|
|
}
|
|
}
|
|
}
|
|
if (!(termcap = mch_fopen(tcap, "r")))
|
|
{
|
|
strcpy(tbuf, tcap);
|
|
return -1;
|
|
}
|
|
|
|
len = 0;
|
|
while (getent(tbuf + len, term, termcap, TBUFSZ - len))
|
|
{
|
|
tcptr = tcbuf; /* Rhialto */
|
|
if ((term = tgetstr("tc", &tcptr))) /* extended entry */
|
|
{
|
|
rewind(termcap);
|
|
len = strlen(tbuf);
|
|
}
|
|
else
|
|
{
|
|
retval = 1;
|
|
tent = tbuf; /* reset it back to the beginning */
|
|
break;
|
|
}
|
|
}
|
|
fclose(termcap);
|
|
return retval;
|
|
}
|
|
|
|
static int
|
|
getent(tbuf, term, termcap, buflen)
|
|
char *tbuf, *term;
|
|
FILE *termcap;
|
|
int buflen;
|
|
{
|
|
char *tptr;
|
|
int tlen = strlen(term);
|
|
|
|
while (nextent(tbuf, termcap, buflen)) /* For each possible entry */
|
|
{
|
|
tptr = tbuf;
|
|
while (*tptr && *tptr != ':') /* : terminates name field */
|
|
{
|
|
char *nexttptr;
|
|
|
|
while (*tptr == '|') /* | seperates names */
|
|
tptr++;
|
|
nexttptr = _find(tptr, ":|"); /* Rhialto */
|
|
if (tptr + tlen == nexttptr &&
|
|
_match(tptr, term) == tlen) /* FOUND! */
|
|
{
|
|
tent = tbuf;
|
|
return 1;
|
|
}
|
|
else /* Look for next name */
|
|
tptr = nexttptr;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
nextent(tbuf, termcap, buflen) /* Read 1 entry from TERMCAP file */
|
|
char *tbuf;
|
|
FILE *termcap;
|
|
int buflen;
|
|
{
|
|
char *lbuf = tbuf; /* lbuf=line buffer */
|
|
/* read lines straight into buffer */
|
|
|
|
while (lbuf < tbuf+buflen && /* There's room and */
|
|
fgets(lbuf, (int)(tbuf+buflen-lbuf), termcap)) /* another line */
|
|
{
|
|
int llen = strlen(lbuf);
|
|
|
|
if (*lbuf == '#') /* eat comments */
|
|
continue;
|
|
if (lbuf[-1] == ':' && /* and whitespace */
|
|
lbuf[0] == '\t' &&
|
|
lbuf[1] == ':')
|
|
{
|
|
strcpy(lbuf, lbuf+2);
|
|
llen -= 2;
|
|
}
|
|
if (lbuf[llen-2] == '\\') /* and continuations */
|
|
lbuf += llen-2;
|
|
else
|
|
{
|
|
lbuf[llen-1]=0; /* no continuation, return */
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0; /* ran into end of file */
|
|
}
|
|
|
|
/*
|
|
* Module: tgetflag
|
|
*
|
|
* Purpose: returns flag true or false as to the existence of a given entry.
|
|
* used with 'bs', 'am', etc...
|
|
*
|
|
* Calling conventions: id is the 2 character capability id.
|
|
*
|
|
* Returned values: 1 for success, 0 for failure.
|
|
*/
|
|
|
|
int
|
|
tgetflag(id)
|
|
char *id;
|
|
{
|
|
char buf[256], *ptr = buf;
|
|
|
|
return tgetstr(id, &ptr) ? 1 : 0;
|
|
}
|
|
|
|
/*
|
|
* Module: tgetnum
|
|
*
|
|
* Purpose: get numeric value such as 'li' or 'co' from termcap.
|
|
*
|
|
* Calling conventions: id = 2 character id.
|
|
*
|
|
* Returned values: -1 for failure, else numerical value.
|
|
*/
|
|
|
|
int
|
|
tgetnum(id)
|
|
char *id;
|
|
{
|
|
char *ptr, buf[256];
|
|
ptr = buf;
|
|
|
|
if (tgetstr(id, &ptr))
|
|
return atoi(buf);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Module: tgetstr
|
|
*
|
|
* Purpose: get terminal capability string from database.
|
|
*
|
|
* Calling conventions: id is the two character capability id.
|
|
* (*buf) points into a hold buffer for the
|
|
* id. the capability is copied into the buffer
|
|
* and (*buf) is advanced to point to the next
|
|
* free byte in the buffer.
|
|
*
|
|
* Returned values: 0 = no such entry, otherwise returns original
|
|
* (*buf) (now a pointer to the string).
|
|
*
|
|
* Notes
|
|
* It also decodes certain escape sequences in the buffer.
|
|
* they should be obvious from the code:
|
|
* \E = escape.
|
|
* \n, \r, \t, \f, \b match the 'c' escapes.
|
|
* ^x matches control-x (^@...^_).
|
|
* \nnn matches nnn octal.
|
|
* \x, where x is anything else, matches x. I differ
|
|
* from the standard library here, in that I allow ^: to match
|
|
* :.
|
|
*
|
|
*/
|
|
|
|
char *
|
|
tgetstr(id, buf)
|
|
char *id, **buf;
|
|
{
|
|
int len = strlen(id);
|
|
char *tmp=tent;
|
|
char *hold;
|
|
int i;
|
|
|
|
do {
|
|
tmp = _find(tmp, ":"); /* For each field */
|
|
while (*tmp == ':') /* skip empty fields */
|
|
tmp++;
|
|
if (!*tmp)
|
|
break;
|
|
|
|
if (_match(id, tmp) == len) {
|
|
tmp += len; /* find '=' '@' or '#' */
|
|
if (*tmp == '@') /* :xx@: entry for tc */
|
|
return 0; /* deleted entry */
|
|
hold= *buf;
|
|
while (*++tmp && *tmp != ':') { /* not at end of field */
|
|
switch(*tmp) {
|
|
case '\\': /* Expand escapes here */
|
|
switch(*++tmp) {
|
|
case 0: /* ignore backslashes */
|
|
tmp--; /* at end of entry */
|
|
break; /* shouldn't happen */
|
|
case 'e':
|
|
case 'E': /* ESC */
|
|
*(*buf)++ = ESC;
|
|
break;
|
|
case 'n': /* \n */
|
|
*(*buf)++ = '\n';
|
|
break;
|
|
case 'r': /* \r */
|
|
*(*buf)++ = '\r';
|
|
break;
|
|
case 't': /* \t */
|
|
*(*buf)++ = '\t';
|
|
break;
|
|
case 'b': /* \b */
|
|
*(*buf)++ = '\b';
|
|
break;
|
|
case 'f': /* \f */
|
|
*(*buf)++ = '\f';
|
|
break;
|
|
case '0': /* \nnn */
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
**buf = 0;
|
|
/* get up to three digits */
|
|
for (i = 0; i < 3 && VIM_ISDIGIT(*tmp); ++i)
|
|
**buf = **buf * 8 + *tmp++ - '0';
|
|
(*buf)++;
|
|
tmp--;
|
|
break;
|
|
default: /* \x, for all other x */
|
|
*(*buf)++= *tmp;
|
|
}
|
|
break;
|
|
case '^': /* control characters */
|
|
++tmp;
|
|
*(*buf)++ = Ctrl_chr(*tmp);
|
|
break;
|
|
default:
|
|
*(*buf)++ = *tmp;
|
|
}
|
|
}
|
|
*(*buf)++ = 0;
|
|
return hold;
|
|
}
|
|
} while (*tmp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Module: tgoto
|
|
*
|
|
* Purpose: decode cm cursor motion string.
|
|
*
|
|
* Calling conventions: cm is cursor motion string. line, col, are the
|
|
* desired destination.
|
|
*
|
|
* Returned values: a string pointing to the decoded string, or "OOPS" if it
|
|
* cannot be decoded.
|
|
*
|
|
* Notes
|
|
* The accepted escapes are:
|
|
* %d as in printf, 0 origin.
|
|
* %2, %3 like %02d, %03d in printf.
|
|
* %. like %c
|
|
* %+x adds <x> to value, then %.
|
|
* %>xy if value>x, adds y. No output.
|
|
* %i increments line& col, no output.
|
|
* %r reverses order of line&col. No output.
|
|
* %% prints as a single %.
|
|
* %n exclusive or row & col with 0140.
|
|
* %B BCD, no output.
|
|
* %D reverse coding (x-2*(x%16)), no output.
|
|
*/
|
|
|
|
char *
|
|
tgoto(cm, col, line)
|
|
char *cm; /* cm string, from termcap */
|
|
int col, /* column, x position */
|
|
line; /* line, y position */
|
|
{
|
|
char gx, gy, /* x, y */
|
|
*ptr, /* pointer in 'cm' */
|
|
reverse = 0, /* reverse flag */
|
|
*bufp, /* pointer in returned string */
|
|
addup = 0, /* add upline */
|
|
addbak = 0, /* add backup */
|
|
c;
|
|
static char buffer[32];
|
|
|
|
if (!cm)
|
|
return "OOPS"; /* Kludge, but standard */
|
|
|
|
bufp = buffer;
|
|
ptr = cm;
|
|
|
|
while (*ptr) {
|
|
if ((c = *ptr++) != '%') { /* normal char */
|
|
*bufp++ = c;
|
|
} else { /* % escape */
|
|
switch(c = *ptr++) {
|
|
case 'd': /* decimal */
|
|
bufp = _addfmt(bufp, "%d", line);
|
|
line = col;
|
|
break;
|
|
case '2': /* 2 digit decimal */
|
|
bufp = _addfmt(bufp, "%02d", line);
|
|
line = col;
|
|
break;
|
|
case '3': /* 3 digit decimal */
|
|
bufp = _addfmt(bufp, "%03d", line);
|
|
line = col;
|
|
break;
|
|
case '>': /* %>xy: if >x, add y */
|
|
gx = *ptr++;
|
|
gy = *ptr++;
|
|
if (col>gx) col += gy;
|
|
if (line>gx) line += gy;
|
|
break;
|
|
case '+': /* %+c: add c */
|
|
line += *ptr++;
|
|
case '.': /* print x/y */
|
|
if (line == '\t' || /* these are */
|
|
line == '\n' || /* chars that */
|
|
line == '\004' || /* UNIX hates */
|
|
line == '\0') {
|
|
line++; /* so go to next pos */
|
|
if (reverse == (line == col))
|
|
addup=1; /* and mark UP */
|
|
else
|
|
addbak=1; /* or BC */
|
|
}
|
|
*bufp++=line;
|
|
line = col;
|
|
break;
|
|
case 'r': /* r: reverse */
|
|
gx = line;
|
|
line = col;
|
|
col = gx;
|
|
reverse = 1;
|
|
break;
|
|
case 'i': /* increment (1-origin screen) */
|
|
col++;
|
|
line++;
|
|
break;
|
|
case '%': /* %%=% literally */
|
|
*bufp++='%';
|
|
break;
|
|
case 'n': /* magic DM2500 code */
|
|
line ^= 0140;
|
|
col ^= 0140;
|
|
break;
|
|
case 'B': /* bcd encoding */
|
|
line = line/10<<4+line%10;
|
|
col = col/10<<4+col%10;
|
|
break;
|
|
case 'D': /* magic Delta Data code */
|
|
line = line-2*(line&15);
|
|
col = col-2*(col&15);
|
|
break;
|
|
default: /* Unknown escape */
|
|
return "OOPS";
|
|
}
|
|
}
|
|
}
|
|
|
|
if (addup) /* add upline */
|
|
if (UP) {
|
|
ptr=UP;
|
|
while (VIM_ISDIGIT(*ptr) || *ptr == '.')
|
|
ptr++;
|
|
if (*ptr == '*')
|
|
ptr++;
|
|
while (*ptr)
|
|
*bufp++ = *ptr++;
|
|
}
|
|
|
|
if (addbak) /* add backspace */
|
|
if (BC) {
|
|
ptr=BC;
|
|
while (VIM_ISDIGIT(*ptr) || *ptr == '.')
|
|
ptr++;
|
|
if (*ptr == '*')
|
|
ptr++;
|
|
while (*ptr)
|
|
*bufp++ = *ptr++;
|
|
}
|
|
else
|
|
*bufp++='\b';
|
|
|
|
*bufp = 0;
|
|
|
|
return(buffer);
|
|
}
|
|
|
|
/*
|
|
* Module: tputs
|
|
*
|
|
* Purpose: decode padding information
|
|
*
|
|
* Calling conventions: cp = string to be padded, affcnt = # of items affected
|
|
* (lines, characters, whatever), outc = routine to output 1 character.
|
|
*
|
|
* Returned values: none
|
|
*
|
|
* Notes
|
|
* cp has padding information ahead of it, in the form
|
|
* nnnTEXT or nnn*TEXT. nnn is the number of milliseconds to delay,
|
|
* and may be a decimal (nnn.mmm). If the asterisk is given, then
|
|
* the delay is multiplied by afcnt. The delay is produced by outputting
|
|
* a number of nulls (or other padding char) after printing the
|
|
* TEXT.
|
|
*
|
|
*/
|
|
|
|
long _bauds[16]={
|
|
0, 50, 75, 110,
|
|
134, 150, 200, 300,
|
|
600, 1200, 1800, 2400,
|
|
4800, 9600, 19200, 19200 };
|
|
|
|
int
|
|
tputs(cp, affcnt, outc)
|
|
char *cp; /* string to print */
|
|
int affcnt; /* Number of lines affected */
|
|
void (*outc) __ARGS((unsigned int));/* routine to output 1 character */
|
|
{
|
|
long frac, /* 10^(#digits after decimal point) */
|
|
counter, /* digits */
|
|
atol __ARGS((const char *));
|
|
|
|
if (VIM_ISDIGIT(*cp)) {
|
|
counter = 0;
|
|
frac = 1000;
|
|
while (VIM_ISDIGIT(*cp))
|
|
counter = counter * 10L + (long)(*cp++ - '0');
|
|
if (*cp == '.')
|
|
while (VIM_ISDIGIT(*++cp)) {
|
|
counter = counter * 10L + (long)(*cp++ - '0');
|
|
frac = frac * 10;
|
|
}
|
|
if (*cp!='*') { /* multiply by affected lines */
|
|
if (affcnt>1) affcnt = 1;
|
|
}
|
|
else
|
|
cp++;
|
|
|
|
/* Calculate number of characters for padding counter/frac ms delay */
|
|
if (ospeed)
|
|
counter = (counter * _bauds[ospeed] * (long)affcnt) / frac;
|
|
|
|
while (*cp) /* output string */
|
|
(*outc)(*cp++);
|
|
if (ospeed)
|
|
while (counter--) /* followed by pad characters */
|
|
(*outc)(PC);
|
|
}
|
|
else
|
|
while (*cp)
|
|
(*outc)(*cp++);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Module: tutil.c
|
|
*
|
|
* Purpose: Utility routines for TERMLIB functions.
|
|
*
|
|
*/
|
|
static int
|
|
_match(s1, s2) /* returns length of text common to s1 and s2 */
|
|
char *s1, *s2;
|
|
{
|
|
int i = 0;
|
|
|
|
while (s1[i] && s1[i] == s2[i])
|
|
i++;
|
|
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
* finds next c in s that's a member of set, returns pointer
|
|
*/
|
|
static char *
|
|
_find(s, set)
|
|
char *s, *set;
|
|
{
|
|
for(; *s; s++)
|
|
{
|
|
char *ptr = set;
|
|
|
|
while (*ptr && *s != *ptr)
|
|
ptr++;
|
|
|
|
if (*ptr)
|
|
return s;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* add val to buf according to format fmt
|
|
*/
|
|
static char *
|
|
_addfmt(buf, fmt, val)
|
|
char *buf, *fmt;
|
|
int val;
|
|
{
|
|
sprintf(buf, fmt, val);
|
|
while (*buf)
|
|
buf++;
|
|
return buf;
|
|
}
|