2 /***************************************************************************
4 ****************************************************************************
5 * ncurses is copyright (C) 1992-1995 *
7 * zmbenhal@netcom.com *
9 * esr@snark.thyrsus.com *
11 * Permission is hereby granted to reproduce and distribute ncurses *
12 * by any means and for any fee, whether alone or as part of a *
13 * larger distribution, in source or in binary form, PROVIDED *
14 * this notice is included with any such distribution, and is not *
15 * removed from any of its header files. Mention of ncurses in any *
16 * applications linked with it is highly appreciated. *
18 * ncurses comes AS IS with no warranty, implied or expressed. *
20 ***************************************************************************/
24 * Termcap compatibility support
26 * If your OS integrator didn't install a terminfo database, you can call
27 * _nc_read_termcap_entry() to support reading and translating capabilities
28 * from the system termcap file. This is a kludge; it will bulk up and slow
29 * down every program that uses ncurses, and translated termcap entries cannot
30 * use full terminfo capabilities. Don't use it unless you absolutely have to;
31 * instead, get your system people to run tic(1) from root on the terminfo
32 * master included with ncurses to translate it into a terminfo database.
34 * If USE_GETCAP is enabled, we use what is effectively a copy of the 4.4BSD
35 * getcap code to fetch entries. There are disadvantages to this; mainly that
36 * getcap(3) does its own resolution, meaning that entries read in in this way
37 * can't reference the terminfo tree. The only thing it buys is faster startup
38 * time, getcap(3) is much faster than our tic parser.
41 #include <curses.priv.h>
45 #include <term_entry.h>
51 MODULE_ID("$Id: read_termcap.c,v 1.21 1996/12/21 14:24:06 tom Exp $")
54 #define TC_UNRESOLVED -1
55 #define TC_NOT_FOUND -2
57 #define TC_REF_LOOP -4
61 * Copyright (c) 1980, 1993
62 * The Regents of the University of California. All rights reserved.
64 * Redistribution and use in source and binary forms, with or without
65 * modification, are permitted provided that the following conditions
67 * 1. Redistributions of source code must retain the above copyright
68 * notice, this list of conditions and the following disclaimer.
69 * 2. Redistributions in binary form must reproduce the above copyright
70 * notice, this list of conditions and the following disclaimer in the
71 * documentation and/or other materials provided with the distribution.
72 * 3. All advertising materials mentioning features or use of this software
73 * must display the following acknowledgment:
74 * This product includes software developed by the University of
75 * California, Berkeley and its contributors.
76 * 4. Neither the name of the University nor the names of its contributors
77 * may be used to endorse or promote products derived from this software
78 * without specific prior written permission.
80 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
81 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
82 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
83 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
84 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
85 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
86 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
87 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
88 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
89 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
93 /* static char sccsid[] = "@(#)termcap.c 8.1 (Berkeley) 6/4/93" */
95 #define PBUFSIZ 512 /* max length of filename path */
96 #define PVECSIZ 32 /* max number of names in path */
98 static char *_nc_cgetcap(char *, const char *, int);
99 static int _nc_cgetent(char **, int *, char **, const char *);
100 static int _nc_cgetmatch(char *, const char *);
101 static int _nc_cgetset(const char *);
102 static int _nc_getent(char **, unsigned int *, int *, int, char **, int, const char *, int, char *);
103 static int _nc_nfcmp(const char *, char *);
104 static int _nc_tgetent(char *, char **, int *, const char *);
107 * termcap - routines for dealing with the terminal capability data base
109 * BUG: Should use a "last" pointer in tbuf, so that searching
110 * for capabilities alphabetically would not be a n**2/2
111 * process when large numbers of capabilities are given.
112 * Note: If we add a last pointer now we will screw up the
113 * tc capability. We really should compile termcap.
115 * Essentially all the work here is scanning and decoding escapes in string
116 * capabilities. We don't use stdio because the editor doesn't, and because
117 * living w/o it is not hard.
120 static char *tbuf; /* termcap buffer */
123 * Get an entry for terminal name in buffer bp from the termcap file.
126 _nc_tgetent(char *bp, char **sourcename, int *lineno, const char *name)
128 static char *the_source;
136 char pathbuf[PBUFSIZ]; /* holds raw path of filenames */
137 char *pathvec[PVECSIZ]; /* to point to names in pathbuf */
138 char **pvec; /* holds usable tail of path vector */
145 cp = getenv("TERMCAP");
148 * TERMCAP can have one of two things in it. It can be the name of a
149 * file to use instead of /etc/termcap. In this case it better start
150 * with a "/". Or it can be an entry to use so we don't have to read
151 * the file. In this case it has to already have the newlines crunched
152 * out. If TERMCAP does not hold a file name then a path of names is
153 * searched instead. The path is found in the TERMPATH variable, or
154 * becomes "$HOME/.termcap /etc/termcap" if no TERMPATH exists.
156 if (!cp || *cp != '/') { /* no TERMCAP or it holds an entry */
157 if ((termpath = getenv("TERMPATH")) != 0) {
158 strncpy(pathbuf, termpath, sizeof(pathbuf)-1);
160 if ((home = getenv("HOME")) != 0) { /* setup path */
161 p += strlen(home); /* path, looking in */
162 strcpy(pathbuf, home); /* $HOME first */
164 } /* if no $HOME look in current directory */
165 #define MY_PATH_DEF ".termcap /etc/termcap /usr/share/misc/termcap"
166 strncpy(p, MY_PATH_DEF, PBUFSIZ - (p - pathbuf));
169 else /* user-defined name in TERMCAP */
170 strncpy(pathbuf, cp, PBUFSIZ); /* still can be tokenized */
172 *fname++ = pathbuf; /* tokenize path into vector of names */
174 if (*p == ' ' || *p == ':') {
177 if (*p != ' ' && *p != ':')
182 if (fname >= pathvec + PVECSIZ) {
188 *fname = 0; /* mark end of vector */
189 if (cp && *cp && *cp != '/') {
190 if (_nc_cgetset(cp) < 0)
194 i = _nc_cgetent(&dummy, lineno, pathvec, name);
200 FreeIfNeeded(the_source);
204 the_source = malloc(strlen(pathvec[i]) + 1);
206 *sourcename = strcpy(the_source, pathvec[i]);
213 * Copyright (c) 1992, 1993
214 * The Regents of the University of California. All rights reserved.
216 * This code is derived from software contributed to Berkeley by
217 * Casey Leedom of Lawrence Livermore National Laboratory.
219 * Redistribution and use in source and binary forms, with or without
220 * modification, are permitted provided that the following conditions
222 * 1. Redistributions of source code must retain the above copyright
223 * notice, this list of conditions and the following disclaimer.
224 * 2. Redistributions in binary form must reproduce the above copyright
225 * notice, this list of conditions and the following disclaimer in the
226 * documentation and/or other materials provided with the distribution.
227 * 3. All advertising materials mentioning features or use of this software
228 * must display the following acknowledgment:
229 * This product includes software developed by the University of
230 * California, Berkeley and its contributors.
231 * 4. Neither the name of the University nor the names of its contributors
232 * may be used to endorse or promote products derived from this software
233 * without specific prior written permission.
235 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
236 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
237 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
238 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
239 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
240 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
241 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
242 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
243 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
244 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
248 /* static char sccsid[] = "@(#)getcap.c 8.3 (Berkeley) 3/25/94"; */
252 #define ESC ('[' & 037) /* ASCII ESC */
253 #define MAX_RECURSION 32 /* maximum getent recursion */
254 #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */
256 #define RECOK (char)0
257 #define TCERR (char)1
258 #define SHADOW (char)2
260 static size_t topreclen; /* toprec length */
261 static char *toprec; /* Additional record specified by cgetset() */
262 static int gottoprec; /* Flag indicating retrieval of toprecord */
265 * Cgetset() allows the addition of a user specified buffer to be added to the
266 * database array, in effect "pushing" the buffer on top of the virtual
267 * database. 0 is returned on success, -1 on failure.
270 _nc_cgetset(const char *ent)
273 FreeIfNeeded(toprec);
278 topreclen = strlen(ent);
279 if ((toprec = malloc (topreclen + 1)) == 0) {
284 (void)strcpy(toprec, ent);
289 * Cgetcap searches the capability record buf for the capability cap with type
290 * `type'. A pointer to the value of cap is returned on success, 0 if the
291 * requested capability couldn't be found.
293 * Specifying a type of ':' means that nothing should follow cap (:cap:). In
294 * this case a pointer to the terminating ':' or NUL will be returned if cap is
297 * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
301 _nc_cgetcap(char *buf, const char *cap, int type)
303 register const char *cp;
309 * Skip past the current capability field - it's either the
310 * name field if this is the first time through the loop, or
311 * the remainder of a field whose name failed to match cap.
316 else if (*bp++ == ':')
321 * Try to match (cap, type) in buf.
323 for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
330 if (*bp != '\0' && *bp != ':')
337 return (*bp == '@' ? 0 : bp);
343 * Cgetent extracts the capability record name from the NULL terminated file
344 * array db_array and returns a pointer to a malloc'd copy of it in buf. Buf
345 * must be retained through all subsequent calls to cgetcap, cgetnum, cgetflag,
346 * and cgetstr, but may then be freed.
350 * positive # on success (i.e., the index in db_array)
351 * TC_UNRESOLVED if we had too many recurrences to resolve
352 * TC_NOT_FOUND if the requested record couldn't be found
353 * TC_SYS_ERR if a system error was encountered (e.g.,couldn't open a file)
354 * TC_REF_LOOP if a potential reference loop is detected
357 _nc_cgetent(char **buf, int *oline, char **db_array, const char *name)
361 return (_nc_getent(buf, &dummy, oline, 0, db_array, -1, name, 0, 0));
365 * Getent implements the functions of cgetent. If fd is non-negative,
366 * *db_array has already been opened and fd is the open file descriptor. We
367 * do this to save time and avoid using up file descriptors for tc=
370 * Getent returns the same success/failure codes as cgetent. On success, a
371 * pointer to a malloc'd capability record with all tc= capabilities fully
372 * expanded and its length (not including trailing ASCII NUL) are left in
376 * + Allocate memory incrementally as needed in chunks of size BFRAG
377 * for capability buffer.
378 * + Recurse for each tc=name and interpolate result. Stop when all
379 * names interpolated, a name can't be found, or depth exceeds
384 char **cap, /* termcap-content */
385 unsigned int *len, /* length, needed for recursion */
386 int *beginning, /* line-number at match */
387 int in_array, /* index in 'db_array[] */
388 char **db_array, /* list of files to search */
394 register char *r_end, *rp;
402 * Return with ``loop detected'' error if we've recurred more than
403 * MAX_RECURSION times.
405 if (depth > MAX_RECURSION)
406 return (TC_REF_LOOP);
409 * Check if we have a top record from cgetset().
411 if (depth == 0 && toprec != 0 && _nc_cgetmatch(toprec, name) == 0) {
412 if ((record = malloc (topreclen + BFRAG)) == 0) {
416 (void)strcpy(record, toprec);
417 rp = record + topreclen + 1;
424 * Allocate first chunk of memory.
426 if ((record = malloc(BFRAG)) == 0) {
430 rp = r_end = record + BFRAG;
434 * Loop through database array until finding the record.
436 for (current = in_array; db_array[current] != 0; current++) {
440 * Open database if not already open.
443 (void)lseek(fd, (off_t)0, SEEK_SET);
445 fd = open(db_array[current], O_RDONLY, 0);
447 /* No error on unfound file. */
458 * Find the requested capability record ...
462 register char *b_end = buf;
463 register char *bp = buf;
468 * There is always room for one more character in record.
469 * R_end always points just past end of record.
470 * Rp always points just past last character in record.
471 * B_end always points just past last character in buf.
472 * Bp always points at next character in buf.
476 int first = lineno + 1;
479 * Read in a line implementing (\, newline)
487 n = read(fd, buf, sizeof(buf));
506 if (rp == record || *(rp-1) != '\\')
512 * Enforce loop invariant: if no room
513 * left in record buffer, try to get
521 newsize = r_end - record + BFRAG;
522 record = realloc(record, newsize);
529 r_end = record + newsize;
533 /* loop invariant lets us do this */
537 * If encountered eof check next file.
543 * Toss blank lines and comments.
545 if (*record == '\0' || *record == '#')
549 * See if this is the record we want ...
551 if (_nc_cgetmatch(record, name) == 0
553 || !_nc_nfcmp(nfield, record))) {
556 break; /* found it! */
565 return (TC_NOT_FOUND);
569 * Got the capability record, but now we have to expand all tc=name
570 * references in it ...
573 register char *newicap, *s;
574 register int newilen;
576 int diff, iret, tclen, oline;
577 char *icap, *scan, *tc, *tcstart, *tcend;
581 * There is room for one more character in record.
582 * R_end points just past end of record.
583 * Rp points just past last character in record.
584 * Scan points at remainder of record that needs to be
585 * scanned for tc=name constructs.
588 tc_not_resolved = FALSE;
590 if ((tc = _nc_cgetcap(scan, "tc", '=')) == 0)
594 * Find end of tc=name and stomp on the trailing `:'
595 * (if present) so we can use it to call ourselves.
608 iret = _nc_getent(&icap, &ilen, &oline, current, db_array, fd, tc, depth+1, 0);
609 newicap = icap; /* Put into a register. */
611 if (iret != TC_SUCCESS) {
613 if (iret < TC_NOT_FOUND) {
619 if (iret == TC_UNRESOLVED)
620 tc_not_resolved = TRUE;
621 /* couldn't resolve tc */
622 if (iret == TC_NOT_FOUND) {
625 tc_not_resolved = TRUE;
630 /* not interested in name field of tc'ed record */
632 while (*s != '\0' && *s++ != ':')
634 newilen -= s - newicap;
637 /* make sure interpolated record is `:'-terminated */
640 *s = ':'; /* overwrite NUL with : */
645 * Make sure there's enough room to insert the
648 diff = newilen - tclen;
649 if (diff >= r_end - rp) {
650 unsigned int pos, tcpos, tcposend;
654 newsize = r_end - record + diff + BFRAG;
655 tcpos = tcstart - record;
656 tcposend = tcend - record;
657 record = realloc(record, newsize);
665 r_end = record + newsize;
667 tcstart = record + tcpos;
668 tcend = record + tcposend;
672 * Insert tc'ed record into our record.
674 s = tcstart + newilen;
675 memmove(s, tcend, rp - tcend);
676 memmove(tcstart, newicap, newilen);
681 * Start scan on `:' so next cgetcap works properly
682 * (cgetcap always skips first field).
689 * Close file (if we opened it), give back any extra memory, and
690 * return capability, length and success.
694 *len = rp - record - 1; /* don't count NUL */
696 if ((record = realloc(record, (size_t)(rp - record))) == 0) {
704 return (TC_UNRESOLVED);
709 * Cgetmatch will return 0 if name is one of the names of the capability
710 * record buf, -1 if not.
713 _nc_cgetmatch(char *buf, const char *name)
715 register const char *np;
719 * Start search at beginning of record.
724 * Try to match a record name.
729 if (*bp == '|' || *bp == ':' || *bp == '\0')
733 } else if (*bp++ != *np++) {
739 * Match failed, skip to next name in record.
741 bp--; /* a '|' or ':' may have stopped the match */
743 if (*bp == '\0' || *bp == ':')
744 return (-1); /* match failed totally */
745 else if (*bp++ == '|')
746 break; /* found next name */
752 * Compare name field of record.
755 _nc_nfcmp(const char *nf, char *rec)
760 for (cp = rec; *cp != ':'; cp++)
765 ret = strcmp(nf, rec);
770 #endif /* USE_GETCAP */
772 int _nc_read_termcap_entry(const char *const tn, TERMTYPE *const tp)
776 char cwd_buf[PATH_MAX];
779 * Here is what the 4.4BSD termcap(3) page prescribes:
781 * It will look in the environment for a TERMCAP variable. If found,
782 * and the value does not begin with a slash, and the terminal type
783 * name is the same as the environment string TERM, the TERMCAP string
784 * is used instead of reading a termcap file. If it does begin with a
785 * slash, the string is used as a path name of the termcap file to
786 * search. If TERMCAP does not begin with a slash and name is
787 * different from TERM, tgetent() searches the files $HOME/.termcap and
788 * /usr/share/misc/termcap, in that order, unless the environment
789 * variable TERMPATH exists, in which case it specifies a list of file
790 * pathnames (separated by spaces or colons) to be searched instead.
792 * It goes on to state:
794 * Whenever multiple files are searched and a tc field occurs in the
795 * requested entry, the entry it names must be found in the same file
796 * or one of the succeeding files.
798 * However, this restriction is relaxed in ncurses; tc references to
799 * previous files are permitted.
801 * This routine returns 1 if an entry is found, 0 if not found, and -1
802 * if the database is not accessible.
806 char *tc, *termpaths[MAXPATHS];
808 bool use_buffer = FALSE;
810 char pathbuf[PATH_MAX];
812 if ((tc = getenv("TERMCAP")) != 0)
814 if (tc[0] == '/') /* interpret as a filename */
817 termpaths[filecount = 1] = 0;
819 else if (_nc_name_match(tc, tn, "|:")) /* treat as a capability file */
822 (void) sprintf(tc_buf, "%.*s\n", (int)sizeof(tc_buf)-2, tc);
824 else if ((tc = getenv("TERMPATH")) != 0)
828 for (cp = tc; *cp; cp++)
832 else if (cp == tc || cp[-1] == '\0')
834 if (filecount >= MAXPATHS - 1)
837 termpaths[filecount++] = cp;
840 termpaths[filecount] = 0;
843 else /* normal case */
845 char envhome[PATH_MAX], *h;
850 * Probably /etc/termcap is a symlink to /usr/share/misc/termcap.
851 * Avoid reading the same file twice.
853 if (access("/etc/termcap", R_OK) == 0)
854 termpaths[filecount++] = "/etc/termcap";
855 else if (access("/usr/share/misc/termcap", R_OK) == 0)
856 termpaths[filecount++] = "/usr/share/misc/termcap";
858 if ((h = getenv("HOME")) != (char *)NULL)
860 /* user's .termcap, if any, should override it */
861 (void) strncpy(envhome, h, PATH_MAX - 10);
862 envhome[PATH_MAX - 10] = '\0';
863 (void) sprintf(pathbuf, "%s/.termcap", envhome);
864 termpaths[filecount++] = pathbuf;
867 termpaths[filecount] = 0;
870 /* parse the sources */
873 _nc_set_source("TERMCAP");
876 * We don't suppress warning messages here. The presumption is
877 * that since it's just a single entry, they won't be a pain.
879 _nc_read_entry_source((FILE *)0, tc_buf, FALSE, FALSE, NULLHOOK);
883 for (i = 0; i < filecount; i++) {
885 T(("Looking for %s in %s", tn, termpaths[i]));
886 if ((fp = fopen(termpaths[i], "r")) != (FILE *)0)
888 _nc_set_source(termpaths[i]);
891 * Suppress warning messages. Otherwise you
892 * get 400 lines of crap from archaic termcap
893 * files as ncurses complains about all the
894 * obsolete capabilities.
896 _nc_read_entry_source(fp, (char*)0, FALSE, TRUE, NULLHOOK);
907 /* we're using getcap(3) */
908 if (_nc_tgetent(tc, &source, &lineno, tn) <= 0)
911 _nc_curr_line = lineno;
912 _nc_set_source(source);
913 _nc_read_entry_source((FILE *)0, tc, FALSE, FALSE, NULLHOOK);
919 /* resolve all use references */
922 /* find a terminal matching tn, if we can */
923 if (getcwd(cwd_buf, sizeof(cwd_buf)) != 0)
925 _nc_set_writedir((char *)0); /* note: this does a chdir */
927 if (_nc_name_match(ep->tterm.term_names, tn, "|:"))
930 * Make a local copy of the terminal
931 * capabilities. Free all entry storage except
932 * the string table for the loaded type (which
933 * we disconnected from the list by NULLing out
934 * ep->tterm.str_table above).
936 memcpy(tp, &ep->tterm, sizeof(TERMTYPE));
937 ep->tterm.str_table = (char *)0;
940 * OK, now try to write the type to user's
941 * terminfo directory. Next time he loads
942 * this, it will come through terminfo.
944 * Advantage: Second and subsequent fetches of
945 * this entry will be very fast.
947 * Disadvantage: After the first time a
948 * termcap type is loaded by its user, editing
949 * it in the /etc/termcap file, or in TERMCAP,
950 * or in a local ~/.termcap, will be
951 * ineffective unless the terminfo entry is
952 * explicitly removed.
955 (void) _nc_write_entry(tp);
964 _nc_free_entries(_nc_head);