1 /* SCCS Id: @(#)tclib.c 3.4 1996/02/25 */
2 /* Copyright (c) Robert Patrick Rankin, 1995 */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* termcap library implementation */
9 #ifndef TERMCAP /* name of default termcap file */
10 #define TERMCAP "/etc/termcap"
12 #ifndef TCBUFSIZ /* size of tgetent buffer; Unix man page says 1024 */
15 #define ESC '\033' /* termcap's '\E' */
16 #define BEL '\007' /* ANSI C's '\a' (we assume ASCII here...) */
18 /* exported variables, as per man page */
23 /* exported routines */
24 int tgetent(char *,const char *);
25 int tgetflag(const char *);
26 int tgetnum(const char *);
27 char *tgetstr(const char *,char **);
28 char *tgoto(const char *,int,int);
29 char *tparam(const char *,char *,int,int,int,int,int);
30 void tputs(const char *,int,int (*)());
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,
40 300, 600, 1200, 1800, 2000, 2400, 3600, 4800, 7200,
41 # else /* assume Unix */
42 200, 300, 600, 1200, 1800, 2400, 4800,
44 9600, -192, -384, /* negative is used as `100 * abs(entry)' */
49 #endif /* !NO_DELAY_PADDING */
51 /* local support code */
52 static int tc_store(const char *,const char *);
53 static char *tc_find(FILE *,const char *,char *,int);
54 static char *tc_name(const char *,char *);
55 static const char *tc_field(const char *,const char **);
58 #define min(a,b) ((a)<(b)?(a):(b))
61 /* retrieve the specified terminal entry and return it in `entbuf' */
64 char *entbuf
; /* size must be at least [TCBUFSIZ] */
69 char *tc
= getenv("TERMCAP");
74 /* if ${TERMCAP} is found as a file, it's not an inline termcap entry */
75 if ((fp
= fopen(tc
? tc
: TERMCAP
, "r")) != 0)
77 /* if ${TERMCAP} isn't a file and `term' matches ${TERM}, use ${TERMCAP} */
79 char *tm
= getenv("TERM");
80 if (tm
&& strcmp(tm
, term
) == 0)
81 return tc_store(term
, tc
);
82 fp
= fopen(TERMCAP
, "r");
84 /* otherwise, look `term' up in the file */
86 char wrkbuf
[TCBUFSIZ
];
87 tc
= tc_find(fp
, term
, wrkbuf
, (int)(sizeof wrkbuf
- strlen(term
)));
88 result
= tc_store(term
, tc
);
96 /* copy the entry into the output buffer */
99 const char *trm
, *ent
;
101 const char *bar
, *col
;
106 if (!ent
|| !*ent
|| !trm
|| !*trm
|| (col
= index(ent
, ':')) == 0)
108 (void) strcpy(tc_entry
, trm
);
109 if (((bar
= index(ent
, '|')) != 0 && bar
< col
)
110 || ((long)(n
= strlen(trm
)) == (long)(col
- ent
)
111 && strncmp(ent
, trm
, n
) == 0))
112 (void) strcat(tc_entry
, col
);
113 else if (*ent
== ':')
114 (void) strcat(tc_entry
, ent
);
116 (void) strcat(strcat(tc_entry
, ":"), ent
);
118 /* initialize global variables */
120 PC
= (k
== -1) ? '\0' : (char)k
;
122 if (!tgetstr("bc", &s
)) (void)strcpy(s
, "\b"), s
+= 2;
124 (void)tgetstr("up", &s
);
125 #ifndef NO_DELAY_PADDING
126 /* caller must set `ospeed' */
127 if ((int)ospeed
>= (int)SIZE(baud_rates
))
128 ospeed
= (short)(SIZE(baud_rates
) - 1);
131 #endif /* !NO_DELAY_PADDING */
136 /* search for an entry in the termcap file */
138 tc_find(fp
, term
, buffer
, bufsiz
)
144 int in
, len
, first
, skip
;
145 char *ip
, *op
, *tc_fetch
, tcbuf
[TCBUFSIZ
];
149 ip
= tcbuf
, in
= min(bufsiz
,TCBUFSIZ
);
151 /* load entire next entry, including any continuations */
153 if (!fgets(ip
, min(in
,BUFSIZ
), fp
)) break;
154 if (first
) skip
= (*ip
== '#'), first
= 0;
155 len
= (int)strlen(ip
);
157 && *(ip
+ len
- 1) == '\n' && *(ip
+ len
- 2) == '\\')
159 ip
+= len
, in
-= len
;
160 } while (*(ip
- 1) != '\n' && in
> 0);
161 if (ferror(fp
) || ip
== buffer
|| *(ip
- 1) != '\n')
163 *--ip
= '\0'; /* strip newline */
164 if (!skip
) ip
= tc_name(term
, tcbuf
);
165 } while (skip
|| !ip
);
167 /* we have the desired entry; strip cruft and look for :tc=other: */
169 for (op
= buffer
; *ip
; ip
++) {
170 if (op
== buffer
|| *(op
- 1) != ':'
171 || (*ip
!= ' ' && *ip
!= '\t' && *ip
!= ':'))
172 *op
++ = *ip
, bufsiz
-= 1;
173 if (ip
[0] == ':' && ip
[1] == 't' && ip
[2] == 'c' && ip
[3] == '=') {
175 if ((ip
= index(tc_fetch
, ':')) != 0) *ip
= '\0';
183 tc_fetch
= tc_find(fp
, tc_fetch
, tcbuf
, min(bufsiz
,TCBUFSIZ
));
186 if (op
> buffer
&& *(op
- 1) == ':' && *tc_fetch
== ':')
188 strcpy(op
, tc_fetch
);
193 /* check whether `ent' contains `nam'; return start of field entries */
199 char *nxt
, *lst
, *p
= ent
;
200 size_t n
= strlen(nam
);
202 if ((lst
= index(p
, ':')) == 0) lst
= p
+ strlen(p
);
205 if ((nxt
= index(p
, '|')) == 0 || nxt
> lst
) nxt
= lst
;
206 if ((long)(nxt
- p
) == (long)n
&& strncmp(p
, nam
, n
) == 0)
213 /* look up a numeric entry */
218 const char *q
, *p
= tc_field(which
, &q
);
222 if (!p
|| p
[2] != '#')
225 if ((n
= (size_t)(q
- p
)) >= sizeof numbuf
)
227 (void) strncpy(numbuf
, p
, n
);
232 /* look up a boolean entry */
237 const char *p
= tc_field(which
, (const char **)0);
239 return (!p
|| p
[2] != ':') ? 0 : 1;
242 /* look up a string entry; update `*outptr' */
244 tgetstr(which
, outptr
)
250 const char *q
, *p
= tc_field(which
, &q
);
252 if (!p
|| p
[2] != '=')
255 if ((q
= index(p
, ':')) == 0) q
= p
+ strlen(p
);
256 r
= result
= *outptr
;
258 switch ((*r
= *p
++)) {
260 switch ((c
= *p
++)) {
261 case 'E': *r
= ESC
; break;
262 case 'a': *r
= BEL
; break;
263 case 'b': *r
= '\b'; break;
264 case 'f': *r
= '\f'; break;
265 case 'n': *r
= '\n'; break;
266 case 'r': *r
= '\r'; break;
267 case 't': *r
= '\t'; break;
268 case '0': case '1': case '2': case '3':
269 case '4': case '5': case '6': case '7':
271 if (*p
>= '0' && *p
<= '7') n
= 8 * n
+ (*p
++ - '0');
272 if (*p
>= '0' && *p
<= '7') n
= 8 * n
+ (*p
++ - '0');
275 /* case '^': case '\\': */
276 default: *r
= c
; break;
281 if (!*r
) *r
= (char)'\200';
293 /* look for a particular field name */
295 tc_field(field
, tc_end
)
299 const char *end
, *q
, *p
= tc_entry
;
303 if ((p
= index(p
, ':')) == 0)
306 if (p
[0] == field
[0] && p
[1] == field
[1]
307 && (p
[2] == ':' || p
[2] == '=' || p
[2] == '#' || p
[2] == '@'))
312 if ((q
= index(p
+ 2, ':')) == 0) q
= end
;
320 static char cmbuf
[64];
322 /* produce a string which will position the cursor at <row,col> if output */
328 return tparam(cm
, cmbuf
, (int)(sizeof cmbuf
), row
, col
, 0, 0);
331 /* format a parameterized string, ala sprintf */
333 tparam(ctl
, buf
, buflen
, row
, col
, row2
, col2
)
334 const char *ctl
; /* parameter control string */
335 char *buf
; /* output buffer */
336 int buflen
; /* ought to have been `size_t'... */
337 int row
, col
, row2
, col2
;
340 char c
, *r
, *z
, *bufend
, numbuf
[32];
342 #ifndef NO_SPECIAL_CHARS_FIXUP
346 av
[0] = row
, av
[1] = col
, av
[2] = row2
, av
[3] = col2
, av
[4] = 0;
348 r
= buf
, bufend
= r
+ buflen
- 1;
350 if ((*r
= *ctl
++) == '%') {
353 switch ((c
= *ctl
++)) {
354 case '%': break; /* '%' already copied */
355 case 'd': fmt
= "%d"; break;
356 case '2': fmt
= "%02d"; break;
357 case '3': fmt
= "%03d"; break;
358 case '+': /*FALLTHRU*/
359 case '.': *r
= (char)av
[ac
++];
360 if (c
== '+') *r
+= *ctl
++;
364 #ifndef NO_SPECIAL_CHARS_FIXUP
365 /* avoid terminal driver intervention for
366 various control characters, to prevent
367 LF from becoming CR+LF, for instance; only
368 makes sense if this is a cursor positioning
369 sequence, but we have no way to check that */
370 while (index("\004\t\n\013\f\r", *r
)) {
371 if (ac
& 1) { /* row */
372 if (!UP
|| !*UP
) break; /* can't fix */
373 ++up
; /* incr row now, later move up */
374 } else { /* column */
375 if (!BC
|| !*BC
) break; /* can't fix */
376 ++bc
; /* incr column, later backspace */
380 #endif /* !NO_SPECIAL_CHARS_FIXUP */
382 case '>': if (av
[ac
] > (*ctl
++ & 0377))
385 case 'r': atmp
= av
[0]; av
[0] = av
[1]; av
[1] = atmp
;
386 atmp
= av
[2]; av
[2] = av
[3]; av
[3] = atmp
;
388 case 'i': ++av
[0]; ++av
[1]; ++av
[2]; ++av
[3];
390 case 'n': av
[0] ^= 0140; av
[1] ^= 0140;
391 av
[2] ^= 0140; av
[3] ^= 0140;
393 case 'B': av
[0] = ((av
[0] / 10) << 4) + (av
[0] % 10);
394 av
[1] = ((av
[1] / 10) << 4) + (av
[1] % 10);
395 av
[2] = ((av
[2] / 10) << 4) + (av
[2] % 10);
396 av
[3] = ((av
[3] / 10) << 4) + (av
[3] % 10);
398 case 'D': av
[0] -= (av
[0] & 15) << 1;
399 av
[1] -= (av
[1] & 15) << 1;
400 av
[2] -= (av
[2] & 15) << 1;
401 av
[3] -= (av
[3] & 15) << 1;
403 default: *++r
= c
; break; /* erroneous entry... */
406 (void) sprintf(numbuf
, fmt
, av
[ac
++]);
407 for (z
= numbuf
; *z
&& r
<= bufend
; z
++)
409 --r
; /* will be re-incremented below */
415 #ifndef NO_SPECIAL_CHARS_FIXUP
418 for (z
= BC
; *z
&& r
<= bufend
; z
++)
421 for (z
= UP
; *z
&& r
<= bufend
; z
++)
426 #endif /* !NO_SPECIAL_CHARS_FIXUP */
431 /* send a string to the terminal, possibly padded with trailing NULs */
433 tputs( string
, range
, output_func
)
434 const char *string
; /* characters to output */
435 int range
; /* number of lines affected, used for `*' delays */
436 int (*output_func
)(); /* actual output routine; return value ignored */
438 register int c
, num
= 0;
439 register const char *p
= string
;
444 /* pick out padding prefix, if any */
445 if (*p
>= '0' && *p
<= '9') {
446 do { /* note: scale `num' by 10 to accommodate fraction */
447 num
+= (*p
++ - '0'), num
*= 10;
448 } while (*p
>= '0' && *p
<= '9');
450 ++p
, num
+= (*p
>= '0' && *p
<= '9') ? (*p
++ - '0') : 0;
455 /* output the string */
456 while ((c
= *p
++) != '\0') {
457 if (c
== '\200') c
= '\0'; /* undo tgetstr's encoding */
458 (void) (*output_func
)(c
);
461 #ifndef NO_DELAY_PADDING
462 /* perform padding */
466 /* figure out how many chars needed to produce desired elapsed time */
467 pad
= (long)baud_rates
[ospeed
];
468 if (pad
< 0) pad
*= -100L;
470 /* 100000 == 10 bits/char * (1000 millisec/sec scaled by 10) */
471 num
= (int)(pad
/ 100000L); /* number of characters */
473 c
= PC
; /* assume output_func isn't allowed to change PC */
475 (void) (*output_func
)(c
);
477 #endif /* !NO_DELAY_PADDING */