1 /****************************************************************************
2 * Copyright 2018-2022,2023 Thomas E. Dickey *
3 * Copyright 2006-2016,2017 Free Software Foundation, Inc. *
5 * Permission is hereby granted, free of charge, to any person obtaining a *
6 * copy of this software and associated documentation files (the *
7 * "Software"), to deal in the Software without restriction, including *
8 * without limitation the rights to use, copy, modify, merge, publish, *
9 * distribute, distribute with modifications, sublicense, and/or sell *
10 * copies of the Software, and to permit persons to whom the Software is *
11 * furnished to do so, subject to the following conditions: *
13 * The above copyright notice and this permission notice shall be included *
14 * in all copies or substantial portions of the Software. *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
19 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
22 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
24 * Except as contained in this notice, the name(s) of the above copyright *
25 * holders shall not be used in advertising or otherwise to promote the *
26 * sale, use or other dealings in this Software without prior written *
28 ****************************************************************************/
30 /****************************************************************************
31 * Author: Thomas E. Dickey *
32 ****************************************************************************/
35 * Iterators for terminal databases.
38 #include <curses.priv.h>
44 #include <hashed_db.h>
47 MODULE_ID("$Id: db_iterator.c,v 1.50 2023/06/24 21:52:32 tom Exp $")
49 #define HaveTicDirectory _nc_globals.have_tic_directory
50 #define KeepTicDirectory _nc_globals.keep_tic_directory
51 #define TicDirectory _nc_globals.tic_directory
52 #define my_blob _nc_globals.dbd_blob
53 #define my_list _nc_globals.dbd_list
54 #define my_size _nc_globals.dbd_size
55 #define my_time _nc_globals.dbd_time
56 #define my_vars _nc_globals.dbd_vars
59 add_to_blob(const char *text, size_t limit)
64 char *last = my_blob + strlen(my_blob);
66 *last++ = NCURSES_PATHSEP;
67 _nc_STRCPY(last, text, limit);
72 check_existence(const char *name, struct stat *sb)
76 if (quick_prefix(name)) {
78 } else if (stat(name, sb) == 0
79 && (S_ISDIR(sb->st_mode)
80 || (S_ISREG(sb->st_mode) && sb->st_size))) {
84 else if (strlen(name) < PATH_MAX - sizeof(DBM_SUFFIX)) {
86 _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp)) "%s%s", name, DBM_SUFFIX);
87 if (stat(temp, sb) == 0 && S_ISREG(sb->st_mode) && sb->st_size) {
96 * Trim newlines (and backslashes preceding those) and tab characters to
97 * help simplify scripting of the quick-dump feature. Leave spaces and
98 * other backslashes alone.
101 trim_formatting(char *source)
103 char *target = source;
106 while ((ch = *source++) != '\0') {
107 if (ch == '\\' && *source == '\n')
109 if (ch == '\n' || ch == '\t')
117 * Store the latest value of an environment variable in my_vars[] so we can
118 * detect if one changes, invalidating the cached search-list.
121 update_getenv(const char *name, DBDIRS which)
125 if (which < dbdLAST) {
127 char *cached_value = my_vars[which].value;
130 if ((value = getenv(name)) != 0) {
131 value = strdup(value);
133 same_value = ((value == 0 && cached_value == 0) ||
136 strcmp(value, cached_value) == 0));
138 /* Set variable name to enable checks in cache_expired(). */
139 my_vars[which].name = name;
142 FreeIfNeeded(my_vars[which].value);
143 my_vars[which].value = value;
152 #if NCURSES_USE_DATABASE || NCURSES_USE_TERMCAP
154 cache_getenv(const char *name, DBDIRS which)
158 (void) update_getenv(name, which);
159 if (which < dbdLAST) {
160 result = my_vars[which].value;
167 * The cache expires if at least a second has passed since the initial lookup,
168 * or if one of the environment variables changed.
170 * Only a few applications use multiple lookups of terminal entries, seems that
171 * aside from bulk I/O such as tic and toe, that leaves interactive programs
172 * which should not be modifying the terminal databases in a way that would
173 * invalidate the search-list.
175 * The "1-second" is to allow for user-directed changes outside the program.
181 time_t now = time((time_t *) 0);
187 for (n = (DBDIRS) 0; n < dbdLAST; ++n) {
188 if (my_vars[n].name != 0
189 && update_getenv(my_vars[n].name, n)) {
201 FreeAndNull(my_blob);
202 FreeAndNull(my_list);
206 update_tic_dir(const char *update)
208 free((char *) TicDirectory);
209 TicDirectory = update;
213 * Record the "official" location of the terminfo directory, according to
214 * the place where we're writing to, or the normal default, if not.
216 NCURSES_EXPORT(const char *)
217 _nc_tic_dir(const char *path)
219 T(("_nc_tic_dir %s", NonNull(path)));
220 if (!KeepTicDirectory) {
222 if (path != TicDirectory)
223 update_tic_dir(strdup(path));
224 HaveTicDirectory = TRUE;
225 } else if (HaveTicDirectory == 0) {
226 if (use_terminfo_vars()) {
228 if ((envp = getenv("TERMINFO")) != 0)
229 return _nc_tic_dir(envp);
233 return TicDirectory ? TicDirectory : TERMINFO;
237 * Special fix to prevent the terminfo directory from being moved after tic
238 * has chdir'd to it. If we let it be changed, then if $TERMINFO has a
239 * relative path, we'll lose track of the actual directory.
242 _nc_keep_tic_dir(const char *path)
245 KeepTicDirectory = TRUE;
254 if (my_blob != 0 && cache_expired()) {
260 * This is a simple iterator which allows the caller to step through the
261 * possible locations for a terminfo directory. ncurses uses this to find
262 * terminfo files to read.
264 NCURSES_EXPORT(const char *)
265 _nc_next_db(DBDIRS * state, int *offset)
270 if ((int) *state < my_size
272 && my_list[*state] != 0) {
273 result = my_list[*state];
279 T(("_nc_next_db %d %s", *state, result));
285 _nc_first_db(DBDIRS * state, int *offset)
287 bool cache_has_expired = FALSE;
291 T((T_CALLED("_nc_first_db")));
293 /* build a blob containing all of the strings we will use for a lookup
296 if (my_blob == 0 || (cache_has_expired = cache_expired())) {
298 const char *values[dbdLAST];
299 struct stat *my_stat;
302 if (cache_has_expired)
305 for (j = 0; j < dbdLAST; ++j)
309 * This is the first item in the list, and is used only when tic is
310 * writing to the database, as a performance improvement.
312 values[dbdTIC] = TicDirectory;
314 #if NCURSES_USE_DATABASE
316 values[dbdCfgList] = TERMINFO_DIRS;
319 values[dbdCfgOnce] = TERMINFO;
323 #if NCURSES_USE_TERMCAP
324 values[dbdCfgList2] = TERMPATH;
327 if (use_terminfo_vars()) {
328 #if NCURSES_USE_DATABASE
329 values[dbdEnvOnce] = cache_getenv("TERMINFO", dbdEnvOnce);
330 values[dbdHome] = _nc_home_terminfo();
331 (void) cache_getenv("HOME", dbdHome);
332 values[dbdEnvList] = cache_getenv("TERMINFO_DIRS", dbdEnvList);
335 #if NCURSES_USE_TERMCAP
336 values[dbdEnvOnce2] = cache_getenv("TERMCAP", dbdEnvOnce2);
337 /* only use $TERMCAP if it is an absolute path */
338 if (values[dbdEnvOnce2] != 0
339 && *values[dbdEnvOnce2] != '/') {
340 values[dbdEnvOnce2] = 0;
342 values[dbdEnvList2] = cache_getenv("TERMPATH", dbdEnvList2);
343 #endif /* NCURSES_USE_TERMCAP */
346 for (j = 0; j < dbdLAST; ++j) {
349 blobsize += 2 + strlen(values[j]);
352 my_blob = malloc(blobsize);
355 for (j = 0; j < dbdLAST; ++j) {
356 add_to_blob(values[j], blobsize);
359 /* Now, build an array which will be pointers to the distinct
360 * strings in the blob.
363 for (j = 0; my_blob[j] != '\0'; ++j) {
364 if (my_blob[j] == NCURSES_PATHSEP)
367 my_list = typeCalloc(char *, blobsize);
368 my_stat = typeCalloc(struct stat, blobsize);
369 if (my_list != 0 && my_stat != 0) {
371 my_list[k++] = my_blob;
372 for (j = 0; my_blob[j] != '\0'; ++j) {
373 if (my_blob[j] == NCURSES_PATHSEP
374 && ((&my_blob[j] - my_list[k - 1]) != 3
375 || !quick_prefix(my_list[k - 1]))) {
377 my_list[k++] = &my_blob[j + 1];
382 * Eliminate duplicates from the list.
384 for (j = 0; my_list[j] != 0; ++j) {
386 if (*my_list[j] == '\0') {
387 char *my_copy = strdup(TERMINFO);
389 my_list[j] = my_copy;
392 trim_formatting(my_list[j]);
393 for (k = 0; k < j; ++k) {
394 if (!strcmp(my_list[j], my_list[k])) {
395 T(("duplicate %s", my_list[j]));
397 while ((my_list[j] = my_list[j + 1]) != 0) {
407 * Eliminate non-existent databases, and those that happen to
408 * be symlinked to another location.
410 for (j = 0; my_list[j] != 0; ++j) {
411 bool found = check_existence(my_list[j], &my_stat[j]);
414 for (k = 0; k < j; ++k) {
415 if (my_stat[j].st_dev == my_stat[k].st_dev
416 && my_stat[j].st_ino == my_stat[k].st_ino) {
424 T(("not found %s", my_list[j]));
426 while ((my_list[k] = my_list[k + 1]) != 0) {
433 my_time = time((time_t *) 0);
435 FreeAndNull(my_blob);
445 _nc_db_iterator_leaks(void)
450 FreeAndNull(my_blob);
452 FreeAndNull(my_list);
453 for (which = 0; (int) which < dbdLAST; ++which) {
454 my_vars[which].name = 0;
455 FreeIfNeeded(my_vars[which].value);
456 my_vars[which].value = 0;
458 update_tic_dir(NULL);