Annotation of src/lib/libterm/termcap.c, Revision 1.41
1.41 ! christos 1: /* $NetBSD: termcap.c,v 1.40 2001/11/02 18:24:20 christos Exp $ */
1.6 cgd 2:
1.1 cgd 3: /*
1.6 cgd 4: * Copyright (c) 1980, 1993
5: * The Regents of the University of California. All rights reserved.
1.1 cgd 6: *
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. All advertising materials mentioning features or use of this software
16: * must display the following acknowledgement:
17: * This product includes software developed by the University of
18: * California, Berkeley and its contributors.
19: * 4. Neither the name of the University nor the names of its contributors
20: * may be used to endorse or promote products derived from this software
21: * without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33: * SUCH DAMAGE.
34: */
35:
1.12 lukem 36: #include <sys/cdefs.h>
1.1 cgd 37: #ifndef lint
1.6 cgd 38: #if 0
39: static char sccsid[] = "@(#)termcap.c 8.1 (Berkeley) 6/4/93";
40: #else
1.41 ! christos 41: __RCSID("$NetBSD: termcap.c,v 1.40 2001/11/02 18:24:20 christos Exp $");
1.6 cgd 42: #endif
1.1 cgd 43: #endif /* not lint */
44:
45:
1.38 christos 46: #include <sys/types.h>
47: #include <sys/param.h>
1.21 lukem 48: #include <assert.h>
49: #include <ctype.h>
1.4 cgd 50: #include <stdio.h>
51: #include <stdlib.h>
52: #include <string.h>
1.13 lukem 53: #include <termcap.h>
1.18 blymn 54: #include <errno.h>
1.1 cgd 55: #include "pathnames.h"
1.23 blymn 56: #include "termcap_private.h"
1.18 blymn 57:
1.38 christos 58: #define PBUFSIZ MAXPATHLEN /* max length of filename path */
59: #define PVECSIZ 32 /* max number of names in path */
60:
1.1 cgd 61: /*
62: * termcap - routines for dealing with the terminal capability data base
63: *
64: * BUG: Should use a "last" pointer in tbuf, so that searching
65: * for capabilities alphabetically would not be a n**2/2
66: * process when large numbers of capabilities are given.
67: * Note: If we add a last pointer now we will screw up the
68: * tc capability. We really should compile termcap.
69: *
70: * Essentially all the work here is scanning and decoding escapes
71: * in string capabilities. We don't use stdio because the editor
72: * doesn't, and because living w/o it is not hard.
73: */
74:
1.6 cgd 75: static char *tbuf; /* termcap buffer */
1.18 blymn 76: static struct tinfo *fbuf; /* untruncated termcap buffer */
1.1 cgd 77:
78: /*
1.31 blymn 79: * Set the termcap entry to the arbitrary string passed in, this can
80: * be used to provide a "dummy" termcap entry if a real one does not
81: * exist. This function will malloc the buffer and space for the
82: * string. If an error occurs return -1 otherwise return 0.
83: */
84: int
85: t_setinfo(struct tinfo **bp, const char *entry)
86: {
1.36 blymn 87: char capability[256], *cap_ptr;
88: size_t limit;
89:
1.37 lukem 90: _DIAGASSERT(bp != NULL);
91: _DIAGASSERT(entry != NULL);
92:
1.31 blymn 93: if ((*bp = malloc(sizeof(struct tinfo))) == NULL)
94: return -1;
95:
96: if (((*bp)->info = (char *) malloc(strlen(entry) + 1)) == NULL)
97: return -1;
98:
99: strcpy((*bp)->info, entry);
1.36 blymn 100:
101: cap_ptr = capability;
102: limit = 255;
103: (*bp)->up = t_getstr(*bp, "up", &cap_ptr, &limit);
1.39 christos 104: if ((*bp)->up)
105: (*bp)->up = strdup((*bp)->up);
1.36 blymn 106: cap_ptr = capability;
107: limit = 255;
108: (*bp)->bc = t_getstr(*bp, "bc", &cap_ptr, &limit);
1.39 christos 109: if ((*bp)->bc)
110: (*bp)->bc = strdup((*bp)->bc);
1.40 christos 111: (*bp)->tbuf = NULL;
1.36 blymn 112:
1.31 blymn 113: return 0;
114: }
115:
116: /*
1.18 blymn 117: * Get an extended entry for the terminal name. This differs from
118: * tgetent only in a) the buffer is malloc'ed for the caller and
119: * b) the termcap entry is not truncated to 1023 characters.
1.1 cgd 120: */
1.18 blymn 121:
1.4 cgd 122: int
1.18 blymn 123: t_getent(bp, name)
124: struct tinfo **bp;
1.14 mycroft 125: const char *name;
1.1 cgd 126: {
1.13 lukem 127: char *p;
128: char *cp;
1.6 cgd 129: char **fname;
130: char *home;
1.27 blymn 131: int i, did_getset;
1.36 blymn 132: size_t limit;
1.6 cgd 133: char pathbuf[PBUFSIZ]; /* holds raw path of filenames */
134: char *pathvec[PVECSIZ]; /* to point to names in pathbuf */
135: char *termpath;
1.36 blymn 136: char capability[256], *cap_ptr;
137:
1.1 cgd 138:
1.21 lukem 139: _DIAGASSERT(bp != NULL);
140: _DIAGASSERT(name != NULL);
141:
1.18 blymn 142: if ((*bp = malloc(sizeof(struct tinfo))) == NULL) return 0;
143:
1.6 cgd 144: fname = pathvec;
1.1 cgd 145: p = pathbuf;
146: cp = getenv("TERMCAP");
147: /*
148: * TERMCAP can have one of two things in it. It can be the
1.9 mikel 149: * name of a file to use instead of
150: * /usr/share/misc/termcap. In this case it better start with
151: * a "/". Or it can be an entry to use so we don't have to
1.16 abs 152: * read the file. In this case cgetset() withh crunch out the
153: * newlines. If TERMCAP does not hold a file name then a path
154: * of names is searched instead. The path is found in the
155: * TERMPATH variable, or becomes _PATH_DEF ("$HOME/.termcap
156: * /usr/share/misc/termcap") if no TERMPATH exists.
1.1 cgd 157: */
158: if (!cp || *cp != '/') { /* no TERMCAP or it holds an entry */
1.7 pk 159: if ((termpath = getenv("TERMPATH")) != NULL)
1.38 christos 160: (void)strlcpy(pathbuf, termpath, sizeof(pathbuf));
1.1 cgd 161: else {
1.7 pk 162: if ((home = getenv("HOME")) != NULL) {
163: /* set up default */
1.1 cgd 164: p += strlen(home); /* path, looking in */
1.38 christos 165: (void)strlcpy(pathbuf, home,
166: sizeof(pathbuf)); /* $HOME first */
167: if ((p - pathbuf) < sizeof(pathbuf) - 1)
168: *p++ = '/';
1.1 cgd 169: } /* if no $HOME look in current directory */
1.38 christos 170: if ((p - pathbuf) < sizeof(pathbuf) - 1) {
171: (void)strlcpy(p, _PATH_DEF,
172: sizeof(pathbuf) - (p - pathbuf));
173: }
1.1 cgd 174: }
175: }
1.38 christos 176: else {
177: /* user-defined name in TERMCAP; still can be tokenized */
178: (void)strlcpy(pathbuf, cp, sizeof(pathbuf));
179: }
1.1 cgd 180:
181: *fname++ = pathbuf; /* tokenize path into vector of names */
182: while (*++p)
183: if (*p == ' ' || *p == ':') {
184: *p = '\0';
185: while (*++p)
186: if (*p != ' ' && *p != ':')
187: break;
188: if (*p == '\0')
189: break;
190: *fname++ = p;
191: if (fname >= pathvec + PVECSIZ) {
192: fname--;
193: break;
194: }
195: }
1.38 christos 196: *fname = NULL; /* mark end of vector */
1.27 blymn 197:
1.28 christos 198: /*
199: * try ignoring TERMCAP if it has a ZZ in it, we do this
200: * because a TERMCAP with ZZ in it indicates the entry has been
201: * exported by another program using the "old" interface, the
202: * termcap entry has been truncated and ZZ points to an address
203: * in the exporting programs memory space which is of no use
204: * here - anyone who is exporting the termcap entry and then
205: * reading it back again in the same program deserves to be
206: * taken out, beaten up, dragged about, shot and then hurt some
207: * more.
208: */
1.27 blymn 209: did_getset = 0;
210: if (cp && *cp && *cp != '/' && strstr(cp, ":ZZ") == NULL) {
211: did_getset = 1;
1.4 cgd 212: if (cgetset(cp) < 0)
1.6 cgd 213: return (-2);
1.27 blymn 214: }
215:
1.11 mrg 216: /*
217: * XXX potential security hole here in a set-id program if the
218: * user had setup name to be built from a path they can not
219: * normally read.
220: */
1.18 blymn 221: (*bp)->info = NULL;
222: i = cgetent(&((*bp)->info), pathvec, name);
1.8 jtc 223:
1.28 christos 224: /*
225: * if we get an error and we skipped doing the cgetset before
226: * we try with TERMCAP in place - we may be using a truncated
227: * termcap entry but what else can one do?
228: */
1.27 blymn 229: if ((i < 0) && (did_getset == 0)) {
230: if (cp && *cp && *cp != '/')
231: if (cgetset(cp) < 0)
232: return (-2);
233: i = cgetent(&((*bp)->info), pathvec, name);
234: }
1.31 blymn 235:
1.6 cgd 236: /* no tc reference loop return code in libterm XXX */
1.4 cgd 237: if (i == -3)
1.6 cgd 238: return (-1);
1.36 blymn 239:
240: /* fill in t_goto capabilities - this prevents memory leaks
241: * and is more efficient than fetching these capabilities
242: * every time t_goto is called.
243: */
244: if (i >= 0) {
245: cap_ptr = capability;
246: limit = 255;
247: (*bp)->up = t_getstr(*bp, "up", &cap_ptr, &limit);
1.39 christos 248: if ((*bp)->up)
249: (*bp)->up = strdup((*bp)->up);
1.36 blymn 250: cap_ptr = capability;
251: limit = 255;
252: (*bp)->bc = t_getstr(*bp, "bc", &cap_ptr, &limit);
1.39 christos 253: if ((*bp)->bc)
254: (*bp)->bc = strdup((*bp)->bc);
1.40 christos 255: (*bp)->tbuf = NULL;
1.36 blymn 256: }
257:
1.6 cgd 258: return (i + 1);
1.1 cgd 259: }
260:
261: /*
1.18 blymn 262: * Get an entry for terminal name in buffer bp from the termcap file.
263: */
264: int
265: tgetent(bp, name)
266: char *bp;
267: const char *name;
268: {
269: int i, plen, elen, c;
270: char *ptrbuf = NULL;
271:
272: i = t_getent(&fbuf, name);
273:
274: if (i == 1) {
1.28 christos 275: /*
276: * stash the full buffer pointer as the ZZ capability
277: * in the termcap buffer passed.
278: */
1.18 blymn 279: plen = asprintf(&ptrbuf, ":ZZ=%p", fbuf->info);
1.38 christos 280: (void)strlcpy(bp, fbuf->info, 1024);
1.18 blymn 281: elen = strlen(bp);
1.28 christos 282: /*
283: * backup over the entry if the addition of the full
284: * buffer pointer will overflow the buffer passed. We
285: * want to truncate the termcap entry on a capability
286: * boundary.
287: */
1.18 blymn 288: if ((elen + plen) > 1023) {
289: bp[1023 - plen] = '\0';
290: for (c = (elen - plen); c > 0; c--) {
291: if (bp[c] == ':') {
292: bp[c] = '\0';
293: break;
294: }
295: }
296: }
297:
298: strcat(bp, ptrbuf);
299: tbuf = bp;
300: }
301:
302: return i;
303: }
304:
305: /*
1.1 cgd 306: * Return the (numeric) option id.
307: * Numeric options look like
308: * li#80
309: * i.e. the option string is separated from the numeric value by
310: * a # character. If the option is not found we return -1.
311: * Note that we handle octal numbers beginning with 0.
312: */
1.4 cgd 313: int
1.18 blymn 314: t_getnum(info, id)
315: struct tinfo *info;
1.14 mycroft 316: const char *id;
1.1 cgd 317: {
1.4 cgd 318: long num;
1.1 cgd 319:
1.28 christos 320: _DIAGASSERT(info != NULL);
1.21 lukem 321: _DIAGASSERT(id != NULL);
322:
1.18 blymn 323: if (cgetnum(info->info, id, &num) == 0)
1.15 agc 324: return (int)(num);
1.4 cgd 325: else
1.6 cgd 326: return (-1);
1.1 cgd 327: }
328:
1.18 blymn 329: int
330: tgetnum(id)
331: const char *id;
332: {
333: return t_getnum(fbuf, id);
334: }
335:
1.1 cgd 336: /*
337: * Handle a flag option.
338: * Flag options are given "naked", i.e. followed by a : or the end
339: * of the buffer. Return 1 if we find the option, or 0 if it is
340: * not given.
341: */
1.18 blymn 342: int t_getflag(info, id)
343: struct tinfo *info;
344: const char *id;
345: {
1.28 christos 346: _DIAGASSERT(info != NULL);
347: _DIAGASSERT(id != NULL);
348:
1.18 blymn 349: return (cgetcap(info->info, id, ':') != NULL);
350: }
351:
1.6 cgd 352: int
1.1 cgd 353: tgetflag(id)
1.14 mycroft 354: const char *id;
1.1 cgd 355: {
1.18 blymn 356: return t_getflag(fbuf, id);
1.1 cgd 357: }
358:
359: /*
360: * Get a string valued option.
361: * These are given as
362: * cl=^Z
363: * Much decoding is done on the strings, and the strings are
364: * placed in area, which is a ref parameter which is updated.
1.18 blymn 365: * limit is the number of characters allowed to be put into
366: * area, this is updated.
1.1 cgd 367: */
368: char *
1.18 blymn 369: t_getstr(info, id, area, limit)
370: struct tinfo *info;
1.14 mycroft 371: const char *id;
372: char **area;
1.19 blymn 373: size_t *limit;
1.1 cgd 374: {
1.4 cgd 375: char *s;
1.1 cgd 376: int i;
1.21 lukem 377:
1.28 christos 378: _DIAGASSERT(info != NULL);
1.21 lukem 379: _DIAGASSERT(id != NULL);
1.29 lukem 380: /* area may be NULL */
1.17 simonb 381:
1.4 cgd 382:
1.28 christos 383: if ((i = cgetstr(info->info, id, &s)) < 0) {
1.18 blymn 384: errno = ENOENT;
1.26 blymn 385: if ((area == NULL) && (limit != NULL))
1.25 blymn 386: *limit = 0;
1.18 blymn 387: return NULL;
388: }
389:
1.20 blymn 390: if (area != NULL) {
1.28 christos 391: /*
392: * check if there is room for the new entry to be put into
393: * area
394: */
1.20 blymn 395: if (limit != NULL && (*limit < i)) {
1.35 thorpej 396: errno = E2BIG;
1.34 christos 397: free(s);
1.20 blymn 398: return NULL;
399: }
1.35 thorpej 400:
1.39 christos 401: (void)strcpy(*area, s);
402: free(s);
403: s = *area;
1.20 blymn 404: *area += i + 1;
405: if (limit != NULL) *limit -= i;
1.35 thorpej 406:
407: return (s);
1.20 blymn 408: } else {
1.28 christos 409: _DIAGASSERT(limit != NULL);
1.20 blymn 410: *limit = i;
1.33 itojun 411: free(s);
1.4 cgd 412: return NULL;
1.18 blymn 413: }
414: }
415:
416: /*
417: * Get a string valued option.
418: * These are given as
419: * cl=^Z
420: * Much decoding is done on the strings, and the strings are
421: * placed in area, which is a ref parameter which is updated.
422: * No checking on area overflow.
423: */
424: char *
425: tgetstr(id, area)
426: const char *id;
427: char **area;
428: {
429: struct tinfo dummy;
1.28 christos 430: char ids[3];
431:
432: _DIAGASSERT(id != NULL);
433: /*
434: * XXX
435: * This is for all the boneheaded programs that relied on tgetstr
436: * to look only at the first 2 characters of the string passed...
437: */
438: ids[0] = id[0];
439: ids[1] = id[1];
440: ids[2] = '\0';
1.18 blymn 441:
442: if ((id[0] == 'Z') && (id[1] == 'Z')) {
443: dummy.info = tbuf;
1.28 christos 444: return t_getstr(&dummy, ids, area, NULL);
1.18 blymn 445: }
446: else
1.28 christos 447: return t_getstr(fbuf, ids, area, NULL);
1.18 blymn 448: }
449:
1.30 blymn 450: /*
1.41 ! christos 451: * Return a string valued option specified by id, allocating memory to
! 452: * an internal buffer as necessary. The memory allocated can be
! 453: * free'd by a call to t_freent().
1.39 christos 454: *
1.41 ! christos 455: * If the string is not found or memory allocation fails then NULL
! 456: * is returned.
1.30 blymn 457: */
1.40 christos 458: #define BSIZE 256
1.30 blymn 459: char *
1.40 christos 460: t_agetstr(struct tinfo *info, const char *id)
1.30 blymn 461: {
1.39 christos 462: size_t new_size;
1.40 christos 463: struct tbuf *tb;
1.30 blymn 464:
465: _DIAGASSERT(info != NULL);
466: _DIAGASSERT(id != NULL);
467:
468: t_getstr(info, id, NULL, &new_size);
469:
1.40 christos 470: /* either the string is empty or the capability does not exist. */
471: if (new_size == 0)
1.30 blymn 472: return NULL;
473:
1.40 christos 474: if ((tb = info->tbuf) == NULL || (tb->eptr - tb->ptr) < new_size) {
475: if (new_size < BSIZE)
476: new_size = BSIZE;
477: else
478: new_size++;
479:
480: if ((tb = malloc(sizeof(*info->tbuf))) == NULL)
481: return NULL;
482:
483: if ((tb->data = tb->ptr = tb->eptr = malloc(new_size)) == NULL)
1.30 blymn 484: return NULL;
1.40 christos 485:
486: tb->eptr += new_size;
487:
488: if (info->tbuf != NULL)
489: tb->next = info->tbuf;
490: else
491: tb->next = NULL;
492:
493: info->tbuf = tb;
1.30 blymn 494: }
1.40 christos 495: return t_getstr(info, id, &tb->ptr, NULL);
1.30 blymn 496: }
497:
1.18 blymn 498: /*
499: * Free the buffer allocated by t_getent
500: *
501: */
502: void
503: t_freent(info)
504: struct tinfo *info;
505: {
1.40 christos 506: struct tbuf *tb;
1.28 christos 507: _DIAGASSERT(info != NULL);
1.18 blymn 508: free(info->info);
1.36 blymn 509: if (info->up != NULL)
510: free(info->up);
511: if (info->bc != NULL)
512: free(info->bc);
1.40 christos 513: for (tb = info->tbuf; tb;) {
514: tb = info->tbuf->next;
515: free(info->tbuf->data);
516: free(info->tbuf);
517: }
1.18 blymn 518: free(info);
1.1 cgd 519: }
1.24 blymn 520:
521: /*
522: * Get the terminal name string from the termcap entry.
523: *
524: */
525: int
1.28 christos 526: t_getterm(info, area, limit)
527: struct tinfo *info;
528: char **area;
529: size_t *limit;
1.24 blymn 530: {
531: char *endp;
532: size_t count;
533:
1.28 christos 534: _DIAGASSERT(info != NULL);
535: if ((endp = strchr(info->info, ':')) == NULL) {
1.24 blymn 536: errno = EINVAL;
537: return -1;
538: }
539:
540:
541: count = endp - info->info + 1;
542: if (area == NULL) {
1.28 christos 543: _DIAGASSERT(limit != NULL);
1.24 blymn 544: *limit = count;
545: return 0;
546: } else {
547: if ((limit != NULL) && (count > *limit)) {
548: errno = E2BIG;
549: return -1;
550: }
551:
1.38 christos 552: (void)strlcpy(*area, info->info, count);
1.24 blymn 553: if (limit != NULL)
554: *limit -= count;
555: }
556:
557: return 0;
558: }
CVSweb <webmaster@jp.NetBSD.org>