jv@mhres.mh.nl (Johan Vromans) (09/02/87)
Here is a UNIX compatible termcap library for VAX/VMS. It implements
the following termcap(3) routines:
tgetent (buf, termname)
tgetnum (id)
tgetflag (id)
tgetstr (id)
tgoto (cm, x, y)
tputs (str, affcnt, outc)
all according to the UNIX termcap(3) documentation.
It includes a vms-specific getenv routine, which allows changing your
terminal type with a DEFINE TERM "type" command, e.g.
DEFINE TERM "hpex"
Don't forget the quotes around the (lowercased) terminal type.
It can read termcap info from a termcap file, but also has a number of
terminal types built-in. See the source for details.
BTW - it is also usable on Unix. (If you don't want to use the curses
emulation of termcap).
Enjoy!
--
Johan Vromans | jv@mh.nl via European backbone
Multihouse N.V., Gouda, the Netherlands | uucp: ..{?????!}mcvax!mh.nl!jv
"It is better to light a candle than to curse the darkness"
[ watch out - there will be another signature at the end of this message ]
-------------------------------- CUT HERE --------------------------------
/*-**-* !<sar> */
/*-**-* termcap.c */
/* tcap.c - termcap routines
*
* This is a termcap compatible library, usable on UNIX and VAX/VMS
* systems.
*
* It can read a termcap file (predefined by TERMCAP) or use built-in
* entries.
*
* The calling sequence of all routines, and their behaviour is as
* documented in termcap(3). Therefore, no additiona documentation
* is supplied.
*
* Compile-time options:
*
* TERMCAP the name of the termcap file, if different from
* the default values
* GETENV use vms specific getenv routine, which translates
* logical names before passing to the 'real' getenv.
* This is required when you want to change your
* terminal type using a DEFINE command.
* DEBUG compile into a test program, which can be used for
* debugging.
*
* Note: padding of delay characters (PC and ospeed) is not supported.
*
* (c) COPYRIGHT 1986, 1987 by Johan Vromans, Multihouse Research
*
* This software is in public domain and may be used and
* distributed freely for non-commercial and non-military purposes,
* provided that the above copyright notice and this distribution
* restriction is retained.
*
* Bugs, remarks and such to jv@mh.nl via european backbone (mcvax)
*/
#ifdef DEBUG
#include <stdio.h>
#endif /* DEBUG */
#include <ctype.h>
#define EOS '\0'
/* built-in terminal types.
*
* vtXXX-YY is normally delivered by getenv("TERM") on most VMS systems.
*
*/
static char *tcp[] = {
/* common vt100. Note that XA and XD make use of the fact that
the second argument of tgoto is repeated */
"vt|vt100|vt100-80|vt100-80am|vt100-am:\
:am:bl=^G:bs:cd=50\\E[J:ce=3\\E[K:cl=50\\E[;H\\E[2J:cm=5\\E[%i%d;%dH:\
:co#80:cr=^M:cs=\\E[%i%d;%dr:do=^J:ho=\\E[H:is=\\E[1;24r\\E[24;1H:\
:k1=\\EOP:k2=\\EOQ:k3=\\EOR:k4=\\EOS:kb=^H:kd=\\EOB:ke=\\E[?1l\\E>:\
:kl=\\EOD:kr=\\EOC:ks=\\E[?1h\\E=:ku=\\EOA:le=^H:li#24:mb=2\\E[5m:\
:md=2\\E[1m:me=2\\E[m:mr=2\\E[7m:nd=2\\E[C:nl=^J:pt:rc=\\E8:\
:rf=/usr/lib/tabset/vt100:rs=\\E>\\E[?3l\\E[?4l\\E[?5l\\E[?7h\\E[?8h:\
:sc=\\E7:se=2\\E[m:so=2\\E[7m:sr=5\\EM:ta=^I:ue=2\\E[m:up=2\\E[A:\
:us=2\\E[4m:vt#3:xn:xo:",
"vt|vt100-w|vt100-132|vt100-132am|vt100-w-am:\
:co#132:tc=vt100:",
"vt|vt102|vt102-80|vt102-80am|vt102-am:\
al=\\E[L:de=\\E[M:dc=\\E[P:im=\\E[4h:ei=\\E[4l:\
:tc=vt100:",
"vt|vt102-w|vt102-132|vt102-132am|vt102-w-am:\
:co#132:tc=vt102:",
"vt|vt200|vt200-80|vt200-80am|vt200-am:\
:kh=\\E[2~:X1=\\E[29~:X2=\\E[28~:kH=\\E[5~:\
:K1=\\E[1~:K5=\\E[6~:K3=\\E[3~:K4=\\E[4~:tc=vt102:",
"vt|vt200-w|vt200-132|vt200-132am|vt200-w-am:\
:co#132:tc=vt200:",
"hp|hpex:\
:am:xs:xo:da:db:mi:bs:co#80:li#24:lm#0:bt=\\Ei:bl=^G:cr=^M:ct=\\E3:\
:cl=\\E&a0y0C\\EJ:ce=\\EK:cd=\\EJ:ch=\\E&a%dC:cm=\\E&a%dy%dC:\
:do=\\EB:le=\\b:nd=\\EC:up=\\EA:dc=\\EP:dl=\\EM:im=\\EQ:so=\\E&dB:\
:us=\\E&dD:me=\\E&d@:ei=\\ER:se=\\E&d@:ue=\\E&d@:\
:al=\\EL:kb=\\b:kd=\\EB:kh=\\Eh:kl=\\ED:\
:kr=\\EC:ku=\\EA:ke=\\E&s0A:ks=\\E&s1A:cv=\\E&a%dY:sf=\\n:st=\\E1:\
:ta=\\t:",
"hp|hp2622|hp2382|hp2392:\
:tc=hpex:",
0 };
char *getenv();
#ifdef vaxc
/* Specific vms-routines */
#include <descrip.h>
#include <ssdef.h>
#ifdef GETENV
/* Vaxc uses built-in values for environment variables like HOME and TERM.
* Therefore, it is not possible to change one's TERM setting with a
* DEFINE command.
*
* This version of getenv first uses sys$trnlog to translate any logicals,
* and passes to the 'real' getenv only if this translation fails.
*/
char *
vms_getenv(arg)
char *arg;
{
int status;
$DESCRIPTOR (desc, "");
$DESCRIPTOR (dd, "");
static char buffer[64] = "";
short cnt;
desc.dsc$a_pointer = arg;
desc.dsc$w_length = strlen(arg);
dd.dsc$a_pointer = buffer;
dd.dsc$w_length = sizeof (buffer);
status = sys$trnlog (&desc, &cnt, &dd, 0, 0, 0);
if (status == SS$_NORMAL) {
buffer[cnt] = EOS;
return(buffer);
}
return(getenv(arg));
}
# define getenv vms_getenv
#endif /* GETENV */
#endif /* vaxc */
char *strcpy ();
static char *data;
#define BUFSIZ 1024
#define MAX_TC 32 /* max number of tc= indirections */
static int tccount; /* detect infinite loops in termcap, init 0 */
#ifdef vaxc
#define TERMCAP "sys$library:termcap.dat"
#endif
#ifndef TERMCAP
#define TERMCAP "/etc/termcap" /* UNIX compatible */
#endif
#define STDERR 2
#define wrerr(txt) write (STDERR, txt, sizeof (txt))
int
tgetent (bp, name)
char *bp;
char *name;
{
register int c;
register int i = 0, cnt = 0;
char ibuf[BUFSIZ];
int fd = 0;
register char *cp = getenv ("TERMCAP");
char *term = getenv ("TERM");
data = bp;
#ifdef DEBUG
fprintf (stderr, "TERM = \"%s\"\n", term ? term : "<null>");
#endif /* DEBUG */
/* TERMCAP can be the name of a file to use instead of /etc/termcap,
* or it can be an entry.
*/
if (cp && *cp) {
#ifdef DEBUG
fprintf (stderr, "TERMCAP = \"%s\"\n", cp);
#endif /* DEBUG */
/* consider it to be a filename if it start with [ or _ (VMS)
* or / (UNIX). Note VMS does also understand filenames with slashes.
*/
if (
#ifdef vaxc
*cp == '[' || *cp == "_" ||
#endif /* vaxc */
*cp == '/' ) {
#ifdef DEBUG
fprintf (stderr, "using file \"%s\"\n", cp);
#endif /* DEBUG */
fd = open (cp, 0);
}
else {
if (term == (char *) 0 || strcmp (name, term) == 0) {
strcpy (bp, cp);
return (tc_check ());
}
}
}
if (fd == 0) {
#ifdef DEBUG
fprintf (stderr, "using file \"%s\"\n", TERMCAP);
#endif /* DEBUG */
fd = open (TERMCAP, 0);
}
#ifdef vaxc
if (term && !strcmp (term, name) && getenv("TERM"))
term = name = getenv("TERM");
#endif /* vaxc */
if (fd < 0) {
char **tcpp = tcp;
#ifdef DEBUG
perror ("termcap");
fprintf (stderr, "using built-ins\n");
#endif /* DEBUG */
while (*tcpp) {
strcpy (data, *tcpp);
if (lku_name (name))
return (tc_check ());
else
tcpp++;
}
*data = EOS;
return (-1);
}
for (;;) {
cp = bp;
for (;;) {
if (i == cnt) {
cnt = read (fd, ibuf, BUFSIZ);
if (cnt <= 0) {
close (fd);
#ifdef DEBUG
perror ("termcap");
#endif /* DEBUG */
return (0);
}
i = 0;
while ((c = ibuf[i]) == ' ' || c == '\t')
i++;
}
c = ibuf[i++];
if (c == '\n') {
if (cp > bp && cp[-1] == '\\'){
cp--;
continue;
}
break;
}
if (cp >= bp+BUFSIZ) {
wrerr ("termcap entry too long\n");
break;
}
else
*cp++ = c;
}
*cp = EOS;
if (lku_name (name)) {
close (fd);
return (tc_check ());
}
}
}
/* tc_check: check the last entry for "tc=xyz". If so, find xyz and append it.
*/
static int
tc_check ()
{
register char *p, *q;
char tcname[16]; /* name of similar terminal */
char tcbuf[BUFSIZ];
char *holddata = data;
int l;
p = data + strlen (data) - 2; /* before the last colon */
while (*--p != ':')
if (p<data) {
wrerr ("bad termcap entry\n");
return (0);
}
p++;
/* p now points to beginning of last field */
if (p[0] != 't' || p[1] != 'c')
return (1);
strcpy (tcname, p+3);
q = tcname;
while (q && *q != ':')
q++;
*q = EOS;
if (++tccount > MAX_TC) {
wrerr ("too much tc='s\n");
return (0);
}
if (tgetent (tcbuf, tcname) != 1)
return (0);
for (q = tcbuf; *q != ':'; q++)
;
l = p - holddata + strlen (q);
if (l > BUFSIZ) {
wrerr ("termcap entry too long\n");
q[BUFSIZ - (p-data)] = EOS;
}
strcpy (p, q+1);
data = holddata;
return (1);
}
/* The first field of the termcap entry is a sequence of names separated
* by |'s, so we compare against each such name. The : after the last name
* terminates the search.
*/
static int
lku_name (namep)
char *namep;
{
char *cp1;
char *cp2 = data;
#ifdef DEBUG
fprintf (stderr, "searching \"%s\"\n", namep);
#endif /* DEBUG */
if (*cp2 == '#')
return (0);
for (;;) {
for (cp1 = namep; *cp1 && *cp2 == *cp1; cp2++, cp1++)
continue;
if (*cp1 == EOS && (*cp2 == '|' || *cp2 == ':' || *cp2 == EOS))
return (1);
while (*cp2 && *cp2 != ':' && *cp2 != '|')
cp2++;
if (*cp2 == EOS || *cp2 == ':')
return (0);
cp2++;
}
}
static advance (ptr)
char **ptr;
{
while (**ptr) {
if (**ptr == '\\') (*ptr) += 2;
else
if (**ptr == ':') {
(*ptr)++;
return (1);
}
else
(*ptr)++;
}
return (0);
}
tgetnum (id)
char *id;
{
char *cp = data;
char c = id[0];
int len = strlen (id);
while (advance (&cp))
if (*cp == c && !strncmp (id, cp, len))
if (cp[len] == '@')
break;
else
if (cp[len] == '#')
return (atoi (cp+len+1));
return (-1);
}
tgetflag (id)
char *id;
{
char *cp = data;
char c = id[0];
int len = strlen (id);
while (advance (&cp))
if (*cp == c && !strncmp (id, cp, len))
if (cp[len] == '@')
break;
else
return (1);
return (0);
}
char *tgetstr (id, area)
char *id;
char **area;
{
char *cp = data;
char c = id[0];
int len = strlen (id);
while (advance (&cp))
if (*cp == c && !strncmp (id, cp, len))
if (cp[len] == '@')
return (0);
else
if (cp[len] == '=') {
char *here = *area;
cp += len+1;
while (*cp) {
if ((c = *cp++) == '\\' && *cp) {
if ((c = *cp++) == 'E') c = '\033';
else
if (c == 'b') c = '\b';
else
if (c == 'n') c = '\n';
else
if (c == 'r') c = '\r';
else
if (c == 't') c = '\t';
else
if (c == 'f') c = '\f';
else
if (isdigit (c)) {
c = c - '0';
if (isdigit (*cp)) {
c = (c << 3) + (*cp++) - '0';
if (isdigit (*cp)) {
c = (c << 3) + (*cp++) - '0';
}
}
}
else
;
}
else
if (c == '^' && *cp) {
c = (*cp++) & 037;
}
else
if (c == ':')
break;
*(*area)++ = c;
}
*(*area)++ = EOS;
return (here);
}
return (0);
}
/*-**-* tgoto.c */
char *BC;
char *UP;
char *tgoto (cm, next, first)
char *cm;
int first;
int next;
{
static char buf [128];
static char oops[] = "OOPS";
char *cp = buf;
register c;
if (!cm || !*cm)
return (oops);
while (*cm) {
if (*cm == '%') {
cm++;
if ((c = *cm++) == 'd') {
cp += sprintf (cp, "%d", first);
first = next;
}
else
if (c == '2') {
cp += sprintf (cp, "%02d", first);
first = next;
}
else
if (c == '3') {
cp += sprintf (cp, "%03d", first);
first = next;
}
else
if (c == '.') {
*cp++ = first;
first = next;
}
else
if (c == '+')
if (*cm) {
first += *cm++;
*cp++ = first;
first = next;
}
else
return (oops);
else
if (c == '>')
if (*cm && *(cm+1)) {
if (first > *cm++)
first += *cm++;
}
else
return (oops);
else
if (c == 'B') {
first = 16*(first/10) + (first%10);
}
else
if (c == 'D') {
first = first - 2*(first%16);
}
else
if (c == 'r') {
c = next;
next = first;
first = c;
}
else
if (c == 'i') {
next++;
first++;
}
else
if (c == '%') {
*cp++ = '%';
}
else
if (c == 'n') {
next ^= 0140;
first ^= 0140;
}
else
return (oops);
}
else
*cp++ = *cm++;
}
*cp = EOS;
return (buf);
}
/*-**-* tputs.c */
char PC;
short ospeed;
tputs (cp, affcnt, outc)
char *cp;
int affcnt;
int (*outc)();
{
if (!cp)
return;
while (*cp >= '0' && *cp <= '9')
cp++;
while (*cp)
/* On VMS sending a linefeed will be interpreted as sending <CR> and <LF> */
/* setting high-order bit will prevent this */
/* All other characters will have high-order bit stripped */
#if vms
if (*cp == '\n')
(*outc)((*cp++) | 0x80);
else
#endif
(*outc)((*cp++) & 0x7f);
}
/*-**-* test.c */
#ifdef DEBUG
#include <stdio.h>
static char buf [1024];
oc (c)
char c;
{
putchar (c);
}
putcap (c)
char *c;
{
tputs (c, 0, oc);
fflush (stdout);
}
main ()
{
static char bb[1024];
char *cp = bb;
char *getenv ();
tgetent (buf, getenv ("TERM"));
fprintf (stderr, "entry = \"%s\"\n", buf);
}
#endif /* DEBUG */
--
Johan Vromans | jv@mh.nl via European backbone
Multihouse N.V., Gouda, the Netherlands | uucp: ..{?????!}mcvax!mh.nl!jv
"It is better to light a candle than to curse the darkness"