1 /* aNetHack 0.0.1 tclib.c $ANH-Date: 1432512788 2015/05/25 00:13:08 $ $ANH-Branch: master $:$ANH-Revision: 1.8 $ */
2 /* Copyright (c) Robert Patrick Rankin, 1995 */
3 /* aNetHack 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 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 (*)()));
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 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 **));
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}
80 char *tm
= getenv("TERM");
81 if (tm
&& strcmp(tm
, term
) == 0)
82 return tc_store(term
, tc
);
83 fp
= fopen(TERMCAP
, "r");
85 /* otherwise, look `term' up in the file */
87 char wrkbuf
[TCBUFSIZ
];
88 tc
= tc_find(fp
, term
, wrkbuf
, (int) (sizeof wrkbuf
- strlen(term
)));
89 result
= tc_store(term
, tc
);
97 /* copy the entry into the output buffer */
100 const char *trm
, *ent
;
102 const char *bar
, *col
;
107 if (!ent
|| !*ent
|| !trm
|| !*trm
|| (col
= index(ent
, ':')) == 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
);
117 (void) strcat(strcat(tc_entry
, ":"), ent
);
119 /* initialize global variables */
121 PC
= (k
== -1) ? '\0' : (char) k
;
123 if (!tgetstr("bc", &s
))
124 (void) strcpy(s
, "\b"), s
+= 2;
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);
133 #endif /* !NO_DELAY_PADDING */
138 /* search for an entry in the termcap file */
140 tc_find(fp
, term
, buffer
, bufsiz
)
146 int in
, len
, first
, skip
;
147 char *ip
, *op
, *tc_fetch
, tcbuf
[TCBUFSIZ
];
151 ip
= tcbuf
, in
= min(bufsiz
, TCBUFSIZ
);
153 /* load entire next entry, including any continuations */
155 if (!fgets(ip
, min(in
, BUFSIZ
), fp
))
158 skip
= (*ip
== '#'), first
= 0;
159 len
= (int) strlen(ip
);
160 if (!skip
&& len
> 1 && *(ip
+ len
- 1) == '\n'
161 && *(ip
+ len
- 2) == '\\')
163 ip
+= len
, in
-= len
;
164 } while (*(ip
- 1) != '\n' && in
> 0);
165 if (ferror(fp
) || ip
== buffer
|| *(ip
- 1) != '\n')
167 *--ip
= '\0'; /* strip newline */
169 ip
= tc_name(term
, tcbuf
);
170 } while (skip
|| !ip
);
172 /* we have the desired entry; strip cruft and look for :tc=other: */
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] == '=') {
180 if ((ip
= index(tc_fetch
, ':')) != 0)
189 tc_fetch
= tc_find(fp
, tc_fetch
, tcbuf
, min(bufsiz
, TCBUFSIZ
));
192 if (op
> buffer
&& *(op
- 1) == ':' && *tc_fetch
== ':')
194 strcpy(op
, tc_fetch
);
199 /* check whether `ent' contains `nam'; return start of field entries */
205 char *nxt
, *lst
, *p
= ent
;
206 size_t n
= strlen(nam
);
208 if ((lst
= index(p
, ':')) == 0)
212 if ((nxt
= index(p
, '|')) == 0 || nxt
> lst
)
214 if ((long) (nxt
- p
) == (long) n
&& strncmp(p
, nam
, n
) == 0)
221 /* look up a numeric entry */
226 const char *q
, *p
= tc_field(which
, &q
);
230 if (!p
|| p
[2] != '#')
233 if ((n
= (size_t)(q
- p
)) >= sizeof numbuf
)
235 (void) strncpy(numbuf
, p
, n
);
240 /* look up a boolean entry */
245 const char *p
= tc_field(which
, (const char **) 0);
247 return (!p
|| p
[2] != ':') ? 0 : 1;
250 /* look up a string entry; update `*outptr' */
252 tgetstr(which
, outptr
)
258 const char *q
, *p
= tc_field(which
, &q
);
260 if (!p
|| p
[2] != '=')
263 if ((q
= index(p
, ':')) == 0)
265 r
= result
= *outptr
;
267 switch ((*r
= *p
++)) {
269 switch ((c
= *p
++)) {
300 if (*p
>= '0' && *p
<= '7')
301 n
= 8 * n
+ (*p
++ - '0');
302 if (*p
>= '0' && *p
<= '7')
303 n
= 8 * n
+ (*p
++ - '0');
306 /* case '^': case '\\': */
327 /* look for a particular field name */
329 tc_field(field
, tc_end
)
333 const char *end
, *q
, *p
= tc_entry
;
337 if ((p
= index(p
, ':')) == 0)
340 if (p
[0] == field
[0] && p
[1] == field
[1]
341 && (p
[2] == ':' || p
[2] == '=' || p
[2] == '#' || p
[2] == '@'))
346 if ((q
= index(p
+ 2, ':')) == 0)
355 static char cmbuf
[64];
357 /* produce a string which will position the cursor at <row,col> if output */
363 return tparam(cm
, cmbuf
, (int) (sizeof cmbuf
), row
, col
, 0, 0);
366 /* format a parameterized string, ala sprintf */
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
;
375 char c
, *r
, *z
, *bufend
, numbuf
[32];
377 #ifndef NO_SPECIAL_CHARS_FIXUP
381 av
[0] = row
, av
[1] = col
, av
[2] = row2
, av
[3] = col2
, av
[4] = 0;
383 r
= buf
, bufend
= r
+ buflen
- 1;
385 if ((*r
= *ctl
++) == '%') {
389 switch ((c
= *ctl
++)) {
391 break; /* '%' already copied */
401 case '+': /*FALLTHRU*/
403 *r
= (char) av
[ac
++];
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 */
418 break; /* can't fix */
419 ++up
; /* incr row now, later move up */
420 } else { /* column */
422 break; /* can't fix */
423 ++bc
; /* incr column, later backspace */
427 #endif /* !NO_SPECIAL_CHARS_FIXUP */
431 if (av
[ac
] > (*ctl
++ & 0377))
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);
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;
474 break; /* erroneous entry... */
477 (void) sprintf(numbuf
, fmt
, av
[ac
++]);
478 for (z
= numbuf
; *z
&& r
<= bufend
; z
++)
480 --r
; /* will be re-incremented below */
486 #ifndef NO_SPECIAL_CHARS_FIXUP
489 for (z
= BC
; *z
&& r
<= bufend
; z
++)
492 for (z
= UP
; *z
&& r
<= bufend
; z
++)
497 #endif /* !NO_SPECIAL_CHARS_FIXUP */
502 /* send a string to the terminal, possibly padded with trailing NULs */
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 (*output_func
)(); /* actual output routine; return value ignored */
509 register int c
, num
= 0;
510 register const char *p
= string
;
515 /* pick out padding prefix, if any */
516 if (*p
>= '0' && *p
<= '9') {
517 do { /* note: scale `num' by 10 to accommodate fraction */
518 num
+= (*p
++ - '0'), num
*= 10;
519 } while (*p
>= '0' && *p
<= '9');
521 ++p
, num
+= (*p
>= '0' && *p
<= '9') ? (*p
++ - '0') : 0;
526 /* output the string */
527 while ((c
= *p
++) != '\0') {
529 c
= '\0'; /* undo tgetstr's encoding */
530 (void) (*output_func
)(c
);
533 #ifndef NO_DELAY_PADDING
534 /* perform padding */
538 /* figure out how many chars needed to produce desired elapsed time */
539 pad
= (long) baud_rates
[ospeed
];
543 /* 100000 == 10 bits/char * (1000 millisec/sec scaled by 10) */
544 num
= (int) (pad
/ 100000L); /* number of characters */
546 c
= PC
; /* assume output_func isn't allowed to change PC */
548 (void) (*output_func
)(c
);
550 #endif /* !NO_DELAY_PADDING */