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..e05daae --- /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; +} |