peter@sugar.hackercorp.com (Peter da Silva) (09/04/90)
Archive-name: termlib The following implementation of termlib is hereby placed in the public domain. It's not fancy, it doesn't inlcude terminfo, but it's small and clean and is useful for porting termlib-based programs like Elvis to other platforms. I originally wrote it for CP/M under the BDS C compiler so it makes few assumptions about the environment. The included makefile is for the Amiga, but I think it unlikely anyone would have much trouble porting it to other machines. The implementation of tgoto is probably out of date, but if you really need more it'd be easy enough to expand. The port of elvis itself to the Amiga is underway. : This archive contains the following files... : 'termlib.h' : 'tinit.c' : 'tgetnum.c' : 'tutil.c' : 'tvars.c' : 'tgoto.c' : 'tgetent.c' : 'tputs.c' : 'cur.c' : 'termcap.c' : 'tgetflag.c' : 'tgetstr.c' : 'Makefile' : To extract them, run the following through /bin/sh echo x - termlib.h sed 's/^X//' > termlib.h << '//END' X/* TERMLIB: Terminal independant database. X * X * Module: termlib.h X * X * Purpose: declare global variables and functions. X */ X X/* termlib.h X * Global variables for termlib X * X*/ X#ifndef AMIGA X#define AMIGA 0 X#endif X Xextern char *tent; /* Pointer to terminal entry, set by tgetent */ Xextern char PC; /* Pad character, default NULL */ Xextern char *UP, *BC; /* Pointers to UP and BC strings from database */ Xextern short ospeed; /* Baud rate (1-16, 1=300, 16=19200), as in stty */ X Xint tgetnum(); Xchar *tgoto(); Xint tgetent(); Xint tgetflag(); Xchar *tgetstr(); X Xchar *_find(); Xchar *_addfmt(); //END echo x - tinit.c sed 's/^X//' > tinit.c << '//END' X/* TERMLIB: Terminal independant database. X * X * Module: tinit X * X * Purpose: simplified terminal initialisation. X * X * Calling conventions: name is name of terminal. X * X * Returned values: none. X * X * Notes X * tinit calls tgetent, then sets up the global X * variables PC, UP, BC, ospeed appropriately. X * X */ X#include "termlib.h" X#include <stdio.h> X#if !AMIGA X#include <sgtty.h> X#endif X X/* tinit.c (libtermlib.a) X * X */ X Xchar tbuf[1024]; /* Buffer for termcap entry */ Xchar junkbuf[1024]; /* Big buffer for junk */ Xchar *junkptr; X Xtinit(name) Xchar *name; X{ X#if !AMIGA X struct sgttyb sgbuf; X#endif X char *ps; X X junkptr = junkbuf; X X tgetent(tbuf, name); X X ps = tgetstr("pc", &junkptr); X if(ps) PC = *ps; X UP = tgetstr("up", &junkptr); X BC = tgetstr("bc", &junkptr); X X#if AMIGA X ospeed=0; X#else X gtty(1, &sgbuf); X ospeed=sgbuf.sg_ospeed; X#endif X} //END echo x - tgetnum.c sed 's/^X//' > tgetnum.c << '//END' X/* TERMLIB: Terminal independant database. X * X * Module: tgetnum X * X * Purpose: get numeric value such as 'li' or 'co' from termcap. X * X * Calling conventions: id = 2 character id. X * X * Returned values: -1 for failure, else numerical value. X */ X#include <stdio.h> X#include <ctype.h> X#include "termlib.h" X X/* tgetnum.c (libtermlib.a) X * X */ X Xtgetnum(id) Xchar *id; X{ X char *ptr, buf[256]; X ptr = buf; X X if(tgetstr(id, &ptr)) X return atoi(buf); X else X return 0; X} //END echo x - tutil.c sed 's/^X//' > tutil.c << '//END' X/* TERMLIB: Terminal independant database. X * X * Module: tutil.c X * X * Purpose: Utility routines for TERMLIB functions. X * X */ X X/* tutil.c (libtermlib.a) X * Utility routines for termlib X * X */ X X_match(s1, s2) /* returns length of text common to s1 and s2 */ Xchar *s1, *s2; X{ X int i = 0; X X while(s1[i] && s1[i] == s2[i]) X i++; X X return i; X} X Xchar * X_find(s, set) /* finds next c in s that's a member of set, returns pointer */ Xchar *s, *set; X{ X for(; *s; s++) { X char *ptr = set; X X while(*ptr && *s != *ptr) X ptr++; X X if(*ptr) X return s; X } X X return s; X} X Xchar * X_addfmt(buf, fmt, val) /* add val to buf according to format fmt */ Xchar *buf, *fmt; Xint val; X{ X sprintf(buf, fmt, val); X while(*buf) X buf++; X return buf; X} //END echo x - tvars.c sed 's/^X//' > tvars.c << '//END' X/* TERMLIB: Terminal independant database. X * X * Module: tvars X * X * Purpose: supply actual global variables. X */ X X/* tvars.c (libtermlib.a) X * Global variables for termlib X * X*/ X Xchar *tent; /* Pointer to terminal entry, set by tgetent */ Xchar PC = 0; /* Pad character, default NULL */ Xchar *UP = 0, *BC = 0; /* Pointers to UP and BC strings from database */ Xshort ospeed; /* Baud rate (1-16, 1=300, 16=19200), as in stty */ //END echo x - tgoto.c sed 's/^X//' > tgoto.c << '//END' X/* TERMLIB: Terminal independant database. X * X * Module: tgoto X * X * Purpose: decode cm cursor motion string. X * X * Calling conventions: cm is cursor motion string. X * line, col, are the desired destination. X * X * Returned values: a string pointing to the decoded string, or X * "OOPS" if it cannot be decoded. X * X * Notes X * The accepted escapes are: X * %d as in printf, 0 origin. X * %2, %3 like %02d, %03d in printf. X * %. like %c X * %+x adds <x> to value, then %. X * %>xy if value>x, adds y. No output. X * %i increments line& col, no output. X * %r reverses order of line&col. No output. X * %% prints as a single %. X * %n exclusive or row & col with 0140. X * %B BCD, no output. X * %D reverse coding (x-2*(x%16)), no output. X */ X#include <stdio.h> X#include <ctype.h> X#include "termlib.h" X X/* tgoto.c (libtermlib.a) X * X */ X Xchar * Xtgoto(cm, col, line) Xchar *cm; /* cm string, from termcap */ Xint col, /* column, x position */ X line; /* line, y position */ X{ X char *_addfmt(), X gx, gy, /* x, y */ X *ptr, /* pointer in 'cm' */ X reverse = 0, /* reverse flag */ X *bufp, /* pointer in returned string */ X addup = 0, /* add upline */ X addbak = 0, /* add backup */ X c; X static char buffer[32]; X X if(!cm) X return "OOPS"; /* Kludge, but standard */ X X bufp = buffer; X ptr = cm; X X while(*ptr) { X if((c = *ptr++) != '%') { /* normal char */ X *bufp++ = c; X } else { /* % escape */ X switch(c = *ptr++) { X case 'd': /* decimal */ X bufp = _addfmt(bufp, "%d", line); X line = col; X break; X case '2': /* 2 digit decimal */ X bufp = _addfmt(bufp, "%02d", line); X line = col; X break; X case '3': /* 3 digit decimal */ X bufp = _addfmt(bufp, "%03d", line); X line = col; X break; X case '>': /* %>xy: if >x, add y */ X gx = *ptr++; X gy = *ptr++; X if(col>gx) col += gy; X if(line>gx) line += gy; X break; X case '+': /* %+c: add c */ X line += *ptr++; X case '.': /* print x/y */ X if(line=='\t' || /* these are */ X line == '\n' || /* chars that */ X line=='\004' || /* UNIX hates */ X line=='\0') { X line++; /* so go to next pos */ X if(reverse==(line==col)) X addup=1; /* and mark UP */ X else X addbak=1; /* or BC */ X } X *bufp++=line; X line = col; X break; X case 'r': /* r: reverse */ X gx = line; X line = col; X col = gx; X reverse = 1; X break; X case 'i': /* increment (1-origin screen) */ X col++; X line++; X break; X case '%': /* %%=% literally */ X *bufp++='%'; X break; X case 'n': /* magic DM2500 code */ X line ^= 0140; X col ^= 0140; X break; X case 'B': /* bcd encoding */ X line = line/10<<4+line%10; X col = col/10<<4+col%10; X break; X case 'D': /* magic Delta Data code */ X line = line-2*(line&15); X col = col-2*(col&15); X break; X default: /* Unknown escape */ X return "OOPS"; X } X } X } X X if(addup) /* add upline */ X if(UP) { X ptr=UP; X while(isdigit(*ptr) || *ptr=='.') X ptr++; X if(*ptr=='*') X ptr++; X while(*ptr) X *bufp++ = *ptr++; X } X X if(addbak) /* add backspace */ X if(BC) { X ptr=BC; X while(isdigit(*ptr) || *ptr=='.') X ptr++; X if(*ptr=='*') X ptr++; X while(*ptr) X *bufp++ = *ptr++; X } X else X *bufp++='\b'; X X *bufp = 0; X X return(buffer); X} //END echo x - tgetent.c sed 's/^X//' > tgetent.c << '//END' X/* TERMLIB: Terminal independant database. X * X * Module: tgetent X * X * Purpose: Get termcap entry for <term> into buffer at <tbuf>. X * X * Calling conventions: char tbuf[1024+], term=canonical name for X * terminal. X * X * Returned values: 1 = success, -1 = can't open file, X * 0 = can't find terminal. X * X * Notes X * Should probably supply static buffer. X * X * Uses environment variables "TERM" and X * "TERMCAP". If TERM = term (that is, if the argument X * matches the environment) then it looks at TERMCAP. X * If TERMCAP begins with a slash, then it assumes X * this is the file to search rather than /etc/termcap. X * If TERMCAP does not begin with a slash, and it X * matches TERM, then this is used as the entry. X * X * This could be simplified considerably for non-UNIX X * systems. X */ X#include <stdio.h> X#include <ctype.h> X#include "termlib.h" X X/* tgetent.c (libtermlib.a) X * X */ X#if AMIGA X#define TERMCAP "s:termcap" X#else X#define TERMCAP "/etc/termcap" X#endif X Xtgetent(tbuf, term) Xchar *tbuf, /* Buffer to hold termcap entry, 1024 bytes max */ X *term; /* Name of terminal */ X{ X char tcbuf[32], /* Temp buffer to handle */ X *tc, /* :tc=: entry for */ X *tcptr = tcbuf; /* extended entries */ X char *tcap = TERMCAP, /* Default termcap file */ X *getenv(); X char *tmp; X FILE *termcap; X X if((tmp=getenv("TERMCAP")) != NULL) { X if(*tmp == '/') /* TERMCAP = name of termcap file */ X tcap = tmp ; X else { /* TERMCAP = termcap entry itself */ X int tlen = strlen(term); X while(*tmp && *tmp != ':') {/* Check if TERM matches */ X while(*tmp == '|') X tmp++; X if(_match(tmp, term)==tlen) { X strcpy(tbuf, tmp); X tent=tbuf; X return 1; X } X else X tmp = _find(tmp, ":|"); X } X } X } X if(!(termcap=fopen(tcap, "r"))) { X strcpy(tbuf, tcap); X return -1; X } X X if(getent(tbuf, term, termcap)) { X if(tc=tgetstr("tc", &tcptr)) { /* extended entry */ X rewind(termcap); X if(getent(tbuf+strlen(tbuf), tc, termcap)) { X fclose(termcap); /* Completed */ X return 1; X } X else { X fclose(termcap); /* Incomplete */ X return 0; X } X } else { X fclose(termcap); /* non-extended entry */ X return 1; X } X } else { X fclose(termcap); /* No entry */ X return 0; X } X} X Xgetent(tbuf, term, termcap) Xchar *tbuf, *term; XFILE *termcap; X{ X char *tptr; X int tlen = strlen(term); X X while(nextent(tbuf, termcap)) { /* For each possible entry */ X tptr = tbuf; X while(*tptr && *tptr != ':') { /* : terminates name field */ X while(*tptr == '|') /* | seperates names */ X tptr++; X if(_match(tptr, term)==tlen) { /* FOUND! */ X fclose(termcap); X tent=tbuf; X return 1; X } X else /* Look for next name */ X tptr = _find(tptr, ":|"); X } X } X X return 0; X} X Xnextent(tbuf, termcap) /* Read 1 entry from TERMCAP file */ Xchar *tbuf; XFILE *termcap; X{ X char *lbuf = /* lbuf=line buffer */ X tbuf; /* read lines straight into buffer */ X X while(lbuf < tbuf+1024 && /* There's room and */ X fgets(lbuf, tbuf+1024-lbuf, termcap)) { /* another line */ X int llen = strlen(lbuf); X X if(*lbuf=='#') /* eat comments */ X continue; X if(lbuf[-1]==':' && /* and whitespace */ X lbuf[0]=='\t' && X lbuf[1]==':') { X strcpy(lbuf, lbuf+2); X llen -= 2; X } X if(lbuf[llen-2]=='\\') /* and continuations */ X lbuf += llen-2; X else { X lbuf[llen-1]=0; /* no continuation, return */ X return 1; X } X } X X return 0; /* ran into end of file */ X} //END echo x - tputs.c sed 's/^X//' > tputs.c << '//END' X/* TERMLIB: Terminal independant database. X * X * Module: tputs X * X * Purpose: decode padding information X * X * Calling conventions: cp = string to be padded, affcnt = # of items X * affected (lines, characters, whatever), X * outc = routine to output 1 character. X * X * Returned values: none X * X * Notes X * cp has padding information ahead of it, in the form X * nnnTEXT or nnn*TEXT. nnn is the number of milliseconds to delay, X * and may be a decimal (nnn.mmm). If the asterisk is given, then X * the delay is multiplied by afcnt. The delay is produced by outputting X * a number of nulls (or other padding char) after printing the X * TEXT. X * X */ X#include <stdio.h> X#include <ctype.h> X#include "termlib.h" X X/* tputs.c (libtermlib.a) X * X */ X Xlong _bauds[16]={ X 0, 50, 75, 110, X 134, 150, 200, 300, X 600, 1200, 1800, 2400, X 4800, 9600, 19200, 19200 }; X Xtputs(cp, affcnt, outc) Xchar *cp; /* string to print */ Xint affcnt; /* Number of lines affected */ Xint (*outc)(); /* routine to output 1 character */ X{ X long frac, /* 10^(#digits after decimal point) */ X counter, /* digits */ X atol(); X X if(isdigit(*cp)) { X counter = 0; X frac = 1000; X while(isdigit(*cp)) X counter = counter * 10L + (long)(*cp++ - '0'); X if(*cp=='.') X while(isdigit(*++cp)) { X counter = counter * 10L + (long)(*cp++ - '0'); X frac = frac * 10; X } X if(*cp!='*') { /* multiply by affected lines */ X if(affcnt>1) affcnt = 1; X } X else X cp++; X X /* Calculate number of characters for padding counter/frac ms delay */ X if(ospeed) X counter = (counter * _bauds[ospeed] * (long)affcnt) / frac; X X while(*cp) /* output string */ X (*outc)(*cp++); X if(ospeed) X while(counter--) /* followed by pad characters */ X (*outc)(PC); X } X else X while(*cp) X (*outc)(*cp++); X} //END echo x - cur.c sed 's/^X//' > cur.c << '//END' X/* Cur: Provide cursor addressing for shell scripts. X * X * Cur performs the same functions as echo, with 2 differences: X * 1. X * a. Arguments of the form -xx result in the generation of the capability X * string xx. The two special strings "cm" and "cs" are handled by X * cur ... -cm x y ... and ... -cs lo hi ... X * b. Arguments of the form #xx return the value of the numeric capability X * xx. X * c. Arguments of the form +terminal force cur to assume that as the X * terminal type. Any number of these can be included, and will be X * evaluated when encountered. X * 2. No newline is appended to the echoed string. X * X * Syntax: cur [string|-xx|#xx|+term]... X * X * Other notes: The code is obvious. X */ X#include <stdio.h> X#include "termlib.h" X Xextern char *junkptr; X Xmain(ac, av) Xint ac; char **av; X{ X int line, col, outch(); X char *val; X X tinit(getenv("TERM")); X X while(--ac) X if(**++av=='-') { X if(val=tgetstr(*av+1, &junkptr)) { X if(strcmp(*av+1, "cm") && X strcmp(*av+1, "cs")) X tputs(val, 1, outch); X else { X col = atoi(*++av); --ac; X line = atoi(*++av); --ac; X tputs(tgoto(val, col, line), 0, outch); X } X } X } else if(**av=='#') { X if(val = tgetstr(*av+1, &junkptr)) X fputs(val, stdout); X } else if(**av=='+') { X tinit(*av+1); X } else X fputs(*av, stdout); X} X Xoutch(c) char c; { putchar(c); } //END echo x - termcap.c sed 's/^X//' > termcap.c << '//END' X/* termcap... print current terminal capabilities. X * X * Termcap prints all the termcap capability strings for the terminal `term'. X * The output is in machine readable form, suitable for use in the construct: X * X * TERMCAP="`termcap`"; export TERMCAP X * X * Syntax: termcap [term] X */ X#include <stdio.h> X Xchar *tent; Xmain(ac, av) Xint ac; Xchar **av; X{ X char tbuf[1024]; X if(ac==1) if(tgetent(tbuf, getenv("TERM"))) { X puts(tbuf); X exit(0); X } else exit(-1); X if(tgetent(tbuf, av[1])) { X puts(tbuf); X exit(0); X } else exit(-1); X} //END echo x - tgetflag.c sed 's/^X//' > tgetflag.c << '//END' X/* The following software is (C) 1984 Peter da Silva, X the Mad Australian, in the public domain. It may X be re-distributed for any purpose with the inclusion X of this notice. */ X/* TERMLIB: Terminal independant database. X * X * Module: tgetflag X * X * Purpose: returns flag true or false as to the existence of a given X * entry. used with 'bs', 'am', etc... X * X * Calling conventions: id is the 2 character capability id. X * X * Returned values: 1 for success, 0 for failure. X */ X#include <stdio.h> X#include <ctype.h> X#include "termlib.h" X X/* tgetflag.c (libtermlib.a) X * XBUILD XOBJS=tinit.o tutil.o tvars.o \ X tgoto.o tputs.o tgetent.o tgetflag.o tgetnum.o tgetstr.o XCFLAGS=-O X Xlibtermlib.a: $(OBJS) termlib.h X ar cr libtermlib.a $(OBJS) X ranlib libtermlib.a X X$(OBJS):: termlib.h XEND X*/ X Xtgetflag(id) Xchar *id; X{ X char buf[256], *ptr = buf; X X return tgetstr(id, &ptr) ? 1 : 0; X} //END echo x - tgetstr.c sed 's/^X//' > tgetstr.c << '//END' X/* The following software is (C) 1984 Peter da Silva, X the Mad Australian, in the public domain. It may X be re-distributed for any purpose with the inclusion X of this notice. */ X/* TERMLIB: Terminal independant database. X * X * Module: tgetstr X * X * Purpose: get terminal capability string from database. X * X * Calling conventions: id is the two character capability id. X * (*buf) points into a hold buffer for the X * id. the capability is copied into the buffer X * and (*buf) is advanced to point to the next X * free byte in the buffer. X * X * Returned values: 0 = no such entry, otherwise returns original X * (*buf) (now a pointer to the string). X * X * Notes X * It also decodes certain escape sequences in the buffer. X * they should be obvious from the code: X * \E = escape. X * \n, \r, \t, \f, \b match the 'c' escapes. X * ^x matches control-x (^@...^_). X * \nnn matches nnn octal. X * \x, where x is anything else, matches x. I differ X * from the standard library here, in that I allow ^: to match X * :. X * X */ X#include <stdio.h> X#include <ctype.h> X#include "termlib.h" X X/* tgetstr.c (libtermlib.a) X * XBUILD XOBJS=tinit.o tutil.o tvars.o \ X tgoto.o tputs.o tgetent.o tgetflag.o tgetnum.o tgetstr.o XCFLAGS=-O X Xlibtermlib.a: $(OBJS) termlib.h X ar cr libtermlib.a $(OBJS) X ranlib libtermlib.a X X$(OBJS):: termlib.h XEND X*/ X Xchar * Xtgetstr(id, buf) Xchar *id, **buf; X{ X int len = strlen(id); X char *tmp=tent; X char *hold; X X do { X tmp = _find(tmp, ":"); /* For each field */ X while(*tmp==':') /* skip empty fields */ X tmp++; X if(!*tmp) X break; X X if(_match(id, tmp)==len) { X tmp += len; /* find '=' '@' or '#' */ X if(*tmp=='@') /* :xx@: entry for tc */ X return 0; /* deleted entry */ X hold= *buf; X while(*++tmp && *tmp != ':') {/* not at end of field */ X switch(*tmp) { X case '\\': /* Expand escapes here */ X switch(*++tmp) { X case 0: /* ignore backslashes */ X tmp--; /* at end of entry */ X break; /* shouldn't happen */ X case 'e': X case 'E': /* ESC */ X *(*buf)++ = '\033'; X break; X case 'n': /* \n */ X *(*buf)++ = '\n'; X break; X case 'r': /* \r */ X *(*buf)++ = '\r'; X break; X case 't': /* \t */ X *(*buf)++ = '\t'; X break; X case 'b': /* \b */ X *(*buf)++ = '\b'; X break; X case 'f': /* \f */ X *(*buf)++ = '\f'; X break; X case '0': /* \nnn */ X case '1': X case '2': X case '3': X case '4': X case '5': X case '6': X case '7': X case '8': X case '9': X **buf = 0; X while(isdigit(*tmp)) X **buf = **buf * 8 + *tmp++ - '0'; X (*buf)++; X tmp--; X break; X default: /* \x, for all other x */ X *(*buf)++= *tmp; X } X break; X case '^': /* control characters */ X *(*buf)++ = *++tmp - '@'; X break; X default: X *(*buf)++ = *tmp; X } X } X *(*buf)++ = 0; X return hold; X } X } while(*tmp); X X return 0; X} //END echo x - Makefile sed 's/^X//' > Makefile << '//END' X# X# The following module order is needed to create a properly sorted library X# for Manx X# X XOBJS=\ X tgetflag.o\ X tgetnum.o\ X tinit.o\ X tgoto.o\ X tputs.o\ X tvars.o\ X tgetent.o\ X tgetstr.o\ X tutil.o XSRC=\ X termlib.h\ X tinit.c\ X tgetnum.c\ X tutil.c\ X tvars.c\ X tgoto.c\ X tgetent.c\ X tputs.c\ X cur.c\ X termcap.c\ X tgetflag.c\ X tgetstr.c\ X Makefile X XCFLAGS= +P -B -DAMIGA=1 XLIB=termlibl32.lib X XLIBDIR=manx:lib XBINDIR=work:c X Xall: cur termcap X X$(LIB): $(OBJS) X lb $(LIB) $(OBJS) X Xtermcap: $(LIB) termcap.o X ln -o termcap termcap.o $(LIB) -lcl32 X Xcur: $(LIB) cur.o X ln -o cur cur.o $(LIB) -lcl32 X Xclean: X delete #?.o #?.lib termcap cur #?.bak X Xinstall: all X copy $(LIB) $(LIBDIR) X copy termcap $(BINDIR) X copy cur $(BINDIR) X Xtermlib.shar: $(SRC) X shar >termlib.shar $(SRC) //END : end of archive. exit 0 -- Peter da Silva. `-_-' <peter@sugar.hackercorp.com>.