1 /* $NetBSD: termcap.c,v 1.54 2006/12/19 02:02:03 uwe Exp $ */
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
35 static char sccsid
[] = "@(#)termcap.c 8.1 (Berkeley) 6/4/93";
37 __RCSID("$NetBSD: termcap.c,v 1.54 2006/12/19 02:02:03 uwe Exp $");
41 #include <sys/types.h>
42 #include <sys/param.h>
50 #include "pathnames.h"
51 #include "termcap_private.h"
53 #define PBUFSIZ MAXPATHLEN /* max length of filename path */
54 #define PVECSIZ 32 /* max number of names in path */
55 #define CAPSIZ 256 /* max capability size */
58 * termcap - routines for dealing with the terminal capability data base
60 * BUG: Should use a "last" pointer in tbuf, so that searching
61 * for capabilities alphabetically would not be a n**2/2
62 * process when large numbers of capabilities are given.
63 * Note: If we add a last pointer now we will screw up the
64 * tc capability. We really should compile termcap.
66 * Essentially all the work here is scanning and decoding escapes
67 * in string capabilities. We don't use stdio because the editor
68 * doesn't, and because living w/o it is not hard.
71 static char *tbuf
= NULL
; /* termcap buffer */
72 static struct tinfo
*fbuf
= NULL
; /* untruncated termcap buffer */
75 * Set the termcap entry to the arbitrary string passed in, this can
76 * be used to provide a "dummy" termcap entry if a real one does not
77 * exist. This function will malloc the buffer and space for the
78 * string. If an error occurs return -1 otherwise return 0.
81 t_setinfo(struct tinfo
**bp
, const char *entry
)
83 char capability
[CAPSIZ
], *cap_ptr
;
86 _DIAGASSERT(bp
!= NULL
);
87 _DIAGASSERT(entry
!= NULL
);
89 if ((*bp
= malloc(sizeof(struct tinfo
))) == NULL
)
92 if (((*bp
)->info
= (char *) malloc(strlen(entry
) + 1)) == NULL
)
95 strcpy((*bp
)->info
, entry
);
98 limit
= sizeof(capability
) - 1;
99 (*bp
)->up
= t_getstr(*bp
, "up", &cap_ptr
, &limit
);
101 (*bp
)->up
= strdup((*bp
)->up
);
102 cap_ptr
= capability
;
103 limit
= sizeof(capability
) - 1;
104 (*bp
)->bc
= t_getstr(*bp
, "bc", &cap_ptr
, &limit
);
106 (*bp
)->bc
= strdup((*bp
)->bc
);
113 * Get an extended entry for the terminal name. This differs from
114 * tgetent only in a) the buffer is malloc'ed for the caller and
115 * b) the termcap entry is not truncated to 1023 characters.
119 t_getent(struct tinfo
**bp
, const char *name
)
127 char pathbuf
[PBUFSIZ
]; /* holds raw path of filenames */
128 char *pathvec
[PVECSIZ
]; /* to point to names in pathbuf */
130 char capability
[CAPSIZ
], *cap_ptr
;
134 _DIAGASSERT(bp
!= NULL
);
135 _DIAGASSERT(name
!= NULL
);
137 if ((*bp
= malloc(sizeof(struct tinfo
))) == NULL
)
142 cp
= getenv("TERMCAP");
144 * TERMCAP can have one of two things in it. It can be the
145 * name of a file to use instead of
146 * /usr/share/misc/termcap. In this case it better start with
147 * a "/". Or it can be an entry to use so we don't have to
148 * read the file. In this case cgetset() withh crunch out the
149 * newlines. If TERMCAP does not hold a file name then a path
150 * of names is searched instead. The path is found in the
151 * TERMPATH variable, or becomes _PATH_DEF ("$HOME/.termcap
152 * /usr/share/misc/termcap") if no TERMPATH exists.
154 if (!cp
|| *cp
!= '/') { /* no TERMCAP or it holds an entry */
155 if ((termpath
= getenv("TERMPATH")) != NULL
)
156 (void)strlcpy(pathbuf
, termpath
, sizeof(pathbuf
));
158 if ((home
= getenv("HOME")) != NULL
) {
160 p
+= strlen(home
); /* path, looking in */
161 (void)strlcpy(pathbuf
, home
,
162 sizeof(pathbuf
)); /* $HOME first */
163 if ((size_t)(p
- pathbuf
) < sizeof(pathbuf
) - 1)
165 } /* if no $HOME look in current directory */
166 if ((size_t)(p
- pathbuf
) < sizeof(pathbuf
) - 1) {
167 (void)strlcpy(p
, _PATH_DEF
,
168 sizeof(pathbuf
) - (p
- pathbuf
));
173 /* user-defined name in TERMCAP; still can be tokenized */
174 (void)strlcpy(pathbuf
, cp
, sizeof(pathbuf
));
177 *fname
++ = pathbuf
; /* tokenize path into vector of names */
179 if (*p
== ' ' || *p
== ':') {
182 if (*p
!= ' ' && *p
!= ':')
187 if (fname
>= pathvec
+ PVECSIZ
) {
192 *fname
= NULL
; /* mark end of vector */
195 * try ignoring TERMCAP if it has a ZZ in it, we do this
196 * because a TERMCAP with ZZ in it indicates the entry has been
197 * exported by another program using the "old" interface, the
198 * termcap entry has been truncated and ZZ points to an address
199 * in the exporting programs memory space which is of no use
200 * here - anyone who is exporting the termcap entry and then
201 * reading it back again in the same program deserves to be
202 * taken out, beaten up, dragged about, shot and then hurt some
206 if (cp
&& *cp
&& *cp
!= '/' && strstr(cp
, ":ZZ") == NULL
) {
208 if (cgetset(cp
) < 0) {
215 * XXX potential security hole here in a set-id program if the
216 * user had setup name to be built from a path they can not
220 i
= cgetent(&((*bp
)->info
), (const char *const *)pathvec
, name
);
223 * if we get an error and we skipped doing the cgetset before
224 * we try with TERMCAP in place - we may be using a truncated
225 * termcap entry but what else can one do?
227 if ((i
< 0) && (did_getset
== 0)) {
228 if (cp
&& *cp
&& *cp
!= '/')
229 if (cgetset(cp
) < 0) {
233 i
= cgetent(&((*bp
)->info
), (const char *const *)pathvec
, name
);
236 /* no tc reference loop return code in libterm XXX */
243 * fill in t_goto capabilities - this prevents memory leaks
244 * and is more efficient than fetching these capabilities
245 * every time t_goto is called.
248 cap_ptr
= capability
;
249 limit
= sizeof(capability
) - 1;
250 (*bp
)->up
= t_getstr(*bp
, "up", &cap_ptr
, &limit
);
252 (*bp
)->up
= strdup((*bp
)->up
);
253 cap_ptr
= capability
;
254 limit
= sizeof(capability
) - 1;
255 (*bp
)->bc
= t_getstr(*bp
, "bc", &cap_ptr
, &limit
);
257 (*bp
)->bc
= strdup((*bp
)->bc
);
272 * Get an entry for terminal name in buffer bp from the termcap file.
275 tgetent(char *bp
, const char *name
)
277 int i
, plen
, elen
, c
;
280 i
= t_getent(&fbuf
, name
);
284 * if the termcap entry is larger than 1023 bytes,
285 * stash the full buffer pointer as the ZZ capability
286 * in the termcap buffer passed.
288 if (strlcpy(bp
, fbuf
->info
, 1024) >= 1024) {
289 plen
= asprintf(&ptrbuf
, ":ZZ=%p", fbuf
->info
);
290 (void)strlcpy(bp
, fbuf
->info
, 1024);
293 * backup over the entry if the addition of the full
294 * buffer pointer will overflow the buffer passed. We
295 * want to truncate the termcap entry on a capability
298 if ((elen
+ plen
) > 1023) {
299 bp
[1023 - plen
] = '\0';
300 for (c
= (elen
- plen
); c
> 0; c
--) {
317 * Return the (numeric) option id.
318 * Numeric options look like
320 * i.e. the option string is separated from the numeric value by
321 * a # character. If the option is not found we return -1.
322 * Note that we handle octal numbers beginning with 0.
325 t_getnum(struct tinfo
*info
, const char *id
)
329 _DIAGASSERT(info
!= NULL
);
330 _DIAGASSERT(id
!= NULL
);
332 if (cgetnum(info
->info
, id
, &num
) == 0)
339 tgetnum(const char *id
)
341 return fbuf
? t_getnum(fbuf
, id
) : -1;
345 * Handle a flag option.
346 * Flag options are given "naked", i.e. followed by a : or the end
347 * of the buffer. Return 1 if we find the option, or 0 if it is
350 int t_getflag(struct tinfo
*info
, const char *id
)
352 _DIAGASSERT(info
!= NULL
);
353 _DIAGASSERT(id
!= NULL
);
355 return (cgetcap(info
->info
, id
, ':') != NULL
);
359 tgetflag(const char *id
)
361 return fbuf
? t_getflag(fbuf
, id
) : 0;
365 * Get a string valued option.
368 * Much decoding is done on the strings, and the strings are
369 * placed in area, which is a ref parameter which is updated.
370 * limit is the number of characters allowed to be put into
371 * area, this is updated.
374 t_getstr(struct tinfo
*info
, const char *id
, char **area
, size_t *limit
)
379 _DIAGASSERT(info
!= NULL
);
380 _DIAGASSERT(id
!= NULL
);
381 /* area may be NULL */
384 if ((i
= cgetstr(info
->info
, id
, &s
)) < 0) {
386 if ((area
== NULL
) && (limit
!= NULL
))
393 * check if there is room for the new entry to be put into
396 if (limit
!= NULL
&& (*limit
< (size_t) i
)) {
402 (void)strcpy(*area
, s
);
410 _DIAGASSERT(limit
!= NULL
);
418 * Get a string valued option.
421 * Much decoding is done on the strings, and the strings are
422 * placed in area, which is a ref parameter which is updated.
423 * No checking on area overflow.
426 tgetstr(const char *id
, char **area
)
428 struct tinfo dummy
, *ti
;
431 _DIAGASSERT(id
!= NULL
);
438 * This is for all the boneheaded programs that relied on tgetstr
439 * to look only at the first 2 characters of the string passed...
445 if ((id
[0] == 'Z') && (id
[1] == 'Z')) {
451 if (area
== NULL
|| *area
== NULL
) {
452 static char capability
[CAPSIZ
];
453 size_t limit
= sizeof(capability
) - 1;
457 return t_getstr(ti
, ids
, &ptr
, &limit
);
459 return t_getstr(ti
, ids
, area
, NULL
);
463 * Return a string valued option specified by id, allocating memory to
464 * an internal buffer as necessary. The memory allocated can be
465 * free'd by a call to t_freent().
467 * If the string is not found or memory allocation fails then NULL
471 t_agetstr(struct tinfo
*info
, const char *id
)
476 _DIAGASSERT(info
!= NULL
);
477 _DIAGASSERT(id
!= NULL
);
479 t_getstr(info
, id
, NULL
, &new_size
);
481 /* either the string is empty or the capability does not exist. */
485 if ((tb
= info
->tbuf
) == NULL
||
486 (size_t) (tb
->eptr
- tb
->ptr
) < (new_size
+ 1)) {
487 if (new_size
< CAPSIZ
)
492 if ((tb
= malloc(sizeof(*info
->tbuf
))) == NULL
)
495 if ((tb
->data
= tb
->ptr
= tb
->eptr
= malloc(new_size
))
501 tb
->eptr
+= new_size
;
503 if (info
->tbuf
!= NULL
)
504 tb
->next
= info
->tbuf
;
510 return t_getstr(info
, id
, &tb
->ptr
, NULL
);
514 * Free the buffer allocated by t_getent
518 t_freent(struct tinfo
*info
)
520 struct tbuf
*tb
, *wb
;
521 _DIAGASSERT(info
!= NULL
);
523 if (info
->up
!= NULL
)
525 if (info
->bc
!= NULL
)
527 for (tb
= info
->tbuf
; tb
;) {
537 * Get the terminal name string from the termcap entry.
541 t_getterm(struct tinfo
*info
, char **area
, size_t *limit
)
546 _DIAGASSERT(info
!= NULL
);
547 if ((endp
= strchr(info
->info
, ':')) == NULL
) {
553 count
= endp
- info
->info
+ 1;
555 _DIAGASSERT(limit
!= NULL
);
559 if ((limit
!= NULL
) && (count
> *limit
)) {
564 (void)strlcpy(*area
, info
->info
, count
);