File: | src/lib/libcurses/tinfo/read_termcap.c |
Warning: | line 778, column 5 Value stored to 'pvec' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | |
63 | MODULE_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 | |
73 | static NCURSES_CONSTconst char * |
74 | get_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 |
92 | static int _nc_cgetmatchcgetmatch(char *, const char *); |
93 | static int _nc_getent(char **, unsigned *, int *, int, char **, int, const char |
94 | *, int, char *); |
95 | static 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 | |
133 | static size_t topreclen; /* toprec length */ |
134 | static char *toprec; /* Additional record specified by cgetset() */ |
135 | static 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 | */ |
142 | static 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 | */ |
173 | static 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 | */ |
229 | static 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)) |
256 | static 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 | */ |
585 | static 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 | */ |
627 | static 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 | |
690 | static 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 | */ |
696 | static char * |
697 | get_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 | |
738 | static char * |
739 | copy_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 | */ |
761 | static 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 |
915 | static int |
916 | add_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 | |
934 | NCURSES_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 |
1160 | extern |
1161 | NCURSES_EXPORT(void)void |
1162 | _nc_read_termcap(void); |
1163 | NCURSES_EXPORT(void)void |
1164 | _nc_read_termcap(void) |
1165 | { |
1166 | } |
1167 | #endif /* PURE_TERMINFO */ |