diff src/termlib.c @ 7:3fc0f57ecb91 v7.0001

updated for version 7.0001
author vimboss
date Sun, 13 Jun 2004 20:20:40 +0000
parents
children a91a2e0c4108
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/src/termlib.c
@@ -0,0 +1,628 @@
+/* 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 independant database. */
+
+#include "vim.h"
+#include "termlib.pro"
+
+#if !defined(AMIGA) && !defined(VMS) && !defined(MACOS) && !defined(RISCOS)
+# 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 == '|')		/* | seperates 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] == ':')
+	{
+	    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.
+ */
+
+    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;
+}