Compat linux/linux32 nice(2) fix. The syscall argument is an increment
[netbsd-mini2440.git] / lib / libterm / termcap.c
blob3e47f3a29406a368a133eae506221209e56b21d4
1 /* $NetBSD: termcap.c,v 1.53 2006/12/18 22:27:18 christos 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.53 2006/12/18 22:27:18 christos 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 * stash the full buffer pointer as the ZZ capability
285 * in the termcap buffer passed.
287 plen = asprintf(&ptrbuf, ":ZZ=%p", fbuf->info);
288 (void)strlcpy(bp, fbuf->info, 1024);
289 elen = strlen(bp);
291 * backup over the entry if the addition of the full
292 * buffer pointer will overflow the buffer passed. We
293 * want to truncate the termcap entry on a capability
294 * boundary.
296 if ((elen + plen) > 1023) {
297 bp[1023 - plen] = '\0';
298 for (c = (elen - plen); c > 0; c--) {
299 if (bp[c] == ':') {
300 bp[c] = '\0';
301 break;
306 strcat(bp, ptrbuf);
307 tbuf = bp;
310 return i;
314 * Return the (numeric) option id.
315 * Numeric options look like
316 * li#80
317 * i.e. the option string is separated from the numeric value by
318 * a # character. If the option is not found we return -1.
319 * Note that we handle octal numbers beginning with 0.
322 t_getnum(struct tinfo *info, const char *id)
324 long num;
326 _DIAGASSERT(info != NULL);
327 _DIAGASSERT(id != NULL);
329 if (cgetnum(info->info, id, &num) == 0)
330 return (int)(num);
331 else
332 return (-1);
336 tgetnum(const char *id)
338 return fbuf ? t_getnum(fbuf, id) : -1;
342 * Handle a flag option.
343 * Flag options are given "naked", i.e. followed by a : or the end
344 * of the buffer. Return 1 if we find the option, or 0 if it is
345 * not given.
347 int t_getflag(struct tinfo *info, const char *id)
349 _DIAGASSERT(info != NULL);
350 _DIAGASSERT(id != NULL);
352 return (cgetcap(info->info, id, ':') != NULL);
356 tgetflag(const char *id)
358 return fbuf ? t_getflag(fbuf, id) : 0;
362 * Get a string valued option.
363 * These are given as
364 * cl=^Z
365 * Much decoding is done on the strings, and the strings are
366 * placed in area, which is a ref parameter which is updated.
367 * limit is the number of characters allowed to be put into
368 * area, this is updated.
370 char *
371 t_getstr(struct tinfo *info, const char *id, char **area, size_t *limit)
373 char *s;
374 int i;
376 _DIAGASSERT(info != NULL);
377 _DIAGASSERT(id != NULL);
378 /* area may be NULL */
381 if ((i = cgetstr(info->info, id, &s)) < 0) {
382 errno = ENOENT;
383 if ((area == NULL) && (limit != NULL))
384 *limit = 0;
385 return NULL;
388 if (area != NULL) {
390 * check if there is room for the new entry to be put into
391 * area
393 if (limit != NULL && (*limit < (size_t) i)) {
394 errno = E2BIG;
395 free(s);
396 return NULL;
399 (void)strcpy(*area, s);
400 free(s);
401 s = *area;
402 *area += i + 1;
403 if (limit != NULL)
404 *limit -= i;
405 return (s);
406 } else {
407 _DIAGASSERT(limit != NULL);
408 *limit = i;
409 free(s);
410 return NULL;
415 * Get a string valued option.
416 * These are given as
417 * cl=^Z
418 * Much decoding is done on the strings, and the strings are
419 * placed in area, which is a ref parameter which is updated.
420 * No checking on area overflow.
422 char *
423 tgetstr(const char *id, char **area)
425 struct tinfo dummy, *ti;
426 char ids[3];
428 _DIAGASSERT(id != NULL);
430 if (fbuf == NULL)
431 return NULL;
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...
438 ids[0] = id[0];
439 ids[1] = id[1];
440 ids[2] = '\0';
442 if ((id[0] == 'Z') && (id[1] == 'Z')) {
443 ti = &dummy;
444 dummy.info = tbuf;
445 } else
446 ti = fbuf;
448 if (area == NULL || *area == NULL) {
449 static char capability[CAPSIZ];
450 size_t limit = sizeof(capability) - 1;
451 char *ptr;
453 ptr = capability;
454 return t_getstr(ti, ids, &ptr, &limit);
455 } else
456 return t_getstr(ti, ids, area, NULL);
460 * Return a string valued option specified by id, allocating memory to
461 * an internal buffer as necessary. The memory allocated can be
462 * free'd by a call to t_freent().
464 * If the string is not found or memory allocation fails then NULL
465 * is returned.
467 char *
468 t_agetstr(struct tinfo *info, const char *id)
470 size_t new_size;
471 struct tbuf *tb;
473 _DIAGASSERT(info != NULL);
474 _DIAGASSERT(id != NULL);
476 t_getstr(info, id, NULL, &new_size);
478 /* either the string is empty or the capability does not exist. */
479 if (new_size == 0)
480 return NULL;
482 if ((tb = info->tbuf) == NULL ||
483 (size_t) (tb->eptr - tb->ptr) < (new_size + 1)) {
484 if (new_size < CAPSIZ)
485 new_size = CAPSIZ;
486 else
487 new_size++;
489 if ((tb = malloc(sizeof(*info->tbuf))) == NULL)
490 return NULL;
492 if ((tb->data = tb->ptr = tb->eptr = malloc(new_size))
493 == NULL) {
494 free(tb);
495 return NULL;
498 tb->eptr += new_size;
500 if (info->tbuf != NULL)
501 tb->next = info->tbuf;
502 else
503 tb->next = NULL;
505 info->tbuf = tb;
507 return t_getstr(info, id, &tb->ptr, NULL);
511 * Free the buffer allocated by t_getent
514 void
515 t_freent(struct tinfo *info)
517 struct tbuf *tb, *wb;
518 _DIAGASSERT(info != NULL);
519 free(info->info);
520 if (info->up != NULL)
521 free(info->up);
522 if (info->bc != NULL)
523 free(info->bc);
524 for (tb = info->tbuf; tb;) {
525 wb = tb;
526 tb = tb->next;
527 free(wb->data);
528 free(wb);
530 free(info);
534 * Get the terminal name string from the termcap entry.
538 t_getterm(struct tinfo *info, char **area, size_t *limit)
540 char *endp;
541 size_t count;
543 _DIAGASSERT(info != NULL);
544 if ((endp = strchr(info->info, ':')) == NULL) {
545 errno = EINVAL;
546 return -1;
550 count = endp - info->info + 1;
551 if (area == NULL) {
552 _DIAGASSERT(limit != NULL);
553 *limit = count;
554 return 0;
555 } else {
556 if ((limit != NULL) && (count > *limit)) {
557 errno = E2BIG;
558 return -1;
561 (void)strlcpy(*area, info->info, count);
562 if (limit != NULL)
563 *limit -= count;
566 return 0;