diff options
Diffstat (limited to 'src/termlib.c')
-rw-r--r-- | src/termlib.c | 618 |
1 files changed, 618 insertions, 0 deletions
diff --git a/src/termlib.c b/src/termlib.c new file mode 100644 index 0000000..0dc5894 --- /dev/null +++ b/src/termlib.c @@ -0,0 +1,618 @@ +/* vi:set ts=8 sts=4 sw=4 noet: */ +/* + * 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 independent database. */ + +#include "vim.h" +#include "termlib.pro" + +#if !defined(AMIGA) && !defined(VMS) +# include <sgtty.h> +#endif + +static int getent(char *, char *, FILE *, int); +static int nextent(char *, FILE *, int); +static int _match(char *, char *); +static char *_addfmt(char *, char *, int); +static char *_find(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( + 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(char *tbuf, char *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 == '|') /* | separates 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; +} + +/* + * Read 1 entry from TERMCAP file. + */ + static int +nextent(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] == ':') + { + STRMOVE(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(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(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(char *id, char **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( + char *cm, /* cm string, from termcap */ + int col, /* column, x position */ + int 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( + char *cp, /* string to print */ + int affcnt, /* Number of lines affected */ + void (*outc)(unsigned int)) /* routine to output 1 character */ +{ + long frac, /* 10^(#digits after decimal point) */ + counter, /* digits */ + atol(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. + * Returns length of text common to s1 and s2. + */ + static int +_match(char *s1, char *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(char *s, char *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(char *buf, char *fmt, int val) +{ + sprintf(buf, fmt, val); + while (*buf) + buf++; + return buf; +} |