1 /****************************************************************************
2 * Copyright (c) 2006-2011,2012 Free Software Foundation, Inc. *
4 * Permission is hereby granted, free of charge, to any person obtaining a *
5 * copy of this software and associated documentation files (the *
6 * "Software"), to deal in the Software without restriction, including *
7 * without limitation the rights to use, copy, modify, merge, publish, *
8 * distribute, distribute with modifications, sublicense, and/or sell *
9 * copies of the Software, and to permit persons to whom the Software is *
10 * furnished to do so, subject to the following conditions: *
12 * The above copyright notice and this permission notice shall be included *
13 * in all copies or substantial portions of the Software. *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
23 * Except as contained in this notice, the name(s) of the above copyright *
24 * holders shall not be used in advertising or otherwise to promote the *
25 * sale, use or other dealings in this Software without prior written *
27 ****************************************************************************/
29 /****************************************************************************
30 * Author: Thomas E. Dickey *
31 ****************************************************************************/
34 * Iterators for terminal databases.
37 #include <curses.priv.h>
43 #include <hashed_db.h>
46 MODULE_ID("$Id: db_iterator.c,v 1.31 2012/02/22 22:40:24 tom Exp $")
48 #define HaveTicDirectory _nc_globals.have_tic_directory
49 #define KeepTicDirectory _nc_globals.keep_tic_directory
50 #define TicDirectory _nc_globals.tic_directory
51 #define my_blob _nc_globals.dbd_blob
52 #define my_list _nc_globals.dbd_list
53 #define my_size _nc_globals.dbd_size
54 #define my_time _nc_globals.dbd_time
55 #define my_vars _nc_globals.dbd_vars
58 add_to_blob(const char *text, size_t limit)
63 char *last = my_blob + strlen(my_blob);
65 *last++ = NCURSES_PATHSEP;
66 _nc_STRCPY(last, text, limit);
71 check_existence(const char *name, struct stat *sb)
75 if (stat(name, sb) == 0
76 && (S_ISDIR(sb->st_mode) || S_ISREG(sb->st_mode))) {
80 else if (strlen(name) < PATH_MAX - sizeof(DBM_SUFFIX)) {
82 _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp)) "%s%s", name, DBM_SUFFIX);
83 if (stat(temp, sb) == 0 && S_ISREG(sb->st_mode)) {
92 * Store the latest value of an environment variable in my_vars[] so we can
93 * detect if one changes, invalidating the cached search-list.
96 update_getenv(const char *name, DBDIRS which)
99 char *value = getenv(name);
101 if (which < dbdLAST) {
102 if (my_vars[which].name == 0 || strcmp(my_vars[which].name, name)) {
103 FreeIfNeeded(my_vars[which].value);
104 my_vars[which].name = name;
105 my_vars[which].value = value;
107 } else if ((my_vars[which].value != 0) ^ (value != 0)) {
108 FreeIfNeeded(my_vars[which].value);
109 my_vars[which].value = value;
111 } else if (value != 0 && strcmp(value, my_vars[which].value)) {
112 FreeIfNeeded(my_vars[which].value);
113 my_vars[which].value = value;
121 cache_getenv(const char *name, DBDIRS which)
125 (void) update_getenv(name, which);
126 if (which < dbdLAST) {
127 result = my_vars[which].value;
133 * The cache expires if at least a second has passed since the initial lookup,
134 * or if one of the environment variables changed.
136 * Only a few applications use multiple lookups of terminal entries, seems that
137 * aside from bulk I/O such as tic and toe, that leaves interactive programs
138 * which should not be modifying the terminal databases in a way that would
139 * invalidate the search-list.
141 * The "1-second" is to allow for user-directed changes outside the program.
147 time_t now = time((time_t *) 0);
153 for (n = (DBDIRS) 0; n < dbdLAST; ++n) {
154 if (my_vars[n].name != 0
155 && update_getenv(my_vars[n].name, n)) {
167 FreeAndNull(my_blob);
168 FreeAndNull(my_list);
172 * Record the "official" location of the terminfo directory, according to
173 * the place where we're writing to, or the normal default, if not.
175 NCURSES_EXPORT(const char *)
176 _nc_tic_dir(const char *path)
178 T(("_nc_tic_dir %s", NonNull(path)));
179 if (!KeepTicDirectory) {
182 HaveTicDirectory = TRUE;
183 } else if (!HaveTicDirectory && use_terminfo_vars()) {
185 if ((envp = getenv("TERMINFO")) != 0)
186 return _nc_tic_dir(envp);
189 return TicDirectory ? TicDirectory : TERMINFO;
193 * Special fix to prevent the terminfo directory from being moved after tic
194 * has chdir'd to it. If we let it be changed, then if $TERMINFO has a
195 * relative path, we'll lose track of the actual directory.
198 _nc_keep_tic_dir(const char *path)
201 KeepTicDirectory = TRUE;
210 if (my_blob != 0 && cache_expired()) {
216 * This is a simple iterator which allows the caller to step through the
217 * possible locations for a terminfo directory. ncurses uses this to find
218 * terminfo files to read.
220 NCURSES_EXPORT(const char *)
221 _nc_next_db(DBDIRS * state, int *offset)
226 if ((int) *state < my_size
228 && my_list[*state] != 0) {
229 result = my_list[*state];
235 T(("_nc_next_db %d %s", *state, result));
241 _nc_first_db(DBDIRS * state, int *offset)
248 /* build a blob containing all of the strings we will use for a lookup
253 const char *values[dbdLAST];
254 struct stat *my_stat;
257 for (j = 0; j < dbdLAST; ++j)
261 * This is the first item in the list, and is used only when tic is
262 * writing to the database, as a performance improvement.
264 values[dbdTIC] = TicDirectory;
268 values[dbdCfgList] = TERMINFO_DIRS;
271 values[dbdCfgOnce] = TERMINFO;
276 values[dbdCfgList2] = TERMPATH;
279 if (use_terminfo_vars()) {
281 values[dbdEnvOnce] = cache_getenv("TERMINFO", dbdEnvOnce);
282 values[dbdHome] = _nc_home_terminfo();
283 (void) cache_getenv("HOME", dbdHome);
284 values[dbdEnvList] = cache_getenv("TERMINFO_DIRS", dbdEnvList);
288 values[dbdEnvOnce2] = cache_getenv("TERMCAP", dbdEnvOnce2);
289 /* only use $TERMCAP if it is an absolute path */
290 if (values[dbdEnvOnce2] != 0
291 && *values[dbdEnvOnce2] != '/') {
292 values[dbdEnvOnce2] = 0;
294 values[dbdEnvList2] = cache_getenv("TERMPATH", dbdEnvList2);
295 #endif /* USE_TERMCAP */
298 for (j = 0; j < dbdLAST; ++j) {
301 blobsize += 2 + strlen(values[j]);
304 my_blob = malloc(blobsize);
307 for (j = 0; j < dbdLAST; ++j) {
308 add_to_blob(values[j], blobsize);
311 /* Now, build an array which will be pointers to the distinct
312 * strings in the blob.
315 for (j = 0; my_blob[j] != '\0'; ++j) {
316 if (my_blob[j] == NCURSES_PATHSEP)
319 my_list = typeCalloc(char *, blobsize);
320 my_stat = typeCalloc(struct stat, blobsize);
321 if (my_list != 0 && my_stat != 0) {
323 my_list[k++] = my_blob;
324 for (j = 0; my_blob[j] != '\0'; ++j) {
325 if (my_blob[j] == NCURSES_PATHSEP) {
327 my_list[k++] = &my_blob[j + 1];
332 * Eliminate duplicates from the list.
334 for (j = 0; my_list[j] != 0; ++j) {
336 if (*my_list[j] == '\0')
337 my_list[j] = strdup(TERMINFO);
339 for (k = 0; k < j; ++k) {
340 if (!strcmp(my_list[j], my_list[k])) {
342 while ((my_list[j] = my_list[j + 1]) != 0) {
352 * Eliminate non-existent databases, and those that happen to
353 * be symlinked to another location.
355 for (j = 0; my_list[j] != 0; ++j) {
356 bool found = check_existence(my_list[j], &my_stat[j]);
359 for (k = 0; k < j; ++k) {
360 if (my_stat[j].st_dev == my_stat[k].st_dev
361 && my_stat[j].st_ino == my_stat[k].st_ino) {
370 while ((my_list[k] = my_list[k + 1]) != 0) {
377 my_time = time((time_t *) 0);
379 FreeAndNull(my_blob);
388 _nc_db_iterator_leaks(void)
391 FreeAndNull(my_blob);
393 FreeAndNull(my_list);