Bug Summary

File:src/lib/libcurses/tinfo/read_termcap.c
Warning:line 778, column 5
Value stored to 'pvec' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name read_termcap.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/lib/libcurses/obj -resource-dir /usr/local/lib/clang/13.0.0 -I . -I /usr/src/lib/libcurses -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/lib/libcurses/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/lib/libcurses/tinfo/read_termcap.c
1/* $OpenBSD: read_termcap.c,v 1.23 2021/10/24 21:24:20 deraadt Exp $ */
2
3/****************************************************************************
4 * Copyright (c) 1998-2005,2006 Free Software Foundation, Inc. *
5 * *
6 * Permission is hereby granted, free of charge, to any person obtaining a *
7 * copy of this software and associated documentation files (the *
8 * "Software"), to deal in the Software without restriction, including *
9 * without limitation the rights to use, copy, modify, merge, publish, *
10 * distribute, distribute with modifications, sublicense, and/or sell *
11 * copies of the Software, and to permit persons to whom the Software is *
12 * furnished to do so, subject to the following conditions: *
13 * *
14 * The above copyright notice and this permission notice shall be included *
15 * in all copies or substantial portions of the Software. *
16 * *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
20 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
23 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
24 * *
25 * Except as contained in this notice, the name(s) of the above copyright *
26 * holders shall not be used in advertising or otherwise to promote the *
27 * sale, use or other dealings in this Software without prior written *
28 * authorization. *
29 ****************************************************************************/
30
31/****************************************************************************
32 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
33 * and: Eric S. Raymond <esr@snark.thyrsus.com> *
34 * and: Thomas E. Dickey 1996-on *
35 ****************************************************************************/
36
37/*
38 * Termcap compatibility support
39 *
40 * If your OS integrator didn't install a terminfo database, you can call
41 * _nc_read_termcap_entry() to support reading and translating capabilities
42 * from the system termcap file. This is a kludge; it will bulk up and slow
43 * down every program that uses ncurses, and translated termcap entries cannot
44 * use full terminfo capabilities. Don't use it unless you absolutely have to;
45 * instead, get your system people to run tic(1) from root on the terminfo
46 * master included with ncurses to translate it into a terminfo database.
47 *
48 * If USE_GETCAP is enabled, we use what is effectively a copy of the 4.4BSD
49 * getcap code to fetch entries. There are disadvantages to this; mainly that
50 * getcap(3) does its own resolution, meaning that entries read in in this way
51 * can't reference the terminfo tree. The only thing it buys is faster startup
52 * time, getcap(3) is much faster than our tic parser.
53 */
54
55#include <curses.priv.h>
56
57#include <ctype.h>
58#include <sys/types.h>
59#include <sys/stat.h>
60#include <tic.h>
61#include <term_entry.h>
62
63MODULE_ID("$Id: read_termcap.c,v 1.23 2021/10/24 21:24:20 deraadt Exp $")
64
65#if !PURE_TERMINFO0
66
67#define TC_SUCCESS0 0
68#define TC_NOT_FOUND-1 -1
69#define TC_SYS_ERR-2 -2
70#define TC_REF_LOOP-3 -3
71#define TC_UNRESOLVED-4 -4 /* this is not returned by BSD cgetent */
72
73static NCURSES_CONSTconst char *
74get_termpath(void)
75{
76 NCURSES_CONSTconst char *result;
77
78 if (!use_terminfo_vars()(!issetugid()) || (result = getenv("TERMPATH")) == 0)
79 result = TERMPATH"none";
80 T(("TERMPATH is %s", result));
81 return result;
82}
83
84#if USE_GETCAP1
85
86#if HAVE_BSD_CGETENT1
87#define _nc_cgetcapcgetcap cgetcap
88#define _nc_cgetent(buf, oline, db_array, name)cgetent(buf, db_array, name) cgetent(buf, db_array, name)
89#define _nc_cgetmatchcgetmatch cgetmatch
90#define _nc_cgetsetcgetset cgetset
91#else
92static int _nc_cgetmatchcgetmatch(char *, const char *);
93static int _nc_getent(char **, unsigned *, int *, int, char **, int, const char
94 *, int, char *);
95static int _nc_nfcmp(const char *, char *);
96
97/*-
98 * Copyright (c) 1992, 1993
99 * The Regents of the University of California. All rights reserved.
100 *
101 * This code is derived from software contributed to Berkeley by
102 * Casey Leedom of Lawrence Livermore National Laboratory.
103 *
104 * Redistribution and use in source and binary forms, with or without
105 * modification, are permitted provided that the following conditions
106 * are met:
107 * 1. Redistributions of source code must retain the above copyright
108 * notice, this list of conditions and the following disclaimer.
109 * 2. Redistributions in binary form must reproduce the above copyright
110 * notice, this list of conditions and the following disclaimer in the
111 * documentation and/or other materials provided with the distribution.
112 * 3. Neither the name of the University nor the names of its contributors
113 * may be used to endorse or promote products derived from this software
114 * without specific prior written permission.
115 *
116 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
117 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
118 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
119 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
120 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
121 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
122 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
123 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
124 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
125 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
126 * SUCH DAMAGE.
127 */
128
129#define BFRAG 1024
130#define BSIZE 1024
131#define MAX_RECURSION 32 /* maximum getent recursion */
132
133static size_t topreclen; /* toprec length */
134static char *toprec; /* Additional record specified by cgetset() */
135static int gottoprec; /* Flag indicating retrieval of toprecord */
136
137/*
138 * Cgetset() allows the addition of a user specified buffer to be added to the
139 * database array, in effect "pushing" the buffer on top of the virtual
140 * database. 0 is returned on success, -1 on failure.
141 */
142static int
143_nc_cgetsetcgetset(const char *ent)
144{
145 if (ent == 0) {
146 FreeIfNeeded(toprec)if ((toprec) != 0) free(toprec);
147 toprec = 0;
148 topreclen = 0;
149 return (0);
150 }
151 topreclen = strlen(ent);
152 if ((toprec = typeMalloc(char, topreclen + 1)(char *)malloc((topreclen + 1)*sizeof(char))) == 0) {
153 errno(*__errno()) = ENOMEM12;
154 return (-1);
155 }
156 gottoprec = 0;
157 (void) strlcpy(toprec, ent, topreclen + 1);
158 return (0);
159}
160
161/*
162 * Cgetcap searches the capability record buf for the capability cap with type
163 * `type'. A pointer to the value of cap is returned on success, 0 if the
164 * requested capability couldn't be found.
165 *
166 * Specifying a type of ':' means that nothing should follow cap (:cap:). In
167 * this case a pointer to the terminating ':' or NUL will be returned if cap is
168 * found.
169 *
170 * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
171 * return 0.
172 */
173static char *
174_nc_cgetcapcgetcap(char *buf, const char *cap, int type)
175{
176 register const char *cp;
177 register char *bp;
178
179 bp = buf;
180 for (;;) {
181 /*
182 * Skip past the current capability field - it's either the
183 * name field if this is the first time through the loop, or
184 * the remainder of a field whose name failed to match cap.
185 */
186 for (;;) {
187 if (*bp == '\0')
188 return (0);
189 else if (*bp++ == ':')
190 break;
191 }
192
193 /*
194 * Try to match (cap, type) in buf.
195 */
196 for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
197 continue;
198 if (*cp != '\0')
199 continue;
200 if (*bp == '@')
201 return (0);
202 if (type == ':') {
203 if (*bp != '\0' && *bp != ':')
204 continue;
205 return (bp);
206 }
207 if (*bp != type)
208 continue;
209 bp++;
210 return (*bp == '@' ? 0 : bp);
211 }
212 /* NOTREACHED */
213}
214
215/*
216 * Cgetent extracts the capability record name from the NULL terminated file
217 * array db_array and returns a pointer to a malloc'd copy of it in buf. Buf
218 * must be retained through all subsequent calls to cgetcap, cgetnum, cgetflag,
219 * and cgetstr, but may then be freed.
220 *
221 * Returns:
222 *
223 * positive # on success (i.e., the index in db_array)
224 * TC_NOT_FOUND if the requested record couldn't be found
225 * TC_SYS_ERR if a system error was encountered (e.g.,couldn't open a file)
226 * TC_REF_LOOP if a potential reference loop is detected
227 * TC_UNRESOLVED if we had too many recurrences to resolve
228 */
229static int
230_nc_cgetent(char **buf, int *oline, char **db_array, const char *name)cgetent(char **buf, char **db_array, const char *name)
231{
232 unsigned dummy;
233
234 return (_nc_getent(buf, &dummy, oline, 0, db_array, -1, name, 0, 0));
235}
236
237/*
238 * Getent implements the functions of cgetent. If fd is non-negative,
239 * *db_array has already been opened and fd is the open file descriptor. We
240 * do this to save time and avoid using up file descriptors for tc=
241 * recursions.
242 *
243 * Getent returns the same success/failure codes as cgetent. On success, a
244 * pointer to a malloc'd capability record with all tc= capabilities fully
245 * expanded and its length (not including trailing ASCII NUL) are left in
246 * *cap and *len.
247 *
248 * Basic algorithm:
249 * + Allocate memory incrementally as needed in chunks of size BFRAG
250 * for capability buffer.
251 * + Recurse for each tc=name and interpolate result. Stop when all
252 * names interpolated, a name can't be found, or depth exceeds
253 * MAX_RECURSION.
254 */
255#define DOALLOC(size) typeRealloc(char, size, record)(char *)_nc_doalloc(record, (size)*sizeof(char))
256static int
257_nc_getent(
258 char **cap, /* termcap-content */
259 unsigned *len, /* length, needed for recursion */
260 int *beginning, /* line-number at match */
261 int in_array, /* index in 'db_array[] */
262 char **db_array, /* list of files to search */
263 int fd,
264 const char *name,
265 int depth,
266 char *nfield)
267{
268 register char *r_end, *rp;
269 int myfd = FALSE0;
270 char *record = 0;
271 int tc_not_resolved;
272 int current;
273 int lineno;
274
275 /*
276 * Return with ``loop detected'' error if we've recurred more than
277 * MAX_RECURSION times.
278 */
279 if (depth > MAX_RECURSION)
280 return (TC_REF_LOOP-3);
281
282 /*
283 * Check if we have a top record from cgetset().
284 */
285 if (depth == 0 && toprec != 0 && _nc_cgetmatchcgetmatch(toprec, name) == 0) {
286 if ((record = DOALLOC(topreclen + BFRAG)) == 0) {
287 errno(*__errno()) = ENOMEM12;
288 return (TC_SYS_ERR-2);
289 }
290 (void) strlcpy(record, toprec, topreclen + BFRAG);
291 rp = record + topreclen + 1;
292 r_end = rp + BFRAG;
293 current = in_array;
294 } else {
295 int foundit;
296
297 /*
298 * Allocate first chunk of memory.
299 */
300 if ((record = DOALLOC(BFRAG)) == 0) {
301 errno(*__errno()) = ENOMEM12;
302 return (TC_SYS_ERR-2);
303 }
304 rp = r_end = record + BFRAG;
305 foundit = FALSE0;
306
307 /*
308 * Loop through database array until finding the record.
309 */
310 for (current = in_array; db_array[current] != 0; current++) {
311 int eof = FALSE0;
312
313 /*
314 * Open database if not already open.
315 */
316 if (fd >= 0) {
317 (void) lseek(fd, (off_t) 0, SEEK_SET0);
318 } else if ((_nc_access(db_array[current], R_OK0x04) < 0)
319 || (fd = open(db_array[current], O_RDONLY0x0000)) < 0) {
320 /* No error on unfound file. */
321 if (errno(*__errno()) == ENOENT2)
322 continue;
323 free(record);
324 return (TC_SYS_ERR-2);
325 } else {
326 myfd = TRUE1;
327 }
328 lineno = 0;
329
330 /*
331 * Find the requested capability record ...
332 */
333 {
334 char buf[2048];
335 register char *b_end = buf;
336 register char *bp = buf;
337 register int c;
338
339 /*
340 * Loop invariants:
341 * There is always room for one more character in record.
342 * R_end always points just past end of record.
343 * Rp always points just past last character in record.
344 * B_end always points just past last character in buf.
345 * Bp always points at next character in buf.
346 */
347
348 for (;;) {
349 int first = lineno + 1;
350
351 /*
352 * Read in a line implementing (\, newline)
353 * line continuation.
354 */
355 rp = record;
356 for (;;) {
357 if (bp >= b_end) {
358 int n;
359
360 n = read(fd, buf, sizeof(buf));
361 if (n <= 0) {
362 if (myfd)
363 (void) close(fd);
364 if (n < 0) {
365 free(record);
366 return (TC_SYS_ERR-2);
367 }
368 fd = -1;
369 eof = TRUE1;
370 break;
371 }
372 b_end = buf + n;
373 bp = buf;
374 }
375
376 c = *bp++;
377 if (c == '\n') {
378 lineno++;
379 if (rp == record || *(rp - 1) != '\\')
380 break;
381 }
382 *rp++ = c;
383
384 /*
385 * Enforce loop invariant: if no room
386 * left in record buffer, try to get
387 * some more.
388 */
389 if (rp >= r_end) {
390 unsigned pos;
391 size_t newsize;
392
393 pos = rp - record;
394 newsize = r_end - record + BFRAG;
395 record = DOALLOC(newsize);
396 if (record == 0) {
397 if (myfd)
398 (void) close(fd);
399 errno(*__errno()) = ENOMEM12;
400 return (TC_SYS_ERR-2);
401 }
402 r_end = record + newsize;
403 rp = record + pos;
404 }
405 }
406 /* loop invariant lets us do this */
407 *rp++ = '\0';
408
409 /*
410 * If encountered eof check next file.
411 */
412 if (eof)
413 break;
414
415 /*
416 * Toss blank lines and comments.
417 */
418 if (*record == '\0' || *record == '#')
419 continue;
420
421 /*
422 * See if this is the record we want ...
423 */
424 if (_nc_cgetmatchcgetmatch(record, name) == 0
425 && (nfield == 0
426 || !_nc_nfcmp(nfield, record))) {
427 foundit = TRUE1;
428 *beginning = first;
429 break; /* found it! */
430 }
431 }
432 }
433 if (foundit)
434 break;
435 }
436
437 if (!foundit)
438 return (TC_NOT_FOUND-1);
439 }
440
441 /*
442 * Got the capability record, but now we have to expand all tc=name
443 * references in it ...
444 */
445 {
446 register char *newicap, *s;
447 register int newilen;
448 unsigned ilen;
449 int diff, iret, tclen, oline;
450 char *icap, *scan, *tc, *tcstart, *tcend;
451
452 /*
453 * Loop invariants:
454 * There is room for one more character in record.
455 * R_end points just past end of record.
456 * Rp points just past last character in record.
457 * Scan points at remainder of record that needs to be
458 * scanned for tc=name constructs.
459 */
460 scan = record;
461 tc_not_resolved = FALSE0;
462 for (;;) {
463 if ((tc = _nc_cgetcapcgetcap(scan, "tc", '=')) == 0)
464 break;
465
466 /*
467 * Find end of tc=name and stomp on the trailing `:'
468 * (if present) so we can use it to call ourselves.
469 */
470 s = tc;
471 while (*s != '\0') {
472 if (*s++ == ':') {
473 *(s - 1) = '\0';
474 break;
475 }
476 }
477 tcstart = tc - 3;
478 tclen = s - tcstart;
479 tcend = s;
480
481 iret = _nc_getent(&icap, &ilen, &oline, current, db_array, fd,
482 tc, depth + 1, 0);
483 newicap = icap; /* Put into a register. */
484 newilen = ilen;
485 if (iret != TC_SUCCESS0) {
486 /* an error */
487 if (iret < TC_NOT_FOUND-1) {
488 if (myfd)
489 (void) close(fd);
490 free(record);
491 return (iret);
492 }
493 if (iret == TC_UNRESOLVED-4)
494 tc_not_resolved = TRUE1;
495 /* couldn't resolve tc */
496 if (iret == TC_NOT_FOUND-1) {
497 *(s - 1) = ':';
498 scan = s - 1;
499 tc_not_resolved = TRUE1;
500 continue;
501 }
502 }
503
504 /* not interested in name field of tc'ed record */
505 s = newicap;
506 while (*s != '\0' && *s++ != ':') ;
507 newilen -= s - newicap;
508 newicap = s;
509
510 /* make sure interpolated record is `:'-terminated */
511 s += newilen;
512 if (*(s - 1) != ':') {
513 *s = ':'; /* overwrite NUL with : */
514 newilen++;
515 }
516
517 /*
518 * Make sure there's enough room to insert the
519 * new record.
520 */
521 diff = newilen - tclen;
522 if (diff >= r_end - rp) {
523 unsigned pos, tcpos, tcposend;
524 size_t newsize;
525
526 pos = rp - record;
527 newsize = r_end - record + diff + BFRAG;
528 tcpos = tcstart - record;
529 tcposend = tcend - record;
530 record = DOALLOC(newsize);
531 if (record == 0) {
532 if (myfd)
533 (void) close(fd);
534 free(icap);
535 errno(*__errno()) = ENOMEM12;
536 return (TC_SYS_ERR-2);
537 }
538 r_end = record + newsize;
539 rp = record + pos;
540 tcstart = record + tcpos;
541 tcend = record + tcposend;
542 }
543
544 /*
545 * Insert tc'ed record into our record.
546 */
547 s = tcstart + newilen;
548 memmove(s, tcend, (size_t) (rp - tcend));
549 memmove(tcstart, newicap, (size_t) newilen);
550 rp += diff;
551 free(icap);
552
553 /*
554 * Start scan on `:' so next cgetcap works properly
555 * (cgetcap always skips first field).
556 */
557 scan = s - 1;
558 }
559 }
560
561 /*
562 * Close file (if we opened it), give back any extra memory, and
563 * return capability, length and success.
564 */
565 if (myfd)
566 (void) close(fd);
567 *len = rp - record - 1; /* don't count NUL */
568 if (r_end > rp) {
569 if ((record = DOALLOC((size_t) (rp - record))) == 0) {
570 errno(*__errno()) = ENOMEM12;
571 return (TC_SYS_ERR-2);
572 }
573 }
574
575 *cap = record;
576 if (tc_not_resolved)
577 return (TC_UNRESOLVED-4);
578 return (current);
579}
580
581/*
582 * Cgetmatch will return 0 if name is one of the names of the capability
583 * record buf, -1 if not.
584 */
585static int
586_nc_cgetmatchcgetmatch(char *buf, const char *name)
587{
588 register const char *np;
589 register char *bp;
590
591 /*
592 * Start search at beginning of record.
593 */
594 bp = buf;
595 for (;;) {
596 /*
597 * Try to match a record name.
598 */
599 np = name;
600 for (;;) {
601 if (*np == '\0') {
602 if (*bp == '|' || *bp == ':' || *bp == '\0')
603 return (0);
604 else
605 break;
606 } else if (*bp++ != *np++) {
607 break;
608 }
609 }
610
611 /*
612 * Match failed, skip to next name in record.
613 */
614 bp--; /* a '|' or ':' may have stopped the match */
615 for (;;) {
616 if (*bp == '\0' || *bp == ':')
617 return (-1); /* match failed totally */
618 else if (*bp++ == '|')
619 break; /* found next name */
620 }
621 }
622}
623
624/*
625 * Compare name field of record.
626 */
627static int
628_nc_nfcmp(const char *nf, char *rec)
629{
630 char *cp, tmp;
631 int ret;
632
633 for (cp = rec; *cp != ':'; cp++) ;
634
635 tmp = *(cp + 1);
636 *(cp + 1) = '\0';
637 ret = strcmp(nf, rec);
638 *(cp + 1) = tmp;
639
640 return (ret);
641}
642#endif /* HAVE_BSD_CGETENT */
643
644/*
645 * Since ncurses provides its own 'tgetent()', we cannot use the native one.
646 * So we reproduce the logic to get down to cgetent() -- or our cut-down
647 * version of that -- to circumvent the problem of configuring against the
648 * termcap library.
649 */
650#define USE_BSD_TGETENT1 1
651
652#if USE_BSD_TGETENT1
653/*
654 * Copyright (c) 1980, 1993
655 * The Regents of the University of California. All rights reserved.
656 *
657 * Redistribution and use in source and binary forms, with or without
658 * modification, are permitted provided that the following conditions
659 * are met:
660 * 1. Redistributions of source code must retain the above copyright
661 * notice, this list of conditions and the following disclaimer.
662 * 2. Redistributions in binary form must reproduce the above copyright
663 * notice, this list of conditions and the following disclaimer in the
664 * documentation and/or other materials provided with the distribution.
665 * 3. All advertising materials mentioning features or use of this software
666 * must display the following acknowledgment:
667 * This product includes software developed by the University of
668 * California, Berkeley and its contributors.
669 * 4. Neither the name of the University nor the names of its contributors
670 * may be used to endorse or promote products derived from this software
671 * without specific prior written permission.
672 *
673 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
674 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
675 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
676 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
677 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
678 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
679 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
680 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
681 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
682 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
683 * SUCH DAMAGE.
684 */
685
686#define PBUFSIZ512 512 /* max length of filename path */
687#define PVECSIZ32 32 /* max number of names in path */
688#define TBUFSIZ(2048*2) (2048*2)
689
690static char *tbuf;
691
692/*
693 * On entry, srcp points to a non ':' character which is the beginning of the
694 * token, if any. We'll try to return a string that doesn't end with a ':'.
695 */
696static char *
697get_tc_token(char **srcp, int *endp)
698{
699 int ch;
700 bool_Bool found = FALSE0;
701 char *s, *base;
702 char *tok = 0;
703
704 *endp = TRUE1;
705 for (s = base = *srcp; *s != '\0';) {
706 ch = *s++;
707 if (ch == '\\') {
708 if (*s == '\0') {
709 break;
710 } else if (*s++ == '\n') {
711 while (isspace(UChar(*s)((unsigned char)(*s))))
712 s++;
713 } else {
714 found = TRUE1;
715 }
716 } else if (ch == ':') {
717 if (found) {
718 tok = base;
719 s[-1] = '\0';
720 *srcp = s;
721 *endp = FALSE0;
722 break;
723 }
724 base = s;
725 } else if (isgraph(UChar(ch)((unsigned char)(ch)))) {
726 found = TRUE1;
727 }
728 }
729
730 /* malformed entry may end without a ':' */
731 if (tok == 0 && found) {
732 tok = base;
733 }
734
735 return tok;
736}
737
738static char *
739copy_tc_token(char *dst, const char *src, size_t len)
740{
741 int ch;
742
743 while ((ch = *src++) != '\0') {
744 if (ch == '\\' && *src == '\n') {
745 while (isspace(UChar(*src)((unsigned char)(*src))))
746 src++;
747 continue;
748 }
749 if (--len == 0) {
750 dst = 0;
751 break;
752 }
753 *dst++ = ch;
754 }
755 return dst;
756}
757
758/*
759 * Get an entry for terminal name in buffer bp from the termcap file.
760 */
761static int
762_nc_tgetent(char *bp, char **sourcename, int *lineno, const char *name)
763{
764 static char *the_source;
765 register char *p;
766 register char *cp;
767 char *dummy = NULL((void*)0);
768 char **fname;
769 char *home;
770 int i;
771 char pathbuf[PBUFSIZ512]; /* holds raw path of filenames */
772 char *pathvec[PVECSIZ32]; /* to point to names in pathbuf */
773 char **pvec; /* holds usable tail of path vector */
774 NCURSES_CONSTconst char *termpath;
775 string_desc desc;
776
777 fname = pathvec;
778 pvec = pathvec;
Value stored to 'pvec' is never read
779 tbuf = bp;
780 p = pathbuf;
781 cp = use_terminfo_vars()(!issetugid())? getenv("TERMCAP") : NULL((void*)0);
782
783 /*
784 * TERMCAP can have one of two things in it. It can be the name of a file
785 * to use instead of /etc/termcap. In this case it better start with a
786 * "/". Or it can be an entry to use so we don't have to read the file.
787 * In this case it has to already have the newlines crunched out. If
788 * TERMCAP does not hold a file name then a path of names is searched
789 * instead. The path is found in the TERMPATH variable, or becomes
790 * "$HOME/.termcap /etc/termcap" if no TERMPATH exists.
791 */
792 _nc_str_init(&desc, pathbuf, sizeof(pathbuf));
793 if (cp == NULL((void*)0)) {
794 _nc_safe_strcpy(&desc, get_termpath());
795 } else if (!_nc_is_abs_path(cp)) { /* TERMCAP holds an entry */
796 if ((termpath = get_termpath()) != 0) {
797 _nc_safe_strcat(&desc, termpath);
798 } else {
799 char temp[PBUFSIZ512];
800 temp[0] = 0;
801 if ((home = getenv("HOME")) != 0 && *home != '\0'
802 && strchr(home, ' ') == 0
803 && strlen(home) < sizeof(temp) - 10) { /* setup path */
804 snprintf(temp, sizeof(temp), "%s/", home); /* $HOME first */
805 }
806 /* if no $HOME look in current directory */
807 strlcat(temp, ".termcap", sizeof temp);
808 _nc_safe_strcat(&desc, temp);
809 _nc_safe_strcat(&desc, " ");
810 _nc_safe_strcat(&desc, get_termpath());
811 }
812 } else { /* user-defined name in TERMCAP */
813 _nc_safe_strcat(&desc, cp); /* still can be tokenized */
814 }
815
816 *fname++ = pathbuf; /* tokenize path into vector of names */
817 while (*++p) {
818 if (*p == ' ' || *p == NCURSES_PATHSEP':') {
819 *p = '\0';
820 while (*++p)
821 if (*p != ' ' && *p != NCURSES_PATHSEP':')
822 break;
823 if (*p == '\0')
824 break;
825 *fname++ = p;
826 if (fname >= pathvec + PVECSIZ32) {
827 fname--;
828 break;
829 }
830 }
831 }
832 *fname = 0; /* mark end of vector */
833 if (_nc_is_abs_path(cp)) {
834 if (_nc_cgetsetcgetset(cp) < 0) {
835 return (TC_SYS_ERR-2);
836 }
837 }
838
839 i = _nc_cgetent(&dummy, lineno, pathvec, name)cgetent(&dummy, pathvec, name);
840
841 /* ncurses' termcap-parsing routines cannot handle multiple adjacent
842 * empty fields, and mistakenly use the last valid cap entry instead of
843 * the first (breaks tc= includes)
844 */
845 if (i >= 0) {
846 char *pd, *ps, *tok;
847 int endflag = FALSE0;
848 char *list[1023];
849 size_t n, count = 0;
850
851 pd = bp;
852 ps = dummy;
853 while (!endflag && (tok = get_tc_token(&ps, &endflag)) != 0) {
854 bool_Bool ignore = FALSE0;
855
856 for (n = 1; n < count; n++) {
857 char *s = list[n];
858 if (s[0] == tok[0]
859 && s[1] == tok[1]) {
860 ignore = TRUE1;
861 break;
862 }
863 }
864 if (ignore != TRUE1) {
865 list[count++] = tok;
866 pd = copy_tc_token(pd, tok, TBUFSIZ(2048*2) - (2 + pd - bp));
867 if (pd == 0) {
868 i = -1;
869 break;
870 }
871 *pd++ = ':';
872 *pd = '\0';
873 }
874 }
875 }
876
877 FreeIfNeeded(dummy)if ((dummy) != 0) free(dummy);
878 FreeIfNeeded(the_source)if ((the_source) != 0) free(the_source);
879 the_source = 0;
880
881 /* This is not related to the BSD cgetent(), but to fake up a suitable
882 * filename for ncurses' error reporting. (If we are not using BSD
883 * cgetent, then it is the actual filename).
884 */
885 if (i >= 0) {
886#if HAVE_BSD_CGETENT1
887 char temp[PATH_MAX1024];
888
889 _nc_str_init(&desc, temp, sizeof(temp));
890 _nc_safe_strcpy(&desc, pathvec[i]);
891 _nc_safe_strcat(&desc, ".db");
892 if (_nc_access(temp, R_OK0x04) == 0) {
893 _nc_safe_strcpy(&desc, pathvec[i]);
894 }
895 if ((the_source = strdup(temp)) != 0)
896 *sourcename = the_source;
897#else
898 if ((the_source = strdup(pathvec[i])) != 0)
899 *sourcename = the_source;
900#endif
901 }
902
903 return (i);
904}
905#endif /* USE_BSD_TGETENT */
906#endif /* USE_GETCAP */
907
908#define MAXPATHS32 32
909
910/*
911 * Add a filename to the list in 'termpaths[]', checking that we really have
912 * a right to open the file.
913 */
914#if !USE_GETCAP1
915static int
916add_tc(char *termpaths[], char *path, int count)
917{
918 char *save = strchr(path, NCURSES_PATHSEP':');
919 if (save != 0)
920 *save = '\0';
921 if (count < MAXPATHS32
922 && _nc_access(path, R_OK0x04) == 0) {
923 termpaths[count++] = path;
924 T(("Adding termpath %s", path));
925 }
926 termpaths[count] = 0;
927 if (save != 0)
928 *save = NCURSES_PATHSEP':';
929 return count;
930}
931#define ADD_TC(path, count) filecount = add_tc(termpaths, path, count)
932#endif /* !USE_GETCAP */
933
934NCURSES_EXPORT(int)int
935_nc_read_termcap_entry(const char *const tn, TERMTYPE *const tp)
936{
937 int found = TGETENT_NO0;
938 ENTRY *ep;
939#if USE_GETCAP_CACHE0
940 char cwd_buf[PATH_MAX1024];
941#endif
942#if USE_GETCAP1
943 char *p, tc[TBUFSIZ(2048*2)];
944 int status;
945 static char *source;
946 static int lineno;
947
948 T(("read termcap entry for %s", tn));
949
950 if (strlen(tn) == 0
951 || strcmp(tn, ".") == 0
952 || strcmp(tn, "..") == 0
953 || _nc_pathlast(tn) != 0) {
954 T(("illegal or missing entry name '%s'", tn));
955 return TGETENT_NO0;
956 }
957
958 if (use_terminfo_vars()(!issetugid()) && (p = getenv("TERMCAP")) != 0
959 && !_nc_is_abs_path(p) && _nc_name_match(p, tn, "|:")) {
960 /* TERMCAP holds a termcap entry */
961 strncpy(tc, p, sizeof(tc) - 1);
962 tc[sizeof(tc) - 1] = '\0';
963 _nc_set_source("TERMCAP");
964 } else {
965 /* we're using getcap(3) */
966 if ((status = _nc_tgetent(tc, &source, &lineno, tn)) < 0)
967 return (status == TC_NOT_FOUND-1 ? TGETENT_NO0 : TGETENT_ERR-1);
968
969 _nc_curr_line = lineno;
970 _nc_set_source(source);
971 }
972 _nc_read_entry_source((FILE *) 0, tc, FALSE0, FALSE0, NULLHOOK(_Bool(*)(ENTRY *))0);
973#else
974 /*
975 * Here is what the 4.4BSD termcap(3) page prescribes:
976 *
977 * It will look in the environment for a TERMCAP variable. If found, and
978 * the value does not begin with a slash, and the terminal type name is the
979 * same as the environment string TERM, the TERMCAP string is used instead
980 * of reading a termcap file. If it does begin with a slash, the string is
981 * used as a path name of the termcap file to search. If TERMCAP does not
982 * begin with a slash and name is different from TERM, tgetent() searches
983 * the files $HOME/.termcap and /usr/share/misc/termcap, in that order,
984 * unless the environment variable TERMPATH exists, in which case it
985 * specifies a list of file pathnames (separated by spaces or colons) to be
986 * searched instead.
987 *
988 * It goes on to state:
989 *
990 * Whenever multiple files are searched and a tc field occurs in the
991 * requested entry, the entry it names must be found in the same file or
992 * one of the succeeding files.
993 *
994 * However, this restriction is relaxed in ncurses; tc references to
995 * previous files are permitted.
996 *
997 * This routine returns 1 if an entry is found, 0 if not found, and -1 if
998 * the database is not accessible.
999 */
1000 FILE *fp;
1001 char *tc, *termpaths[MAXPATHS32];
1002 int filecount = 0;
1003 int j, k;
1004 bool_Bool use_buffer = FALSE0;
1005 bool_Bool normal = TRUE1;
1006 char tc_buf[1024];
1007 char pathbuf[PATH_MAX1024];
1008 char *copied = 0;
1009 char *cp;
1010 struct stat test_stat[MAXPATHS32];
1011
1012 termpaths[filecount] = 0;
1013 if (use_terminfo_vars()(!issetugid()) && (tc = getenv("TERMCAP")) != 0) {
1014 if (_nc_is_abs_path(tc)) { /* interpret as a filename */
1015 ADD_TC(tc, 0);
1016 normal = FALSE0;
1017 } else if (_nc_name_match(tc, tn, "|:")) { /* treat as a capability file */
1018 use_buffer = TRUE1;
1019 (void) snprintf(tc_buf, sizeof(tc_buf), "%.*s\n", (int) sizeof(tc_buf) - 2, tc);
1020 normal = FALSE0;
1021 }
1022 }
1023
1024 if (normal) { /* normal case */
1025 char envhome[PATH_MAX1024], *h;
1026
1027 copied = strdup(get_termpath());
1028 for (cp = copied; *cp; cp++) {
1029 if (*cp == NCURSES_PATHSEP':')
1030 *cp = '\0';
1031 else if (cp == copied || cp[-1] == '\0') {
1032 ADD_TC(cp, filecount);
1033 }
1034 }
1035
1036#define PRIVATE_CAP "%s/.termcap"
1037
1038 if (use_terminfo_vars()(!issetugid()) && (h = getenv("HOME")) != NULL((void*)0) && *h != '\0'
1039 && (strlen(h) + sizeof(PRIVATE_CAP)) < PATH_MAX1024) {
1040 /* user's .termcap, if any, should override it */
1041 (void) strlcpy(envhome, h, sizeof(envhome);
1042 (void) snprintf(pathbuf, sizeof(pathbuf), PRIVATE_CAP, envhome);
1043 ADD_TC(pathbuf, filecount);
1044 }
1045 }
1046
1047 /*
1048 * Probably /etc/termcap is a symlink to /usr/share/misc/termcap.
1049 * Avoid reading the same file twice.
1050 */
1051#if HAVE_LINK1
1052 for (j = 0; j < filecount; j++) {
1053 bool_Bool omit = FALSE0;
1054 if (stat(termpaths[j], &test_stat[j]) != 0
1055 || (test_stat[j].st_mode & S_IFMT0170000) != S_IFREG0100000) {
1056 omit = TRUE1;
1057 } else {
1058 for (k = 0; k < j; k++) {
1059 if (test_stat[k].st_dev == test_stat[j].st_dev
1060 && test_stat[k].st_ino == test_stat[j].st_ino) {
1061 omit = TRUE1;
1062 break;
1063 }
1064 }
1065 }
1066 if (omit) {
1067 T(("Path %s is a duplicate", termpaths[j]));
1068 for (k = j + 1; k < filecount; k++) {
1069 termpaths[k - 1] = termpaths[k];
1070 test_stat[k - 1] = test_stat[k];
1071 }
1072 --filecount;
1073 --j;
1074 }
1075 }
1076#endif
1077
1078 /* parse the sources */
1079 if (use_buffer) {
1080 _nc_set_source("TERMCAP");
1081
1082 /*
1083 * We don't suppress warning messages here. The presumption is
1084 * that since it's just a single entry, they won't be a pain.
1085 */
1086 _nc_read_entry_source((FILE *) 0, tc_buf, FALSE0, FALSE0, NULLHOOK(_Bool(*)(ENTRY *))0);
1087 } else {
1088 int i;
1089
1090 for (i = 0; i < filecount; i++) {
1091
1092 T(("Looking for %s in %s", tn, termpaths[i]));
1093 if (_nc_access(termpaths[i], R_OK0x04) == 0
1094 && (fp = fopen(termpaths[i], "r")) != (FILE *) 0) {
1095 _nc_set_source(termpaths[i]);
1096
1097 /*
1098 * Suppress warning messages. Otherwise you get 400 lines of
1099 * crap from archaic termcap files as ncurses complains about
1100 * all the obsolete capabilities.
1101 */
1102 _nc_read_entry_source(fp, (char *) 0, FALSE0, TRUE1, NULLHOOK(_Bool(*)(ENTRY *))0);
1103
1104 (void) fclose(fp);
1105 }
1106 }
1107 }
1108 if (copied != 0)
1109 free(copied);
1110#endif /* USE_GETCAP */
1111
1112 if (_nc_head == 0)
1113 return (TGETENT_ERR-1);
1114
1115 /* resolve all use references */
1116 _nc_resolve_uses2(TRUE1, FALSE0);
1117
1118 /* find a terminal matching tn, if we can */
1119#if USE_GETCAP_CACHE0
1120 if (getcwd(cwd_buf, sizeof(cwd_buf)) != 0) {
1121 _nc_set_writedir((char *) 0); /* note: this does a chdir */
1122#endif
1123 for_entry_list(ep)for (ep = _nc_head; ep; ep = ep->next) {
1124 if (_nc_name_match(ep->tterm.term_names, tn, "|:")) {
1125 /*
1126 * Make a local copy of the terminal capabilities, delinked
1127 * from the list.
1128 */
1129 *tp = ep->tterm;
1130 _nc_delink_entry(_nc_head, &(ep->tterm));
1131 free(ep);
1132
1133 /*
1134 * OK, now try to write the type to user's terminfo directory.
1135 * Next time he loads this, it will come through terminfo.
1136 *
1137 * Advantage: Second and subsequent fetches of this entry will
1138 * be very fast.
1139 *
1140 * Disadvantage: After the first time a termcap type is loaded
1141 * by its user, editing it in the /etc/termcap file, or in
1142 * TERMCAP, or in a local ~/.termcap, will be ineffective
1143 * unless the terminfo entry is explicitly removed.
1144 */
1145#if USE_GETCAP_CACHE0
1146 (void) _nc_write_entry(tp);
1147#endif
1148 found = TGETENT_YES1;
1149 break;
1150 }
1151 }
1152#if USE_GETCAP_CACHE0
1153 chdir(cwd_buf);
1154 }
1155#endif
1156
1157 return (found);
1158}
1159#else
1160extern
1161NCURSES_EXPORT(void)void
1162_nc_read_termcap(void);
1163NCURSES_EXPORT(void)void
1164_nc_read_termcap(void)
1165{
1166}
1167#endif /* PURE_TERMINFO */