| /* 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 independent database. */ |
| |
| #include "vim.h" |
| #include "termlib.pro" |
| |
| #if !defined(AMIGA) && !defined(VMS) && !defined(MACOS) |
| # 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 == '|') /* | 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; |
| } |
| |
| 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] == ':') |
| { |
| 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(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; |
| } |