Fix mdoc(7)/man(7) mix up.
[netbsd-mini2440.git] / lib / libterm / termcap.c
blobcaab32ff00e648ae1b57219529084a0e71e50040
1 /* $NetBSD: termcap.c,v 1.54 2006/12/19 02:02:03 uwe Exp $ */
3 /*
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
9 * are met:
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
29 * SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)termcap.c 8.1 (Berkeley) 6/4/93";
36 #else
37 __RCSID("$NetBSD: termcap.c,v 1.54 2006/12/19 02:02:03 uwe Exp $");
38 #endif
39 #endif /* not lint */
41 #include <sys/types.h>
42 #include <sys/param.h>
43 #include <assert.h>
44 #include <ctype.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <termcap.h>
49 #include <errno.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.
80 int
81 t_setinfo(struct tinfo **bp, const char *entry)
83 char capability[CAPSIZ], *cap_ptr;
84 size_t limit;
86 _DIAGASSERT(bp != NULL);
87 _DIAGASSERT(entry != NULL);
89 if ((*bp = malloc(sizeof(struct tinfo))) == NULL)
90 return -1;
92 if (((*bp)->info = (char *) malloc(strlen(entry) + 1)) == NULL)
93 return -1;
95 strcpy((*bp)->info, entry);
97 cap_ptr = capability;
98 limit = sizeof(capability) - 1;
99 (*bp)->up = t_getstr(*bp, "up", &cap_ptr, &limit);
100 if ((*bp)->up)
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);
105 if ((*bp)->bc)
106 (*bp)->bc = strdup((*bp)->bc);
107 (*bp)->tbuf = NULL;
109 return 0;
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)
121 char *p;
122 char *cp;
123 char **fname;
124 char *home;
125 int i, did_getset;
126 size_t limit;
127 char pathbuf[PBUFSIZ]; /* holds raw path of filenames */
128 char *pathvec[PVECSIZ]; /* to point to names in pathbuf */
129 char *termpath;
130 char capability[CAPSIZ], *cap_ptr;
131 int error;
134 _DIAGASSERT(bp != NULL);
135 _DIAGASSERT(name != NULL);
137 if ((*bp = malloc(sizeof(struct tinfo))) == NULL)
138 return 0;
140 fname = pathvec;
141 p = pathbuf;
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));
157 else {
158 if ((home = getenv("HOME")) != NULL) {
159 /* set up default */
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)
164 *p++ = '/';
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));
172 else {
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 */
178 while (*++p)
179 if (*p == ' ' || *p == ':') {
180 *p = '\0';
181 while (*++p)
182 if (*p != ' ' && *p != ':')
183 break;
184 if (*p == '\0')
185 break;
186 *fname++ = p;
187 if (fname >= pathvec + PVECSIZ) {
188 fname--;
189 break;
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
203 * more.
205 did_getset = 0;
206 if (cp && *cp && *cp != '/' && strstr(cp, ":ZZ") == NULL) {
207 did_getset = 1;
208 if (cgetset(cp) < 0) {
209 error = -2;
210 goto out;
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
217 * normally read.
219 (*bp)->info = NULL;
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) {
230 error = -2;
231 goto out;
233 i = cgetent(&((*bp)->info), (const char *const *)pathvec, name);
236 /* no tc reference loop return code in libterm XXX */
237 if (i == -3) {
238 error = -1;
239 goto out;
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.
247 if (i >= 0) {
248 cap_ptr = capability;
249 limit = sizeof(capability) - 1;
250 (*bp)->up = t_getstr(*bp, "up", &cap_ptr, &limit);
251 if ((*bp)->up)
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);
256 if ((*bp)->bc)
257 (*bp)->bc = strdup((*bp)->bc);
258 (*bp)->tbuf = NULL;
259 } else {
260 error = i + 1;
261 goto out;
264 return (i + 1);
265 out:
266 free(*bp);
267 *bp = NULL;
268 return error;
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;
278 char *ptrbuf = NULL;
280 i = t_getent(&fbuf, name);
282 if (i == 1) {
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);
291 elen = strlen(bp);
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
296 * boundary.
298 if ((elen + plen) > 1023) {
299 bp[1023 - plen] = '\0';
300 for (c = (elen - plen); c > 0; c--) {
301 if (bp[c] == ':') {
302 bp[c] = '\0';
303 break;
308 strcat(bp, ptrbuf);
310 tbuf = bp;
313 return i;
317 * Return the (numeric) option id.
318 * Numeric options look like
319 * li#80
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)
327 long num;
329 _DIAGASSERT(info != NULL);
330 _DIAGASSERT(id != NULL);
332 if (cgetnum(info->info, id, &num) == 0)
333 return (int)(num);
334 else
335 return (-1);
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
348 * not given.
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.
366 * These are given as
367 * cl=^Z
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.
373 char *
374 t_getstr(struct tinfo *info, const char *id, char **area, size_t *limit)
376 char *s;
377 int i;
379 _DIAGASSERT(info != NULL);
380 _DIAGASSERT(id != NULL);
381 /* area may be NULL */
384 if ((i = cgetstr(info->info, id, &s)) < 0) {
385 errno = ENOENT;
386 if ((area == NULL) && (limit != NULL))
387 *limit = 0;
388 return NULL;
391 if (area != NULL) {
393 * check if there is room for the new entry to be put into
394 * area
396 if (limit != NULL && (*limit < (size_t) i)) {
397 errno = E2BIG;
398 free(s);
399 return NULL;
402 (void)strcpy(*area, s);
403 free(s);
404 s = *area;
405 *area += i + 1;
406 if (limit != NULL)
407 *limit -= i;
408 return (s);
409 } else {
410 _DIAGASSERT(limit != NULL);
411 *limit = i;
412 free(s);
413 return NULL;
418 * Get a string valued option.
419 * These are given as
420 * cl=^Z
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.
425 char *
426 tgetstr(const char *id, char **area)
428 struct tinfo dummy, *ti;
429 char ids[3];
431 _DIAGASSERT(id != NULL);
433 if (fbuf == NULL)
434 return NULL;
437 * XXX
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...
441 ids[0] = id[0];
442 ids[1] = id[1];
443 ids[2] = '\0';
445 if ((id[0] == 'Z') && (id[1] == 'Z')) {
446 ti = &dummy;
447 dummy.info = tbuf;
448 } else
449 ti = fbuf;
451 if (area == NULL || *area == NULL) {
452 static char capability[CAPSIZ];
453 size_t limit = sizeof(capability) - 1;
454 char *ptr;
456 ptr = capability;
457 return t_getstr(ti, ids, &ptr, &limit);
458 } else
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
468 * is returned.
470 char *
471 t_agetstr(struct tinfo *info, const char *id)
473 size_t new_size;
474 struct tbuf *tb;
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. */
482 if (new_size == 0)
483 return NULL;
485 if ((tb = info->tbuf) == NULL ||
486 (size_t) (tb->eptr - tb->ptr) < (new_size + 1)) {
487 if (new_size < CAPSIZ)
488 new_size = CAPSIZ;
489 else
490 new_size++;
492 if ((tb = malloc(sizeof(*info->tbuf))) == NULL)
493 return NULL;
495 if ((tb->data = tb->ptr = tb->eptr = malloc(new_size))
496 == NULL) {
497 free(tb);
498 return NULL;
501 tb->eptr += new_size;
503 if (info->tbuf != NULL)
504 tb->next = info->tbuf;
505 else
506 tb->next = NULL;
508 info->tbuf = tb;
510 return t_getstr(info, id, &tb->ptr, NULL);
514 * Free the buffer allocated by t_getent
517 void
518 t_freent(struct tinfo *info)
520 struct tbuf *tb, *wb;
521 _DIAGASSERT(info != NULL);
522 free(info->info);
523 if (info->up != NULL)
524 free(info->up);
525 if (info->bc != NULL)
526 free(info->bc);
527 for (tb = info->tbuf; tb;) {
528 wb = tb;
529 tb = tb->next;
530 free(wb->data);
531 free(wb);
533 free(info);
537 * Get the terminal name string from the termcap entry.
541 t_getterm(struct tinfo *info, char **area, size_t *limit)
543 char *endp;
544 size_t count;
546 _DIAGASSERT(info != NULL);
547 if ((endp = strchr(info->info, ':')) == NULL) {
548 errno = EINVAL;
549 return -1;
553 count = endp - info->info + 1;
554 if (area == NULL) {
555 _DIAGASSERT(limit != NULL);
556 *limit = count;
557 return 0;
558 } else {
559 if ((limit != NULL) && (count > *limit)) {
560 errno = E2BIG;
561 return -1;
564 (void)strlcpy(*area, info->info, count);
565 if (limit != NULL)
566 *limit -= count;
569 return 0;