summaryrefslogtreecommitdiffstats
path: root/src/termlib.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/termlib.c618
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;
+}