home *** CD-ROM | disk | FTP | other *** search
- /* vi:sw=4:ts=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 */
-
- /* TERMLIB: Terminal independant database. */
-
- #include "vim.h"
- #include "proto.h"
- #include "proto/termlib.pro"
-
- #ifndef AMIGA
- # include <sgtty.h>
- #endif
-
- static int getent __PARMS((char *, char *, FILE *, int));
- static int nextent __PARMS((char *, FILE *, int));
- static int _match __PARMS((char *, char *));
- static char *_addfmt __PARMS((char *, char *, int));
- static char *_find __PARMS((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.
- */
-
- #ifdef AMIGA
- # define TERMCAPFILE "s:termcap"
- #else
- # define TERMCAPFILE "/etc/termcap"
- #endif
-
- 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 *)vimgetenv("TERMCAP")) != NULL)
- {
- if (*tmp == '/') /* TERMCAP = name of termcap file */
- tcap = tmp ;
- else /* TERMCAP = termcap entry itself */
- {
- int tlen = strlen(term);
-
- while (*tmp && *tmp != ':') /* Check if TERM matches */
- {
- while (*tmp == '|')
- tmp++;
- if (_match(tmp, term) == tlen)
- {
- strcpy(tbuf, tmp);
- tent = tbuf;
- return 1;
- }
- else
- tmp = _find(tmp, ":|");
- }
- }
- }
- if (!(termcap = fopen(tcap, "r")))
- {
- strcpy(tbuf, tcap);
- return -1;
- }
-
- len = 0;
- while (getent(tbuf + len, term, termcap, TBUFSZ - len))
- {
- if ((term = tgetstr("tc", &tcptr))) /* extended entry */
- {
- rewind(termcap);
- len = strlen(tbuf);
- }
- else
- {
- retval = 1;
- tent = tbuf;
- 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 */
- {
- while (*tptr == '|') /* | seperates names */
- tptr++;
- if (_match(tptr, term) == tlen) /* FOUND! */
- {
- tent = tbuf;
- return 1;
- }
- else /* Look for next name */
- tptr = _find(tptr, ":|");
- }
- }
- 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.
- */
-
- 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.
- */
-
- 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)++ = '\033';
- 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 && isdigit(*tmp); ++i)
- **buf = **buf * 8 + *tmp++ - '0';
- (*buf)++;
- tmp--;
- break;
- default: /* \x, for all other x */
- *(*buf)++= *tmp;
- }
- break;
- case '^': /* control characters */
- *(*buf)++ = *++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 (isdigit(*ptr) || *ptr == '.')
- ptr++;
- if (*ptr == '*')
- ptr++;
- while (*ptr)
- *bufp++ = *ptr++;
- }
-
- if (addbak) /* add backspace */
- if (BC) {
- ptr=BC;
- while (isdigit(*ptr) || *ptr == '.')
- ptr++;
- if (*ptr == '*')
- ptr++;
- while (*ptr)
- *bufp++ = *ptr++;
- }
- else
- *bufp++='\b';
-
- *bufp = 0;
-
- return(buffer);
- }
-
- /*
- * Module: tinit
- *
- * Purpose: simplified terminal initialisation.
- *
- * Calling conventions: name is name of terminal.
- *
- * Returned values: none.
- *
- * Notes
- * tinit calls tgetent, then sets up the global
- * variables PC, UP, BC, ospeed appropriately.
- *
- */
-
- #if 0 /* already included in term.c */
-
- char tbuf[TBUFSZ]; /* Buffer for termcap entry */
- char junkbuf[TBUFSZ]; /* Big buffer for junk */
- char *junkptr;
-
- tinit(name)
- char *name;
- {
- #ifndef AMIGA
- struct sgttyb sgbuf;
- #endif
- char *ps;
-
- junkptr = junkbuf;
-
- tgetent(tbuf, name);
-
- ps = tgetstr("pc", &junkptr);
- if (ps) PC = *ps;
- UP = tgetstr("up", &junkptr);
- BC = tgetstr("bc", &junkptr);
-
- #ifdef AMIGA
- ospeed=0;
- #else
- gtty(1, &sgbuf);
- ospeed=sgbuf.sg_ospeed;
- #endif
- return 0;
- }
- #endif
-
- /*
- * 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 };
-
- 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();
-
- if (isdigit(*cp)) {
- counter = 0;
- frac = 1000;
- while (isdigit(*cp))
- counter = counter * 10L + (long)(*cp++ - '0');
- if (*cp == '.')
- while (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;
- }
-
- static char *
- _find(s, set) /* finds next c in s that's a member of set, returns pointer */
- char *s, *set;
- {
- for(; *s; s++) {
- char *ptr = set;
-
- while (*ptr && *s != *ptr)
- ptr++;
-
- if (*ptr)
- return s;
- }
-
- return s;
- }
-
- static char *
- _addfmt(buf, fmt, val) /* add val to buf according to format fmt */
- char *buf, *fmt;
- int val;
- {
- sprintf(buf, fmt, val);
- while (*buf)
- buf++;
- return buf;
- }
-