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