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"