From b4f3a16892fb24bd538953076553d2a846afe87e Mon Sep 17 00:00:00 2001 From: jwalz Date: Sat, 5 Jan 2002 21:05:58 +0000 Subject: [PATCH] *** empty log message *** --- sys/share/tclib.c | 482 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 482 insertions(+) create mode 100644 sys/share/tclib.c diff --git a/sys/share/tclib.c b/sys/share/tclib.c new file mode 100644 index 000000000..4cd1a3963 --- /dev/null +++ b/sys/share/tclib.c @@ -0,0 +1,482 @@ +/* SCCS Id: @(#)tclib.c 3.3 96/02/25 */ +/* Copyright (c) Robert Patrick Rankin, 1995 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* termcap library implementation */ + +#include "config.h" + +#ifndef TERMCAP /* name of default termcap file */ +#define TERMCAP "/etc/termcap" +#endif +#ifndef TCBUFSIZ /* size of tgetent buffer; Unix man page says 1024 */ +#define TCBUFSIZ 1024 +#endif +#define ESC '\033' /* termcap's '\E' */ +#define BEL '\007' /* ANSI C's '\a' (we assume ASCII here...) */ + +/* exported variables, as per man page */ +char PC; +char *BC, *UP; +short ospeed; + +/* exported routines */ +int FDECL(tgetent, (char *,const char *)); +int FDECL(tgetflag, (const char *)); +int FDECL(tgetnum, (const char *)); +char *FDECL(tgetstr, (const char *,char **)); +char *FDECL(tgoto, (const char *,int,int)); +char *FDECL(tparam, (const char *,char *,int,int,int,int,int)); +void FDECL(tputs, (const char *,int,int (*)())); + +/* local support data */ +static char *tc_entry; +static char bc_up_buf[24]; +#ifndef NO_DELAY_PADDING +/* `ospeed' to baud rate conversion table, adapted from GNU termcap-1.2 */ +static short baud_rates[] = { + 0, 50, 75, 110, 135, 150, +# ifdef VMS + 300, 600, 1200, 1800, 2000, 2400, 3600, 4800, 7200, +# else /* assume Unix */ + 200, 300, 600, 1200, 1800, 2400, 4800, +# endif + 9600, -192, -384, /* negative is used as `100 * abs(entry)' */ +# ifdef VMS + -576, -768, -1152, +# endif +}; +#endif /* !NO_DELAY_PADDING */ + +/* local support code */ +static int FDECL(tc_store, (const char *,const char *)); +static char *FDECL(tc_find, (FILE *,const char *,char *,int)); +static char *FDECL(tc_name, (const char *,char *)); +static const char *FDECL(tc_field, (const char *,const char **)); + +#ifndef min +#define min(a,b) ((a)<(b)?(a):(b)) +#endif + +/* retrieve the specified terminal entry and return it in `entbuf' */ +int +tgetent(entbuf, term) + char *entbuf; /* size must be at least [TCBUFSIZ] */ + const char *term; +{ + int result; + FILE *fp; + char *tc = getenv("TERMCAP"); + + tc_entry = entbuf; + if (!entbuf || !term) + return -1; + /* if ${TERMCAP} is found as a file, it's not an inline termcap entry */ + if ((fp = fopen(tc ? tc : TERMCAP, "r")) != 0) + tc = 0; + /* if ${TERMCAP} isn't a file and `term' matches ${TERM}, use ${TERMCAP} */ + if (tc) { + char *tm = getenv("TERM"); + if (tm && strcmp(tm, term) == 0) + return tc_store(term, tc); + fp = fopen(TERMCAP, "r"); + } + /* otherwise, look `term' up in the file */ + if (fp) { + char wrkbuf[TCBUFSIZ]; + tc = tc_find(fp, term, wrkbuf, (int)(sizeof wrkbuf - strlen(term))); + result = tc_store(term, tc); + (void) fclose(fp); + } else { + result = -1; + } + return result; +} + +/* copy the entry into the output buffer */ +static int +tc_store(trm, ent) + const char *trm, *ent; +{ + const char *bar, *col; + char *s; + size_t n; + int k; + + if (!ent || !*ent || !trm || !*trm || (col = index(ent, ':')) == 0) + return 0; + (void) strcpy(tc_entry, trm); + if (((bar = index(ent, '|')) != 0 && bar < col) + || ((long)(n = strlen(trm)) == (long)(col - ent) + && strncmp(ent, trm, n) == 0)) + (void) strcat(tc_entry, col); + else if (*ent == ':') + (void) strcat(tc_entry, ent); + else + (void) strcat(strcat(tc_entry, ":"), ent); + + /* initialize global variables */ + k = tgetnum("pc"); + PC = (k == -1) ? '\0' : (char)k; + BC = s = bc_up_buf; + if (!tgetstr("bc", &s)) (void)strcpy(s, "\b"), s += 2; + UP = s; + (void)tgetstr("up", &s); +#ifndef NO_DELAY_PADDING + /* caller must set `ospeed' */ + if ((int)ospeed >= (int)SIZE(baud_rates)) + ospeed = (short)(SIZE(baud_rates) - 1); + else if (ospeed < 0) + ospeed = 0; +#endif /* !NO_DELAY_PADDING */ + + return 1; +} + +/* search for an entry in the termcap file */ +static char * +tc_find(fp, term, buffer, bufsiz) + FILE *fp; + const char *term; + char *buffer; + int bufsiz; +{ + int in, len, first, skip; + char *ip, *op, *tc_fetch, tcbuf[TCBUFSIZ]; + + buffer[0] = '\0'; + do { + ip = tcbuf, in = min(bufsiz,TCBUFSIZ); + first = 1, skip = 0; + /* load entire next entry, including any continuations */ + do { + if (!fgets(ip, min(in,BUFSIZ), fp)) break; + if (first) skip = (*ip == '#'), first = 0; + len = (int)strlen(ip); + if (!skip && len > 1 + && *(ip + len - 1) == '\n' && *(ip + len - 2) == '\\') + len -= 2; + ip += len, in -= len; + } while (*(ip - 1) != '\n' && in > 0); + if (ferror(fp) || ip == buffer || *(ip - 1) != '\n') + return (char *)0; + *--ip = '\0'; /* strip newline */ + if (!skip) ip = tc_name(term, tcbuf); + } while (skip || !ip); + + /* we have the desired entry; strip cruft and look for :tc=other: */ + tc_fetch = 0; + for (op = buffer; *ip; ip++) { + if (op == buffer || *(op - 1) != ':' + || (*ip != ' ' && *ip != '\t' && *ip != ':')) + *op++ = *ip, bufsiz -= 1; + if (ip[0] == ':' && ip[1] == 't' && ip[2] == 'c' && ip[3] == '=') { + tc_fetch = &ip[4]; + if ((ip = index(tc_fetch, ':')) != 0) *ip = '\0'; + break; + } + } + *op = '\0'; + + if (tc_fetch) { + rewind(fp); + tc_fetch = tc_find(fp, tc_fetch, tcbuf, min(bufsiz,TCBUFSIZ)); + if (!tc_fetch) + return (char *)0; + if (op > buffer && *(op - 1) == ':' && *tc_fetch == ':') + ++tc_fetch; + strcpy(op, tc_fetch); + } + return buffer; +} + +/* check whether `ent' contains `nam'; return start of field entries */ +static char * +tc_name(nam, ent) + const char *nam; + char *ent; +{ + char *nxt, *lst, *p = ent; + size_t n = strlen(nam); + + if ((lst = index(p, ':')) == 0) lst = p + strlen(p); + + while (p < lst) { + if ((nxt = index(p, '|')) == 0 || nxt > lst) nxt = lst; + if ((long)(nxt - p) == (long)n && strncmp(p, nam, n) == 0) + return lst; + p = nxt + 1; + } + return (char *)0; +} + +/* look up a numeric entry */ +int +tgetnum(which) + const char *which; +{ + const char *q, *p = tc_field(which, &q); + char numbuf[32]; + size_t n; + + if (!p || p[2] != '#') + return -1; + p += 3; + if ((n = (size_t)(q - p)) >= sizeof numbuf) + return -1; + (void) strncpy(numbuf, p, n); + numbuf[n] = '\0'; + return atoi(numbuf); +} + +/* look up a boolean entry */ +int +tgetflag(which) + const char *which; +{ + const char *p = tc_field(which, (const char **)0); + + return (!p || p[2] != ':') ? 0 : 1; +} + +/* look up a string entry; update `*outptr' */ +char * +tgetstr(which, outptr) + const char *which; + char **outptr; +{ + int n; + char c, *r, *result; + const char *q, *p = tc_field(which, &q); + + if (!p || p[2] != '=') + return (char *)0; + p += 3; + if ((q = index(p, ':')) == 0) q = p + strlen(p); + r = result = *outptr; + while (p < q) { + switch ((*r = *p++)) { + case '\\': + switch ((c = *p++)) { + case 'E': *r = ESC; break; + case 'a': *r = BEL; break; + case 'b': *r = '\b'; break; + case 'f': *r = '\f'; break; + case 'n': *r = '\n'; break; + case 'r': *r = '\r'; break; + case 't': *r = '\t'; break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + n = c - '0'; + if (*p >= '0' && *p <= '7') n = 8 * n + (*p++ - '0'); + if (*p >= '0' && *p <= '7') n = 8 * n + (*p++ - '0'); + *r = (char)n; + break; + /* case '^': case '\\': */ + default: *r = c; break; + } + break; + case '^': + *r = (*p++ & 037); + if (!*r) *r = (char)'\200'; + break; + default: + break; + } + ++r; + } + *r++ = '\0'; + *outptr = r; + return result; +} + +/* look for a particular field name */ +static const char * +tc_field(field, tc_end) + const char *field; + const char **tc_end; +{ + const char *end, *q, *p = tc_entry; + + end = p + strlen(p); + while (p < end) { + if ((p = index(p, ':')) == 0) + break; + ++p; + if (p[0] == field[0] && p[1] == field[1] + && (p[2] == ':' || p[2] == '=' || p[2] == '#' || p[2] == '@')) + break; + } + if (tc_end) { + if (p) { + if ((q = index(p + 2, ':')) == 0) q = end; + } else + q = 0; + *tc_end = q; + } + return p; +} + +static char cmbuf[64]; + +/* produce a string which will position the cursor at if output */ +char * +tgoto(cm, col, row) + const char *cm; + int col, row; +{ + return tparam(cm, cmbuf, (int)(sizeof cmbuf), row, col, 0, 0); +} + +/* format a parameterized string, ala sprintf */ +char * +tparam(ctl, buf, buflen, row, col, row2, col2) + const char *ctl; /* parameter control string */ + char *buf; /* output buffer */ + int buflen; /* ought to have been `size_t'... */ + int row, col, row2, col2; +{ + int atmp, ac, av[5]; + char c, *r, *z, *bufend, numbuf[32]; + const char *fmt; +#ifndef NO_SPECIAL_CHARS_FIXUP + int bc = 0, up = 0; +#endif + + av[0] = row, av[1] = col, av[2] = row2, av[3] = col2, av[4] = 0; + ac = 0; + r = buf, bufend = r + buflen - 1; + while (*ctl) { + if ((*r = *ctl++) == '%') { + if (ac > 4) ac = 4; + fmt = 0; + switch ((c = *ctl++)) { + case '%': break; /* '%' already copied */ + case 'd': fmt = "%d"; break; + case '2': fmt = "%02d"; break; + case '3': fmt = "%03d"; break; + case '+': /*FALLTHRU*/ + case '.': *r = (char)av[ac++]; + if (c == '+') *r += *ctl++; + if (!*r) { + *r = (char)'\200'; + } else { +#ifndef NO_SPECIAL_CHARS_FIXUP + /* avoid terminal driver intervention for + various control characters, to prevent + LF from becoming CR+LF, for instance; only + makes sense if this is a cursor positioning + sequence, but we have no way to check that */ + while (index("\004\t\n\013\f\r", *r)) { + if (ac & 1) { /* row */ + if (!UP || !*UP) break; /* can't fix */ + ++up; /* incr row now, later move up */ + } else { /* column */ + if (!BC || !*BC) break; /* can't fix */ + ++bc; /* incr column, later backspace */ + } + (*r)++; + } +#endif /* !NO_SPECIAL_CHARS_FIXUP */ + } break; + case '>': if (av[ac] > (*ctl++ & 0377)) + av[ac] += *ctl; + ++ctl; break; + case 'r': atmp = av[0]; av[0] = av[1]; av[1] = atmp; + atmp = av[2]; av[2] = av[3]; av[3] = atmp; + --r; break; + case 'i': ++av[0]; ++av[1]; ++av[2]; ++av[3]; + --r; break; + case 'n': av[0] ^= 0140; av[1] ^= 0140; + av[2] ^= 0140; av[3] ^= 0140; + --r; break; + case 'B': av[0] = ((av[0] / 10) << 4) + (av[0] % 10); + av[1] = ((av[1] / 10) << 4) + (av[1] % 10); + av[2] = ((av[2] / 10) << 4) + (av[2] % 10); + av[3] = ((av[3] / 10) << 4) + (av[3] % 10); + --r; break; + case 'D': av[0] -= (av[0] & 15) << 1; + av[1] -= (av[1] & 15) << 1; + av[2] -= (av[2] & 15) << 1; + av[3] -= (av[3] & 15) << 1; + --r; break; + default: *++r = c; break; /* erroneous entry... */ + } + if (fmt) { + (void) sprintf(numbuf, fmt, av[ac++]); + for (z = numbuf; *z && r <= bufend; z++) + *r++ = *z; + --r; /* will be re-incremented below */ + } + } + if (++r > bufend) + return (char *)0; + } +#ifndef NO_SPECIAL_CHARS_FIXUP + if (bc || up) { + while (--bc >= 0) + for (z = BC; *z && r <= bufend; z++) + *r++ = *z; + while (--up >= 0) + for (z = UP; *z && r <= bufend; z++) + *r++ = *z; + if (r > bufend) + return (char *)0; + } +#endif /* !NO_SPECIAL_CHARS_FIXUP */ + *r = '\0'; + return buf; +} + +/* send a string to the terminal, possibly padded with trailing NULs */ +void +tputs( string, range, output_func ) +const char *string; /* characters to output */ +int range; /* number of lines affected, used for `*' delays */ +int (*output_func)(); /* actual output routine; return value ignored */ +{ + register int c, num = 0; + register const char *p = string; + + if (!p || !*p) + return; + + /* pick out padding prefix, if any */ + if (*p >= '0' && *p <= '9') { + do { /* note: scale `num' by 10 to accommodate fraction */ + num += (*p++ - '0'), num *= 10; + } while (*p >= '0' && *p <= '9'); + if (*p == '.') + ++p, num += (*p >= '0' && *p <= '9') ? (*p++ - '0') : 0; + if (*p == '*') + ++p, num *= range; + } + + /* output the string */ + while ((c = *p++) != '\0') { + if (c == '\200') c = '\0'; /* undo tgetstr's encoding */ + (void) (*output_func)(c); + } + +#ifndef NO_DELAY_PADDING + /* perform padding */ + if (num) { + long pad; + + /* figure out how many chars needed to produce desired elapsed time */ + pad = (long)baud_rates[ospeed]; + if (pad < 0) pad *= -100L; + pad *= (long)num; + /* 100000 == 10 bits/char * (1000 millisec/sec scaled by 10) */ + num = (int)(pad / 100000L); /* number of characters */ + + c = PC; /* assume output_func isn't allowed to change PC */ + while (--num >= 0) + (void) (*output_func)(c); + } +#endif /* !NO_DELAY_PADDING */ + + return; +} + +/*tclib.c*/