1 /* NetHack 3.6	tclib.c	$NHDT-Date: 1432512788 2015/05/25 00:13:08 $  $NHDT-Branch: master $:$NHDT-Revision: 1.8 $ */
2 /* Copyright (c) Robert Patrick Rankin, 1995			  */
3 /* NetHack may be freely redistributed.  See license for details. */
4 
5 /* termcap library implementation */
6 
7 #include "config.h"
8 
9 #ifndef TERMCAP /* name of default termcap file */
10 #define TERMCAP "/etc/termcap"
11 #endif
12 #ifndef TCBUFSIZ /* size of tgetent buffer; Unix man page says 1024 */
13 #define TCBUFSIZ 1024
14 #endif
15 #define ESC '\033' /* termcap's '\E' */
16 #define BEL '\007' /* ANSI C's '\a' (we assume ASCII here...) */
17 
18 /* exported variables, as per man page */
19 char PC;
20 char *BC, *UP;
21 short ospeed;
22 
23 /* exported routines */
24 int FDECL(tgetent, (char *, const char *));
25 int FDECL(tgetflag, (const char *));
26 int FDECL(tgetnum, (const char *));
27 char *FDECL(tgetstr, (const char *, char **));
28 char *FDECL(tgoto, (const char *, int, int));
29 char *FDECL(tparam, (const char *, char *, int, int, int, int, int));
30 void FDECL(tputs, (const char *, int, int (*)(int)));
31 
32 /* local support data */
33 static char *tc_entry;
34 static char bc_up_buf[24];
35 #ifndef NO_DELAY_PADDING
36 /* `ospeed' to baud rate conversion table, adapted from GNU termcap-1.2 */
37 static short baud_rates[] = {
38     0,    50,   75,    110,  135,  150,
39 #ifdef VMS
40     300,  600,  1200,  1800, 2000, 2400, 3600, 4800, 7200,
41 #else /* assume Unix */
42     200,  300,  600,  1200, 1800, 2400, 4800,
43 #endif
44     9600, -192, -384, /* negative is used as `100 * abs(entry)' */
45 #ifdef VMS
46     -576, -768, -1152,
47 #endif
48 };
49 #endif /* !NO_DELAY_PADDING */
50 
51 /* local support code */
52 static int FDECL(tc_store, (const char *, const char *));
53 static char *FDECL(tc_find, (FILE *, const char *, char *, int));
54 static char *FDECL(tc_name, (const char *, char *));
55 static const char *FDECL(tc_field, (const char *, const char **));
56 
57 #ifndef min
58 #define min(a, b) ((a) < (b) ? (a) : (b))
59 #endif
60 
61 /* retrieve the specified terminal entry and return it in `entbuf' */
62 int
tgetent(entbuf,term)63 tgetent(entbuf, term)
64 char *entbuf; /* size must be at least [TCBUFSIZ] */
65 const char *term;
66 {
67     int result;
68     FILE *fp;
69     char *tc = getenv("TERMCAP");
70 
71     tc_entry = entbuf;
72     if (!entbuf || !term)
73         return -1;
74     /* if ${TERMCAP} is found as a file, it's not an inline termcap entry */
75     if ((fp = fopen(tc ? tc : TERMCAP, "r")) != 0)
76         tc = 0;
77     /* if ${TERMCAP} isn't a file and `term' matches ${TERM}, use ${TERMCAP}
78      */
79     if (tc) {
80         char *tm = getenv("TERM");
81         if (tm && strcmp(tm, term) == 0)
82             return tc_store(term, tc);
83         fp = fopen(TERMCAP, "r");
84     }
85     /* otherwise, look `term' up in the file */
86     if (fp) {
87         char wrkbuf[TCBUFSIZ];
88         tc = tc_find(fp, term, wrkbuf, (int) (sizeof wrkbuf - strlen(term)));
89         result = tc_store(term, tc);
90         (void) fclose(fp);
91     } else {
92         result = -1;
93     }
94     return result;
95 }
96 
97 /* copy the entry into the output buffer */
98 static int
tc_store(trm,ent)99 tc_store(trm, ent)
100 const char *trm, *ent;
101 {
102     const char *bar, *col;
103     char *s;
104     size_t n;
105     int k;
106 
107     if (!ent || !*ent || !trm || !*trm || (col = index(ent, ':')) == 0)
108         return 0;
109     (void) strcpy(tc_entry, trm);
110     if (((bar = index(ent, '|')) != 0 && bar < col)
111         || ((long) (n = strlen(trm)) == (long) (col - ent)
112             && strncmp(ent, trm, n) == 0))
113         (void) strcat(tc_entry, col);
114     else if (*ent == ':')
115         (void) strcat(tc_entry, ent);
116     else
117         (void) strcat(strcat(tc_entry, ":"), ent);
118 
119     /* initialize global variables */
120     k = tgetnum("pc");
121     PC = (k == -1) ? '\0' : (char) k;
122     BC = s = bc_up_buf;
123     if (!tgetstr("bc", &s))
124         (void) strcpy(s, "\b"), s += 2;
125     UP = s;
126     (void) tgetstr("up", &s);
127 #ifndef NO_DELAY_PADDING
128     /* caller must set `ospeed' */
129     if ((int) ospeed >= (int) SIZE(baud_rates))
130         ospeed = (short) (SIZE(baud_rates) - 1);
131     else if (ospeed < 0)
132         ospeed = 0;
133 #endif /* !NO_DELAY_PADDING */
134 
135     return 1;
136 }
137 
138 /* search for an entry in the termcap file */
139 static char *
tc_find(fp,term,buffer,bufsiz)140 tc_find(fp, term, buffer, bufsiz)
141 FILE *fp;
142 const char *term;
143 char *buffer;
144 int bufsiz;
145 {
146     int in, len, first, skip;
147     char *ip, *op, *tc_fetch, tcbuf[TCBUFSIZ];
148 
149     buffer[0] = '\0';
150     do {
151         ip = tcbuf, in = min(bufsiz, TCBUFSIZ);
152         first = 1, skip = 0;
153         /* load entire next entry, including any continuations */
154         do {
155             if (!fgets(ip, min(in, BUFSIZ), fp))
156                 break;
157             if (first)
158                 skip = (*ip == '#'), first = 0;
159             len = (int) strlen(ip);
160             if (!skip && len > 1 && *(ip + len - 1) == '\n'
161                 && *(ip + len - 2) == '\\')
162                 len -= 2;
163             ip += len, in -= len;
164         } while (*(ip - 1) != '\n' && in > 0);
165         if (ferror(fp) || ip == buffer || *(ip - 1) != '\n')
166             return (char *) 0;
167         *--ip = '\0'; /* strip newline */
168         if (!skip)
169             ip = tc_name(term, tcbuf);
170     } while (skip || !ip);
171 
172     /* we have the desired entry; strip cruft and look for :tc=other: */
173     tc_fetch = 0;
174     for (op = buffer; *ip; ip++) {
175         if (op == buffer || *(op - 1) != ':'
176             || (*ip != ' ' && *ip != '\t' && *ip != ':'))
177             *op++ = *ip, bufsiz -= 1;
178         if (ip[0] == ':' && ip[1] == 't' && ip[2] == 'c' && ip[3] == '=') {
179             tc_fetch = &ip[4];
180             if ((ip = index(tc_fetch, ':')) != 0)
181                 *ip = '\0';
182             break;
183         }
184     }
185     *op = '\0';
186 
187     if (tc_fetch) {
188         rewind(fp);
189         tc_fetch = tc_find(fp, tc_fetch, tcbuf, min(bufsiz, TCBUFSIZ));
190         if (!tc_fetch)
191             return (char *) 0;
192         if (op > buffer && *(op - 1) == ':' && *tc_fetch == ':')
193             ++tc_fetch;
194         strcpy(op, tc_fetch);
195     }
196     return buffer;
197 }
198 
199 /* check whether `ent' contains `nam'; return start of field entries */
200 static char *
tc_name(nam,ent)201 tc_name(nam, ent)
202 const char *nam;
203 char *ent;
204 {
205     char *nxt, *lst, *p = ent;
206     size_t n = strlen(nam);
207 
208     if ((lst = index(p, ':')) == 0)
209         lst = p + strlen(p);
210 
211     while (p < lst) {
212         if ((nxt = index(p, '|')) == 0 || nxt > lst)
213             nxt = lst;
214         if ((long) (nxt - p) == (long) n && strncmp(p, nam, n) == 0)
215             return lst;
216         p = nxt + 1;
217     }
218     return (char *) 0;
219 }
220 
221 /* look up a numeric entry */
222 int
tgetnum(which)223 tgetnum(which)
224 const char *which;
225 {
226     const char *q, *p = tc_field(which, &q);
227     char numbuf[32];
228     size_t n;
229 
230     if (!p || p[2] != '#')
231         return -1;
232     p += 3;
233     if ((n = (size_t)(q - p)) >= sizeof numbuf)
234         return -1;
235     (void) strncpy(numbuf, p, n);
236     numbuf[n] = '\0';
237     return atoi(numbuf);
238 }
239 
240 /* look up a boolean entry */
241 int
tgetflag(which)242 tgetflag(which)
243 const char *which;
244 {
245     const char *p = tc_field(which, (const char **) 0);
246 
247     return (!p || p[2] != ':') ? 0 : 1;
248 }
249 
250 /* look up a string entry; update `*outptr' */
251 char *
tgetstr(which,outptr)252 tgetstr(which, outptr)
253 const char *which;
254 char **outptr;
255 {
256     int n;
257     char c, *r, *result;
258     const char *q, *p = tc_field(which, &q);
259 
260     if (!p || p[2] != '=')
261         return (char *) 0;
262     p += 3;
263     if ((q = index(p, ':')) == 0)
264         q = p + strlen(p);
265     r = result = *outptr;
266     while (p < q) {
267         switch ((*r = *p++)) {
268         case '\\':
269             switch ((c = *p++)) {
270             case 'E':
271                 *r = ESC;
272                 break;
273             case 'a':
274                 *r = BEL;
275                 break;
276             case 'b':
277                 *r = '\b';
278                 break;
279             case 'f':
280                 *r = '\f';
281                 break;
282             case 'n':
283                 *r = '\n';
284                 break;
285             case 'r':
286                 *r = '\r';
287                 break;
288             case 't':
289                 *r = '\t';
290                 break;
291             case '0':
292             case '1':
293             case '2':
294             case '3':
295             case '4':
296             case '5':
297             case '6':
298             case '7':
299                 n = c - '0';
300                 if (*p >= '0' && *p <= '7')
301                     n = 8 * n + (*p++ - '0');
302                 if (*p >= '0' && *p <= '7')
303                     n = 8 * n + (*p++ - '0');
304                 *r = (char) n;
305                 break;
306             /* case '^': case '\\': */
307             default:
308                 *r = c;
309                 break;
310             }
311             break;
312         case '^':
313             *r = (*p++ & 037);
314             if (!*r)
315                 *r = (char) '\200';
316             break;
317         default:
318             break;
319         }
320         ++r;
321     }
322     *r++ = '\0';
323     *outptr = r;
324     return result;
325 }
326 
327 /* look for a particular field name */
328 static const char *
tc_field(field,tc_end)329 tc_field(field, tc_end)
330 const char *field;
331 const char **tc_end;
332 {
333     const char *end, *q, *p = tc_entry;
334 
335     end = p + strlen(p);
336     while (p < end) {
337         if ((p = index(p, ':')) == 0)
338             break;
339         ++p;
340         if (p[0] == field[0] && p[1] == field[1]
341             && (p[2] == ':' || p[2] == '=' || p[2] == '#' || p[2] == '@'))
342             break;
343     }
344     if (tc_end) {
345         if (p) {
346             if ((q = index(p + 2, ':')) == 0)
347                 q = end;
348         } else
349             q = 0;
350         *tc_end = q;
351     }
352     return p;
353 }
354 
355 static char cmbuf[64];
356 
357 /* produce a string which will position the cursor at <row,col> if output */
358 char *
tgoto(cm,col,row)359 tgoto(cm, col, row)
360 const char *cm;
361 int col, row;
362 {
363     return tparam(cm, cmbuf, (int) (sizeof cmbuf), row, col, 0, 0);
364 }
365 
366 /* format a parameterized string, ala sprintf */
367 char *
tparam(ctl,buf,buflen,row,col,row2,col2)368 tparam(ctl, buf, buflen, row, col, row2, col2)
369 const char *ctl; /* parameter control string */
370 char *buf;       /* output buffer */
371 int buflen;      /* ought to have been `size_t'... */
372 int row, col, row2, col2;
373 {
374     int atmp, ac, av[5];
375     char c, *r, *z, *bufend, numbuf[32];
376     const char *fmt;
377 #ifndef NO_SPECIAL_CHARS_FIXUP
378     int bc = 0, up = 0;
379 #endif
380 
381     av[0] = row, av[1] = col, av[2] = row2, av[3] = col2, av[4] = 0;
382     ac = 0;
383     r = buf, bufend = r + buflen - 1;
384     while (*ctl) {
385         if ((*r = *ctl++) == '%') {
386             if (ac > 4)
387                 ac = 4;
388             fmt = 0;
389             switch ((c = *ctl++)) {
390             case '%':
391                 break; /* '%' already copied */
392             case 'd':
393                 fmt = "%d";
394                 break;
395             case '2':
396                 fmt = "%02d";
397                 break;
398             case '3':
399                 fmt = "%03d";
400                 break;
401             case '+': /*FALLTHRU*/
402             case '.':
403                 *r = (char) av[ac++];
404                 if (c == '+')
405                     *r += *ctl++;
406                 if (!*r) {
407                     *r = (char) '\200';
408                 } else {
409 #ifndef NO_SPECIAL_CHARS_FIXUP
410                     /* avoid terminal driver intervention for
411                        various control characters, to prevent
412                        LF from becoming CR+LF, for instance; only
413                        makes sense if this is a cursor positioning
414                        sequence, but we have no way to check that */
415                     while (index("\004\t\n\013\f\r", *r)) {
416                         if (ac & 1) { /* row */
417                             if (!UP || !*UP)
418                                 break; /* can't fix */
419                             ++up;      /* incr row now, later move up */
420                         } else {       /* column */
421                             if (!BC || !*BC)
422                                 break; /* can't fix */
423                             ++bc;      /* incr column, later backspace */
424                         }
425                         (*r)++;
426                     }
427 #endif /* !NO_SPECIAL_CHARS_FIXUP */
428                 }
429                 break;
430             case '>':
431                 if (av[ac] > (*ctl++ & 0377))
432                     av[ac] += *ctl;
433                 ++ctl;
434                 break;
435             case 'r':
436                 atmp = av[0];
437                 av[0] = av[1];
438                 av[1] = atmp;
439                 atmp = av[2];
440                 av[2] = av[3];
441                 av[3] = atmp;
442                 --r;
443                 break;
444             case 'i':
445                 ++av[0];
446                 ++av[1];
447                 ++av[2];
448                 ++av[3];
449                 --r;
450                 break;
451             case 'n':
452                 av[0] ^= 0140;
453                 av[1] ^= 0140;
454                 av[2] ^= 0140;
455                 av[3] ^= 0140;
456                 --r;
457                 break;
458             case 'B':
459                 av[0] = ((av[0] / 10) << 4) + (av[0] % 10);
460                 av[1] = ((av[1] / 10) << 4) + (av[1] % 10);
461                 av[2] = ((av[2] / 10) << 4) + (av[2] % 10);
462                 av[3] = ((av[3] / 10) << 4) + (av[3] % 10);
463                 --r;
464                 break;
465             case 'D':
466                 av[0] -= (av[0] & 15) << 1;
467                 av[1] -= (av[1] & 15) << 1;
468                 av[2] -= (av[2] & 15) << 1;
469                 av[3] -= (av[3] & 15) << 1;
470                 --r;
471                 break;
472             default:
473                 *++r = c;
474                 break; /* erroneous entry... */
475             }
476             if (fmt) {
477                 (void) sprintf(numbuf, fmt, av[ac++]);
478                 for (z = numbuf; *z && r <= bufend; z++)
479                     *r++ = *z;
480                 --r; /* will be re-incremented below */
481             }
482         }
483         if (++r > bufend)
484             return (char *) 0;
485     }
486 #ifndef NO_SPECIAL_CHARS_FIXUP
487     if (bc || up) {
488         while (--bc >= 0)
489             for (z = BC; *z && r <= bufend; z++)
490                 *r++ = *z;
491         while (--up >= 0)
492             for (z = UP; *z && r <= bufend; z++)
493                 *r++ = *z;
494         if (r > bufend)
495             return (char *) 0;
496     }
497 #endif /* !NO_SPECIAL_CHARS_FIXUP */
498     *r = '\0';
499     return buf;
500 }
501 
502 /* send a string to the terminal, possibly padded with trailing NULs */
503 void
tputs(string,range,output_func)504 tputs(string, range, output_func)
505 const char *string; /* characters to output */
506 int range;          /* number of lines affected, used for `*' delays */
507 int FDECL((*output_func),(int)); /* actual output routine;
508                                   * return value ignored */
509 {
510     register int c, num = 0;
511     register const char *p = string;
512 
513     if (!p || !*p)
514         return;
515 
516     /* pick out padding prefix, if any */
517     if (*p >= '0' && *p <= '9') {
518         do { /* note: scale `num' by 10 to accommodate fraction */
519             num += (*p++ - '0'), num *= 10;
520         } while (*p >= '0' && *p <= '9');
521         if (*p == '.')
522             ++p, num += (*p >= '0' && *p <= '9') ? (*p++ - '0') : 0;
523         if (*p == '*')
524             ++p, num *= range;
525     }
526 
527     /* output the string */
528     while ((c = *p++) != '\0') {
529         if (c == '\200')
530             c = '\0'; /* undo tgetstr's encoding */
531         (void) (*output_func)(c);
532     }
533 
534 #ifndef NO_DELAY_PADDING
535     /* perform padding */
536     if (num) {
537         long pad;
538 
539         /* figure out how many chars needed to produce desired elapsed time */
540         pad = (long) baud_rates[ospeed];
541         if (pad < 0)
542             pad *= -100L;
543         pad *= (long) num;
544         /* 100000 == 10 bits/char * (1000 millisec/sec scaled by 10) */
545         num = (int) (pad / 100000L); /* number of characters */
546 
547         c = PC; /* assume output_func isn't allowed to change PC */
548         while (--num >= 0)
549             (void) (*output_func)(c);
550     }
551 #endif /* !NO_DELAY_PADDING */
552 
553     return;
554 }
555 
556 /*tclib.c*/
557